Merge branch 'master' into from_upstream
Conflicts: .github/workflows/snapshot.yml .gitmodules src/flash/nor/drivers.c src/helper/jep106.inc src/rtos/hwthread.c src/target/riscv/riscv.c src/target/target.c Change-Id: I62f65e10d15dcda4c405d4042cce1d96f8e1680a
This commit is contained in:
commit
11b8110443
|
@ -1,3 +1,3 @@
|
|||
[submodule "jimtcl"]
|
||||
path = jimtcl
|
||||
url = https://github.com/msteveb/jimtcl
|
||||
url = https://github.com/msteveb/jimtcl.git
|
||||
|
|
|
@ -43,13 +43,14 @@ if INTERNAL_JIMTCL
|
|||
AM_CPPFLAGS += -I$(top_srcdir)/jimtcl \
|
||||
-I$(top_builddir)/jimtcl
|
||||
endif
|
||||
EXTRA_DIST_NEWS != ls $(srcdir)/NEWS-*
|
||||
EXTRA_DIST += \
|
||||
BUGS \
|
||||
HACKING \
|
||||
NEWTAPS \
|
||||
README.Windows \
|
||||
README.OSX \
|
||||
$(wildcard $(srcdir)/NEWS*) \
|
||||
$(EXTRA_DIST_NEWS) \
|
||||
Doxyfile.in \
|
||||
tools/logger.pl \
|
||||
tools/rlink_make_speed_table \
|
||||
|
|
207
NEWS
207
NEWS
|
@ -2,27 +2,232 @@ This file includes highlights of the changes made in the OpenOCD
|
|||
source archive release.
|
||||
|
||||
JTAG Layer:
|
||||
* add debug level 4 for verbose I/O debug
|
||||
* bitbang, add read buffer to improve performance
|
||||
* Cadence SystemVerilog Direct Programming Interface (DPI) adapter driver
|
||||
* CMSIS-DAP v2 (USB bulk based) adapter driver
|
||||
* Cypress KitProg adapter driver
|
||||
* FTDI FT232R sync bitbang adapter driver
|
||||
* Linux GPIOD bitbang adapter driver through libgpiod
|
||||
* Mellanox rshim USB or PCIe adapter driver
|
||||
* Nuvoton Nu-Link and Nu-Link2 adapter drivers
|
||||
* NXP IMX GPIO mmap based adapter driver
|
||||
* ST-Link consolidate all versions in single config
|
||||
* ST-Link read properly old USB serial numbers
|
||||
* STLink/V3 support (for ST devices only !)
|
||||
* STM8 SWIM transport
|
||||
* TI XDS110 adapter driver
|
||||
* Xilinx XVC/PCIe adapter driver
|
||||
|
||||
Boundary Scan:
|
||||
|
||||
Target Layer:
|
||||
* 64 bit address support
|
||||
* ARCv2 target support
|
||||
* ARM Cortex-A hypervisor mode support
|
||||
* ARM Cortex-M fast PC sampling support for profiling
|
||||
* ARM generic CTI support
|
||||
* ARM generic mem-ap target support
|
||||
* ARMv7-A MMU tools
|
||||
* ARMv7m traces add TCP stream server
|
||||
* ARMv8 AARCH64 target support and semihosting support
|
||||
* ARMv8 AARCH64 disassembler support through capstone library
|
||||
* ARMv8-M target support
|
||||
* EnSilica eSi-RISC target support, including instruction tracing
|
||||
eSi-Trace support
|
||||
* MIPS64 target support
|
||||
* Motorola SREC S6 record image file support
|
||||
* RISC-V target support
|
||||
* SEGGER Real Time Transfer (RTT) initial support (for single target,
|
||||
Cortex-M only)
|
||||
* ST STM8 target support
|
||||
* Various MIPS32 target improvements
|
||||
|
||||
Flash Layer:
|
||||
* Atheros (ath79) SPI interface support
|
||||
* Atmel atmega128rfa1 support
|
||||
* Atmel SAM D21, D51, DA1, E51, E53, E54, G55, R30 support
|
||||
* Atmel SAMC2?N* support
|
||||
* Cypress PSoC5LP, PSoC6 support
|
||||
* EnSilica eSi-RISC support
|
||||
* Foshan Synwit Tech SWM050 support
|
||||
* Maxim Integrated MAX32XXX support
|
||||
* Nordic Semiconductor nRF51822, nRF52810, nRF52832 support
|
||||
* NXP Kinetis K27, K28, KE1x, KEAx, KL28, KL8x, KV5x, KWx support
|
||||
* Renesas RPC HF support
|
||||
* SH QSPI support
|
||||
* SiFive Freedom E support
|
||||
* Silicon Labs EFR-family, EZR32HG support
|
||||
* ST BlueNRG support
|
||||
* ST STM32 QUAD/OCTO-SPI interface support for Flash, FRAM and EEPROM
|
||||
* ST STM32F72x, STM32F4x3, STM32H7xx support
|
||||
* ST STM32G0xx, STM32G4xx, STM32L4x, STM32WB, STM32WL support
|
||||
* ST STM32L5x support (non secure mode)
|
||||
* TI CC13xx, CC26xx, CC32xx support
|
||||
* TI MSP432 support
|
||||
* Winner Micro w600 support
|
||||
* Xilinx XCF platform support
|
||||
* Various discrete SPI NOR flashes support
|
||||
|
||||
Board, Target, and Interface Configuration Scripts:
|
||||
* 8devices LIMA board config
|
||||
* Achilles Instant-Development Kit Arria 10 board config
|
||||
* Amazon Kindle 2 and DX board config
|
||||
* Analog Devices ADSP-SC58x, ADSP-SC584-EZBRD board config
|
||||
* Andes Technology ADP-XC7KFF676 board config
|
||||
* Andes Technology Corvette-F1 board config
|
||||
* ARM Musca A board config
|
||||
* Arty Spartan 7 FPGA board config
|
||||
* Atmel SAMD10 Xplained mini board config
|
||||
* Atmel SAMD11 Xplained Pro board config
|
||||
* Atmel SAM G55 Xplained Pro board config
|
||||
* AVNET UltraZED EG StarterKit board config
|
||||
* Blue Pill STM32F103C8 board config
|
||||
* DP Busblaster v4.1a board config
|
||||
* DPTechnics DPT-Board-v1 board config
|
||||
* Emcraft imx8 SOM BSB board config
|
||||
* Globalscale ESPRESSObin board config
|
||||
* Kasli board config
|
||||
* Kintex Ultrascale XCKU040 board config
|
||||
* Knovative KC-100 board config
|
||||
* LeMaker HiKey board config
|
||||
* Microchip (Atmel) SAME54 Xplained Pro board config
|
||||
* Microchip (Atmel) SAML11 Xplained Pro board config
|
||||
* Nordic module NRF52 board config
|
||||
* Numato Lab Mimas A7 board config
|
||||
* NXP Freedom FRDM-LS1012A board config
|
||||
* NXP IMX7SABRE board config
|
||||
* NXP IMX8MP-EVK board config
|
||||
* NXP MC-IMX8M-EVK board config
|
||||
* QuickLogic QuickFeather board config
|
||||
* Renesas R-Car E2, H2, M2 board config
|
||||
* Renesas R-Car Salvator-X(S) board config
|
||||
* Renesas RZ/A1H GR-Peach board config
|
||||
* Rigado BMD-300 board config
|
||||
* Sayma AMC board config
|
||||
* Sifive e31arty, e51arty, hifive1 board config
|
||||
* ST B-L475E-IOT01A board config
|
||||
* ST BlueNRG idb007v1, idb008v1, idb011v1 board config
|
||||
* ST STM32F412g discovery board config
|
||||
* ST STM32F413h discovery board config
|
||||
* ST STM32F469i discovery board config
|
||||
* ST STM32F7 Nucleo board config
|
||||
* ST STM32F723e discovery board config
|
||||
* ST STM32F746g discovery board config
|
||||
* ST STM32F769i discovery board config
|
||||
* ST STM32H735g discovery board config
|
||||
* ST STM32H743zi Nucleo board config
|
||||
* ST STM32H745i discovery board config
|
||||
* ST STM32H747i discovery board config
|
||||
* ST STM32H750b discovery board config
|
||||
* ST STM32H7b3i discovery board config
|
||||
* ST STM32H7x_dual_qspi board config
|
||||
* ST STM32H7x3i Eval boards config
|
||||
* ST STM32L073 Nucleo board config
|
||||
* ST STM32L476g discovery board config
|
||||
* ST STM32L496g discovery board config
|
||||
* ST STM32L4p5g discovery board config
|
||||
* ST STM32L4r9i discovery board config
|
||||
* ST STM32L5 Nucleo board config
|
||||
* ST STM32MP15x DK2 board config
|
||||
* ST STM32WB Nucleo board config
|
||||
* ST STM8L152R8 Nucleo board config
|
||||
* Synopsys DesignWare ARC EM board config
|
||||
* Synopsys DesignWare ARC HSDK board config
|
||||
* TI BeagleBone family boards config
|
||||
* TI CC13xx, CC26xx, CC32xx LaunchPad board config
|
||||
* TI MSP432 LaunchPad board config
|
||||
* Tocoding Poplar board config
|
||||
* TP-Link WDR4300 board config
|
||||
* Allwinner V3s target config
|
||||
* Andes Technology NDS V5 target config
|
||||
* Atmel atmega128rfa1 target config
|
||||
* ARM corelink SSE-200 target config
|
||||
* Atheros_ar9344 target config
|
||||
* Cypress PSoC5LP, PSoC6 target config
|
||||
* EnSilica eSi-RISC target config
|
||||
* Foshan Synwit Tech SWM050 target config
|
||||
* GigaDevice GD32VF103 target config
|
||||
* Hisilicon Hi3798 target config
|
||||
* Hisilicon Hi6220 target config
|
||||
* Infineon TLE987x target config
|
||||
* Marvell Armada 3700 target config
|
||||
* Maxim Integrated MAX32XXX target config
|
||||
* Mellanox BlueField target config
|
||||
* Microchip (Atmel) SAME5x, SAML1x target config
|
||||
* NXP IMX6SX, IMX6UL, IMX7, IMX7ULP, IMX8 target config
|
||||
* NXP Kinetis KE1xZ, KE1xF target config
|
||||
* NXP LPC84x, LPC8Nxx, LS1012A, NHS31xx target config
|
||||
* Qualcomm QCA4531 target config
|
||||
* QuickLogic EOS S3 target config
|
||||
* Renesas R-Car E2, H2, M2 target config
|
||||
* Renesas R-Car Gen3 target config
|
||||
* Renesas RZ/A1H target config
|
||||
* Rockchip RK3308 target config
|
||||
* ST BlueNRG target config
|
||||
* ST STM32G0, STM32G4, STM32H7, STM32L0, STM32L5 target config
|
||||
* ST STM32MP15x target config
|
||||
* ST STM32WBx, STM32WLEx target config
|
||||
* ST STM8L152, S003, S103, S105 target config
|
||||
* Synopsys DesignWare ARC EM target config
|
||||
* Synopsys DesignWare ARC HS Development Kit SoC target config
|
||||
* TI CC13xx, CC26xx, CC32xx target config
|
||||
* TI TNETC4401 target config
|
||||
* Xilinx UltraScale+ target config
|
||||
* Altera 5M570Z (MAXV family) CPLD config
|
||||
* Xilinx Ultrascale, XCF CPLD config
|
||||
* Intel (Altera) Arria10 FPGA config
|
||||
* Cadence SystemVerilog Direct Programming Interface (DPI) interface config
|
||||
* Cypress KitProg interface config
|
||||
* Digilent SMT2 NC interface config
|
||||
* DLN-2 example of Linux GPIOD interface config
|
||||
* FTDI C232HM interface config
|
||||
* HIE JTAG Debugger interface config
|
||||
* In-Circuit's ICprog interface config
|
||||
* isodebug isolated JTAG/SWD+UART interface config
|
||||
* Mellanox rshim USB or PCIe interface config
|
||||
* Nuvoton Nu-Link interface config
|
||||
* NXP IMX GPIO mmap based interface config
|
||||
* Steppenprobe open hardware interface config
|
||||
* TI XDS110 interface config
|
||||
|
||||
Server Layer:
|
||||
* 64 bit address support
|
||||
* default bind to IPv4 localhost
|
||||
* gdb: allow multiple connections
|
||||
* gdb: architecture element support
|
||||
* gdb: vCont, vRun support
|
||||
* telnet: handle Ctrl+A, Ctrl+E and Ctrl+K
|
||||
|
||||
RTOS:
|
||||
* Chromium-EC rtos support
|
||||
* hwthread pseudo rtos support
|
||||
* NuttX rtos support
|
||||
* RIOT rtos support
|
||||
|
||||
Documentation:
|
||||
* Improve STM32 flash driver
|
||||
* Various typo fix and improvements
|
||||
|
||||
Build and Release:
|
||||
* Add libutil to support jimtcl version 0.80
|
||||
* Clang warning fixes
|
||||
* GitHub workflow for Win32 snapshot binaries
|
||||
* Handle Tcl return values consistently
|
||||
* Mitigation for CVE-2018-5704: Prevent some forms of Cross
|
||||
Protocol Scripting attacks
|
||||
* Support for libftdi 1.5
|
||||
* Travis-CI basic support
|
||||
* Update libjaylink to version 0.2.0
|
||||
* Update jimtcl to version 0.79
|
||||
* Use external (optional) library capstone for ARM and AARCH64 disassembly
|
||||
|
||||
|
||||
This release also contains a number of other important functional and
|
||||
cosmetic bugfixes. For more details about what has changed since the
|
||||
last release, see the git repository history:
|
||||
|
||||
http://sourceforge.net/p/openocd/code/ci/v0.x.0/log/?path=
|
||||
http://sourceforge.net/p/openocd/code/ci/v0.11.0-rc1/log/?path=
|
||||
|
||||
|
||||
For older NEWS, see the NEWS files associated with each release
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -1,7 +1,8 @@
|
|||
AC_PREREQ(2.64)
|
||||
AC_INIT([openocd], [0.10.0+dev],
|
||||
AC_INIT([openocd], [0.11.0-rc1+dev],
|
||||
[OpenOCD Mailing List <openocd-devel@lists.sourceforge.net>])
|
||||
AC_CONFIG_SRCDIR([src/openocd.c])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
m4_include([config_subdir.m4])dnl
|
||||
|
||||
|
@ -43,6 +44,7 @@ AC_TYPE_LONG_LONG_INT
|
|||
|
||||
AC_SEARCH_LIBS([ioperm], [ioperm])
|
||||
AC_SEARCH_LIBS([dlopen], [dl])
|
||||
AC_SEARCH_LIBS([openpty], [util])
|
||||
|
||||
AC_CHECK_HEADERS([sys/socket.h])
|
||||
AC_CHECK_HEADERS([elf.h])
|
||||
|
@ -118,6 +120,7 @@ m4_define([USB1_ADAPTERS],
|
|||
[[ft232r], [Bitbang mode of FT232R based devices], [FT232R]],
|
||||
[[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]],
|
||||
[[xds110], [TI XDS110 Debug Probe], [XDS110]],
|
||||
[[cmsis_dap_v2], [CMSIS-DAP v2 Compliant Debugger], [CMSIS_DAP_USB]],
|
||||
[[osbdm], [OSBDM (JTAG only) Programmer], [OSBDM]],
|
||||
[[opendous], [eStick/opendous JTAG Programmer], [OPENDOUS]],
|
||||
[[aice], [Andes JTAG Programmer], [AICE]]])
|
||||
|
@ -128,7 +131,7 @@ m4_define([USB0_ADAPTERS],
|
|||
[[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]]])
|
||||
|
||||
m4_define([HIDAPI_ADAPTERS],
|
||||
[[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP]],
|
||||
[[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP_HID]],
|
||||
[[nulink], [Nu-Link Programmer], [HLADAPTER_NULINK]]])
|
||||
|
||||
m4_define([HIDAPI_USB1_ADAPTERS],
|
||||
|
@ -669,7 +672,11 @@ for hidapi_lib in hidapi hidapi-hidraw hidapi-libusb; do
|
|||
])
|
||||
done
|
||||
|
||||
PKG_CHECK_MODULES([LIBFTDI], [libftdi1], [use_libftdi=yes], [
|
||||
PKG_CHECK_MODULES([LIBFTDI], [libftdi1], [
|
||||
use_libftdi=yes
|
||||
PKG_CHECK_EXISTS([libftdi1 >= 1.5],
|
||||
[AC_DEFINE([HAVE_LIBFTDI_TCIOFLUSH], [1], [Define if your libftdi has ftdi_tcioflush()])])
|
||||
], [
|
||||
PKG_CHECK_MODULES([LIBFTDI], [libftdi], [use_libftdi=yes], [use_libftdi=no])
|
||||
])
|
||||
|
||||
|
@ -827,6 +834,7 @@ AS_IF([test "x${gcc_wextra}" = "xyes"], [
|
|||
GCC_WARNINGS="${GCC_WARNINGS} -Wbad-function-cast"
|
||||
GCC_WARNINGS="${GCC_WARNINGS} -Wcast-align"
|
||||
GCC_WARNINGS="${GCC_WARNINGS} -Wredundant-decls"
|
||||
GCC_WARNINGS="${GCC_WARNINGS} -Wpointer-arith"
|
||||
])
|
||||
AS_IF([test "x${gcc_werror}" = "xyes"], [
|
||||
GCC_WARNINGS="${GCC_WARNINGS} -Werror"
|
||||
|
|
|
@ -167,6 +167,7 @@ ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", MODE="660", GROUP="plugdev",
|
|||
|
||||
# Keil Software, Inc. ULink
|
||||
ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2710", MODE="660", GROUP="plugdev", TAG+="uaccess"
|
||||
ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2750", MODE="660", GROUP="plugdev", TAG+="uaccess"
|
||||
|
||||
# CMSIS-DAP compatible adapters
|
||||
ATTRS{product}=="*CMSIS-DAP*", MODE="660", GROUP="plugdev", TAG+="uaccess"
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
# paths refer to the build file system.
|
||||
#
|
||||
# This script is probably more useful as a reference than as a complete build
|
||||
# tool but for some configurations it may be usable as-is. It only cross-
|
||||
# builds libusb-1.0, hidapi and libftdi from source, but the script can be
|
||||
# tool but for some configurations it may be usable as-is. It only cross-builds
|
||||
# libusb-1.0, hidapi, libftdi and capstone from source, but the script can be
|
||||
# extended to build other prerequisites in a similar manner.
|
||||
#
|
||||
# Usage:
|
||||
|
@ -39,17 +39,20 @@ WORK_DIR=$PWD
|
|||
: ${LIBUSB1_SRC:=/path/to/libusb1}
|
||||
: ${HIDAPI_SRC:=/path/to/hidapi}
|
||||
: ${LIBFTDI_SRC:=/path/to/libftdi}
|
||||
: ${CAPSTONE_SRC:=/path/to/capstone}
|
||||
|
||||
OPENOCD_SRC=`readlink -m $OPENOCD_SRC`
|
||||
LIBUSB1_SRC=`readlink -m $LIBUSB1_SRC`
|
||||
HIDAPI_SRC=`readlink -m $HIDAPI_SRC`
|
||||
LIBFTDI_SRC=`readlink -m $LIBFTDI_SRC`
|
||||
CAPSTONE_SRC=`readlink -m $CAPSTONE_SRC`
|
||||
|
||||
HOST_TRIPLET=$1
|
||||
BUILD_DIR=$WORK_DIR/$HOST_TRIPLET-build
|
||||
LIBUSB1_BUILD_DIR=$BUILD_DIR/libusb1
|
||||
HIDAPI_BUILD_DIR=$BUILD_DIR/hidapi
|
||||
LIBFTDI_BUILD_DIR=$BUILD_DIR/libftdi
|
||||
CAPSTONE_BUILD_DIR=$BUILD_DIR/capstone
|
||||
OPENOCD_BUILD_DIR=$BUILD_DIR/openocd
|
||||
|
||||
## Root of host file tree
|
||||
|
@ -129,6 +132,26 @@ if [ -d $LIBFTDI_SRC ] ; then
|
|||
make install DESTDIR=$SYSROOT
|
||||
fi
|
||||
|
||||
# capstone build & install into sysroot
|
||||
if [ -d $CAPSTONE_SRC ] ; then
|
||||
mkdir -p $CAPSTONE_BUILD_DIR
|
||||
cd $CAPSTONE_BUILD_DIR
|
||||
cp -r $CAPSTONE_SRC/* .
|
||||
make install DESTDIR=$SYSROOT PREFIX=$PREFIX \
|
||||
CROSS="${HOST_TRIPLET}-" \
|
||||
$CAPSTONE_CONFIG
|
||||
# fix the generated capstone.pc
|
||||
CAPSTONE_PC_FILE=${SYSROOT}${PREFIX}/lib/pkgconfig/capstone.pc
|
||||
sed -i '/^libdir=/d' $CAPSTONE_PC_FILE
|
||||
sed -i '/^includedir=/d' $CAPSTONE_PC_FILE
|
||||
sed -i '/^archive=/d' $CAPSTONE_PC_FILE
|
||||
sed -i '1s;^;prefix=/usr \
|
||||
exec_prefix=${prefix} \
|
||||
libdir=${exec_prefix}/lib \
|
||||
includedir=${prefix}/include\n\n;' $CAPSTONE_PC_FILE
|
||||
fi
|
||||
|
||||
|
||||
# OpenOCD build & install into sysroot
|
||||
mkdir -p $OPENOCD_BUILD_DIR
|
||||
cd $OPENOCD_BUILD_DIR
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
BIN2C = ../../../../src/helper/bin2char.sh
|
||||
|
||||
SRCS=stmqspi_erase_check.S stmqspi_crc32.S stmqspi_read.S stmqspi_write.S \
|
||||
stmoctospi_erase_check.S stmoctospi_crc32.S stmoctospi_read.S stmoctospi_write.S
|
||||
OBJS=$(patsubst %.S,%.inc,$(SRCS))
|
||||
|
||||
CROSS_COMPILE ?= arm-none-eabi-
|
||||
|
||||
CC=$(CROSS_COMPILE)gcc
|
||||
OBJCOPY=$(CROSS_COMPILE)objcopy
|
||||
OBJDUMP=$(CROSS_COMPILE)objdump
|
||||
LD=$(CROSS_COMPILE)ld
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
%.o: %.S Makefile
|
||||
$(CC) -Wall -Werror -Wa,-adhlmn -o $@ -c $< > $(@:.o=.lst)
|
||||
@enscript -Easm -T 4 -G -o - $(@:.o=.lst) | ps2pdf - $(@:.o=.pdf) || true
|
||||
|
||||
%.elf: %.o
|
||||
$(LD) -s -defsym=_start=0 -o $@ $<
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
%.inc: %.bin
|
||||
$(BIN2C) < $< > $@
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.elf *.lst *.pdf *.bin *.inc
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
.INTERMEDIATE: $(patsubst %.S,%.o,$(SRCS)) $(patsubst %.S,%.elf,$(SRCS)) $(patsubst %.S,%.bin,$(SRCS))
|
|
@ -0,0 +1,679 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# Helper for generating GPIO setup for STM32F0, F4, F7, H7, L0, L1, L4, L4+
|
||||
# and F1 (for 'stmqspi' and 'cmspi' drivers).
|
||||
#
|
||||
# Each pin is configured by "PortAndBit:Conf:Speed"
|
||||
# 'PortAndBit' specifies Port and bit number
|
||||
# 'Conf' is one of 'AFx' (alternate), 'P' (output), 'IN' (input),
|
||||
# (each optionally by 'P' (push-pull) or 'O' (open-drain)),
|
||||
# (all optionally followed by 'UP' (pull-up), or 'DO' (pull-down))
|
||||
# 'Speed' is one of 'L' (low), 'M' (medium), 'H' (high), 'V' (very high)
|
||||
#
|
||||
# Port configuration can be given on command line as a single string (pins separated by commas)
|
||||
# or via CubeMX generated file. The latter must consist of the quadspi.c / octospi.c and the
|
||||
# corresponding header. The precise spelling in these files doesn't seem to be consistent, though ...
|
||||
#
|
||||
# Pins have to be ordered this way:
|
||||
# - I2C: SDA, SCL
|
||||
# - SPI (1 line): NCS, CLK, IO1/MISO, IO0/MOSI
|
||||
# - DPI (2 lines): NCS, CLK, IO1/MISO, IO0/MOSI
|
||||
# - QPI (4 lines): NCS, CLK, IO3/NHOLD, IO2/NWP, IO1/MISO, IO0/MOSI
|
||||
# For dual flash: BK_1 first, then BK_2. If single NCS for both, omit NCS in BK_2
|
||||
# For octal flash: NCS, CLK, DQS, IO7 down to IO0
|
||||
|
||||
use strict;
|
||||
use Getopt::Std;
|
||||
|
||||
my $GPIO_BASE;
|
||||
my $Conf;
|
||||
my $STM32F1 = 0;
|
||||
|
||||
# "Blue-Pill stm32f103cbt6 board w/ cmspi
|
||||
#$STM32F1 = 1;
|
||||
#$GPIO_BASE = 0x40010800;
|
||||
#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB15:INUP:V";
|
||||
#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB01:INUP:V";
|
||||
|
||||
#$STM32F1 = 1;
|
||||
#$GPIO_BASE = 0x40010800;
|
||||
#$Conf = "PB07:INUP:V, PB06:INUP:V";
|
||||
|
||||
# mini-stm32f030f4p6 board w/ cmspi
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PB01:PP:V, PA05:PP:V, PA06:INUP:V, PA07:INUP:V";
|
||||
|
||||
# stm32f407vet6 board w/ cmspi
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB00:PP:M, PB03:PP:V, PB04:INUP:V, PB05:INUP:V";
|
||||
|
||||
# stm32f412g-disco quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB02:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V, PG06:AF10:V";
|
||||
|
||||
# stm32f413h-disco
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V, PG06:AF10:V";
|
||||
|
||||
# stm32f469i-disco quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB06:AF10:V, PF10:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB06:PP:M, PF10:PP:V, PF06:INUP:V, PF07:INUP:V, PF09:INUP:V, PF08:INUP:V";
|
||||
|
||||
# stm32f723e-disco quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V";
|
||||
|
||||
# stm32f746g-disco quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#Conf = "PB06:AF10:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PD12:INUP:V, PD11:INUP:V";
|
||||
|
||||
# stm32f769i-disco quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PC10:INUP:V, PC09:INUP:V, ";
|
||||
|
||||
# b-l475e-iot01a quad
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V";
|
||||
|
||||
# stm32l476g-disco quad
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V";
|
||||
|
||||
# stm32l496g-disco quad
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PB11:AF10:V, PB01:AF10:V, PB00:AF10:V";
|
||||
|
||||
# stm32l4r9i-disco octal
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PG15:AF05:V, PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PH10:AF05:V, PH09:AF05:V, "
|
||||
# . "PH08:AF05:V, PI11:AF05:V, PI10:AF05:V, PI09:AF05:V, PI06:AF05:V";
|
||||
|
||||
# stm32l4p5g-disco octal/octal
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PA07:AF10:V, PA06:AF10:V, PC03:AF10:V, PD07:AF10:V, PD05:AF10:V, PD04:AF10:V, "
|
||||
# . "PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V, PG06:AF03:V";
|
||||
#$Conf = "PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V, "
|
||||
# . "PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PG01:AF05:V, PG00:AF05:V";
|
||||
|
||||
# nucleo-f767zi dual quad
|
||||
#$GPIO_BASE = 0x40020000;
|
||||
#$Conf = "PB06:AF10:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, "
|
||||
# . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
|
||||
#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
|
||||
|
||||
# nucleo-h743zi dual quad
|
||||
#$GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PB10:AF09:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, "
|
||||
# . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
|
||||
#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
|
||||
|
||||
# nucleo-h7a3zi dual quad
|
||||
#$GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PB10:AF09:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V, "
|
||||
# . "PC11:AF09:V, PE10:AF10:V, PD06:AF10:V, PE08:AF10:V, PE07:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
|
||||
#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
|
||||
|
||||
# nucleo-l4r5zi one dual quad single NCS
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, "
|
||||
# . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V";
|
||||
#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
|
||||
|
||||
# nucleo-l552ze-q dual quad with single NCS
|
||||
#$GPIO_BASE = 0x42020000;
|
||||
#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, "
|
||||
# . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V";
|
||||
#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
|
||||
|
||||
# nucleo-g071rb dual quad
|
||||
#$GPIO_BASE = 0x50000000;
|
||||
#$Conf = "PA00:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, PB11:INPUP:H, PB01:INPUP:H";
|
||||
#$Conf = "PA01:PPUP:H, PA04:PPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, PB05:INPUP:V";
|
||||
|
||||
# nucleo-g474re dual quad with single NCS
|
||||
#$GPIO_BASE = 0x48000000;
|
||||
#$Conf = "PB11:AF10:H, PB10:AF10:V, PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V, "
|
||||
# . "PC04:AF10:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V";
|
||||
# w/ cmspi
|
||||
#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PA06:INPUP:V, PA07:INPUP:V, PB00:INPUP:V, PB01:INPUP:V";
|
||||
#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PC04:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, PC01:INPUP:V";
|
||||
|
||||
# stm32h745i-disco dual quad with single NCS
|
||||
#$GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PG06:AF10:H, PF10:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, "
|
||||
# . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
|
||||
|
||||
# stm32h747i-disco dual quad with single NCS
|
||||
#GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PG06:AF10:H, PB02:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, "
|
||||
# . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
|
||||
|
||||
# stm32h7b3i-disco octal
|
||||
#$GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PG06:AF10:V, PB02:AF09:V, PC05:AF10:V, PD07:AF10:V, PG09:AF09:V, PH03:AF09:V, PC01:AF10:V, "
|
||||
# . "PF06:AF10:V, PF07:AF10:V, PF09:AF10:V, PD11:AF09:V";
|
||||
|
||||
# stm32h735g-disco octal
|
||||
#$GPIO_BASE = 0x58020000;
|
||||
#$Conf = "PG06:AF10:V, PF10:AF09:V, PB02:AF10:V, PD07:AF10:V, PG09:AF09:V, PD05:AF10:V, PD04:AF10:V, "
|
||||
# . "PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V";
|
||||
|
||||
# stm32l562e-disco octal
|
||||
#$GPIO_BASE = 0x42020000;
|
||||
#$Conf = "PA02:AF10:V, PA03:AF10:V, PB02:AF10:V, PC00:AF03:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V, "
|
||||
# . "PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V";
|
||||
|
||||
&getopts('b:c:f:t');
|
||||
if ($Getopt::Std::opt_b eq '')
|
||||
{
|
||||
if ($GPIO_BASE eq '')
|
||||
{
|
||||
die("usage: $0 [ -1 ] -b io_base [ -c port_configuration ] [ -f conf_file ]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$GPIO_BASE = eval $Getopt::Std::opt_b;
|
||||
}
|
||||
|
||||
if ($Getopt::Std::opt_c eq '')
|
||||
{
|
||||
if (($Conf eq '') && ($Getopt::Std::opt_f eq ''))
|
||||
{
|
||||
die("usage: $0 [ -b io_base ] ( -c port_configuration | -f conf_file )");
|
||||
}
|
||||
}#
|
||||
else
|
||||
{
|
||||
$Conf = $Getopt::Std::opt_c . ',';
|
||||
}
|
||||
|
||||
$STM32F1 = $Getopt::Std::opt_t;
|
||||
|
||||
my $Sep = "\t";
|
||||
my $Form = "${Sep}mmw 0x%08X 0x%08X 0x%08X\t;# ";
|
||||
|
||||
my $GPIO_OFFS;
|
||||
my $GPIO_CRL;
|
||||
my $GPIO_CRH;
|
||||
my $GPIO_MODER;
|
||||
my $GPIO_OTYPER;
|
||||
my $GPIO_OSPEEDR;
|
||||
my $GPIO_PUPDR;
|
||||
my $GPIO_IDR;
|
||||
my $GPIO_ODR;
|
||||
my $GPIO_AFRL;
|
||||
my $GPIO_AFRH;
|
||||
|
||||
if ($STM32F1)
|
||||
{
|
||||
# offsets for F1 devices
|
||||
$GPIO_OFFS = 0x400;
|
||||
$GPIO_CRL = 0x00;
|
||||
$GPIO_CRH = 0x04;
|
||||
$GPIO_IDR = 0x08;
|
||||
$GPIO_ODR = 0x0C;
|
||||
}
|
||||
else
|
||||
{
|
||||
# these offsets are identical on all F0, F4, F7, H7, L4, L4+ devices up to now
|
||||
$GPIO_OFFS = 0x400;
|
||||
$GPIO_MODER = 0x00;
|
||||
$GPIO_OTYPER = 0x04;
|
||||
$GPIO_OSPEEDR = 0x08;
|
||||
$GPIO_PUPDR = 0x0C;
|
||||
$GPIO_IDR = 0x10;
|
||||
$GPIO_ODR = 0x14;
|
||||
$GPIO_AFRL = 0x20;
|
||||
$GPIO_AFRH = 0x24;
|
||||
}
|
||||
|
||||
my @Out = ( { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { } );
|
||||
my @Port = ( );
|
||||
my $Exor;
|
||||
my %Conf;
|
||||
my $Pins = "${Sep}#";
|
||||
|
||||
my $pins;
|
||||
my $altn;
|
||||
my %defs;
|
||||
|
||||
if ($Getopt::Std::opt_f ne '')
|
||||
{
|
||||
open(CONF_FILE, '<', $Getopt::Std::opt_f) || die("can't open $Getopt::Std::opt_f");
|
||||
while (my $line = <CONF_FILE>)
|
||||
{
|
||||
if ($line =~ /^\s*#define\s+.?(QSPI|QUAD_?SPI|OCTOSPI[^_]*)\w+_(Port|Pin)\s/)
|
||||
{
|
||||
if ($line =~ /#define\s+(\w+)\s+(\w+)/)
|
||||
{
|
||||
$defs{$1} = $2;
|
||||
}
|
||||
else
|
||||
{
|
||||
die($line);
|
||||
}
|
||||
}
|
||||
elsif ($line =~ /^\s*(P[A-Z])([0-9]+)\s*-+>\s+.?(QSPI|QUAD_?SPI|OCTO_?SPI[^_]*)_(\w+)/)
|
||||
{
|
||||
$Conf{$4} = sprintf("%s%02d", $1, $2);
|
||||
}
|
||||
elsif ($line =~ /^\s*GPIO_InitStruct.Pin\s*=\s*([^;]+\w)/)
|
||||
{
|
||||
$pins = $1;
|
||||
while ($line !~ /;/)
|
||||
{
|
||||
$line = <CONF_FILE>;
|
||||
$line =~ /^\s*([^;]+\w)/;
|
||||
$pins .= $1;
|
||||
}
|
||||
}
|
||||
elsif ($line =~ /^\s*GPIO_InitStruct.Alternate\s*=\s*GPIO_AF([0-9]+)/)
|
||||
{
|
||||
$altn = $1;
|
||||
}
|
||||
elsif ($line =~ /^\s*HAL_GPIO_Init\s*\(\s*(\w+)\s*,/)
|
||||
{
|
||||
my $port = $1;
|
||||
if ($port =~ /GPIO([A-Z])/)
|
||||
{
|
||||
$port = $1;
|
||||
}
|
||||
elsif (exists($defs{$port}))
|
||||
{
|
||||
$defs{$port} =~ /GPIO([A-Z])/;
|
||||
$port = $1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\n");
|
||||
next;
|
||||
}
|
||||
my @pin = split(/\s*\|\s*/, $pins);
|
||||
foreach my $pin (@pin)
|
||||
{
|
||||
my $bit;
|
||||
if (exists($defs{$pin}))
|
||||
{
|
||||
$defs{$pin} =~ /GPIO_PIN_([0-9]+)/;
|
||||
$bit = $1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$pin =~ /GPIO_PIN_([0-9]+)/;
|
||||
$bit = $1;
|
||||
}
|
||||
$Conf .= sprintf("P%s%02d:AF%02d:V, ", $port, $bit, $altn);
|
||||
}
|
||||
$pins = '';
|
||||
$altn = 0;
|
||||
}
|
||||
}
|
||||
close(CONF_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
my @names = ( );
|
||||
my @conf = split(/\s*,\s*/, $Conf);
|
||||
|
||||
if (@conf == 2)
|
||||
{
|
||||
push(@names, 'SDA', 'SCL');
|
||||
} else {
|
||||
if (@conf == 3)
|
||||
{
|
||||
push(@names, 'NCS', 'CLK', 'IO0/DIO');
|
||||
}
|
||||
elsif (@conf == 4)
|
||||
{
|
||||
push(@names, 'NCS', 'CLK','IO1/MISO', 'IO0/MOSI');
|
||||
}
|
||||
elsif (@conf == 6)
|
||||
{
|
||||
push(@names, 'NCS', 'CLK', 'IO3/NHOLD', 'IO2/NWP', 'IO1/MISO', 'IO0/MOSI');
|
||||
}
|
||||
elsif (@conf == 10)
|
||||
{
|
||||
push(@names, 'NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI');
|
||||
push(@names, 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI');
|
||||
}
|
||||
elsif (@conf == 11)
|
||||
{
|
||||
push(@names, 'BK_1_NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI');
|
||||
push(@names, 'BK_2_NCS', 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI');
|
||||
}
|
||||
else
|
||||
{
|
||||
die("invalid config");
|
||||
}
|
||||
}
|
||||
|
||||
for (my $index = 0; $index < @conf; $index++)
|
||||
{
|
||||
uc($conf[$index]) =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/;
|
||||
$Pins .= sprintf(" %s: P%s%02d,", $names[$index], $1, $2);
|
||||
}
|
||||
chop($Pins);
|
||||
}
|
||||
|
||||
if (exists $Conf{'BK1_IO0'})
|
||||
{
|
||||
# QuadSPI on F4, F7, H7
|
||||
my $line;
|
||||
for my $i ('NCS', 'BK1_NCS', 'CLK', 'BK1_IO3', 'BK1_IO2', 'BK1_IO1', 'BK1_IO0')
|
||||
{
|
||||
(exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
|
||||
}
|
||||
}
|
||||
|
||||
if (exists $Conf{'BK2_IO0'})
|
||||
{
|
||||
# QuadSPI on F4, F7, H7
|
||||
my $line;
|
||||
for my $i ('NCS', 'BK2_NCS', 'CLK', 'BK2_IO3', 'BK2_IO2', 'BK2_IO1', 'BK2_IO0')
|
||||
{
|
||||
(exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
|
||||
}
|
||||
}
|
||||
|
||||
if (exists $Conf{'P1_IO0'})
|
||||
{
|
||||
# OctoSPI on L4+, L5, H7
|
||||
my $line;
|
||||
for my $i ('P1_NCS', 'P1_CLK', 'P1_DQS', 'P1_IO7', 'P1_IO6', 'P1_IO5', 'P1_IO4',
|
||||
'P1_IO3', 'P1_IO2', 'P1_IO1', 'P1_IO0')
|
||||
{
|
||||
(exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
|
||||
}
|
||||
}
|
||||
|
||||
if (exists $Conf{'P2_IO0'})
|
||||
{
|
||||
# OctoSPI on L4+, H7
|
||||
my $line;
|
||||
for my $i ('P2_NCS', 'P2_CLK', 'P2_DQS', 'P2_IO7', 'P2_IO6', 'P2_IO5', 'P2_IO4',
|
||||
'P2_IO3', 'P2_IO2', 'P2_IO1', 'P2_IO0')
|
||||
{
|
||||
(exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
|
||||
}
|
||||
}
|
||||
|
||||
my @Col = ( );
|
||||
my @conf = split(/\s*,\s*/, $Conf);
|
||||
|
||||
if (@conf == 3)
|
||||
{
|
||||
splice(@conf, 2, 0, 'NONE', 'NONE', 'NONE');
|
||||
}
|
||||
elsif (@conf == 4)
|
||||
{
|
||||
splice(@conf, 2, 0, 'NONE', 'NONE');
|
||||
}
|
||||
|
||||
foreach my $line (@conf)
|
||||
{
|
||||
$line = uc($line);
|
||||
$line =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/;
|
||||
my $port = $1;
|
||||
my $pin = $2;
|
||||
my $conf = $3;
|
||||
my $speed = $4;
|
||||
|
||||
my $MODER = 0x0;
|
||||
my $OTYPER = 0x0;
|
||||
my $OSPEEDR = 0x0;
|
||||
my $PUPDR = 0x0;
|
||||
my $AFR = 0x0;
|
||||
my $num = ord(${port}) - ord('A');
|
||||
my $out = $Out[$num];
|
||||
|
||||
(exists $$out{'DEF'}) || ($$out{'DEF'} = 0);
|
||||
|
||||
if ($conf eq '')
|
||||
{
|
||||
if ($line ne 'NONE')
|
||||
{
|
||||
printf(STDERR "invalid conf %s\n", $line);
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($conf =~ /^AF([0-9]+)(|P|O)(|UP|DO)$/)
|
||||
{
|
||||
if ($STM32F1)
|
||||
{
|
||||
printf(STDERR "no alternate %s for F1 family\n", $line);
|
||||
next;
|
||||
}
|
||||
if (($1 < 0) || ($1 > 15))
|
||||
{
|
||||
printf(STDERR "invalid alternate %s\n", $line);
|
||||
next;
|
||||
}
|
||||
$MODER = 0x2;
|
||||
$AFR = $1;
|
||||
if ($pin <= 7)
|
||||
{
|
||||
$$out{'AFRL_H'} |= ($AFR << (${pin} << 2));
|
||||
$$out{'AFRL_L'} |= (($AFR ^ 0xF) << (${pin} << 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
$$out{'AFRH_H'} |= ($AFR << ((${pin} - 8) << 2));
|
||||
$$out{'AFRH_L'} |= (($AFR ^ 0xF) << ((${pin} - 8) << 2));
|
||||
}
|
||||
if ($2 ne '') {
|
||||
$OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
|
||||
$$out{'OTYPER_H'} |= ($OTYPER << $pin);
|
||||
$$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
|
||||
}
|
||||
$PUPDR = ($3 eq 'UP') ? 0x1 : (($3 eq 'DO') ? 0x2 : 0x0);
|
||||
$$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1));
|
||||
$$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1));
|
||||
$conf = sprintf("AF%02d%s%s", $AFR, $2, $3);
|
||||
}
|
||||
elsif ($conf =~ /^IN(|P|O)(|UP|DO)$/)
|
||||
{
|
||||
if ($STM32F1)
|
||||
{
|
||||
$MODER = ($1 eq '') ? 0x4 : 0x8;
|
||||
($2 eq 'UP') && ($$out{'PUPDR_H'} |= (1 << ${pin}));
|
||||
($2 eq 'DO') && ($$out{'PUPDR_L'} |= (1 << ${pin}));
|
||||
}
|
||||
else
|
||||
{
|
||||
$MODER = 0x0;
|
||||
if ($1 ne '')
|
||||
{
|
||||
$OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
|
||||
$$out{'OTYPER_H'} |= ($OTYPER << $pin);
|
||||
$$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
|
||||
}
|
||||
$PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0);
|
||||
$$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1));
|
||||
$$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1));
|
||||
}
|
||||
($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin}));
|
||||
($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin}));
|
||||
}
|
||||
elsif ($conf =~ /^P(P|O)(|UP|DO)$/)
|
||||
{
|
||||
if ($STM32F1)
|
||||
{
|
||||
$MODER = ($1 eq 'O') ? 0x4 : 0x0;
|
||||
$MODER |= (($speed eq 'V') ? 0x03 : (($speed eq 'L') ? 0x2 : 0x1));
|
||||
if ($2 ne '')
|
||||
{
|
||||
printf(STDERR "WARNING: no output w/ pull-up/pull-down for F1 family %s\n", $line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$MODER = 0x1;
|
||||
$OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
|
||||
$$out{'OTYPER_H'} |= ($OTYPER << $pin);
|
||||
$$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
|
||||
$PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0);
|
||||
$$out{'PUPDR_H'} |= ($PUPDR << ($pin << 1));
|
||||
$$out{'PUPDR_L'} |= (($PUPDR ^ 0x3) << ($pin << 1));
|
||||
}
|
||||
($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin}));
|
||||
($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin}));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(STDERR "invalid conf %s\n", $line);
|
||||
next;
|
||||
}
|
||||
|
||||
if ($$out{'DEF'} & (1<< ${pin}))
|
||||
{
|
||||
printf(STDERR "redefinition: %s\n", $line);
|
||||
}
|
||||
|
||||
if ($STM32F1)
|
||||
{
|
||||
if ($pin >= 8)
|
||||
{
|
||||
$$out{'CRH_H'} |= ($MODER << (($pin & 0x7) << 2));
|
||||
$$out{'CRH_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
$$out{'CRL_H'} |= ($MODER << (($pin & 0x7) << 2));
|
||||
$$out{'CRL_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2));
|
||||
}
|
||||
|
||||
$Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port) - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin);
|
||||
my $exor = 0xB << (($pin & 0x7) << 2);
|
||||
(($MODER & 0x3) == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X",
|
||||
((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF,
|
||||
((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF, $exor));
|
||||
}
|
||||
else
|
||||
{
|
||||
$$out{'DEF'} |= (1 << ${pin});
|
||||
$$out{'MODER_H'} |= ($MODER << (${pin} << 1));
|
||||
$$out{'MODER_L'} |= (($MODER ^ 0x3) << (${pin} << 1));
|
||||
|
||||
$OSPEEDR = (($speed eq 'V') ? 0x3 : (($speed eq 'H') ? 0x2 : (($speed eq 'M') ? 0x1 : 0x0)));
|
||||
$$out{'OSPEEDR_H'} |= ($OSPEEDR << (${pin} << 1));
|
||||
$$out{'OSPEEDR_L'} |= (($OSPEEDR ^ 0x3) << (${pin} << 1));
|
||||
|
||||
$Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port) - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin);
|
||||
my $exor = (0x1 << ($pin << 1));
|
||||
($MODER == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X", (${GPIO_MODER}-${GPIO_ODR}) & 0x3FF,
|
||||
(${GPIO_MODER}-${GPIO_ODR}) & 0x3FF, $exor));
|
||||
}
|
||||
|
||||
push(@{$Port[$num]}, sprintf("P%s%02d:%s:%s", $port, $pin, $conf, $speed));
|
||||
push(@Col, $Exor);
|
||||
}
|
||||
|
||||
my $Col = sprintf("${Sep}0x%03X ", (${GPIO_IDR}-${GPIO_ODR}) & 0x3FF);
|
||||
for (my $i = 0; $i < @Col; $i++)
|
||||
{
|
||||
if (($i != 0) && (($i % 2) == 0))
|
||||
{
|
||||
(($i + 1) < @Col) && ($Col .= "\\\n${Sep}");
|
||||
}
|
||||
$Col .= sprintf("%s ", $Col[$i]);
|
||||
}
|
||||
printf("%s\n", $Col);
|
||||
|
||||
my @Col = ( );
|
||||
my $Set;
|
||||
for (my $i = 0; $i < @Out; $i++)
|
||||
{
|
||||
my $out = $Out[$i];
|
||||
my $addr = ${GPIO_BASE} + $i * ${GPIO_OFFS};
|
||||
my $count = 0;
|
||||
|
||||
if ($STM32F1)
|
||||
{
|
||||
if (($$out{'CRH_H'} | $$out{'CRH_L'} | $$out{'CRL_H'} | $$out{'CRL_L'} |
|
||||
$$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0)
|
||||
{
|
||||
push(@Col, sort({ $b cmp $a } @{$Port[$i]}));
|
||||
|
||||
$Set .= sprintf("\n%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')),
|
||||
join(", ", sort({ $b cmp $a } @{$Port[$i]})));
|
||||
|
||||
(($$out{'CRL_H'} | $$out{'CRL_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}CRL\n", $addr + ${GPIO_CRL}, $$out{'CRL_H'}, $$out{'CRL_L'}));
|
||||
|
||||
(($$out{'CRH_H'} | $$out{'CRH_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}CRH\n", $addr + ${GPIO_CRH}, $$out{'CRH_H'}, $$out{'CRH_L'}));
|
||||
|
||||
(($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}ODR/PUPDR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'}));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (($$out{'MODER_H'} | $$out{'MODER_L'} |
|
||||
$$out{'OTYPER_H'} | $$out{'OTYPER_L'} |
|
||||
$$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'} |
|
||||
$$out{'PUPDR_H'} | $$out{'PUPDR_L'} |
|
||||
$$out{'ODR_H'} | $$out{'ODR_L'} |
|
||||
$$out{'AFRL_H'} | $$out{'AFRL_L'} |
|
||||
$$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0)
|
||||
{
|
||||
push(@Col, sort({ $b cmp $a } @{$Port[$i]}));
|
||||
|
||||
$Set .= sprintf("%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')),
|
||||
join(", ", sort({ $b cmp $a } @{$Port[$i]})));
|
||||
|
||||
(($$out{'MODER_H'} | $$out{'MODER_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}MODER\n", $addr + ${GPIO_MODER}, $$out{'MODER_H'}, $$out{'MODER_L'}));
|
||||
|
||||
(($$out{'OTYPER_H'} | $$out{'OTYPER_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}OTYPER\n", $addr + ${GPIO_OTYPER}, $$out{'OTYPER_H'}, $$out{'OTYPER_L'}));
|
||||
|
||||
(($$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}OSPEEDR\n", $addr + ${GPIO_OSPEEDR}, $$out{'OSPEEDR_H'}, $$out{'OSPEEDR_L'}));
|
||||
|
||||
(($$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}PUPDR\n", $addr + ${GPIO_PUPDR}, $$out{'PUPDR_H'}, $$out{'PUPDR_L'}));
|
||||
|
||||
(($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}ODR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'}));
|
||||
|
||||
(($$out{'AFRL_H'} | $$out{'AFRL_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}AFRL\n", $addr + ${GPIO_AFRL}, $$out{'AFRL_H'}, $$out{'AFRL_L'}));
|
||||
|
||||
(($$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0) &&
|
||||
($Set .= sprintf("${Form}AFRH\n", $addr + ${GPIO_AFRH}, $$out{'AFRH_H'}, $$out{'AFRH_L'}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $Col = '';
|
||||
for (my $i = 0; $i < @Col; $i++)
|
||||
{
|
||||
if (($i % 6) == 0)
|
||||
{
|
||||
chop($Col);
|
||||
(($i + 1) < @Col) && ($Col .= "\n${Sep}#");
|
||||
}
|
||||
$Col .= sprintf(" %s,", $Col[$i]);
|
||||
}
|
||||
chop($Col);
|
||||
#printf("\n%s\n", $Pins);
|
||||
printf("%s\n", $Col);
|
||||
printf("%s\n", $Set);
|
|
@ -0,0 +1,123 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), crc32 (out)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - OCTOSPI io_base
|
||||
|
||||
* Clobbered:
|
||||
* r4 - tmp
|
||||
* r5 - address of OCTOSPI_DR
|
||||
* r6 - address of OCTOSPI_CCR
|
||||
* r7 - tmp
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR)
|
||||
|
||||
.macro octospi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #OCTOSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #OCTOSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
movs r4, #0x00 /* initialize crc */
|
||||
mvns r4, r4 /* to 0xFFFFFFFF */
|
||||
start_read:
|
||||
octospi_abort /* start in clean state */
|
||||
movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of OCTOSPI_DR */
|
||||
movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */
|
||||
adds r6, r6, r5 /* address of OCTOSPI_CCR */
|
||||
wait_busy
|
||||
ldr r7, cr_page_read /* indirect read mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then read to end of page */
|
||||
mov r7, r0 /* else read all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for read */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */
|
||||
ldr r7, tcr_page_read /* TCR for read */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_page_read /* IR for read */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
str r2, [r3, #OCTOSPI_AR] /* store SPI start address */
|
||||
ldr r6, =0x04C11DB7 /* CRC32 polynomial */
|
||||
read_loop:
|
||||
ldrb r7, [r5] /* read next byte from DR */
|
||||
lsls r7, r7, #24 /* shift into msb */
|
||||
eors r4, r4, r7
|
||||
.rept 8 /* unrolled bit loop */
|
||||
asrs r7, r4, #31 /* copy bit 31 into bits 0 to 31 */
|
||||
ands r7, r7, r6 /* r7 neg. -> CRC32XOR, pos. -> 0x0 */
|
||||
lsls r4, r4, #1 /* shift result */
|
||||
eors r4, r4, r7 /* eor by CRC32XOR or 0x0 */
|
||||
.endr
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi exit /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne read_loop /* if not, then next byte */
|
||||
page_end:
|
||||
bal start_read /* then next page */
|
||||
.pool
|
||||
|
||||
exit:
|
||||
mvns r0, r4 /* invert to get final result */
|
||||
octospi_abort /* to idle state */
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
cr_page_read:
|
||||
.space 4 /* OCTOSPI_CR value for read command */
|
||||
ccr_page_read:
|
||||
.space 4 /* OCTOSPI_CCR value for read command */
|
||||
tcr_page_read:
|
||||
.space 4 /* OCTOSPI_TCR value for read command */
|
||||
ir_page_read:
|
||||
.space 4 /* OCTOSPI_IR value for read command */
|
|
@ -0,0 +1,13 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
|
||||
0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,
|
||||
0x5f,0x62,0x22,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,
|
||||
0x07,0x46,0x1f,0x64,0x1e,0x4f,0x37,0x60,0x1e,0x4f,0xb7,0x60,0x1e,0x4f,0x37,0x61,
|
||||
0x9a,0x64,0x15,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
|
||||
0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
|
||||
0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
|
||||
0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
|
||||
0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38,0x05,0xd4,
|
||||
0x0a,0x42,0xd7,0xd1,0xb8,0xe7,0x00,0x00,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25,
|
||||
0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,108 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - sector count
|
||||
* r1 - QSPI io_base
|
||||
|
||||
* Clobbered:
|
||||
* r2 - r7 tmp */
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR)
|
||||
|
||||
.macro octospi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r1, #OCTOSPI_CR] /* get OCTOSPI_CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r1, #OCTOSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r1, #OCTOSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r1, #OCTOSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
adr r2, buffer /* pointer to start of buffer */
|
||||
movs r3, #OCTOSPI_DR /* load OCTOSPI_DR address offset */
|
||||
adds r3, r3, r1 /* address of OCTOSPI_DR */
|
||||
sector_start:
|
||||
octospi_abort /* start in clean state */
|
||||
movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */
|
||||
adds r6, r6, r3 /* address of OCTOSPI_CCR */
|
||||
wait_busy
|
||||
ldr r7, cr_page_read /* indirect read mode */
|
||||
str r7, [r1, #OCTOSPI_CR] /* set mode */
|
||||
ldmia r2!, {r4, r5} /* load address offset, length */
|
||||
subs r2, r2, #4 /* point to length */
|
||||
subs r5, r5, #1 /* decrement sector length for DLR */
|
||||
str r5, [r1, #OCTOSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for read */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */
|
||||
ldr r7, tcr_page_read /* TCR for read */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_page_read /* IR for read */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
str r4, [r1, #OCTOSPI_AR] /* store SPI start address */
|
||||
ldr r6, [r2, #4] /* load initial value */
|
||||
read_loop:
|
||||
ldrb r4, [r3, #0] /* read next byte from DR */
|
||||
movs r7, #0xFF /* fill bits 8-15 */
|
||||
lsls r7, r7, #8 /* with ones */
|
||||
orrs r4, r4, r7 /* copy ones to left of read byte */
|
||||
ands r6, r6, r4 /* and read byte to result */
|
||||
lsls r4, r4, #8 /* shift result into higher byte */
|
||||
orrs r6, r6, r4 /* or read byte to result */
|
||||
subs r5, r5, #1 /* decrement byte (count-1) */
|
||||
bpl read_loop /* again if sector not completed */
|
||||
adds r5, r5, #1 /* increment count due to the -1 */
|
||||
stmia r2!, {r5, r6} /* save final count and result for sector */
|
||||
subs r0, r0, #1 /* decrement sector count */
|
||||
bne sector_start /* next sector? */
|
||||
octospi_abort /* to idle state */
|
||||
|
||||
exit:
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
cr_page_read:
|
||||
.space 4 /* OCTOSPI_CR value for read command */
|
||||
ccr_page_read:
|
||||
.space 4 /* OCTOSPI_CCR value for read command */
|
||||
tcr_page_read:
|
||||
.space 4 /* OCTOSPI_TCR value for read command */
|
||||
ir_page_read:
|
||||
.space 4 /* OCTOSPI_IR value for read command */
|
||||
|
||||
.equ buffer, .
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x1b,0xa2,0x50,0x23,0x5b,0x18,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xb0,0x26,
|
||||
0xf6,0x18,0x0f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x4f,0x62,0x10,0x4f,0x0f,0x60,
|
||||
0x30,0xca,0x04,0x3a,0x01,0x3d,0x0d,0x64,0x0e,0x4f,0x37,0x60,0x0e,0x4f,0xb7,0x60,
|
||||
0x0e,0x4f,0x37,0x61,0x8c,0x64,0x56,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43,
|
||||
0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38,
|
||||
0xd9,0xd1,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,142 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), remaining bytes (out, 0 means successful)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - OCTOSPI io_base
|
||||
* r8 - fifo start
|
||||
* r9 - fifo end + 1
|
||||
|
||||
* Clobbered:
|
||||
* r4 - wp
|
||||
* r5 - address of OCTOSPI_DR
|
||||
* r6 - address of OCTOSPI_CCR
|
||||
* r7 - tmp
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR)
|
||||
|
||||
.macro octospi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #OCTOSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #OCTOSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
ldr r4, wp /* load wp */
|
||||
start_read:
|
||||
octospi_abort /* start in clean state */
|
||||
movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of OCTOSPI_DR */
|
||||
movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */
|
||||
adds r6, r6, r5 /* address of OCTOSPI_CCR */
|
||||
wait_busy
|
||||
ldr r7, cr_page_read /* indirect read mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then write to end of page */
|
||||
mov r7, r0 /* else write all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for read */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */
|
||||
ldr r7, tcr_page_read /* TCR for read */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_page_read /* IR for read */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
str r2, [r3, #OCTOSPI_AR] /* store SPI start address */
|
||||
read_loop:
|
||||
ldrb r7, [r5] /* read next byte from DR */
|
||||
strb r7, [r4, #0] /* write next byte */
|
||||
adds r4, r4, #1 /* increment internal wp */
|
||||
cmp r4, r9 /* internal wp beyond end? */
|
||||
blo wait_fifo /* if no, then ok */
|
||||
mov r4, r8 /* else wrap around */
|
||||
wait_fifo:
|
||||
ldr r7, rp /* get rp */
|
||||
cmp r7, #0 /* if rp equals 0 */
|
||||
beq exit /* then abort */
|
||||
cmp r4, r7 /* check if fifo full */
|
||||
beq wait_fifo /* wait until not full */
|
||||
adr r7, wp /* get address of wp */
|
||||
str r4, [r7] /* store updated wp */
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi exit /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne read_loop /* if not, then next byte */
|
||||
page_end:
|
||||
bal start_read /* then next page */
|
||||
|
||||
exit:
|
||||
adds r0, r0, #1 /* increment count due to the -1 */
|
||||
octospi_abort /* to idle state */
|
||||
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
cr_page_read:
|
||||
.space 4 /* OCTOSPI_CR value for read command */
|
||||
ccr_page_read:
|
||||
.space 4 /* OCTOSPI_CCR value for read command */
|
||||
tcr_page_read:
|
||||
.space 4 /* OCTOSPI_TCR value for read command */
|
||||
ir_page_read:
|
||||
.space 4 /* OCTOSPI_IR value for read command */
|
||||
|
||||
.equ wp, . /* wp, uint32_t */
|
||||
.equ rp, wp + 4 /* rp, uint32_t */
|
||||
.equ buffer, rp + 4 /* buffer follows right away */
|
|
@ -0,0 +1,12 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x27,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25,
|
||||
0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,
|
||||
0x1c,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,
|
||||
0x1f,0x64,0x19,0x4f,0x37,0x60,0x19,0x4f,0xb7,0x60,0x19,0x4f,0x37,0x61,0x9a,0x64,
|
||||
0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0x4f,0x00,0x2f,
|
||||
0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,0x02,0xd4,
|
||||
0x0a,0x42,0xed,0xd1,0xcf,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
|
||||
0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,219 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), remaining bytes (out, 0 means successful)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - OCTOSPI io_base
|
||||
* r8 - fifo start
|
||||
* r9 - fifo end + 1
|
||||
|
||||
* Clobbered:
|
||||
* r4 - rp
|
||||
* r5 - address of OCTOSPI_DR
|
||||
* r6 - address of OCTOSPI_CCR
|
||||
* r7 - tmp
|
||||
* r10 - single 0x0 / dual 0x1
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR)
|
||||
#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR)
|
||||
|
||||
.macro octospi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #OCTOSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #OCTOSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
ldr r4, rp /* load rp */
|
||||
ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI_CR register */
|
||||
lsls r7, r7, #(31-SPI_DUAL_FLASH) /* clear higher order bits */
|
||||
lsrs r7, r7, #31 /* DUAL_FLASH bit into bit 0 */
|
||||
mov r10, r7 /* save in r10 */
|
||||
wip_loop:
|
||||
octospi_abort /* start in clean state */
|
||||
movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of OCTOSPI_DR */
|
||||
movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */
|
||||
adds r6, r6, r5 /* address of OCTOSPI_CCR */
|
||||
wait_busy
|
||||
ldr r7, cr_read_status /* indirect read mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
mov r7, r10 /* get dual bit */
|
||||
str r7, [r3, #OCTOSPI_DLR] /* one or two (for dual) bytes */
|
||||
ldr r7, ccr_read_status /* CCR for status read */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate status read */
|
||||
ldr r7, tcr_read_status /* TCR for status read */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_read_status /* IR for status read */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
movs r7, #0 /* dummy address */
|
||||
str r7, [r3, #OCTOSPI_AR] /* into AR (for 8-line mode) */
|
||||
ldrb r7, [r5] /* get first status register */
|
||||
lsrs r7, r7, #(SPIFLASH_BSY+1) /* if first flash busy, */
|
||||
bcs wip_loop /* then poll again */
|
||||
mov r7, r10 /* get dual bit */
|
||||
tst r7, r7 /* dual mode ? */
|
||||
beq write_enable /* not dual, then ok */
|
||||
ldrb r7, [r5] /* get second status register */
|
||||
lsrs r7, r7, #(SPIFLASH_BSY+1) /* if second flash busy, */
|
||||
bcs wip_loop /* then poll again */
|
||||
write_enable:
|
||||
tst r0, r0 /* test residual count */
|
||||
bmi exit /* if negative, then finished */
|
||||
wait_busy
|
||||
ldr r7, cr_write_enable /* indirect write mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
ldr r7, ccr_write_enable /* CCR for write enable */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate write enable */
|
||||
ldr r7, tcr_write_enable /* TCR for write enable */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* write enable instruction */
|
||||
ldr r7, ir_write_enable /* IR for write enable */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
movs r7, #0 /* silicon bug in L5? dummy write */
|
||||
str r7, [r3, #OCTOSPI_AR] /* into AR resolves issue */
|
||||
wait_busy
|
||||
ldr r7, cr_read_status /* indirect read mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
mov r7, r10 /* get dual count */
|
||||
str r7, [r3, #OCTOSPI_DLR] /* one or two (for dual) bytes */
|
||||
ldr r7, ccr_read_status /* CCR for status read */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate status read */
|
||||
ldr r7, tcr_read_status /* TCR for status read */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_read_status /* IR for status read */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
movs r7, #0 /* dummy address */
|
||||
str r7, [r3, #OCTOSPI_AR] /* into AR (for 8-line mode) */
|
||||
ldrb r7, [r5] /* get first status register */
|
||||
lsrs r7, r7, #(SPIFLASH_WE+1) /* if first flash not */
|
||||
bcc error /* write enabled, then error */
|
||||
mov r7, r10 /* get dual bit */
|
||||
tst r7, r7 /* dual mode ? */
|
||||
beq start_write /* not dual, then ok */
|
||||
ldrb r7, [r5] /* get second status register */
|
||||
lsrs r7, r7, #(SPIFLASH_WE+1) /* if second flash not */
|
||||
bcc error /* write enabled, then error */
|
||||
start_write:
|
||||
wait_busy
|
||||
ldr r7, cr_page_write /* indirect write mode */
|
||||
str r7, [r3, #OCTOSPI_CR] /* set mode */
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then write to end of page */
|
||||
mov r7, r0 /* else write all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_write /* CCR for page write */
|
||||
str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */
|
||||
ldr r7, tcr_page_write /* TCR for page write */
|
||||
str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */
|
||||
ldr r7, ir_page_write /* IR for page write */
|
||||
str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */
|
||||
str r2, [r3, #OCTOSPI_AR] /* store SPI start address */
|
||||
write_loop:
|
||||
ldr r7, wp /* get wp */
|
||||
cmp r7, #0 /* if wp equals 0 */
|
||||
beq exit /* then abort */
|
||||
cmp r4, r7 /* check if fifo empty */
|
||||
beq write_loop /* wait until not empty */
|
||||
ldrb r7, [r4, #0] /* read next byte */
|
||||
strb r7, [r5] /* write next byte to DR */
|
||||
adds r4, r4, #1 /* increment internal rp */
|
||||
cmp r4, r9 /* internal rp beyond end? */
|
||||
blo upd_write /* if no, then ok */
|
||||
mov r4, r8 /* else wrap around */
|
||||
upd_write:
|
||||
adr r7, rp /* get address of rp */
|
||||
str r4, [r7] /* store updated rp */
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi page_end /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne write_loop /* if not, then next byte */
|
||||
page_end:
|
||||
ldr r7, [r3, #OCTOSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_TCF+1) /* shift TCF into C */
|
||||
bcc page_end /* loop until TCF set */
|
||||
bal wip_loop /* then next page */
|
||||
|
||||
error:
|
||||
movs r0, #0 /* return 0xFFFFFFFF */
|
||||
subs r0, r0, #2 /* for error */
|
||||
exit:
|
||||
adds r0, r0, #1 /* increment count due to the -1 */
|
||||
octospi_abort /* to idle state */
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
cr_read_status:
|
||||
.space 4 /* OCTOSPI_CR value for READ_STATUS command */
|
||||
ccr_read_status:
|
||||
.space 4 /* OCTOSPI_CCR value for READ_STATUS command */
|
||||
tcr_read_status:
|
||||
.space 4 /* OCTOSPI_TCR value for READ_STATUS command */
|
||||
ir_read_status:
|
||||
.space 4 /* OCTOSPI_IR value for READ_STATUS command */
|
||||
|
||||
cr_write_enable:
|
||||
.space 4 /* OCTOSPI_CR value for WRITE_ENABLE command */
|
||||
ccr_write_enable:
|
||||
.space 4 /* OCTOSPI_CCR value for WRITE_ENABLE command */
|
||||
tcr_write_enable:
|
||||
.space 4 /* OCTOSPI_TCR value for WRITE_ENABLE command */
|
||||
ir_write_enable:
|
||||
.space 4 /* OCTOSPI_IR value for WRITE_ENABLE command */
|
||||
|
||||
cr_page_write:
|
||||
.space 4 /* OCTOSPI_CR value for PAGE_PROG command */
|
||||
ccr_page_write:
|
||||
.space 4 /* OCTOSPI_CCR value for PAGE_PROG command */
|
||||
tcr_page_write:
|
||||
.space 4 /* OCTOSPI_TCR value for PAGE_PROG command */
|
||||
ir_page_write:
|
||||
.space 4 /* OCTOSPI_IR value for PAGE_PROG command */
|
||||
|
||||
.equ wp, . /* wp, uint32_t */
|
||||
.equ rp, wp + 4 /* rp, uint32_t */
|
||||
.equ buffer, rp + 4 /* buffer follows right away */
|
|
@ -0,0 +1,21 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x4f,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25,
|
||||
0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,
|
||||
0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x39,0x4f,0x1f,0x60,0x57,0x46,0x1f,0x64,
|
||||
0x38,0x4f,0x37,0x60,0x38,0x4f,0xb7,0x60,0x38,0x4f,0x37,0x61,0x00,0x27,0x9f,0x64,
|
||||
0x2f,0x78,0x7f,0x08,0xe3,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08,
|
||||
0xdd,0xd2,0x00,0x42,0x55,0xd4,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,
|
||||
0x2f,0x4f,0x1f,0x60,0x2f,0x4f,0x37,0x60,0x2f,0x4f,0xb7,0x60,0x2f,0x4f,0x37,0x61,
|
||||
0x00,0x27,0x9f,0x64,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x24,0x4f,
|
||||
0x1f,0x60,0x57,0x46,0x1f,0x64,0x23,0x4f,0x37,0x60,0x23,0x4f,0xb7,0x60,0x23,0x4f,
|
||||
0x37,0x61,0x00,0x27,0x9f,0x64,0x2f,0x78,0xbf,0x08,0x30,0xd3,0x57,0x46,0x3f,0x42,
|
||||
0x02,0xd0,0x2f,0x78,0xbf,0x08,0x2a,0xd3,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,
|
||||
0x5f,0x62,0x1f,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,
|
||||
0x07,0x46,0x1f,0x64,0x1b,0x4f,0x37,0x60,0x1b,0x4f,0xb7,0x60,0x1b,0x4f,0x37,0x61,
|
||||
0x9a,0x64,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42,0xfa,0xd0,0x27,0x78,0x2f,0x70,
|
||||
0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,
|
||||
0x01,0xd4,0x0a,0x42,0xed,0xd1,0x1f,0x6a,0xbf,0x08,0xfc,0xd3,0x87,0xe7,0x00,0x20,
|
||||
0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x00,0xbe,0xc0,0x46,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,108 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), crc32 (out)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - QSPI io_base
|
||||
|
||||
* Clobbered:
|
||||
* r4 - rp
|
||||
* r5 - address of QSPI_DR
|
||||
* r7 - tmp
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
.macro qspi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #QSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #QSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #QSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
movs r4, #0x00 /* initialize crc */
|
||||
mvns r4, r4 /* to 0xFFFFFFFF */
|
||||
start_read:
|
||||
qspi_abort /* start in clean state */
|
||||
movs r5, #QSPI_DR /* load QSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of QSPI_DR */
|
||||
wait_busy
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then read to end of page */
|
||||
mov r7, r0 /* else read all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for page read */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate transfer */
|
||||
str r2, [r3, #QSPI_AR] /* store SPI start address */
|
||||
ldr r7, [r3, #QSPI_SR] /* wait for command startup */
|
||||
ldr r6, =0x04C11DB7 /* CRC32 polynomial */
|
||||
read_loop:
|
||||
ldrb r7, [r5] /* read next byte from DR */
|
||||
lsls r7, r7, #24 /* shift into msb */
|
||||
eors r4, r4, r7
|
||||
.rept 8 /* unrolled bit loop */
|
||||
asrs r7, r4, #31 /* copy bit 31 into bits 0 to 31 */
|
||||
ands r7, r7, r6 /* r7 neg. -> CRC32XOR, pos. -> 0x0 */
|
||||
lsls r4, r4, #1 /* shift result */
|
||||
eors r4, r4, r7 /* eor by CRC32XOR or 0x0 */
|
||||
.endr
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi exit /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne read_loop /* if not, then next byte */
|
||||
page_end:
|
||||
bal start_read /* then next page */
|
||||
.pool
|
||||
|
||||
exit:
|
||||
mvns r0, r4 /* invert to get final result */
|
||||
qspi_abort /* to idle state */
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_page_read:
|
||||
.space 4 /* QSPI_CCR value for read command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
|
@ -0,0 +1,12 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
|
||||
0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46,
|
||||
0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x1c,0x4f,0x5f,0x61,
|
||||
0x9a,0x61,0x9f,0x68,0x14,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40,
|
||||
0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
|
||||
0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
|
||||
0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
|
||||
0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38,
|
||||
0x04,0xd4,0x0a,0x42,0xd7,0xd1,0xbf,0xe7,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25,
|
||||
0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,91 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - sector count
|
||||
* r1 - QSPI io_base
|
||||
|
||||
* Clobbered:
|
||||
* r2 - r7 tmp */
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
.macro qspi_abort
|
||||
movs r4, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r1, #QSPI_CR] /* get QSPI_CR register */
|
||||
orrs r7, r7, r4 /* set abort bit */
|
||||
str r7, [r1, #QSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r1, #QSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r1, #QSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
adr r2, buffer /* pointer to start of buffer */
|
||||
movs r3, #QSPI_DR /* load QSPI_DR address offset */
|
||||
add r3, r3, r1 /* address of QSPI_DR */
|
||||
sector_start:
|
||||
qspi_abort /* start in clean state */
|
||||
ldmia r2!, {r4, r5, r6} /* load address offset, length, initial value */
|
||||
subs r2, r2, #8 /* point to length */
|
||||
subs r5, r5, #1 /* decrement sector length for DLR */
|
||||
wait_busy
|
||||
str r5, [r1, #QSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for page read */
|
||||
str r7, [r1, #QSPI_CCR] /* initiate transfer */
|
||||
str r4, [r1, #QSPI_AR] /* store SPI start address */
|
||||
ldr r7, [r1, #QSPI_SR] /* wait for command startup */
|
||||
read_loop:
|
||||
ldrb r4, [r3] /* read next byte from DR */
|
||||
movs r7, #0xFF /* fill bits 8-15 */
|
||||
lsls r7, r7, #8 /* with ones */
|
||||
orrs r4, r4, r7 /* copy ones to left of read byte */
|
||||
ands r6, r6, r4 /* and read byte to result */
|
||||
lsls r4, r4, #8 /* shift result into higher byte */
|
||||
orrs r6, r6, r4 /* or read byte to result */
|
||||
subs r5, r5, #1 /* decrement byte (count-1) */
|
||||
bpl read_loop /* again if sector not completed */
|
||||
adds r5, r5, #1 /* increment count due to the -1 */
|
||||
stmia r2!, {r5, r6} /* save final count and result for sector */
|
||||
subs r0, r0, #1 /* decrement sector count */
|
||||
bne sector_start /* next sector? */
|
||||
qspi_abort /* to idle state */
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_page_read:
|
||||
.space 4 /* QSPI_CCR value for read command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.equ buffer, .
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x17,0xa2,0x20,0x23,0x0b,0x44,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0x70,0xca,
|
||||
0x08,0x3a,0x01,0x3d,0x8f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xcf,0x60,0x0d,0x61,
|
||||
0x0c,0x4f,0x4f,0x61,0x8c,0x61,0x8f,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43,
|
||||
0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38,
|
||||
0xe1,0xd1,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,127 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), remaining bytes (out, 0 means successful)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - QSPI io_base
|
||||
* r8 - fifo start
|
||||
* r9 - fifo end + 1
|
||||
|
||||
* Clobbered:
|
||||
* r4 - wp
|
||||
* r5 - address of QSPI_DR
|
||||
* r7 - tmp
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
.macro qspi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #QSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #QSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #QSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
ldr r4, wp /* load wp */
|
||||
start_read:
|
||||
qspi_abort /* start in clean state */
|
||||
movs r5, #QSPI_DR /* load QSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of QSPI_DR */
|
||||
wait_busy
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then read to end of page */
|
||||
mov r7, r0 /* else read all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_read /* CCR for page read */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate transfer */
|
||||
str r2, [r3, #QSPI_AR] /* store SPI start address */
|
||||
ldr r7, [r3, #QSPI_SR] /* wait for command startup */
|
||||
read_loop:
|
||||
ldrb r7, [r5] /* read next byte from DR */
|
||||
strb r7, [r4, #0] /* write next byte */
|
||||
adds r4, r4, #1 /* increment internal wp */
|
||||
cmp r4, r9 /* internal wp beyond end? */
|
||||
blo wait_fifo /* if no, then ok */
|
||||
mov r4, r8 /* else wrap around */
|
||||
wait_fifo:
|
||||
ldr r7, rp /* get rp */
|
||||
cmp r7, #0 /* if rp equals 0 */
|
||||
beq exit /* then abort */
|
||||
cmp r4, r7 /* check if fifo full */
|
||||
beq wait_fifo /* wait until not full */
|
||||
adr r7, wp /* get address of wp */
|
||||
str r4, [r7] /* store updated wp */
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi exit /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne read_loop /* if not, then next byte */
|
||||
page_end:
|
||||
bal start_read /* then next page */
|
||||
|
||||
exit:
|
||||
adds r0, r0, #1 /* increment count due to the -1 */
|
||||
qspi_abort /* to idle state */
|
||||
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_page_read:
|
||||
.space 4 /* QSPI_CCR value for read command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.equ wp, . /* wp, uint32_t */
|
||||
.equ rp, wp + 4 /* rp, uint32_t */
|
||||
.equ buffer, rp + 4 /* buffer follows right away */
|
|
@ -0,0 +1,11 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x24,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25,
|
||||
0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46,0x0f,0x43,
|
||||
0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x18,0x4f,0x5f,0x61,0x9a,0x61,
|
||||
0x9f,0x68,0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0x4f,
|
||||
0x00,0x2f,0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,
|
||||
0x02,0xd4,0x0a,0x42,0xed,0xd1,0xd6,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,
|
||||
0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
@ -0,0 +1,177 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2016 - 2018 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
.text
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
.thumb_func
|
||||
|
||||
/* Params:
|
||||
* r0 - total count (bytes), remaining bytes (out, 0 means successful)
|
||||
* r1 - flash page size
|
||||
* r2 - address offset into flash
|
||||
* r3 - QSPI io_base
|
||||
* r8 - fifo start
|
||||
* r9 - fifo end + 1
|
||||
|
||||
* Clobbered:
|
||||
* r4 - rp
|
||||
* r5 - address of QSPI_DR
|
||||
* r7 - tmp
|
||||
* r10 - single 0x0 / dual 0x1
|
||||
*/
|
||||
|
||||
#include "../../../../src/flash/nor/stmqspi.h"
|
||||
|
||||
.macro qspi_abort
|
||||
movs r5, #(1<<SPI_ABORT) /* abort bit mask */
|
||||
ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */
|
||||
orrs r7, r7, r5 /* set abort bit */
|
||||
str r7, [r3, #QSPI_CR] /* store new CR register */
|
||||
.endm
|
||||
|
||||
.macro wait_busy
|
||||
0:
|
||||
ldr r7, [r3, #QSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */
|
||||
bcs 0b /* loop until BUSY cleared */
|
||||
movs r7, #(1<<SPI_TCF) /* TCF bitmask */
|
||||
str r7, [r3, #QSPI_FCR] /* clear TCF flag */
|
||||
.endm
|
||||
|
||||
start:
|
||||
subs r0, r0, #1 /* decrement count for DLR */
|
||||
subs r1, r1, #1 /* page size mask and for DLR */
|
||||
ldr r4, rp /* load rp */
|
||||
ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */
|
||||
lsls r7, r7, #(31-SPI_DUAL_FLASH) /* clear higher order bits */
|
||||
lsrs r7, r7, #31 /* DUAL_FLASH bit into bit 0 */
|
||||
mov r10, r7 /* save in r10 */
|
||||
wip_loop:
|
||||
qspi_abort /* start in clean state */
|
||||
movs r5, #QSPI_DR /* load QSPI_DR address offset */
|
||||
adds r5, r5, r3 /* address of QSPI_DR */
|
||||
wait_busy
|
||||
mov r7, r10 /* get dual bit */
|
||||
str r7, [r3, #QSPI_DLR] /* one or two (for dual) bytes */
|
||||
ldr r7, ccr_read_status /* CCR for status read */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate status read */
|
||||
ldr r7, [r3, #QSPI_SR] /* wait for command startup */
|
||||
ldrb r7, [r5] /* get first status register */
|
||||
lsrs r7, r7, #(SPIFLASH_BSY+1) /* if first flash busy, */
|
||||
bcs wip_loop /* then poll again */
|
||||
mov r7, r10 /* get dual bit */
|
||||
tst r7, r7 /* dual mode ? */
|
||||
beq write_enable /* not dual, then ok */
|
||||
ldrb r7, [r5] /* get second status register */
|
||||
lsrs r7, r7, #(SPIFLASH_BSY+1) /* if second flash busy, */
|
||||
bcs wip_loop /* then poll again */
|
||||
write_enable:
|
||||
tst r0, r0 /* test residual count */
|
||||
bmi exit /* if negative, then finished */
|
||||
wait_busy
|
||||
ldr r7, ccr_write_enable /* CCR for write enable */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate write enable */
|
||||
wait_busy
|
||||
mov r7, r10 /* get dual bit */
|
||||
str r7, [r3, #QSPI_DLR] /* one or two (for dual) bytes */
|
||||
ldr r7, ccr_read_status /* CCR for status read */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate status read */
|
||||
ldr r7, [r3, #QSPI_SR] /* wait for command startup */
|
||||
ldrb r7, [r5] /* get first status register */
|
||||
lsrs r7, r7, #(SPIFLASH_WE+1) /* if first flash not */
|
||||
bcc error /* write enabled, then error */
|
||||
mov r7, r10 /* get dual bit */
|
||||
tst r7, r7 /* dual mode ? */
|
||||
beq start_write /* not dual, then ok */
|
||||
ldrb r7, [r5] /* get second status register */
|
||||
lsrs r7, r7, #(SPIFLASH_WE+1) /* if second flash not */
|
||||
bcc error /* write enabled, then error */
|
||||
start_write:
|
||||
wait_busy
|
||||
mov r7, r2 /* get current start address */
|
||||
orrs r7, r7, r1 /* end of current page */
|
||||
subs r7, r7, r2 /* count-1 to end of page */
|
||||
cmp r7, r0 /* if this count <= remaining */
|
||||
bls write_dlr /* then write to end of page */
|
||||
mov r7, r0 /* else write all remaining */
|
||||
write_dlr:
|
||||
str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */
|
||||
ldr r7, ccr_page_write /* CCR for page write */
|
||||
str r7, [r3, #QSPI_CCR] /* initiate transfer */
|
||||
str r2, [r3, #QSPI_AR] /* store SPI start address */
|
||||
ldr r7, [r3, #QSPI_SR] /* wait for command startup */
|
||||
write_loop:
|
||||
ldr r7, wp /* get wp */
|
||||
cmp r7, #0 /* if wp equals 0 */
|
||||
beq exit /* then abort */
|
||||
cmp r4, r7 /* check if fifo empty */
|
||||
beq write_loop /* wait until not empty */
|
||||
ldrb r7, [r4, #0] /* read next byte */
|
||||
strb r7, [r5] /* write next byte to DR */
|
||||
adds r4, r4, #1 /* increment internal rp */
|
||||
cmp r4, r9 /* internal rp beyond end? */
|
||||
blo upd_write /* if no, then ok */
|
||||
mov r4, r8 /* else wrap around */
|
||||
upd_write:
|
||||
adr r7, rp /* get address of rp */
|
||||
str r4, [r7] /* store updated rp */
|
||||
adds r2, r2, #1 /* increment address */
|
||||
subs r0, r0, #1 /* decrement (count-1) */
|
||||
bmi page_end /* stop if no data left */
|
||||
tst r2, r1 /* page end ? */
|
||||
bne write_loop /* if not, then next byte */
|
||||
page_end:
|
||||
ldr r7, [r3, #QSPI_SR] /* load status */
|
||||
lsrs r7, r7, #(SPI_TCF+1) /* shift TCF into C */
|
||||
bcc page_end /* loop until TCF set */
|
||||
bal wip_loop /* then next page */
|
||||
|
||||
error:
|
||||
movs r0, #0 /* return 0xFFFFFFFF */
|
||||
subs r0, r0, #2 /* for error */
|
||||
exit:
|
||||
adds r0, r0, #1 /* increment count due to the -1 */
|
||||
qspi_abort /* to idle state */
|
||||
|
||||
.align 2 /* align to word, bkpt is 4 words */
|
||||
bkpt #0 /* before code end for exit_point */
|
||||
.align 2 /* align to word */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_read_status:
|
||||
.space 4 /* QSPI_CCR value for READ_STATUS command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_write_enable:
|
||||
.space 4 /* QSPI_CCR value for WRITE_ENABLE command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.space 4 /* not used */
|
||||
ccr_page_write:
|
||||
.space 4 /* QSPI_CCR value for PAGE_PROG command */
|
||||
.space 4 /* not used */
|
||||
.space 4 /* not used */
|
||||
|
||||
.equ wp, . /* wp, uint32_t */
|
||||
.equ rp, wp + 4 /* rp, uint32_t */
|
||||
.equ buffer, rp + 4 /* buffer follows right away */
|
|
@ -0,0 +1,18 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x01,0x38,0x01,0x39,0x41,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25,
|
||||
0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,
|
||||
0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61,0x2c,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78,
|
||||
0x7f,0x08,0xec,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08,0xe6,0xd2,
|
||||
0x00,0x42,0x41,0xd4,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x27,0x4f,
|
||||
0x5f,0x61,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61,
|
||||
0x1e,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78,0xbf,0x08,0x2b,0xd3,0x57,0x46,0x3f,0x42,
|
||||
0x02,0xd0,0x2f,0x78,0xbf,0x08,0x25,0xd3,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,
|
||||
0xdf,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,
|
||||
0x1a,0x4f,0x5f,0x61,0x9a,0x61,0x9f,0x68,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42,
|
||||
0xfa,0xd0,0x27,0x78,0x2f,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0xa7,
|
||||
0x3c,0x60,0x01,0x32,0x01,0x38,0x01,0xd4,0x0a,0x42,0xed,0xd1,0x9f,0x68,0xbf,0x08,
|
||||
0xfc,0xd3,0xa4,0xe7,0x00,0x20,0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,
|
||||
0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
336
doc/openocd.texi
336
doc/openocd.texi
|
@ -30,9 +30,9 @@ of the Open On-Chip Debugger (OpenOCD).
|
|||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.2 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the section entitled ``GNU
|
||||
Free Documentation License''.
|
||||
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
|
||||
copy of the license is included in the section entitled ``GNU Free
|
||||
Documentation License''.
|
||||
@end quotation
|
||||
@end copying
|
||||
|
||||
|
@ -79,7 +79,6 @@ Free Documentation License''.
|
|||
* JTAG Commands:: JTAG Commands
|
||||
* Boundary Scan Commands:: Boundary Scan Commands
|
||||
* Utility Commands:: Utility Commands
|
||||
* TFTP:: TFTP
|
||||
* GDB and OpenOCD:: Using GDB and OpenOCD
|
||||
* Tcl Scripting API:: Tcl Scripting API
|
||||
* FAQ:: Frequently Asked Questions
|
||||
|
@ -624,6 +623,13 @@ emulation model of target hardware.
|
|||
@item @b{xlnx_pcie_xvc}
|
||||
@* A JTAG driver exposing Xilinx Virtual Cable over PCI Express to OpenOCD as JTAG/SWD interface.
|
||||
|
||||
@item @b{linuxgpiod}
|
||||
@* A bitbang JTAG driver using Linux GPIO through library libgpiod.
|
||||
|
||||
@item @b{sysfsgpio}
|
||||
@* A bitbang JTAG driver using Linux legacy sysfs GPIO.
|
||||
This is deprecated from Linux v5.3; prefer using @b{linuxgpiod}.
|
||||
|
||||
@end itemize
|
||||
|
||||
@node About Jim-Tcl
|
||||
|
@ -2430,7 +2436,8 @@ and a specific set of GPIOs is used.
|
|||
@end deffn
|
||||
|
||||
@deffn {Interface Driver} {cmsis-dap}
|
||||
ARM CMSIS-DAP compliant based adapter.
|
||||
ARM CMSIS-DAP compliant based adapter v1 (USB HID based)
|
||||
or v2 (USB bulk).
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_vid_pid} [vid pid]+
|
||||
The vendor ID and product ID of the CMSIS-DAP device. If not specified
|
||||
|
@ -2446,6 +2453,23 @@ Specifies the @var{serial} of the CMSIS-DAP device to use.
|
|||
If not specified, serial numbers are not considered.
|
||||
@end deffn
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_backend} [@option{auto}|@option{usb_bulk}|@option{hid}]
|
||||
Specifies how to communicate with the adapter:
|
||||
|
||||
@itemize @minus
|
||||
@item @option{hid} Use HID generic reports - CMSIS-DAP v1
|
||||
@item @option{usb_bulk} Use USB bulk - CMSIS-DAP v2
|
||||
@item @option{auto} First try USB bulk CMSIS-DAP v2, if not found try HID CMSIS-DAP v1.
|
||||
This is the default if @command{cmsis_dap_backend} is not specified.
|
||||
@end itemize
|
||||
@end deffn
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_usb interface} [number]
|
||||
Specifies the @var{number} of the USB interface to use in v2 mode (USB bulk).
|
||||
In most cases need not to be specified and interfaces are searched by
|
||||
interface string or for user class interface.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {cmsis-dap info}
|
||||
Display various device information, like hardware version, firmware version, current bus status.
|
||||
@end deffn
|
||||
|
@ -2881,8 +2905,8 @@ The following example shows how to read 4 bytes from the EMUCOM channel 0x0:
|
|||
@deffn {Config} {jlink usb} <@option{0} to @option{3}>
|
||||
Set the USB address of the interface, in case more than one adapter is connected
|
||||
to the host. If not specified, USB addresses are not considered. Device
|
||||
selection via USB address is deprecated and the serial number should be used
|
||||
instead.
|
||||
selection via USB address is not always unambiguous. It is recommended to use
|
||||
the serial number instead, if possible.
|
||||
|
||||
As a configuration command, it can be used only before 'init'.
|
||||
@end deffn
|
||||
|
@ -3223,6 +3247,22 @@ pinout.
|
|||
@end deffn
|
||||
|
||||
|
||||
@deffn {Interface Driver} {linuxgpiod}
|
||||
Linux provides userspace access to GPIO through libgpiod since Linux kernel version v4.6.
|
||||
The driver emulates either JTAG and SWD transport through bitbanging.
|
||||
|
||||
See @file{interface/dln-2-gpiod.cfg} for a sample config.
|
||||
@end deffn
|
||||
|
||||
|
||||
@deffn {Interface Driver} {sysfsgpio}
|
||||
Linux legacy userspace access to GPIO through sysfs is deprecated from Linux kernel version v5.3.
|
||||
Prefer using @b{linuxgpiod}, instead.
|
||||
|
||||
See @file{interface/sysfsgpio-raspberrypi.cfg} for a sample config.
|
||||
@end deffn
|
||||
|
||||
|
||||
@deffn {Interface Driver} {openjtag}
|
||||
OpenJTAG compatible USB adapter.
|
||||
This defines some driver-specific commands:
|
||||
|
@ -4694,6 +4734,11 @@ possible values of the parameter @var{number}, which are not only numeric values
|
|||
Use this option to override, for this target only, the global parameter set with
|
||||
command @command{gdb_port}.
|
||||
@xref{gdb_port,,command gdb_port}.
|
||||
|
||||
@item @code{-gdb-max-connections} @var{number} -- EXPERIMENTAL: set the maximum
|
||||
number of GDB connections that are allowed for the target. Default is 1.
|
||||
A negative value for @var{number} means unlimited connections.
|
||||
See @xref{gdbmeminspect,,Using GDB as a non-intrusive memory inspector}.
|
||||
@end itemize
|
||||
@end deffn
|
||||
|
||||
|
@ -5249,6 +5294,18 @@ it has been removed by the @option{unlock} flag.
|
|||
|
||||
@end deffn
|
||||
|
||||
@deffn Command {flash verify_image} filename [offset] [type]
|
||||
Verify the image @file{filename} to the current target's flash bank(s).
|
||||
Parameters follow the description of 'flash write_image'.
|
||||
In contrast to the 'verify_image' command, for banks with specific
|
||||
verify method, that one is used instead of the usual target's read
|
||||
memory methods. This is necessary for flash banks not readable by
|
||||
ordinary memory reads.
|
||||
This command gives only an overall good/bad result for each bank, not
|
||||
addresses of individual failed bytes as it's intended only as quick
|
||||
check for successful programming.
|
||||
@end deffn
|
||||
|
||||
@section Other Flash commands
|
||||
@cindex flash protection
|
||||
|
||||
|
@ -5507,6 +5564,117 @@ flash bank $_FLASHNAME stmsmi 0xf8000000 0 0 0 $_TARGETNAME
|
|||
|
||||
@end deffn
|
||||
|
||||
@deffn {Flash Driver} stmqspi
|
||||
@cindex STMicroelectronics QuadSPI/OctoSPI Interface
|
||||
@cindex QuadSPI
|
||||
@cindex OctoSPI
|
||||
@cindex stmqspi
|
||||
Some devices from STMicroelectronics include a proprietary ``QuadSPI Interface''
|
||||
(e.g. STM32F4, STM32F7, STM32L4) or ``OctoSPI Interface'' (e.g. STM32L4+)
|
||||
controller able to drive one or even two (dual mode) external SPI flash devices.
|
||||
The OctoSPI is a superset of QuadSPI, its presence is detected automatically.
|
||||
Currently only the regular command mode is supported, whereas the HyperFlash
|
||||
mode is not.
|
||||
|
||||
QuadSPI/OctoSPI makes the flash contents directly accessible in the CPU address
|
||||
space; in case of dual mode both devices must be of the same type and are
|
||||
mapped in the same memory bank (even and odd addresses interleaved).
|
||||
CPU can directly read data, execute code (but not boot) from QuadSPI bank.
|
||||
|
||||
The 'flash bank' command only requires the @var{base} parameter and the extra
|
||||
parameter @var{io_base} in order to identify the memory bank. Both are fixed
|
||||
by hardware, see datasheet or RM. All other parameters are ignored.
|
||||
|
||||
The controller must be initialized after each reset and properly configured
|
||||
for memory-mapped read operation for the particular flash chip(s), for the full
|
||||
list of available register settings cf. the controller's RM. This setup is quite
|
||||
board specific (that's why booting from this memory is not possible). The
|
||||
flash driver infers all parameters from current controller register values when
|
||||
'flash probe @var{bank_id}' is executed.
|
||||
|
||||
Normal OpenOCD commands like @command{mdw} can be used to display the flash content,
|
||||
but only after proper controller initialization as decribed above. However,
|
||||
due to a silicon bug in some devices, attempting to access the very last word
|
||||
should be avoided.
|
||||
|
||||
It is possible to use two (even different) flash chips alternatingly, if individual
|
||||
bank chip selects are available. For some package variants, this is not the case
|
||||
due to limited pin count. To switch from one to another, adjust FSEL bit accordingly
|
||||
and re-issue 'flash probe bank_id'. Note that the bank base address will @emph{not}
|
||||
change, so the address spaces of both devices will overlap. In dual flash mode
|
||||
both chips must be identical regarding size and most other properties.
|
||||
|
||||
Block or sector protection internal to the flash chip is not handled by this
|
||||
driver at all, but can be dealt with manually by the 'cmd' command, see below.
|
||||
The sector protection via 'flash protect' command etc. is completely internal to
|
||||
openocd, intended only to prevent accidental erase or overwrite and it does not
|
||||
persist across openocd invocations.
|
||||
|
||||
OpenOCD contains a hardcoded list of flash devices with their properties,
|
||||
these are auto-detected. If a device is not included in this list, SFDP discovery
|
||||
is attempted. If this fails or gives inappropriate results, manual setting is
|
||||
required (see 'set' command).
|
||||
|
||||
@example
|
||||
flash bank $_FLASHNAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
|
||||
flash bank $_FLASHNAME stmqspi 0x70000000 0 0 0 $_TARGETNAME 0xA0001400
|
||||
@end example
|
||||
|
||||
There are three specific commands
|
||||
@deffn Command {stmqspi mass_erase} bank_id
|
||||
Clears sector protections and performs a mass erase. Works only if there is no
|
||||
chip specific write protection engaged.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {stmqspi set} bank_id name total_size page_size read_cmd fread_cmd pprg_cmd mass_erase_cmd sector_size sector_erase_cmd
|
||||
Set flash parameters: @var{name} human readable string, @var{total_size} size
|
||||
in bytes, @var{page_size} is write page size. @var{read_cmd}, @var{fread_cmd} and @var{pprg_cmd}
|
||||
are commands for reading and page programming. @var{fread_cmd} is used in DPI and QPI modes,
|
||||
@var{read_cmd} in normal SPI (single line) mode. @var{mass_erase_cmd}, @var{sector_size}
|
||||
and @var{sector_erase_cmd} are optional.
|
||||
|
||||
This command is required if chip id is not hardcoded yet and e.g. for EEPROMs or FRAMs
|
||||
which don't support an id command.
|
||||
|
||||
In dual mode parameters of both chips are set identically. The parameters refer to
|
||||
a single chip, so the whole bank gets twice the specified capacity etc.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {stmqspi cmd} bank_id resp_num cmd_byte ...
|
||||
If @var{resp_num} is zero, sends command @var{cmd_byte} and following data
|
||||
bytes. In dual mode command byte is sent to @emph{both} chips but data bytes are
|
||||
sent @emph{alternatingly} to chip 1 and 2, first to flash 1, second to flash 2, etc.,
|
||||
i.e. the total number of bytes (including cmd_byte) must be odd.
|
||||
|
||||
If @var{resp_num} is not zero, cmd and at most four following data bytes are
|
||||
sent, in dual mode @emph{simultaneously} to both chips. Then @var{resp_num} bytes
|
||||
are read interleaved from both chips starting with chip 1. In this case
|
||||
@var{resp_num} must be even.
|
||||
|
||||
Note the hardware dictated subtle difference of those two cases in dual-flash mode.
|
||||
|
||||
To check basic communication settings, issue
|
||||
@example
|
||||
stmqspi cmd bank_id 0 0x04; stmqspi cmd bank_id 1 0x05; stmqspi cmd bank_id 0 0x06; stmqspi cmd bank_id 1 0x05
|
||||
@end example
|
||||
for single flash mode or
|
||||
@example
|
||||
stmqspi cmd bank_id 0 0x04; stmqspi cmd bank_id 2 0x05; stmqspi cmd bank_id 0 0x06; stmqspi cmd bank_id 2 0x05
|
||||
@end example
|
||||
for dual flash mode. This should return the status register contents.
|
||||
|
||||
In 8-line mode, @var{cmd_byte} is sent twice - first time as given, second time
|
||||
complemented. Additionally, in 8-line mode only, some commands (e.g. Read Status)
|
||||
need a dummy address, e.g.
|
||||
@example
|
||||
stmqspi cmd bank_id 1 0x05 0x00 0x00 0x00 0x00
|
||||
@end example
|
||||
should return the status register contents.
|
||||
|
||||
@end deffn
|
||||
|
||||
@end deffn
|
||||
|
||||
@deffn {Flash Driver} mrvlqspi
|
||||
This driver supports QSPI flash controller of Marvell's Wireless
|
||||
Microcontroller platform.
|
||||
|
@ -6989,10 +7157,9 @@ The @var{num} parameter is a value shown by @command{flash banks}.
|
|||
@end deffn
|
||||
|
||||
@deffn {Flash Driver} stm32l4x
|
||||
All members of the STM32L4, STM32L4+, STM32WB, STM32WL and STM32G4
|
||||
All members of the STM32 G0, G4, L4, L4+, L5, WB and WL
|
||||
microcontroller families from STMicroelectronics include internal flash
|
||||
and use ARM Cortex-M4 cores.
|
||||
Additionally this driver supports STM32G0 family with ARM Cortex-M0+ core.
|
||||
and use ARM Cortex-M0+, M4 and M33 cores.
|
||||
The driver automatically recognizes a number of these chips using
|
||||
the chip identification register, and autoconfigures itself.
|
||||
|
||||
|
@ -8279,6 +8446,94 @@ the watchpoint should trigger. The value may be first be masked
|
|||
using @var{mask} to mark ``don't care'' fields.
|
||||
@end deffn
|
||||
|
||||
|
||||
@section Real Time Transfer (RTT)
|
||||
|
||||
Real Time Transfer (RTT) is an interface specified by SEGGER based on basic
|
||||
memory reads and writes to transfer data bidirectionally between target and host.
|
||||
The specification is independent of the target architecture.
|
||||
Every target that supports so called "background memory access", which means
|
||||
that the target memory can be accessed by the debugger while the target is
|
||||
running, can be used.
|
||||
This interface is especially of interest for targets without
|
||||
Serial Wire Output (SWO), such as ARM Cortex-M0, or where semihosting is not
|
||||
applicable because of real-time constraints.
|
||||
|
||||
@quotation Note
|
||||
The current implementation supports only single target devices.
|
||||
@end quotation
|
||||
|
||||
The data transfer between host and target device is organized through
|
||||
unidirectional up/down-channels for target-to-host and host-to-target
|
||||
communication, respectively.
|
||||
|
||||
@quotation Note
|
||||
The current implementation does not respect channel buffer flags.
|
||||
They are used to determine what happens when writing to a full buffer, for
|
||||
example.
|
||||
@end quotation
|
||||
|
||||
Channels are exposed via raw TCP/IP connections. One or more RTT servers can be
|
||||
assigned to each channel to make them accessible to an unlimited number
|
||||
of TCP/IP connections.
|
||||
|
||||
@deffn Command {rtt setup} address size ID
|
||||
Configure RTT for the currently selected target.
|
||||
Once RTT is started, OpenOCD searches for a control block with the
|
||||
identifier @var{ID} starting at the memory address @var{address} within the next
|
||||
@var{size} bytes.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt start}
|
||||
Start RTT.
|
||||
If the control block location is not known, OpenOCD starts searching for it.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt stop}
|
||||
Stop RTT.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt polling_interval [interval]}
|
||||
Display the polling interval.
|
||||
If @var{interval} is provided, set the polling interval.
|
||||
The polling interval determines (in milliseconds) how often the up-channels are
|
||||
checked for new data.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt channels}
|
||||
Display a list of all channels and their properties.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt channellist}
|
||||
Return a list of all channels and their properties as Tcl list.
|
||||
The list can be manipulated easily from within scripts.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt server start} port channel
|
||||
Start a TCP server on @var{port} for the channel @var{channel}.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {rtt server stop} port
|
||||
Stop the TCP sever with port @var{port}.
|
||||
@end deffn
|
||||
|
||||
The following example shows how to setup RTT using the SEGGER RTT implementation
|
||||
on the target device.
|
||||
|
||||
@example
|
||||
resume
|
||||
|
||||
rtt setup 0x20000000 2048 "SEGGER RTT"
|
||||
rtt start
|
||||
|
||||
rtt server start 9090 0
|
||||
@end example
|
||||
|
||||
In this example, OpenOCD searches the control block with the ID "SEGGER RTT"
|
||||
starting at 0x20000000 for 2048 bytes. The RTT channel 0 is exposed through the
|
||||
TCP/IP port 9090.
|
||||
|
||||
|
||||
@section Misc Commands
|
||||
|
||||
@cindex profiling
|
||||
|
@ -8593,7 +8848,7 @@ CTI is mandatory for core run control and each core has an individual
|
|||
CTI instance attached to it. OpenOCD has limited support for CTI using
|
||||
the @emph{cti} group of commands.
|
||||
|
||||
@deffn Command {cti create} cti_name @option{-dap} dap_name @option{-ap-num} apn @option{-ctibase} base_address
|
||||
@deffn Command {cti create} cti_name @option{-dap} dap_name @option{-ap-num} apn @option{-baseaddr} base_address
|
||||
Creates a CTI instance @var{cti_name} on the DAP instance @var{dap_name} on MEM-AP
|
||||
@var{apn}. The @var{base_address} must match the base address of the CTI
|
||||
on the respective MEM-AP. All arguments are mandatory. This creates a
|
||||
|
@ -9201,7 +9456,7 @@ Selects whether interrupts will be processed when single stepping
|
|||
@cindex ITM
|
||||
@cindex ETM
|
||||
|
||||
@deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal (@var{filename} | -)}) @
|
||||
@deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal (@var{filename} | @var{:port} | -)}) @
|
||||
(@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
|
||||
@var{TRACECLKIN_freq} [@var{trace_freq}]))
|
||||
|
||||
|
@ -9221,23 +9476,28 @@ Command options:
|
|||
@itemize @minus
|
||||
@item @option{disable} disable TPIU handling;
|
||||
@item @option{external} configure TPIU to let user capture trace
|
||||
output externally (with an additional UART or logic analyzer hardware);
|
||||
@item @option{internal @var{filename}} configure TPIU and debug adapter to
|
||||
gather trace data and append it to @var{filename} (which can be
|
||||
either a regular file or a named pipe);
|
||||
@item @option{internal -} configure TPIU and debug adapter to
|
||||
gather trace data, but not write to any file. Useful in conjunction with the @command{tcl_trace} command;
|
||||
output externally (with an additional UART or logic analyzer hardware).
|
||||
@item @option{internal (@var{filename} | @var{:port} | -)} configure TPIU and debug adapter to
|
||||
gather trace data then:
|
||||
|
||||
@itemize @minus
|
||||
@item append it to a regular file or a named pipe if @var{filename} is specified.
|
||||
@item listen to a TCP/IP port if @var{:port} is specified, then broadcast the trace data over this port.
|
||||
@item if '-' is specified, OpenOCD will forward trace data to @command{tcl_trace} command.
|
||||
@*@b{Note:} while broadcasting to file or TCP, the forwarding to @command{tcl_trace} will remain active.
|
||||
@end itemize
|
||||
|
||||
@item @option{sync @var{port_width}} use synchronous parallel trace output
|
||||
mode, and set port width to @var{port_width};
|
||||
mode, and set port width to @var{port_width}.
|
||||
@item @option{manchester} use asynchronous SWO mode with Manchester
|
||||
coding;
|
||||
coding.
|
||||
@item @option{uart} use asynchronous SWO mode with NRZ (same as
|
||||
regular UART 8N1) coding;
|
||||
regular UART 8N1) coding.
|
||||
@item @var{formatter_enable} is @option{on} or @option{off} to enable
|
||||
or disable TPIU formatter which needs to be used when both ITM and ETM
|
||||
data is to be output via SWO;
|
||||
data is to be output via SWO.
|
||||
@item @var{TRACECLKIN_freq} this should be specified to match target's
|
||||
current TRACECLKIN frequency (usually the same as HCLK);
|
||||
current TRACECLKIN frequency (usually the same as HCLK).
|
||||
@item @var{trace_freq} trace port frequency. Can be omitted in
|
||||
internal mode to let the adapter driver select the maximum supported
|
||||
rate automatically.
|
||||
|
@ -10411,28 +10671,6 @@ For quickstart instructions run:
|
|||
openocd -f tools/firmware-recovery.tcl -c firmware_help
|
||||
@end example
|
||||
|
||||
@node TFTP
|
||||
@chapter TFTP
|
||||
@cindex TFTP
|
||||
If OpenOCD runs on an embedded host (as ZY1000 does), then TFTP can
|
||||
be used to access files on PCs (either the developer's PC or some other PC).
|
||||
|
||||
The way this works on the ZY1000 is to prefix a filename by
|
||||
"/tftp/ip/" and append the TFTP path on the TFTP
|
||||
server (tftpd). For example,
|
||||
|
||||
@example
|
||||
load_image /tftp/10.0.0.96/c:\temp\abc.elf
|
||||
@end example
|
||||
|
||||
will load c:\temp\abc.elf from the developer pc (10.0.0.96) into memory as
|
||||
if the file was hosted on the embedded host.
|
||||
|
||||
In order to achieve decent performance, you must choose a TFTP server
|
||||
that supports a packet size bigger than the default packet size (512 bytes). There
|
||||
are numerous TFTP servers out there (free and commercial) and you will have to do
|
||||
a bit of googling to find something that fits your requirements.
|
||||
|
||||
@node GDB and OpenOCD
|
||||
@chapter GDB and OpenOCD
|
||||
@cindex GDB
|
||||
|
@ -10656,7 +10894,13 @@ of a running target. Do not use GDB commands @command{continue},
|
|||
and GDB would require stopping the target to get the prompt back.
|
||||
|
||||
Do not use this mode under an IDE like Eclipse as it caches values of
|
||||
previously shown varibles.
|
||||
previously shown variables.
|
||||
|
||||
It's also possible to connect more than one GDB to the same target by the
|
||||
target's configuration option @code{-gdb-max-connections}. This allows, for
|
||||
example, one GDB to run a script that continuously polls a set of variables
|
||||
while other GDB can be used interactively. Be extremely careful in this case,
|
||||
because the two GDB can easily get out-of-sync.
|
||||
|
||||
@section RTOS Support
|
||||
@cindex RTOS Support
|
||||
|
|
|
@ -53,7 +53,8 @@ endif
|
|||
%D%/target/libtarget.la \
|
||||
%D%/server/libserver.la \
|
||||
%D%/rtos/librtos.la \
|
||||
%D%/helper/libhelper.la
|
||||
%D%/helper/libhelper.la \
|
||||
%D%/rtt/librtt.la
|
||||
|
||||
BIN2C = $(srcdir)/%D%/helper/bin2char.sh
|
||||
|
||||
|
@ -83,3 +84,4 @@ include %D%/rtos/Makefile.am
|
|||
include %D%/server/Makefile.am
|
||||
include %D%/flash/Makefile.am
|
||||
include %D%/pld/Makefile.am
|
||||
include %D%/rtt/Makefile.am
|
||||
|
|
|
@ -860,7 +860,7 @@ static int validate_target_state(struct nand_device *nand)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int ecc_status_v1(struct nand_device *nand)
|
||||
static int ecc_status_v1(struct nand_device *nand)
|
||||
{
|
||||
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
|
||||
struct target *target = nand->target;
|
||||
|
@ -886,7 +886,7 @@ int ecc_status_v1(struct nand_device *nand)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int ecc_status_v2(struct nand_device *nand)
|
||||
static int ecc_status_v2(struct nand_device *nand)
|
||||
{
|
||||
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
|
||||
struct target *target = nand->target;
|
||||
|
|
|
@ -52,10 +52,12 @@ NOR_DRIVERS = \
|
|||
%D%/psoc5lp.c \
|
||||
%D%/psoc6.c \
|
||||
%D%/renesas_rpchf.c \
|
||||
%D%/sfdp.c \
|
||||
%D%/sh_qspi.c \
|
||||
%D%/sim3x.c \
|
||||
%D%/spi.c \
|
||||
%D%/stmsmi.c \
|
||||
%D%/stmqspi.c \
|
||||
%D%/stellaris.c \
|
||||
%D%/stm32f1x.c \
|
||||
%D%/stm32f2x.c \
|
||||
|
@ -84,6 +86,8 @@ NORHEADERS = \
|
|||
%D%/imp.h \
|
||||
%D%/non_cfi.h \
|
||||
%D%/ocl.h \
|
||||
%D%/sfdp.h \
|
||||
%D%/spi.h \
|
||||
%D%/stm32l4x.h \
|
||||
%D%/stmqspi.h \
|
||||
%D%/msp432.h
|
||||
|
|
|
@ -3099,7 +3099,7 @@ FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command)
|
|||
* is owned by this bank. This simplification works only for one shot
|
||||
* deallocation like current flash_free_all_banks()
|
||||
*/
|
||||
void sam3_free_driver_priv(struct flash_bank *bank)
|
||||
static void sam3_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct sam3_chip *chip = all_sam3_chips;
|
||||
while (chip) {
|
||||
|
|
|
@ -676,6 +676,9 @@ showall:
|
|||
}
|
||||
if ((who >= 0) && (((unsigned)who) < SAMV_NUM_GPNVM_BITS)) {
|
||||
r = samv_get_gpnvm(target, who, &v);
|
||||
if (r != ERROR_OK)
|
||||
return r;
|
||||
|
||||
command_print(CMD, "samv-gpnvm%u: %u", who, v);
|
||||
return r;
|
||||
} else {
|
||||
|
|
|
@ -43,7 +43,7 @@ struct flash_ctrl_priv_data {
|
|||
char *part_name;
|
||||
};
|
||||
|
||||
const struct flash_ctrl_priv_data flash_priv_data_1 = {
|
||||
static const struct flash_ctrl_priv_data flash_priv_data_1 = {
|
||||
.die_id_reg = 0x4090001C,
|
||||
.jtag_idcode_reg = 0x40900028,
|
||||
.flash_base = 0x10040000,
|
||||
|
@ -53,7 +53,7 @@ const struct flash_ctrl_priv_data flash_priv_data_1 = {
|
|||
.part_name = "BLUENRG-1",
|
||||
};
|
||||
|
||||
const struct flash_ctrl_priv_data flash_priv_data_2 = {
|
||||
static const struct flash_ctrl_priv_data flash_priv_data_2 = {
|
||||
.die_id_reg = 0x4090001C,
|
||||
.jtag_idcode_reg = 0x40900028,
|
||||
.flash_base = 0x10040000,
|
||||
|
@ -63,7 +63,7 @@ const struct flash_ctrl_priv_data flash_priv_data_2 = {
|
|||
.part_name = "BLUENRG-2",
|
||||
};
|
||||
|
||||
const struct flash_ctrl_priv_data flash_priv_data_lp = {
|
||||
static const struct flash_ctrl_priv_data flash_priv_data_lp = {
|
||||
.die_id_reg = 0x40000000,
|
||||
.jtag_idcode_reg = 0x40000004,
|
||||
.flash_base = 0x10040000,
|
||||
|
@ -79,7 +79,11 @@ struct bluenrgx_flash_bank {
|
|||
const struct flash_ctrl_priv_data *flash_ptr;
|
||||
};
|
||||
|
||||
const struct flash_ctrl_priv_data *flash_ctrl[] = {&flash_priv_data_1, &flash_priv_data_2, &flash_priv_data_lp};
|
||||
static const struct flash_ctrl_priv_data *flash_ctrl[] = {
|
||||
&flash_priv_data_1,
|
||||
&flash_priv_data_2,
|
||||
&flash_priv_data_lp
|
||||
};
|
||||
|
||||
/* flash_bank bluenrg-x 0 0 0 0 <target#> */
|
||||
FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command)
|
||||
|
|
|
@ -94,7 +94,7 @@ int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first,
|
|||
}
|
||||
|
||||
int flash_driver_write(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||
{
|
||||
int retval;
|
||||
|
||||
|
@ -135,6 +135,43 @@ int default_flash_read(struct flash_bank *bank,
|
|||
return target_read_buffer(bank->target, offset + bank->base, count, buffer);
|
||||
}
|
||||
|
||||
int flash_driver_verify(struct flash_bank *bank,
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = bank->driver->verify ? bank->driver->verify(bank, buffer, offset, count) :
|
||||
default_flash_verify(bank, buffer, offset, count);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("verify failed in bank at " TARGET_ADDR_FMT " starting at 0x%8.8" PRIx32,
|
||||
bank->base, offset);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int default_flash_verify(struct flash_bank *bank,
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||
{
|
||||
uint32_t target_crc, image_crc;
|
||||
int retval;
|
||||
|
||||
retval = image_calculate_checksum(buffer, count, &image_crc);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = target_checksum_memory(bank->target, offset + bank->base, count, &target_crc);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
LOG_DEBUG("addr " TARGET_ADDR_FMT ", len 0x%08" PRIx32 ", crc 0x%08" PRIx32 " 0x%08" PRIx32,
|
||||
offset + bank->base, count, ~image_crc, ~target_crc);
|
||||
if (target_crc == image_crc)
|
||||
return ERROR_OK;
|
||||
else
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
void flash_bank_add(struct flash_bank *bank)
|
||||
{
|
||||
/* put flash bank in linked list */
|
||||
|
@ -697,12 +734,12 @@ static bool flash_write_check_gap(struct flash_bank *bank,
|
|||
}
|
||||
|
||||
|
||||
int flash_write_unlock(struct target *target, struct image *image,
|
||||
uint32_t *written, bool erase, bool unlock)
|
||||
int flash_write_unlock_verify(struct target *target, struct image *image,
|
||||
uint32_t *written, bool erase, bool unlock, bool write, bool verify)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
|
||||
int section;
|
||||
unsigned int section;
|
||||
uint32_t section_offset;
|
||||
struct flash_bank *c;
|
||||
int *padding;
|
||||
|
@ -727,8 +764,8 @@ int flash_write_unlock(struct target *target, struct image *image,
|
|||
* whereas an image can have sections out of order. */
|
||||
struct imagesection **sections = malloc(sizeof(struct imagesection *) *
|
||||
image->num_sections);
|
||||
int i;
|
||||
for (i = 0; i < image->num_sections; i++)
|
||||
|
||||
for (unsigned int i = 0; i < image->num_sections; i++)
|
||||
sections[i] = &image->sections[i];
|
||||
|
||||
qsort(sections, image->num_sections, sizeof(struct imagesection *),
|
||||
|
@ -738,7 +775,7 @@ int flash_write_unlock(struct target *target, struct image *image,
|
|||
while (section < image->num_sections) {
|
||||
uint32_t buffer_idx;
|
||||
uint8_t *buffer;
|
||||
int section_last;
|
||||
unsigned int section_last;
|
||||
target_addr_t run_address = sections[section]->base_address + section_offset;
|
||||
uint32_t run_size = sections[section]->size - section_offset;
|
||||
int pad_bytes = 0;
|
||||
|
@ -932,8 +969,17 @@ int flash_write_unlock(struct target *target, struct image *image,
|
|||
}
|
||||
|
||||
if (retval == ERROR_OK) {
|
||||
/* write flash sectors */
|
||||
retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
|
||||
if (write) {
|
||||
/* write flash sectors */
|
||||
retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == ERROR_OK) {
|
||||
if (verify) {
|
||||
/* verify flash sectors */
|
||||
retval = flash_driver_verify(c, buffer, run_address - c->base, run_size);
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
@ -957,7 +1003,7 @@ done:
|
|||
int flash_write(struct target *target, struct image *image,
|
||||
uint32_t *written, bool erase)
|
||||
{
|
||||
return flash_write_unlock(target, image, written, erase, false);
|
||||
return flash_write_unlock_verify(target, image, written, erase, false, true, false);
|
||||
}
|
||||
|
||||
struct flash_sector *alloc_block_array(uint32_t offset, uint32_t size,
|
||||
|
|
|
@ -200,6 +200,7 @@ void default_flash_free_driver_priv(struct flash_bank *bank);
|
|||
|
||||
/** Deallocates all flash banks */
|
||||
void flash_free_all_banks(void);
|
||||
|
||||
/**
|
||||
* Provides default read implementation for flash memory.
|
||||
* @param bank The bank to read.
|
||||
|
@ -210,6 +211,18 @@ void flash_free_all_banks(void);
|
|||
*/
|
||||
int default_flash_read(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
|
||||
/**
|
||||
* Provides default verify implementation for flash memory.
|
||||
* @param bank The bank to verify.
|
||||
* @param buffer The data bytes to verify.
|
||||
* @param offset The offset into the chip to verify.
|
||||
* @param count The number of bytes to verify.
|
||||
* @returns ERROR_OK if successful; otherwise, an error code.
|
||||
*/
|
||||
int default_flash_verify(struct flash_bank *bank,
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
|
||||
/**
|
||||
* Provides default erased-bank check handling. Checks to see if
|
||||
* the flash driver knows they are erased; if things look uncertain,
|
||||
|
@ -217,7 +230,6 @@ int default_flash_read(struct flash_bank *bank,
|
|||
* @returns ERROR_OK if successful; otherwise, an error code.
|
||||
*/
|
||||
int default_flash_blank_check(struct flash_bank *bank);
|
||||
|
||||
/**
|
||||
* Returns the flash bank specified by @a name, which matches the
|
||||
* driver name and a suffix (option) specify the driver-specific
|
||||
|
|
|
@ -155,6 +155,20 @@ struct flash_driver {
|
|||
int (*read)(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
|
||||
/**
|
||||
* Verify data in flash. Note CPU address will be
|
||||
* "bank->base + offset", while the physical address is
|
||||
* dependent upon current target MMU mappings.
|
||||
*
|
||||
* @param bank The bank to verify
|
||||
* @param buffer The data bytes to verify against.
|
||||
* @param offset The offset into the chip to verify.
|
||||
* @param count The number of bytes to verify.
|
||||
* @returns ERROR_OK if successful; otherwise, an error code.
|
||||
*/
|
||||
int (*verify)(struct flash_bank *bank,
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
|
||||
/**
|
||||
* Probe to determine what kind of flash is present.
|
||||
* This is invoked by the "probe" script command.
|
||||
|
|
|
@ -44,6 +44,7 @@ extern const struct flash_driver faux_flash;
|
|||
extern const struct flash_driver fm3_flash;
|
||||
extern const struct flash_driver fm4_flash;
|
||||
extern const struct flash_driver fespi_flash;
|
||||
extern const struct flash_driver gd32vf103_flash;
|
||||
extern const struct flash_driver jtagspi_flash;
|
||||
extern const struct flash_driver kinetis_flash;
|
||||
extern const struct flash_driver kinetis_ke_flash;
|
||||
|
@ -75,7 +76,7 @@ extern const struct flash_driver stm32f2x_flash;
|
|||
extern const struct flash_driver stm32lx_flash;
|
||||
extern const struct flash_driver stm32l4x_flash;
|
||||
extern const struct flash_driver stm32h7x_flash;
|
||||
extern const struct flash_driver gd32vf103_flash;
|
||||
extern const struct flash_driver stmqspi_flash;
|
||||
extern const struct flash_driver stmsmi_flash;
|
||||
extern const struct flash_driver str7x_flash;
|
||||
extern const struct flash_driver str9x_flash;
|
||||
|
@ -150,6 +151,7 @@ static const struct flash_driver * const flash_drivers[] = {
|
|||
&stm32h7x_flash,
|
||||
&gd32vf103_flash,
|
||||
&stmsmi_flash,
|
||||
&stmqspi_flash,
|
||||
&str7x_flash,
|
||||
&str9x_flash,
|
||||
&str9xpec_flash,
|
||||
|
|
|
@ -42,12 +42,14 @@ int flash_driver_erase(struct flash_bank *bank, unsigned int first,
|
|||
int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first,
|
||||
unsigned int last);
|
||||
int flash_driver_write(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
int flash_driver_read(struct flash_bank *bank,
|
||||
uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
int flash_driver_verify(struct flash_bank *bank,
|
||||
const uint8_t *buffer, uint32_t offset, uint32_t count);
|
||||
|
||||
/* write (optional verify) an image to flash memory of the given target */
|
||||
int flash_write_unlock(struct target *target, struct image *image,
|
||||
uint32_t *written, bool erase, bool unlock);
|
||||
int flash_write_unlock_verify(struct target *target, struct image *image,
|
||||
uint32_t *written, bool erase, bool unlock, bool write, bool verify);
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_IMP_H */
|
||||
|
|
|
@ -434,7 +434,7 @@ static int kinetis_ke_prepare_flash(struct flash_bank *bank)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int kinetis_ke_stop_watchdog(struct target *target)
|
||||
static int kinetis_ke_stop_watchdog(struct target *target)
|
||||
{
|
||||
struct working_area *watchdog_algorithm;
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
|
|
|
@ -1103,9 +1103,9 @@ static int lpc2000_write(struct flash_bank *bank, const uint8_t *buffer, uint32_
|
|||
|
||||
uint32_t original_value = buf_get_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32);
|
||||
if (original_value != checksum) {
|
||||
LOG_WARNING("Verification will fail since checksum in image (0x%8.8" PRIx32 ") to be written to flash is "
|
||||
LOG_WARNING("Boot verification checksum in image (0x%8.8" PRIx32 ") to be written to flash is "
|
||||
"different from calculated vector checksum (0x%8.8" PRIx32 ").", original_value, checksum);
|
||||
LOG_WARNING("To remove this warning modify build tools on developer PC to inject correct LPC vector "
|
||||
LOG_WARNING("OpenOCD will write the correct checksum. To remove this warning modify build tools on developer PC to inject correct LPC vector "
|
||||
"checksum.");
|
||||
}
|
||||
|
||||
|
|
|
@ -635,9 +635,9 @@ COMMAND_HANDLER(lpc2900_handle_write_custom_command)
|
|||
|
||||
/* The image will always start at offset 0 */
|
||||
struct image image;
|
||||
image.base_address_set = 1;
|
||||
image.base_address_set = true;
|
||||
image.base_address = 0;
|
||||
image.start_address_set = 0;
|
||||
image.start_address_set = false;
|
||||
|
||||
const char *filename = CMD_ARGV[1];
|
||||
const char *type = (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL;
|
||||
|
|
|
@ -761,7 +761,7 @@ static int mrvlqspi_flash_write(struct flash_bank *bank, const uint8_t *buffer,
|
|||
return retval;
|
||||
}
|
||||
|
||||
int mrvlqspi_flash_read(struct flash_bank *bank, uint8_t *buffer,
|
||||
static int mrvlqspi_flash_read(struct flash_bank *bank, uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
|
@ -914,7 +914,7 @@ static int mrvlqspi_flash_erase_check(struct flash_bank *bank)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int mrvlqspi_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
static int mrvlqspi_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv;
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ struct nrf5_info {
|
|||
bool ficr_info_valid;
|
||||
struct nrf52_ficr_info ficr_info;
|
||||
const struct nrf5_device_spec *spec;
|
||||
uint32_t hwid;
|
||||
uint16_t hwid;
|
||||
enum nrf5_features features;
|
||||
unsigned int flash_size_kb;
|
||||
unsigned int ram_size_kb;
|
||||
|
@ -328,7 +328,7 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip)
|
|||
do {
|
||||
res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read NVMC_READY register");
|
||||
LOG_ERROR("Error waiting NVMC_READY: generic flash write/erase error (check protection etc...)");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -440,6 +440,38 @@ error:
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int nrf5_protect_check_clenr0(struct flash_bank *bank)
|
||||
{
|
||||
int res;
|
||||
uint32_t clenr0;
|
||||
struct nrf5_bank *nbank = bank->driver_priv;
|
||||
struct nrf5_info *chip = nbank->chip;
|
||||
|
||||
assert(chip != NULL);
|
||||
|
||||
res = target_read_u32(chip->target, NRF51_FICR_CLENR0,
|
||||
&clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read code region 0 size[FICR]");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (clenr0 == 0xFFFFFFFF) {
|
||||
res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
|
||||
&clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read code region 0 size[UICR]");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_protected =
|
||||
clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int nrf5_protect_check_bprot(struct flash_bank *bank)
|
||||
{
|
||||
struct nrf5_bank *nbank = bank->driver_priv;
|
||||
|
@ -469,9 +501,6 @@ static int nrf5_protect_check_bprot(struct flash_bank *bank)
|
|||
|
||||
static int nrf5_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
int res;
|
||||
uint32_t clenr0;
|
||||
|
||||
/* UICR cannot be write protected so just return early */
|
||||
if (bank->base == NRF5_UICR_BASE)
|
||||
return ERROR_OK;
|
||||
|
@ -484,53 +513,20 @@ static int nrf5_protect_check(struct flash_bank *bank)
|
|||
if (chip->features & NRF5_FEATURE_BPROT)
|
||||
return nrf5_protect_check_bprot(bank);
|
||||
|
||||
if (!(chip->features & NRF5_FEATURE_SERIES_51)) {
|
||||
LOG_WARNING("Flash protection of this nRF device is not supported");
|
||||
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||
}
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51)
|
||||
return nrf5_protect_check_clenr0(bank);
|
||||
|
||||
res = target_read_u32(chip->target, NRF51_FICR_CLENR0,
|
||||
&clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read code region 0 size[FICR]");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (clenr0 == 0xFFFFFFFF) {
|
||||
res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
|
||||
&clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read code region 0 size[UICR]");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_protected =
|
||||
clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
|
||||
|
||||
return ERROR_OK;
|
||||
LOG_WARNING("Flash protection of this nRF device is not supported");
|
||||
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
|
||||
static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int first,
|
||||
unsigned int last)
|
||||
{
|
||||
int res;
|
||||
uint32_t clenr0, ppfc;
|
||||
struct nrf5_info *chip;
|
||||
|
||||
/* UICR cannot be write protected so just bail out early */
|
||||
if (bank->base == NRF5_UICR_BASE)
|
||||
return ERROR_FAIL;
|
||||
|
||||
res = nrf5_get_probed_chip_if_halted(bank, &chip);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
if (!(chip->features & NRF5_FEATURE_SERIES_51)) {
|
||||
LOG_ERROR("Flash protection setting of this nRF device is not supported");
|
||||
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||
}
|
||||
struct nrf5_bank *nbank = bank->driver_priv;
|
||||
struct nrf5_info *chip = nbank->chip;
|
||||
|
||||
if (first != 0) {
|
||||
LOG_ERROR("Code region 0 must start at the beginning of the bank");
|
||||
|
@ -552,25 +548,59 @@ static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
|
|||
res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
|
||||
&clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read code region 0 size[UICR]");
|
||||
LOG_ERROR("Couldn't read code region 0 size from UICR");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (clenr0 == 0xFFFFFFFF) {
|
||||
res = target_write_u32(chip->target, NRF51_UICR_CLENR0,
|
||||
clenr0);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't write code region 0 size[UICR]");
|
||||
return res;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!set || clenr0 != 0xFFFFFFFF) {
|
||||
LOG_ERROR("You need to perform chip erase before changing the protection settings");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
nrf5_protect_check(bank);
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
goto error;
|
||||
|
||||
return ERROR_OK;
|
||||
clenr0 = bank->sectors[last].offset + bank->sectors[last].size;
|
||||
res = target_write_u32(chip->target, NRF51_UICR_CLENR0, clenr0);
|
||||
|
||||
int res2 = nrf5_wait_for_nvmc(chip);
|
||||
|
||||
if (res == ERROR_OK)
|
||||
res = res2;
|
||||
|
||||
if (res == ERROR_OK)
|
||||
LOG_INFO("A reset or power cycle is required for the new protection settings to take effect.");
|
||||
else
|
||||
LOG_ERROR("Couldn't write code region 0 size to UICR");
|
||||
|
||||
error:
|
||||
nrf5_nvmc_read_only(chip);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
|
||||
unsigned int last)
|
||||
{
|
||||
int res;
|
||||
struct nrf5_info *chip;
|
||||
|
||||
/* UICR cannot be write protected so just bail out early */
|
||||
if (bank->base == NRF5_UICR_BASE) {
|
||||
LOG_ERROR("UICR page does not support protection");
|
||||
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||
}
|
||||
|
||||
res = nrf5_get_probed_chip_if_halted(bank, &chip);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51)
|
||||
return nrf5_protect_clenr0(bank, set, first, last);
|
||||
|
||||
LOG_ERROR("Flash protection setting is not supported on this nRF5 device");
|
||||
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static bool nrf5_info_variant_to_str(uint32_t variant, char *bf)
|
||||
|
@ -618,7 +648,7 @@ static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size)
|
|||
variant, &variant[2]);
|
||||
|
||||
} else {
|
||||
res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%08" PRIx32 ")",
|
||||
res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%04" PRIx16 ")",
|
||||
chip->hwid);
|
||||
}
|
||||
if (res <= 0)
|
||||
|
@ -735,14 +765,15 @@ static int nrf5_probe(struct flash_bank *bank)
|
|||
struct nrf5_info *chip = nbank->chip;
|
||||
struct target *target = chip->target;
|
||||
|
||||
res = target_read_u32(target, NRF5_FICR_CONFIGID, &chip->hwid);
|
||||
uint32_t configid;
|
||||
res = target_read_u32(target, NRF5_FICR_CONFIGID, &configid);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read CONFIGID register");
|
||||
return res;
|
||||
}
|
||||
|
||||
chip->hwid &= 0xFFFF; /* HWID is stored in the lower two
|
||||
* bytes of the CONFIGID register */
|
||||
/* HWID is stored in the lower two bytes of the CONFIGID register */
|
||||
chip->hwid = configid & 0xFFFF;
|
||||
|
||||
/* guess a nRF51 series if the device has no FICR INFO and we don't know HWID */
|
||||
chip->features = NRF5_FEATURE_SERIES_51;
|
||||
|
@ -820,8 +851,6 @@ static int nrf5_probe(struct flash_bank *bank)
|
|||
if (!bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
nrf5_protect_check(bank);
|
||||
|
||||
chip->bank[0].probed = true;
|
||||
|
||||
} else {
|
||||
|
@ -983,7 +1012,7 @@ static int nrf5_ll_flash_write(struct nrf5_info *chip, uint32_t address, const u
|
|||
0, NULL,
|
||||
ARRAY_SIZE(reg_params), reg_params,
|
||||
source->address, source->size,
|
||||
write_algorithm->address, 0,
|
||||
write_algorithm->address, write_algorithm->address + sizeof(nrf5_flash_write_code) - 2,
|
||||
&armv7m_info);
|
||||
|
||||
target_free_working_area(target, source);
|
||||
|
@ -1011,6 +1040,34 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
|
|||
assert(offset % 4 == 0);
|
||||
assert(count % 4 == 0);
|
||||
|
||||
/* UICR CLENR0 based protection used on nRF51 is somewhat clumsy:
|
||||
* RM reads: Code running from code region 1 will not be able to write
|
||||
* to code region 0.
|
||||
* Unfortunately the flash loader running from RAM can write to both
|
||||
* code regions whithout any hint the protection is violated.
|
||||
*
|
||||
* Update protection state and check if any flash sector to be written
|
||||
* is protected. */
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51) {
|
||||
|
||||
res = nrf5_protect_check_clenr0(bank);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
for (unsigned int sector = 0; sector < bank->num_sectors; sector++) {
|
||||
struct flash_sector *bs = &bank->sectors[sector];
|
||||
|
||||
/* Start offset in or before this sector? */
|
||||
/* End offset in or behind this sector? */
|
||||
if ((offset < (bs->offset + bs->size))
|
||||
&& ((offset + count - 1) >= bs->offset)
|
||||
&& bs->is_protected == 1) {
|
||||
LOG_ERROR("Write refused, sector %d is protected", sector);
|
||||
return ERROR_FLASH_PROTECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
goto error;
|
||||
|
@ -1037,11 +1094,36 @@ static int nrf5_erase(struct flash_bank *bank, unsigned int first,
|
|||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
/* For each sector to be erased */
|
||||
for (unsigned int s = first; s <= last && res == ERROR_OK; s++)
|
||||
res = nrf5_erase_page(bank, chip, &bank->sectors[s]);
|
||||
/* UICR CLENR0 based protection used on nRF51 prevents erase
|
||||
* absolutely silently. NVMC has no flag to indicate the protection
|
||||
* was violated.
|
||||
*
|
||||
* Update protection state and check if any flash sector to be erased
|
||||
* is protected. */
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51) {
|
||||
|
||||
return res;
|
||||
res = nrf5_protect_check_clenr0(bank);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* For each sector to be erased */
|
||||
for (unsigned int s = first; s <= last && res == ERROR_OK; s++) {
|
||||
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51
|
||||
&& bank->sectors[s].is_protected == 1) {
|
||||
LOG_ERROR("Flash sector %d is protected", s);
|
||||
return ERROR_FLASH_PROTECTED;
|
||||
}
|
||||
|
||||
res = nrf5_erase_page(bank, chip, &bank->sectors[s]);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Error erasing sector %d", s);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void nrf5_free_driver_priv(struct flash_bank *bank)
|
||||
|
@ -1158,23 +1240,16 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
|
|||
}
|
||||
|
||||
res = nrf5_erase_all(chip);
|
||||
if (res != ERROR_OK) {
|
||||
if (res == ERROR_OK) {
|
||||
LOG_INFO("Mass erase completed.");
|
||||
if (chip->features & NRF5_FEATURE_SERIES_51)
|
||||
LOG_INFO("A reset or power cycle is required if the flash was protected before.");
|
||||
|
||||
} else {
|
||||
LOG_ERROR("Failed to erase the chip");
|
||||
nrf5_protect_check(bank);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = nrf5_protect_check(bank);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to check chip's write protection");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = get_flash_bank_by_addr(target, NRF5_UICR_BASE, true, &bank);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
return ERROR_OK;
|
||||
return res;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(nrf5_handle_info_command)
|
||||
|
|
|
@ -139,7 +139,7 @@ struct psoc4_chip_family {
|
|||
uint32_t flags;
|
||||
};
|
||||
|
||||
const struct psoc4_chip_family psoc4_families[] = {
|
||||
static const struct psoc4_chip_family psoc4_families[] = {
|
||||
{ 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY },
|
||||
{ 0x9A, "PSoC4000", .flags = 0 },
|
||||
{ 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 },
|
||||
|
|
|
@ -151,12 +151,6 @@ static int sromalgo_prepare(struct target *target)
|
|||
if (hr != ERROR_OK)
|
||||
return hr;
|
||||
|
||||
/* Restore THUMB bit in xPSR register */
|
||||
const struct armv7m_common *cm = target_to_armv7m(target);
|
||||
hr = cm->store_core_reg_u32(target, ARMV7M_xPSR, 0x01000000);
|
||||
if (hr != ERROR_OK)
|
||||
return hr;
|
||||
|
||||
/* Allocate Working Area for Stack and Flash algorithm */
|
||||
hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &g_stack_area);
|
||||
if (hr != ERROR_OK)
|
||||
|
@ -908,7 +902,7 @@ COMMAND_HANDLER(psoc6_handle_mass_erase_command)
|
|||
* @param target current target
|
||||
* @return ERROR_OK in case of success, ERROR_XXX code otherwise
|
||||
*************************************************************************************************/
|
||||
int handle_reset_halt(struct target *target)
|
||||
static int handle_reset_halt(struct target *target)
|
||||
{
|
||||
int hr;
|
||||
uint32_t reset_addr;
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "spi.h"
|
||||
#include "sfdp.h"
|
||||
|
||||
#define SFDP_MAGIC 0x50444653
|
||||
#define SFDP_ACCESS_PROT 0xFF
|
||||
#define SFDP_BASIC_FLASH 0xFF00
|
||||
#define SFDP_4BYTE_ADDR 0xFF84
|
||||
|
||||
static const char *sfdp_name = "sfdp";
|
||||
|
||||
struct sfdp_hdr {
|
||||
uint32_t signature;
|
||||
uint32_t revision;
|
||||
};
|
||||
|
||||
struct sfdp_phdr {
|
||||
uint32_t revision;
|
||||
uint32_t ptr;
|
||||
};
|
||||
|
||||
struct sfdp_basic_flash_param {
|
||||
uint32_t fast_addr; /* 01: fast read and 3/4 address bytes */
|
||||
uint32_t density; /* 02: memory density */
|
||||
uint32_t fast_1x4; /* 03: 1-1-4 and 1-4-4 fast read */
|
||||
uint32_t fast_1x2; /* 04: 1-2-2 and 1-1-2 fast read */
|
||||
uint32_t fast_444; /* 05: 4-4-4 and 2-2-2 fast read */
|
||||
uint32_t read_222; /* 06: 2-2-2 fast read instr and dummy */
|
||||
uint32_t read_444; /* 07: 4-4-4 fast read instr and dummy */
|
||||
uint32_t erase_t12; /* 08: erase types 1, 2 */
|
||||
uint32_t erase_t34; /* 09: erase types 3, 4 */
|
||||
uint32_t erase_time; /* 10: erase times for types 1 - 4 */
|
||||
uint32_t chip_byte; /* 11: chip erase time, byte prog time, page prog */
|
||||
uint32_t susp_time; /* 12: suspend and resume times */
|
||||
uint32_t susp_instr; /* 13: suspend and resume instr */
|
||||
uint32_t pwrd_instr; /* 14: powerdown instr */
|
||||
uint32_t quad_req; /* 15: quad enable requirements */
|
||||
uint32_t addr_reset; /* 16: 3-/4-byte addressing and reset */
|
||||
uint32_t read_1x8; /* 17: 1-1-8 and 1-8-8 fast read instr and dummy */
|
||||
uint32_t dtr_drive; /* 18: dtr modes and drive strength */
|
||||
uint32_t octal_req; /* 19: octal enable requirements */
|
||||
uint32_t speed_888; /* 20: speed in 8-8-8 modes */
|
||||
};
|
||||
|
||||
struct sfdp_4byte_addr_param {
|
||||
uint32_t flags; /* 01: various flags */
|
||||
uint32_t erase_t1234; /* 02: erase commands */
|
||||
};
|
||||
|
||||
/* Try to get parameters from flash via SFDP */
|
||||
int spi_sfdp(struct flash_bank *bank, struct flash_device *dev,
|
||||
read_sfdp_block_t read_sfdp_block)
|
||||
{
|
||||
struct sfdp_hdr header;
|
||||
struct sfdp_phdr *pheaders = NULL;
|
||||
uint32_t *ptable = NULL;
|
||||
unsigned int j, k, nph;
|
||||
int retval, erase_type = 0;
|
||||
|
||||
memset(dev, 0, sizeof(struct flash_device));
|
||||
|
||||
/* retrieve SFDP header */
|
||||
memset(&header, 0, sizeof(header));
|
||||
retval = read_sfdp_block(bank, 0x0, sizeof(header) >> 2, (uint32_t *)&header);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
LOG_DEBUG("header 0x%08" PRIx32 " 0x%08" PRIx32, header.signature, header.revision);
|
||||
if (header.signature != SFDP_MAGIC) {
|
||||
LOG_INFO("no SDFP found");
|
||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||
}
|
||||
if (((header.revision >> 24) & 0xFF) != SFDP_ACCESS_PROT) {
|
||||
LOG_ERROR("access protocol 0x%02x not implemented",
|
||||
(header.revision >> 24) & 0xFFU);
|
||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||
}
|
||||
|
||||
/* retrieve table of parameter headers */
|
||||
nph = ((header.revision >> 16) & 0xFF) + 1;
|
||||
LOG_DEBUG("parameter headers: %d", nph);
|
||||
pheaders = malloc(sizeof(struct sfdp_phdr) * nph);
|
||||
if (pheaders == NULL) {
|
||||
LOG_ERROR("not enough memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
memset(pheaders, 0, sizeof(struct sfdp_phdr) * nph);
|
||||
retval = read_sfdp_block(bank, sizeof(header),
|
||||
(sizeof(struct sfdp_phdr) >> 2) * nph, (uint32_t *)pheaders);
|
||||
if (retval != ERROR_OK)
|
||||
goto err;
|
||||
|
||||
for (k = 0; k < nph; k++) {
|
||||
uint8_t words = (pheaders[k].revision >> 24) & 0xFF;
|
||||
uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00) | (pheaders[k].revision & 0xFF);
|
||||
uint32_t ptr = pheaders[k].ptr & 0xFFFFFF;
|
||||
|
||||
LOG_DEBUG("pheader %d len=0x%02" PRIx8 " id=0x%04" PRIx16
|
||||
" ptr=0x%06" PRIx32, k, words, id, ptr);
|
||||
|
||||
/* retrieve parameter table */
|
||||
ptable = malloc(words << 2);
|
||||
if (ptable == NULL) {
|
||||
LOG_ERROR("not enough memory");
|
||||
retval = ERROR_FAIL;
|
||||
goto err;
|
||||
}
|
||||
retval = read_sfdp_block(bank, ptr, words, ptable);
|
||||
if (retval != ERROR_OK)
|
||||
goto err;
|
||||
|
||||
for (j = 0; j < words; j++)
|
||||
LOG_DEBUG("word %02d 0x%08X", j + 1, ptable[j]);
|
||||
|
||||
if (id == SFDP_BASIC_FLASH) {
|
||||
struct sfdp_basic_flash_param *table = (struct sfdp_basic_flash_param *)ptable;
|
||||
uint16_t erase;
|
||||
|
||||
if (words < 9) {
|
||||
LOG_ERROR("id=0x%04" PRIx16 " invalid length %d", id, words);
|
||||
retval = ERROR_FLASH_BANK_NOT_PROBED;
|
||||
goto err;
|
||||
}
|
||||
|
||||
LOG_DEBUG("basic flash parameter table");
|
||||
/* dummy device name */
|
||||
dev->name = sfdp_name;
|
||||
|
||||
/* default instructions */
|
||||
dev->read_cmd = SPIFLASH_READ;
|
||||
dev->pprog_cmd = SPIFLASH_PAGE_PROGRAM;
|
||||
dev->chip_erase_cmd = SPIFLASH_MASS_ERASE;
|
||||
|
||||
/* get device size */
|
||||
if (table->density & (1UL << 31))
|
||||
dev->size_in_bytes = 1UL << ((table->density & ~(1UL << 31)) - 3);
|
||||
else
|
||||
dev->size_in_bytes = (table->density + 1) >> 3;
|
||||
|
||||
/* 2-2-2 read instruction, not used */
|
||||
if (table->fast_444 & (1UL << 0))
|
||||
dev->qread_cmd = (table->read_222 >> 24) & 0xFF;
|
||||
|
||||
/* 4-4-4 read instruction */
|
||||
if (table->fast_444 & (1UL << 4))
|
||||
dev->qread_cmd = (table->read_444 >> 24) & 0xFF;
|
||||
|
||||
/* find the largest erase block size and instruction */
|
||||
erase = (table->erase_t12 >> 0) & 0xFFFF;
|
||||
erase_type = 1;
|
||||
if (((table->erase_t12 >> 16) & 0xFF) > (erase & 0xFF)) {
|
||||
erase = (table->erase_t12 >> 16) & 0xFFFF;
|
||||
erase_type = 2;
|
||||
}
|
||||
if (((table->erase_t34 >> 0) & 0xFF) > (erase & 0xFF)) {
|
||||
erase = (table->erase_t34 >> 0) & 0xFFFF;
|
||||
erase_type = 3;
|
||||
}
|
||||
if (((table->erase_t34 >> 16) & 0xFF) > (erase & 0xFF)) {
|
||||
erase = (table->erase_t34 >> 16) & 0xFFFF;
|
||||
erase_type = 4;
|
||||
}
|
||||
dev->erase_cmd = (erase >> 8) & 0xFF;
|
||||
dev->sectorsize = 1UL << (erase & 0xFF);
|
||||
|
||||
if ((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) {
|
||||
/* get Program Page Size, if chip_byte present, that's optional */
|
||||
dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0x0F);
|
||||
} else {
|
||||
/* no explicit page size specified ... */
|
||||
if (table->fast_addr & (1UL << 2)) {
|
||||
/* Write Granularity = 1, use 64 bytes */
|
||||
dev->pagesize = 1UL << 6;
|
||||
} else {
|
||||
/* Write Granularity = 0, use 16 bytes */
|
||||
dev->pagesize = 1UL << 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->size_in_bytes > (1UL << 24)) {
|
||||
if (((table->fast_addr >> 17) & 0x3) == 0x0)
|
||||
LOG_ERROR("device needs paging - not implemented");
|
||||
|
||||
/* 4-byte addresses needed if more than 16 MBytes */
|
||||
if (((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) &&
|
||||
(table->addr_reset & (1UL << 29))) {
|
||||
/* dedicated 4-byte-address instructions, hopefully these ...
|
||||
* this entry is unfortunately optional as well
|
||||
* a subsequent 4-byte address table may overwrite this */
|
||||
dev->read_cmd = 0x13;
|
||||
dev->pprog_cmd = 0x12;
|
||||
dev->erase_cmd = 0xDC;
|
||||
if (dev->qread_cmd != 0)
|
||||
dev->qread_cmd = 0xEC;
|
||||
} else if (((table->fast_addr >> 17) & 0x3) == 0x1)
|
||||
LOG_INFO("device has to be switched to 4-byte addresses");
|
||||
}
|
||||
} else if (id == SFDP_4BYTE_ADDR) {
|
||||
struct sfdp_4byte_addr_param *table = (struct sfdp_4byte_addr_param *)ptable;
|
||||
|
||||
if (words >= (offsetof(struct sfdp_4byte_addr_param, erase_t1234)
|
||||
+ sizeof(table->erase_t1234)) >> 2) {
|
||||
LOG_INFO("4-byte address parameter table");
|
||||
|
||||
/* read and page program instructions */
|
||||
if (table->flags & (1UL << 0))
|
||||
dev->read_cmd = 0x13;
|
||||
if (table->flags & (1UL << 5))
|
||||
dev->qread_cmd = 0xEC;
|
||||
if (table->flags & (1UL << 6))
|
||||
dev->pprog_cmd = 0x12;
|
||||
|
||||
/* erase instructions */
|
||||
if ((erase_type == 1) && (table->flags & (1UL << 9)))
|
||||
dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFF;
|
||||
else if ((erase_type == 2) && (table->flags & (1UL << 10)))
|
||||
dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFF;
|
||||
else if ((erase_type == 3) && (table->flags & (1UL << 11)))
|
||||
dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFF;
|
||||
else if ((erase_type == 4) && (table->flags & (1UL << 12)))
|
||||
dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFF;
|
||||
} else
|
||||
LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %d", id, words);
|
||||
} else
|
||||
LOG_DEBUG("unimplemented parameter table id=0x%04" PRIx16, id);
|
||||
|
||||
free(ptable);
|
||||
ptable = NULL;
|
||||
}
|
||||
|
||||
if (erase_type != 0) {
|
||||
LOG_INFO("valid SFDP detected");
|
||||
retval = ERROR_OK;
|
||||
} else {
|
||||
LOG_ERROR("incomplete/invalid SFDP");
|
||||
retval = ERROR_FLASH_BANK_NOT_PROBED;
|
||||
}
|
||||
|
||||
err:
|
||||
free(pheaders);
|
||||
free(ptable);
|
||||
|
||||
return retval;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_SFDP_H
|
||||
#define OPENOCD_FLASH_NOR_SFDP_H
|
||||
|
||||
/* per JESD216D 'addr' is *byte* based but must be word aligned,
|
||||
* 'buffer' is word based, word aligned and always little-endian encoded,
|
||||
* in the flash, 'addr_len' is 3 or 4, 'dummy' ***usually*** 8
|
||||
*
|
||||
* the actual number of dummy clocks should be worked out by this function
|
||||
* dynamically, i.e. by scanning the first few bytes for the SFDP signature
|
||||
*
|
||||
* buffer contents is supposed to be returned in ***host*** endianness */
|
||||
typedef int (*read_sfdp_block_t)(struct flash_bank *bank, uint32_t addr,
|
||||
uint32_t words, uint32_t *buffer);
|
||||
|
||||
extern int spi_sfdp(struct flash_bank *bank, struct flash_device *dev,
|
||||
read_sfdp_block_t read_sfdp_block);
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_SFDP_H */
|
|
@ -1,5 +1,5 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Andreas Bolsch *
|
||||
* Copyright (C) 2018-2019 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* Copyright (C) 2012 by George Harris *
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
/* data structure to maintain flash ids from different vendors */
|
||||
struct flash_device {
|
||||
char *name;
|
||||
const char *name;
|
||||
uint8_t read_cmd;
|
||||
uint8_t qread_cmd;
|
||||
uint8_t pprog_cmd;
|
||||
|
@ -87,6 +87,8 @@ extern const struct flash_device flash_devices[];
|
|||
#define SPIFLASH_PAGE_PROGRAM 0x02 /* Page Program */
|
||||
#define SPIFLASH_FAST_READ 0x0B /* Fast Read */
|
||||
#define SPIFLASH_READ 0x03 /* Normal Read */
|
||||
#define SPIFLASH_MASS_ERASE 0xC7 /* Mass Erase */
|
||||
#define SPIFLASH_READ_SFDP 0x5A /* Read Serial Flash Discoverable Parameters */
|
||||
|
||||
#define SPIFLASH_DEF_PAGESIZE 256 /* default for non-page-oriented devices (FRAMs) */
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ static int stm32x_check_operation_supported(struct flash_bank *bank)
|
|||
/* if we have a dual flash bank device then
|
||||
* we need to perform option byte stuff on bank0 only */
|
||||
if (stm32x_info->register_base != FLASH_REG_BASE_B0) {
|
||||
LOG_ERROR("Option Byte Operation's must use bank0");
|
||||
LOG_ERROR("Option byte operations must use bank 0");
|
||||
return ERROR_FLASH_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,8 +81,7 @@
|
|||
* http://www.st.com/resource/en/reference_manual/dm00530369.pdf
|
||||
*/
|
||||
|
||||
/*
|
||||
* STM32G0xxx series for reference.
|
||||
/* STM32G0xxx series for reference.
|
||||
*
|
||||
* RM0444 (STM32G0x1)
|
||||
* http://www.st.com/resource/en/reference_manual/dm00371828.pdf
|
||||
|
@ -91,10 +90,9 @@
|
|||
* http://www.st.com/resource/en/reference_manual/dm00463896.pdf
|
||||
*/
|
||||
|
||||
/*
|
||||
* STM32G4xxx series for reference.
|
||||
/* STM32G4xxx series for reference.
|
||||
*
|
||||
* RM0440 (STM32G43x/44x/47x/48x)
|
||||
* RM0440 (STM32G43x/44x/47x/48x/49x/4Ax)
|
||||
* http://www.st.com/resource/en/reference_manual/dm00355726.pdf
|
||||
*
|
||||
* Cat. 2 devices have single bank only, page size is 2kByte.
|
||||
|
@ -104,12 +102,60 @@
|
|||
*
|
||||
* Bank mode is controlled by bit 22 (DBANK) in option bytes register.
|
||||
* Both banks are treated as a single OpenOCD bank.
|
||||
*
|
||||
* Cat. 4 devices have single bank only, page size is 2kByte.
|
||||
*/
|
||||
|
||||
/* STM32L5xxx series for reference.
|
||||
*
|
||||
* RM0428 (STM32L552xx/STM32L562xx)
|
||||
* http://www.st.com/resource/en/reference_manual/dm00346336.pdf
|
||||
*/
|
||||
|
||||
/* Erase time can be as high as 25ms, 10x this and assume it's toast... */
|
||||
|
||||
#define FLASH_ERASE_TIMEOUT 250
|
||||
|
||||
enum stm32l4_flash_reg_index {
|
||||
STM32_FLASH_ACR_INDEX,
|
||||
STM32_FLASH_KEYR_INDEX,
|
||||
STM32_FLASH_OPTKEYR_INDEX,
|
||||
STM32_FLASH_SR_INDEX,
|
||||
STM32_FLASH_CR_INDEX,
|
||||
STM32_FLASH_OPTR_INDEX,
|
||||
STM32_FLASH_WRP1AR_INDEX,
|
||||
STM32_FLASH_WRP1BR_INDEX,
|
||||
STM32_FLASH_WRP2AR_INDEX,
|
||||
STM32_FLASH_WRP2BR_INDEX,
|
||||
STM32_FLASH_REG_INDEX_NUM,
|
||||
};
|
||||
|
||||
static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
|
||||
[STM32_FLASH_ACR_INDEX] = 0x000,
|
||||
[STM32_FLASH_KEYR_INDEX] = 0x008,
|
||||
[STM32_FLASH_OPTKEYR_INDEX] = 0x00C,
|
||||
[STM32_FLASH_SR_INDEX] = 0x010,
|
||||
[STM32_FLASH_CR_INDEX] = 0x014,
|
||||
[STM32_FLASH_OPTR_INDEX] = 0x020,
|
||||
[STM32_FLASH_WRP1AR_INDEX] = 0x02C,
|
||||
[STM32_FLASH_WRP1BR_INDEX] = 0x030,
|
||||
[STM32_FLASH_WRP2AR_INDEX] = 0x04C,
|
||||
[STM32_FLASH_WRP2BR_INDEX] = 0x050,
|
||||
};
|
||||
|
||||
static const uint32_t stm32l5_ns_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
|
||||
[STM32_FLASH_ACR_INDEX] = 0x000,
|
||||
[STM32_FLASH_KEYR_INDEX] = 0x008,
|
||||
[STM32_FLASH_OPTKEYR_INDEX] = 0x010,
|
||||
[STM32_FLASH_SR_INDEX] = 0x020,
|
||||
[STM32_FLASH_CR_INDEX] = 0x028,
|
||||
[STM32_FLASH_OPTR_INDEX] = 0x040,
|
||||
[STM32_FLASH_WRP1AR_INDEX] = 0x058,
|
||||
[STM32_FLASH_WRP1BR_INDEX] = 0x05C,
|
||||
[STM32_FLASH_WRP2AR_INDEX] = 0x068,
|
||||
[STM32_FLASH_WRP2BR_INDEX] = 0x06C,
|
||||
};
|
||||
|
||||
struct stm32l4_rev {
|
||||
const uint16_t rev;
|
||||
const char *str;
|
||||
|
@ -123,6 +169,7 @@ struct stm32l4_part_info {
|
|||
const uint16_t max_flash_size_kb;
|
||||
const bool has_dual_bank;
|
||||
const uint32_t flash_regs_base;
|
||||
const uint32_t *default_flash_regs;
|
||||
const uint32_t fsize_addr;
|
||||
};
|
||||
|
||||
|
@ -135,10 +182,11 @@ struct stm32l4_flash_bank {
|
|||
uint32_t user_bank_size;
|
||||
uint32_t wrpxxr_mask;
|
||||
const struct stm32l4_part_info *part_info;
|
||||
const uint32_t *flash_regs;
|
||||
};
|
||||
|
||||
/* human readable list of families this drivers supports */
|
||||
static const char *device_families = "STM32L4/L4+/WB/WL/G4/G0";
|
||||
/* human readable list of families this drivers supports (sorted alphabetically) */
|
||||
static const char *device_families = "STM32G0/G4/L4/L4+/L5/WB/WL";
|
||||
|
||||
static const struct stm32l4_rev stm32_415_revs[] = {
|
||||
{ 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" }
|
||||
|
@ -184,6 +232,14 @@ static const struct stm32l4_rev stm32_471_revs[] = {
|
|||
{ 0x1001, "Z" },
|
||||
};
|
||||
|
||||
static const struct stm32l4_rev stm32_472_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x2000, "B" },
|
||||
};
|
||||
|
||||
static const struct stm32l4_rev stm32_479_revs[] = {
|
||||
{ 0x1000, "A" },
|
||||
};
|
||||
|
||||
static const struct stm32l4_rev stm32_495_revs[] = {
|
||||
{ 0x2001, "2.1" },
|
||||
};
|
||||
|
@ -205,6 +261,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 1024,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -215,6 +272,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 256,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -225,6 +283,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 128,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -235,6 +294,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 1024,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -245,6 +305,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 512,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -255,6 +316,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 128,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -265,6 +327,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 64,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -275,6 +338,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 128,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -285,6 +349,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 512,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -295,6 +360,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 2048,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -305,6 +371,29 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 1024,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
.id = 0x472,
|
||||
.revs = stm32_472_revs,
|
||||
.num_revs = ARRAY_SIZE(stm32_472_revs),
|
||||
.device_str = "STM32L55/L56xx",
|
||||
.max_flash_size_kb = 512,
|
||||
.has_dual_bank = true,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l5_ns_flash_regs,
|
||||
.fsize_addr = 0x0BFA05E0,
|
||||
},
|
||||
{
|
||||
.id = 0x479,
|
||||
.revs = stm32_479_revs,
|
||||
.num_revs = ARRAY_SIZE(stm32_479_revs),
|
||||
.device_str = "STM32G49/G4Axx",
|
||||
.max_flash_size_kb = 512,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x40022000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -315,6 +404,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 1024,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x58004000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -325,6 +415,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 512,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x58004000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
{
|
||||
|
@ -335,6 +426,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
|
|||
.max_flash_size_kb = 256,
|
||||
.has_dual_bank = false,
|
||||
.flash_regs_base = 0x58004000,
|
||||
.default_flash_regs = stm32l4_flash_regs,
|
||||
.fsize_addr = 0x1FFF75E0,
|
||||
},
|
||||
};
|
||||
|
@ -368,16 +460,37 @@ static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t r
|
|||
return stm32l4_info->part_info->flash_regs_base + reg_offset;
|
||||
}
|
||||
|
||||
static inline uint32_t stm32l4_get_flash_reg_by_index(struct flash_bank *bank,
|
||||
enum stm32l4_flash_reg_index reg_index)
|
||||
{
|
||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
return stm32l4_get_flash_reg(bank, stm32l4_info->flash_regs[reg_index]);
|
||||
}
|
||||
|
||||
static inline int stm32l4_read_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t *value)
|
||||
{
|
||||
return target_read_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value);
|
||||
}
|
||||
|
||||
static inline int stm32l4_read_flash_reg_by_index(struct flash_bank *bank,
|
||||
enum stm32l4_flash_reg_index reg_index, uint32_t *value)
|
||||
{
|
||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
return stm32l4_read_flash_reg(bank, stm32l4_info->flash_regs[reg_index], value);
|
||||
}
|
||||
|
||||
static inline int stm32l4_write_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t value)
|
||||
{
|
||||
return target_write_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value);
|
||||
}
|
||||
|
||||
static inline int stm32l4_write_flash_reg_by_index(struct flash_bank *bank,
|
||||
enum stm32l4_flash_reg_index reg_index, uint32_t value)
|
||||
{
|
||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
return stm32l4_write_flash_reg(bank, stm32l4_info->flash_regs[reg_index], value);
|
||||
}
|
||||
|
||||
static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
{
|
||||
uint32_t status;
|
||||
|
@ -385,7 +498,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
|||
|
||||
/* wait for busy to clear */
|
||||
for (;;) {
|
||||
retval = stm32l4_read_flash_reg(bank, STM32_FLASH_SR, &status);
|
||||
retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, &status);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
LOG_DEBUG("status: 0x%" PRIx32 "", status);
|
||||
|
@ -398,7 +511,6 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
|||
alive_sleep(1);
|
||||
}
|
||||
|
||||
|
||||
if (status & FLASH_WRPERR) {
|
||||
LOG_ERROR("stm32x device protected");
|
||||
retval = ERROR_FAIL;
|
||||
|
@ -411,7 +523,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
|||
/* If this operation fails, we ignore it and report the original
|
||||
* retval
|
||||
*/
|
||||
stm32l4_write_flash_reg(bank, STM32_FLASH_SR, status & FLASH_ERROR);
|
||||
stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, status & FLASH_ERROR);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -424,7 +536,7 @@ static int stm32l4_unlock_reg(struct flash_bank *bank)
|
|||
/* first check if not already unlocked
|
||||
* otherwise writing on STM32_FLASH_KEYR will fail
|
||||
*/
|
||||
int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
|
||||
int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -432,15 +544,15 @@ static int stm32l4_unlock_reg(struct flash_bank *bank)
|
|||
return ERROR_OK;
|
||||
|
||||
/* unlock flash registers */
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY1);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY1);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY2);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY2);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
|
||||
retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -456,7 +568,7 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
|
|||
{
|
||||
uint32_t ctrl;
|
||||
|
||||
int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
|
||||
int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -464,15 +576,15 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
|
|||
return ERROR_OK;
|
||||
|
||||
/* unlock option registers */
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY1);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY1);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY2);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY2);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
|
||||
retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -508,14 +620,14 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset,
|
|||
if (retval != ERROR_OK)
|
||||
goto err_lock;
|
||||
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OPTSTRT);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OPTSTRT);
|
||||
if (retval != ERROR_OK)
|
||||
goto err_lock;
|
||||
|
||||
retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
|
||||
|
||||
err_lock:
|
||||
retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK | FLASH_OPTLOCK);
|
||||
retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK);
|
||||
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
@ -528,11 +640,11 @@ static int stm32l4_protect_check(struct flash_bank *bank)
|
|||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
|
||||
uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br;
|
||||
stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1AR, &wrp1ar);
|
||||
stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1BR, &wrp1br);
|
||||
stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP1AR_INDEX, &wrp1ar);
|
||||
stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP1BR_INDEX, &wrp1br);
|
||||
if (stm32l4_info->part_info->has_dual_bank) {
|
||||
stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar);
|
||||
stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br);
|
||||
stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP2AR_INDEX, &wrp2ar);
|
||||
stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP2BR_INDEX, &wrp2br);
|
||||
} else {
|
||||
/* prevent uninitialized errors */
|
||||
wrp2ar = 0;
|
||||
|
@ -611,7 +723,7 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first,
|
|||
erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER;
|
||||
} else
|
||||
erase_flags |= i << FLASH_PAGE_SHIFT;
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, erase_flags);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, erase_flags);
|
||||
if (retval != ERROR_OK)
|
||||
break;
|
||||
|
||||
|
@ -623,7 +735,7 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first,
|
|||
}
|
||||
|
||||
err_lock:
|
||||
retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
|
||||
retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
|
||||
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
@ -651,7 +763,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first,
|
|||
reg_value = ((last & 0xFF) << 16) | begin;
|
||||
}
|
||||
|
||||
ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff);
|
||||
ret = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_WRP2AR_INDEX], reg_value, 0xffffffff);
|
||||
}
|
||||
/* Bank 1 */
|
||||
reg_value = 0xFF; /* Default to bank un-protected */
|
||||
|
@ -661,7 +773,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first,
|
|||
reg_value = (end << 16) | (first & 0xFF);
|
||||
}
|
||||
|
||||
ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff);
|
||||
ret = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_WRP1AR_INDEX], reg_value, 0xffffffff);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -727,8 +839,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
|||
buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, address);
|
||||
buf_set_u32(reg_params[3].value, 0, 32, count);
|
||||
buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_SR));
|
||||
buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_CR));
|
||||
buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX));
|
||||
buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX));
|
||||
|
||||
retval = target_run_flash_async_algorithm(target, buffer, count, 8,
|
||||
0, NULL,
|
||||
|
@ -748,7 +860,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
|||
if (error != 0) {
|
||||
LOG_ERROR("flash write failed = %08" PRIx32, error);
|
||||
/* Clear but report errors */
|
||||
stm32l4_write_flash_reg(bank, STM32_FLASH_SR, error);
|
||||
stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, error);
|
||||
retval = ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
@ -825,7 +937,7 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
|
|||
retval = stm32l4_write_block(bank, buffer, offset, count / 8);
|
||||
|
||||
err_lock:
|
||||
retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
|
||||
retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("block write failed");
|
||||
|
@ -838,17 +950,17 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
|
|||
{
|
||||
int retval;
|
||||
|
||||
/* try stm32l4/l4+/wb/g4 id register first, then stm32g0 id register */
|
||||
retval = target_read_u32(bank->target, DBGMCU_IDCODE_L4_G4, id);
|
||||
if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) {
|
||||
retval = target_read_u32(bank->target, DBGMCU_IDCODE_G0, id);
|
||||
if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) {
|
||||
LOG_ERROR("can't get device id");
|
||||
return (retval == ERROR_OK) ? ERROR_FAIL : retval;
|
||||
}
|
||||
/* 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);
|
||||
if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff))
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
return retval;
|
||||
LOG_ERROR("can't get the device id");
|
||||
return (retval == ERROR_OK) ? ERROR_FAIL : retval;
|
||||
}
|
||||
|
||||
static int stm32l4_probe(struct flash_bank *bank)
|
||||
|
@ -880,6 +992,7 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
}
|
||||
|
||||
part_info = stm32l4_info->part_info;
|
||||
stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs;
|
||||
|
||||
char device_info[1024];
|
||||
retval = bank->driver->info(bank, device_info, sizeof(device_info));
|
||||
|
@ -913,7 +1026,7 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
assert((flash_size_kb != 0xffff) && flash_size_kb);
|
||||
|
||||
/* read flash option register */
|
||||
retval = stm32l4_read_flash_reg(bank, STM32_FLASH_OPTR, &options);
|
||||
retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -924,6 +1037,7 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
int page_size_kb = 0;
|
||||
|
||||
stm32l4_info->dual_bank_mode = false;
|
||||
bool use_dbank_bit = false;
|
||||
|
||||
switch (device_id) {
|
||||
case 0x415: /* STM32L47/L48xx */
|
||||
|
@ -952,6 +1066,7 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
case 0x464: /* STM32L41/L42xx */
|
||||
case 0x466: /* STM32G03/G04xx */
|
||||
case 0x468: /* STM32G43/G44xx */
|
||||
case 0x479: /* STM32G49/G4Axx */
|
||||
case 0x497: /* STM32WLEx */
|
||||
/* single bank flash */
|
||||
page_size_kb = 2;
|
||||
|
@ -989,7 +1104,7 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
page_size_kb = 8;
|
||||
num_pages = flash_size_kb / page_size_kb;
|
||||
stm32l4_info->bank1_sectors = num_pages;
|
||||
const bool use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
|
||||
use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
|
||||
if ((use_dbank_bit && (options & BIT(22))) ||
|
||||
(!use_dbank_bit && (options & BIT(21)))) {
|
||||
stm32l4_info->dual_bank_mode = true;
|
||||
|
@ -998,6 +1113,23 @@ static int stm32l4_probe(struct flash_bank *bank)
|
|||
stm32l4_info->bank1_sectors = num_pages / 2;
|
||||
}
|
||||
break;
|
||||
case 0x472: /* STM32L55/L56xx */
|
||||
/* STM32L55/L56xx can be single/dual bank:
|
||||
* if size = 512K check DBANK bit(22)
|
||||
* if size = 256K check DB256K bit(21)
|
||||
*/
|
||||
page_size_kb = 4;
|
||||
num_pages = flash_size_kb / page_size_kb;
|
||||
stm32l4_info->bank1_sectors = num_pages;
|
||||
use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
|
||||
if ((use_dbank_bit && (options & BIT(22))) ||
|
||||
(!use_dbank_bit && (options & BIT(21)))) {
|
||||
stm32l4_info->dual_bank_mode = true;
|
||||
page_size_kb = 2;
|
||||
num_pages = flash_size_kb / page_size_kb;
|
||||
stm32l4_info->bank1_sectors = num_pages / 2;
|
||||
}
|
||||
break;
|
||||
case 0x495: /* STM32WB5x */
|
||||
case 0x496: /* STM32WB3x */
|
||||
/* single bank flash */
|
||||
|
@ -1086,19 +1218,17 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
|
|||
for (unsigned int i = 0; i < part_info->num_revs; i++) {
|
||||
if (rev_id == part_info->revs[i].rev) {
|
||||
rev_str = part_info->revs[i].str;
|
||||
|
||||
if (rev_str != NULL) {
|
||||
snprintf(buf, buf_size, "%s - Rev: %s%s",
|
||||
part_info->device_str, rev_str, stm32l4_info->probed ?
|
||||
(stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : "");
|
||||
return ERROR_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)%s",
|
||||
part_info->device_str, rev_id, stm32l4_info->probed ?
|
||||
(stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : "");
|
||||
int buf_len = snprintf(buf, buf_size, "%s - Rev %s : 0x%04x",
|
||||
part_info->device_str, rev_str ? rev_str : "'unknown'", rev_id);
|
||||
|
||||
if (stm32l4_info->probed)
|
||||
snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank",
|
||||
stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single");
|
||||
|
||||
return ERROR_OK;
|
||||
} else {
|
||||
snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families);
|
||||
|
@ -1133,18 +1263,18 @@ static int stm32l4_mass_erase(struct flash_bank *bank)
|
|||
if (retval != ERROR_OK)
|
||||
goto err_lock;
|
||||
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action);
|
||||
if (retval != ERROR_OK)
|
||||
goto err_lock;
|
||||
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action | FLASH_STRT);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action | FLASH_STRT);
|
||||
if (retval != ERROR_OK)
|
||||
goto err_lock;
|
||||
|
||||
retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
|
||||
|
||||
err_lock:
|
||||
retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
|
||||
retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
|
||||
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
@ -1257,7 +1387,7 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command)
|
|||
* "Note: If the read protection is set while the debugger is still
|
||||
* connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset."
|
||||
*/
|
||||
retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBL_LAUNCH);
|
||||
retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH);
|
||||
|
||||
command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
|
||||
|
||||
|
@ -1288,7 +1418,8 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
|
|||
}
|
||||
|
||||
/* set readout protection level 1 by erasing the RDP option byte */
|
||||
if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != ERROR_OK) {
|
||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], 0, 0x000000FF) != ERROR_OK) {
|
||||
command_print(CMD, "%s failed to lock device", bank->driver->name);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -1315,7 +1446,9 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
|
|||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) {
|
||||
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
|
||||
if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
|
||||
RDP_LEVEL_0, 0x000000FF) != ERROR_OK) {
|
||||
command_print(CMD, "%s failed to unlock device", bank->driver->name);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -19,18 +19,6 @@
|
|||
#ifndef OPENOCD_FLASH_NOR_STM32L4X
|
||||
#define OPENOCD_FLASH_NOR_STM32L4X
|
||||
|
||||
/* Flash registers offsets */
|
||||
#define STM32_FLASH_ACR 0x00
|
||||
#define STM32_FLASH_KEYR 0x08
|
||||
#define STM32_FLASH_OPTKEYR 0x0c
|
||||
#define STM32_FLASH_SR 0x10
|
||||
#define STM32_FLASH_CR 0x14
|
||||
#define STM32_FLASH_OPTR 0x20
|
||||
#define STM32_FLASH_WRP1AR 0x2c
|
||||
#define STM32_FLASH_WRP1BR 0x30
|
||||
#define STM32_FLASH_WRP2AR 0x4c
|
||||
#define STM32_FLASH_WRP2BR 0x50
|
||||
|
||||
/* FLASH_CR register bits */
|
||||
#define FLASH_PG (1 << 0)
|
||||
#define FLASH_PER (1 << 1)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,125 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2016 - 2018 by Andreas Bolsch *
|
||||
* andreas.bolsch@mni.thm.de *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_STMQSPI_H
|
||||
#define OPENOCD_FLASH_NOR_STMQSPI_H
|
||||
|
||||
#include "spi.h"
|
||||
|
||||
/* QSPI register offsets */
|
||||
#define QSPI_CR (0x00) /* Control register */
|
||||
#define QSPI_DCR (0x04) /* Device configuration register */
|
||||
#define QSPI_SR (0x08) /* Status register */
|
||||
#define QSPI_FCR (0x0C) /* Flag clear register */
|
||||
#define QSPI_DLR (0x10) /* Data length register */
|
||||
#define QSPI_CCR (0x14) /* Communication configuration register */
|
||||
#define QSPI_AR (0x18) /* Address register */
|
||||
#define QSPI_ABR (0x1C) /* Alternate bytes register */
|
||||
#define QSPI_DR (0x20) /* Data register */
|
||||
|
||||
/* common bits in QSPI_CR and OCTOSPI_CR */
|
||||
#define SPI_FSEL_FLASH 7 /* Select flash 2 */
|
||||
#define SPI_DUAL_FLASH 6 /* Dual flash mode */
|
||||
#define SPI_ABORT 1 /* Abort bit */
|
||||
|
||||
/* common bits in QSPI_DCR and OCTOSPI_DCR1 */
|
||||
#define SPI_FSIZE_POS 16 /* bit position of FSIZE */
|
||||
#define SPI_FSIZE_LEN 5 /* width of FSIZE field */
|
||||
|
||||
/* common bits in QSPI_SR/FCR and OCTOSPI_SR/FCR */
|
||||
#define SPI_BUSY 5 /* Busy flag */
|
||||
#define SPI_FTF 2 /* FIFO threshold flag */
|
||||
#define SPI_TCF 1 /* Transfer complete flag */
|
||||
|
||||
/* fields in QSPI_CCR */
|
||||
#define QSPI_DDRM 31 /* position of DDRM bit */
|
||||
#define SPI_DMODE_POS 24 /* bit position of DMODE */
|
||||
#define QSPI_DCYC_POS 18 /* bit position of DCYC */
|
||||
#define QSPI_DCYC_LEN 5 /* width of DCYC field */
|
||||
#define QSPI_DCYC_MASK ((BIT(QSPI_DCYC_LEN) - 1) << QSPI_DCYC_POS)
|
||||
#define SPI_ADSIZE_POS 12 /* bit position of ADSIZE */
|
||||
|
||||
#define QSPI_WRITE_MODE 0x00000000U /* indirect write mode */
|
||||
#define QSPI_READ_MODE 0x04000000U /* indirect read mode */
|
||||
#define QSPI_MM_MODE 0x0C000000U /* memory mapped mode */
|
||||
#define QSPI_ALTB_MODE 0x0003C000U /* alternate byte mode */
|
||||
#define QSPI_4LINE_MODE 0x03000F00U /* 4 lines for data, addr, instr */
|
||||
#define QSPI_NO_DATA (~0x03000000U) /* no data */
|
||||
#define QSPI_NO_ALTB (~QSPI_ALTB_MODE) /* no alternate */
|
||||
#define QSPI_NO_ADDR (~0x00000C00U) /* no address */
|
||||
#define QSPI_ADDR3 (0x2U << SPI_ADSIZE_POS) /* 3 byte address */
|
||||
#define QSPI_ADDR4 (0x3U << SPI_ADSIZE_POS) /* 4 byte address */
|
||||
|
||||
/* OCTOSPI register offsets */
|
||||
#define OCTOSPI_CR (0x000) /* Control register */
|
||||
#define OCTOSPI_DCR1 (0x008) /* Device configuration register 1 */
|
||||
#define OCTOSPI_DCR2 (0x00C) /* Device configuration register 2 */
|
||||
#define OCTOSPI_DCR3 (0x010) /* Device configuration register 3 */
|
||||
#define OCTOSPI_SR (0x020) /* Status register */
|
||||
#define OCTOSPI_FCR (0x024) /* Flag clear register */
|
||||
#define OCTOSPI_DLR (0x040) /* Data length register */
|
||||
#define OCTOSPI_AR (0x048) /* Address register */
|
||||
#define OCTOSPI_DR (0x050) /* Data register */
|
||||
#define OCTOSPI_CCR (0x100) /* Communication configuration register */
|
||||
#define OCTOSPI_TCR (0x108) /* Timing configuration register */
|
||||
#define OCTOSPI_IR (0x110) /* Instruction register */
|
||||
#define OCTOSPI_WCCR (0x180) /* Write communication configuration register */
|
||||
#define OCTOSPI_WIR (0x190) /* Write instruction register */
|
||||
#define OCTOSPI_MAGIC (0x3FC) /* Magic ID register, deleted from RM, why? */
|
||||
|
||||
#define OCTO_MAGIC_ID 0xA3C5DD01 /* Magic ID, deleted from RM, why? */
|
||||
|
||||
/* additional bits in OCTOSPI_CR */
|
||||
#define OCTOSPI_WRITE_MODE 0x00000000U /* indirect write mode */
|
||||
#define OCTOSPI_READ_MODE 0x10000000U /* indirect read mode */
|
||||
#define OCTOSPI_MM_MODE 0x30000000U /* memory mapped mode */
|
||||
|
||||
/* additional fields in OCTOSPI_DCR1 */
|
||||
#define OCTOSPI_MTYP_POS (24) /* bit position of MTYP */
|
||||
#define OCTOSPI_MTYP_LEN (3) /* width of MTYP field */
|
||||
#define OCTOSPI_MTYP_MASK ((BIT(OCTOSPI_MTYP_LEN) - 1) << OCTOSPI_MTYP_POS)
|
||||
|
||||
/* fields in OCTOSPI_CCR */
|
||||
#define OCTOSPI_ALTB_MODE 0x001F0000U /* alternate byte mode */
|
||||
#define OCTOSPI_8LINE_MODE 0x0F003F3FU /* 8 lines DTR for data, addr, instr */
|
||||
#define OCTOSPI_NO_DATA (~0x0F000000U) /* no data */
|
||||
#define OCTOSPI_NO_ALTB (~OCTOSPI_ALTB_MODE) /* no alternate */
|
||||
#define OCTOSPI_NO_ADDR (~0x00000F00U) /* no address */
|
||||
#define OCTOSPI_ADDR3 (0x2U << SPI_ADSIZE_POS) /* 3 byte address */
|
||||
#define OCTOSPI_ADDR4 (0x3U << SPI_ADSIZE_POS) /* 4 byte address */
|
||||
#define OCTOSPI_DQSEN 29 /* DQS enable */
|
||||
#define OCTOSPI_DDTR 27 /* DTR for data */
|
||||
#define OCTOSPI_NO_DDTR (~BIT(OCTOSPI_DDTR)) /* no DTR for data, but maybe still DQS */
|
||||
#define OCTOSPI_ISIZE_MASK (0x30) /* ISIZE field */
|
||||
|
||||
/* fields in OCTOSPI_TCR */
|
||||
#define OCTOSPI_DCYC_POS 0 /* bit position of DCYC */
|
||||
#define OCTOSPI_DCYC_LEN 5 /* width of DCYC field */
|
||||
#define OCTOSPI_DCYC_MASK ((BIT(OCTOSPI_DCYC_LEN) - 1) << OCTOSPI_DCYC_POS)
|
||||
|
||||
#define IS_OCTOSPI (stmqspi_info->octo)
|
||||
#define SPI_CR (IS_OCTOSPI ? OCTOSPI_CR : QSPI_CR)
|
||||
#define SPI_DCR (IS_OCTOSPI ? OCTOSPI_DCR1 : QSPI_DCR)
|
||||
#define SPI_SR (IS_OCTOSPI ? OCTOSPI_SR : QSPI_SR)
|
||||
#define SPI_FCR (IS_OCTOSPI ? OCTOSPI_FCR : QSPI_FCR)
|
||||
#define SPI_DLR (IS_OCTOSPI ? OCTOSPI_DLR : QSPI_DLR)
|
||||
#define SPI_AR (IS_OCTOSPI ? OCTOSPI_AR : QSPI_AR)
|
||||
#define SPI_DR (IS_OCTOSPI ? OCTOSPI_DR : QSPI_DR)
|
||||
#define SPI_CCR (IS_OCTOSPI ? OCTOSPI_CCR : QSPI_CCR)
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_STMQSPI_H */
|
|
@ -41,34 +41,33 @@
|
|||
#include <jtag/jtag.h>
|
||||
#include <helper/time_support.h>
|
||||
|
||||
#define SMI_READ_REG(a) (_SMI_READ_REG(a))
|
||||
#define _SMI_READ_REG(a) \
|
||||
{ \
|
||||
int __a; \
|
||||
uint32_t __v; \
|
||||
#define SMI_READ_REG(a) \
|
||||
({ \
|
||||
int _ret; \
|
||||
uint32_t _value; \
|
||||
\
|
||||
__a = target_read_u32(target, io_base + (a), &__v); \
|
||||
if (__a != ERROR_OK) \
|
||||
return __a; \
|
||||
__v; \
|
||||
}
|
||||
_ret = target_read_u32(target, io_base + (a), &_value); \
|
||||
if (_ret != ERROR_OK) \
|
||||
return _ret; \
|
||||
_value; \
|
||||
})
|
||||
|
||||
#define SMI_WRITE_REG(a, v) \
|
||||
{ \
|
||||
int __r; \
|
||||
int _retval; \
|
||||
\
|
||||
__r = target_write_u32(target, io_base + (a), (v)); \
|
||||
if (__r != ERROR_OK) \
|
||||
return __r; \
|
||||
_retval = target_write_u32(target, io_base + (a), (v)); \
|
||||
if (_retval != ERROR_OK) \
|
||||
return _retval; \
|
||||
}
|
||||
|
||||
#define SMI_POLL_TFF(timeout) \
|
||||
{ \
|
||||
int __r; \
|
||||
int _retval; \
|
||||
\
|
||||
__r = poll_tff(target, io_base, timeout); \
|
||||
if (__r != ERROR_OK) \
|
||||
return __r; \
|
||||
_retval = poll_tff(target, io_base, timeout); \
|
||||
if (_retval != ERROR_OK) \
|
||||
return _retval; \
|
||||
}
|
||||
|
||||
#define SMI_SET_SW_MODE() SMI_WRITE_REG(SMI_CR1, \
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* Implements Tcl commands used to access NOR flash facilities.
|
||||
*/
|
||||
|
||||
COMMAND_HELPER(flash_command_get_bank_maybe_probe, unsigned name_index,
|
||||
static COMMAND_HELPER(flash_command_get_bank_maybe_probe, unsigned name_index,
|
||||
struct flash_bank **bank, bool do_probe)
|
||||
{
|
||||
const char *name = CMD_ARGV[name_index];
|
||||
|
@ -440,6 +440,58 @@ COMMAND_HANDLER(handle_flash_write_image_command)
|
|||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
|
||||
if (CMD_ARGC >= 2) {
|
||||
image.base_address_set = true;
|
||||
COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address);
|
||||
} else {
|
||||
image.base_address_set = false;
|
||||
image.base_address = 0x0;
|
||||
}
|
||||
|
||||
image.start_address_set = false;
|
||||
|
||||
retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = flash_write_unlock_verify(target, &image, &written, auto_erase,
|
||||
auto_unlock, true, false);
|
||||
if (retval != ERROR_OK) {
|
||||
image_close(&image);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
|
||||
command_print(CMD, "wrote %" PRIu32 " bytes from file %s "
|
||||
"in %fs (%0.3f KiB/s)", written, CMD_ARGV[0],
|
||||
duration_elapsed(&bench), duration_kbps(&bench, written));
|
||||
}
|
||||
|
||||
image_close(&image);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_flash_verify_image_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
struct image image;
|
||||
uint32_t verified;
|
||||
|
||||
int retval;
|
||||
|
||||
if (CMD_ARGC < 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
if (!target) {
|
||||
LOG_ERROR("no target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
|
||||
if (CMD_ARGC >= 2) {
|
||||
image.base_address_set = 1;
|
||||
COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address);
|
||||
|
@ -454,16 +506,17 @@ COMMAND_HANDLER(handle_flash_write_image_command)
|
|||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = flash_write_unlock(target, &image, &written, auto_erase, auto_unlock);
|
||||
retval = flash_write_unlock_verify(target, &image, &verified, false,
|
||||
false, false, true);
|
||||
if (retval != ERROR_OK) {
|
||||
image_close(&image);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
|
||||
command_print(CMD, "wrote %" PRIu32 " bytes from file %s "
|
||||
"in %fs (%0.3f KiB/s)", written, CMD_ARGV[0],
|
||||
duration_elapsed(&bench), duration_kbps(&bench, written));
|
||||
command_print(CMD, "verified %" PRIu32 " bytes from file %s "
|
||||
"in %fs (%0.3f KiB/s)", verified, CMD_ARGV[0],
|
||||
duration_elapsed(&bench), duration_kbps(&bench, verified));
|
||||
}
|
||||
|
||||
image_close(&image);
|
||||
|
@ -1143,7 +1196,15 @@ static const struct command_registration flash_exec_command_handlers[] = {
|
|||
.mode = COMMAND_EXEC,
|
||||
.usage = "[erase] [unlock] filename [offset [file_type]]",
|
||||
.help = "Write an image to flash. Optionally first unprotect "
|
||||
"and/or erase the region to be used. Allow optional "
|
||||
"and/or erase the region to be used. Allow optional "
|
||||
"offset from beginning of bank (defaults to zero)",
|
||||
},
|
||||
{
|
||||
.name = "verify_image",
|
||||
.handler = handle_flash_verify_image_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "filename [offset [file_type]]",
|
||||
.help = "Verify an image against flash. Allow optional "
|
||||
"offset from beginning of bank (defaults to zero)",
|
||||
},
|
||||
{
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#define ID_XCF32P 0x05059093
|
||||
#define ID_MEANINGFUL_MASK 0x0FFFFFFF
|
||||
|
||||
const char *xcf_name_list[] = {
|
||||
static const char * const xcf_name_list[] = {
|
||||
"XCF08P",
|
||||
"XCF16P",
|
||||
"XCF32P",
|
||||
|
@ -399,7 +399,7 @@ static void flip_u8(uint8_t *out, const uint8_t *in, int len)
|
|||
* Function presumes need of bit reversing if it can not exactly detects
|
||||
* the opposite.
|
||||
*/
|
||||
bool need_bit_reverse(const uint8_t *buffer)
|
||||
static bool need_bit_reverse(const uint8_t *buffer)
|
||||
{
|
||||
const size_t L = 20;
|
||||
uint8_t reference[L];
|
||||
|
|
|
@ -116,6 +116,7 @@ proc stm32l1x args { eval stm32lx $args }
|
|||
# stm32[g0|g4|wb|wl] uses the same flash driver as the stm32l4x
|
||||
proc stm32g0x args { eval stm32l4x $args }
|
||||
proc stm32g4x args { eval stm32l4x $args }
|
||||
proc stm32l5x args { eval stm32l4x $args }
|
||||
proc stm32wbx args { eval stm32l4x $args }
|
||||
proc stm32wlx args { eval stm32l4x $args }
|
||||
|
||||
|
|
|
@ -349,7 +349,9 @@ static int register_command_handler(struct command_context *cmd_ctx,
|
|||
{
|
||||
Jim_Interp *interp = cmd_ctx->interp;
|
||||
|
||||
#if 0
|
||||
LOG_DEBUG("registering '%s'...", c->name);
|
||||
#endif
|
||||
|
||||
Jim_CmdProc *func = c->handler ? &script_command : &command_unknown;
|
||||
int retval = Jim_CreateCommand(interp, c->name, func, c, NULL);
|
||||
|
@ -920,39 +922,29 @@ COMMAND_HANDLER(handle_help_command)
|
|||
bool full = strcmp(CMD_NAME, "help") == 0;
|
||||
int retval;
|
||||
struct command *c = CMD_CTX->commands;
|
||||
char *cmd_match = NULL;
|
||||
char *cmd_match;
|
||||
|
||||
if (CMD_ARGC == 0)
|
||||
cmd_match = "";
|
||||
else if (CMD_ARGC >= 1) {
|
||||
unsigned i;
|
||||
if (CMD_ARGC <= 0)
|
||||
cmd_match = strdup("");
|
||||
|
||||
for (i = 0; i < CMD_ARGC; ++i) {
|
||||
if (NULL != cmd_match) {
|
||||
char *prev = cmd_match;
|
||||
else {
|
||||
cmd_match = strdup(CMD_ARGV[0]);
|
||||
|
||||
cmd_match = alloc_printf("%s %s", cmd_match, CMD_ARGV[i]);
|
||||
free(prev);
|
||||
if (NULL == cmd_match) {
|
||||
LOG_ERROR("unable to build search string");
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
cmd_match = alloc_printf("%s", CMD_ARGV[i]);
|
||||
if (NULL == cmd_match) {
|
||||
LOG_ERROR("unable to build search string");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 1; i < CMD_ARGC && cmd_match; ++i) {
|
||||
char *prev = cmd_match;
|
||||
cmd_match = alloc_printf("%s %s", prev, CMD_ARGV[i]);
|
||||
free(prev);
|
||||
}
|
||||
} else
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (cmd_match == NULL) {
|
||||
LOG_ERROR("unable to build search string");
|
||||
return -ENOMEM;
|
||||
}
|
||||
retval = CALL_COMMAND_HANDLER(command_help_show_list,
|
||||
c, 0, full, cmd_match);
|
||||
|
||||
if (CMD_ARGC >= 1)
|
||||
free(cmd_match);
|
||||
free(cmd_match);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
/* Autogenerated with update_jep106.pl*/
|
||||
/*
|
||||
* Should be autogenerated with update_jep106.pl but latest
|
||||
* file from JEDEC is only available in PDF form. The PDF
|
||||
* also breaks the pdftotext flow due to embedded watermarks.
|
||||
* Created with a mix of scripts and manual editing.
|
||||
*/
|
||||
[0][0x01 - 1] = "AMD",
|
||||
[0][0x02 - 1] = "AMI",
|
||||
[0][0x03 - 1] = "Fairchild",
|
||||
|
@ -194,7 +199,7 @@
|
|||
[1][0x43 - 1] = "Suwa Electronics",
|
||||
[1][0x44 - 1] = "Transmeta",
|
||||
[1][0x45 - 1] = "Micron CMS",
|
||||
[1][0x46 - 1] = "American Computer & Digital",
|
||||
[1][0x46 - 1] = "American Computer & Digital Components Inc",
|
||||
[1][0x47 - 1] = "Enhance 3000 Inc",
|
||||
[1][0x48 - 1] = "Tower Semiconductor",
|
||||
[1][0x49 - 1] = "CPU Design",
|
||||
|
@ -438,7 +443,7 @@
|
|||
[3][0x3b - 1] = "Concept Computer",
|
||||
[3][0x3c - 1] = "SILCOM",
|
||||
[3][0x3d - 1] = "3Dlabs",
|
||||
[3][0x3e - 1] = "c’t Magazine",
|
||||
[3][0x3e - 1] = "c't Magazine",
|
||||
[3][0x3f - 1] = "Sanera Systems",
|
||||
[3][0x40 - 1] = "Silicon Packets",
|
||||
[3][0x41 - 1] = "Viasystems Group",
|
||||
|
@ -532,7 +537,7 @@
|
|||
[4][0x1b - 1] = "Phonex Broadband",
|
||||
[4][0x1c - 1] = "Skyworks Solutions",
|
||||
[4][0x1d - 1] = "Entropic Communications",
|
||||
[4][0x1e - 1] = "I’M Intelligent Memory Ltd",
|
||||
[4][0x1e - 1] = "I'M Intelligent Memory Ltd",
|
||||
[4][0x1f - 1] = "Zensys A/S",
|
||||
[4][0x20 - 1] = "Legend Silicon Corp",
|
||||
[4][0x21 - 1] = "Sci-worx GmbH",
|
||||
|
@ -610,7 +615,7 @@
|
|||
[4][0x69 - 1] = "Century Micro Inc",
|
||||
[4][0x6a - 1] = "Icera Semiconductor",
|
||||
[4][0x6b - 1] = "Mediaworks Integrated Systems",
|
||||
[4][0x6c - 1] = "O’Neil Product Development",
|
||||
[4][0x6c - 1] = "O'Neil Product Development",
|
||||
[4][0x6d - 1] = "Supreme Top Technology Ltd",
|
||||
[4][0x6e - 1] = "MicroDisplay Corporation",
|
||||
[4][0x6f - 1] = "Team Group Inc",
|
||||
|
@ -837,7 +842,7 @@
|
|||
[6][0x50 - 1] = "Silego Technology Inc",
|
||||
[6][0x51 - 1] = "Kinglife",
|
||||
[6][0x52 - 1] = "Ability Industries Ltd",
|
||||
[6][0x53 - 1] = "Silicon Power Computer &",
|
||||
[6][0x53 - 1] = "Silicon Power Computer & Communications",
|
||||
[6][0x54 - 1] = "Augusta Technology Inc",
|
||||
[6][0x55 - 1] = "Nantronics Semiconductors",
|
||||
[6][0x56 - 1] = "Hilscher Gesellschaft",
|
||||
|
@ -961,7 +966,7 @@
|
|||
[7][0x4e - 1] = "Mustang",
|
||||
[7][0x4f - 1] = "Orca Systems",
|
||||
[7][0x50 - 1] = "Passif Semiconductor",
|
||||
[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing)",
|
||||
[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing) Inc",
|
||||
[7][0x52 - 1] = "Memphis Electronic",
|
||||
[7][0x53 - 1] = "Beckhoff Automation GmbH",
|
||||
[7][0x54 - 1] = "Harmony Semiconductor Corp",
|
||||
|
@ -985,7 +990,7 @@
|
|||
[7][0x66 - 1] = "Avalanche Technology",
|
||||
[7][0x67 - 1] = "R&D Center ELVEES OJSC",
|
||||
[7][0x68 - 1] = "KingboMars Technology Co Ltd",
|
||||
[7][0x69 - 1] = "High Bridge Solutions Industria",
|
||||
[7][0x69 - 1] = "High Bridge Solutions Industria Eletronica",
|
||||
[7][0x6a - 1] = "Transcend Technology Co Ltd",
|
||||
[7][0x6b - 1] = "Everspin Technologies",
|
||||
[7][0x6c - 1] = "Hon-Hai Precision",
|
||||
|
@ -1031,7 +1036,7 @@
|
|||
[8][0x17 - 1] = "Axell Corporation",
|
||||
[8][0x18 - 1] = "Essencore Limited",
|
||||
[8][0x19 - 1] = "Phytium",
|
||||
[8][0x1a - 1] = "Xi’an UniIC Semiconductors Co Ltd",
|
||||
[8][0x1a - 1] = "Xi'an UniIC Semiconductors Co Ltd",
|
||||
[8][0x1b - 1] = "Ambiq Micro",
|
||||
[8][0x1c - 1] = "eveRAM Technology Inc",
|
||||
[8][0x1d - 1] = "Infomax",
|
||||
|
@ -1083,7 +1088,7 @@
|
|||
[8][0x4b - 1] = "in2H2 inc",
|
||||
[8][0x4c - 1] = "Shenzhen Pango Microsystems Co Ltd",
|
||||
[8][0x4d - 1] = "Vasekey",
|
||||
[8][0x4e - 1] = "Cal-Comp Industria de",
|
||||
[8][0x4e - 1] = "Cal-Comp Industria de Semicondutores",
|
||||
[8][0x4f - 1] = "Eyenix Co Ltd",
|
||||
[8][0x50 - 1] = "Heoriady",
|
||||
[8][0x51 - 1] = "Accelerated Memory Production Inc",
|
||||
|
@ -1114,7 +1119,7 @@
|
|||
[8][0x6a - 1] = "Axign",
|
||||
[8][0x6b - 1] = "Kingred Electronic Technology Ltd",
|
||||
[8][0x6c - 1] = "Chao Yue Zhuo Computer Business Dept.",
|
||||
[8][0x6d - 1] = "Guangzhou Si Nuo Electronic",
|
||||
[8][0x6d - 1] = "Guangzhou Si Nuo Electronic Technology.",
|
||||
[8][0x6e - 1] = "Crocus Technology Inc",
|
||||
[8][0x6f - 1] = "Creative Chips GmbH",
|
||||
[8][0x70 - 1] = "GE Aviation Systems LLC.",
|
||||
|
@ -1130,6 +1135,8 @@
|
|||
[8][0x7a - 1] = "AltoBeam",
|
||||
[8][0x7b - 1] = "Wave Computing",
|
||||
[8][0x7c - 1] = "Beijing TrustNet Technology Co Ltd",
|
||||
[8][0x7d - 1] = "Innovium Inc",
|
||||
[8][0x7e - 1] = "Starsway Technology Limited",
|
||||
[9][0x01 - 1] = "Weltronics Co LTD",
|
||||
[9][0x02 - 1] = "VMware Inc",
|
||||
[9][0x03 - 1] = "Hewlett Packard Enterprise",
|
||||
|
@ -1160,6 +1167,7 @@
|
|||
[9][0x1c - 1] = "MemxPro Inc",
|
||||
[9][0x1d - 1] = "Tammuz Co Ltd",
|
||||
[9][0x1e - 1] = "Allwinner Technology",
|
||||
[9][0x1f - 1] = "Shenzhen City Futian District Qing Xuan Tong Computer Trading Firm",
|
||||
[9][0x20 - 1] = "XMC",
|
||||
[9][0x21 - 1] = "Teclast",
|
||||
[9][0x22 - 1] = "Maxsun",
|
||||
|
@ -1272,7 +1280,7 @@
|
|||
[10][0x0f - 1] = "UNIC Memory Technology Co Ltd",
|
||||
[10][0x10 - 1] = "Shenzhen Huafeng Science Technology",
|
||||
[10][0x11 - 1] = "ChangXin Memory Technologies Inc",
|
||||
[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer",
|
||||
[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer Trading Firm",
|
||||
[10][0x13 - 1] = "SambaNova Systems",
|
||||
[10][0x14 - 1] = "V-GEN",
|
||||
[10][0x15 - 1] = "Jump Trading",
|
||||
|
@ -1291,7 +1299,7 @@
|
|||
[10][0x22 - 1] = "Shenzhen Chuangshifeida Technology",
|
||||
[10][0x23 - 1] = "Guangzhou MiaoYuanJi Technology",
|
||||
[10][0x24 - 1] = "ADVAN Inc",
|
||||
[10][0x25 - 1] = "Shenzhen Qianhai Weishengda",
|
||||
[10][0x25 - 1] = "Shenzhen Qianhai Weishengda Electronic Commerce Company Ltd",
|
||||
[10][0x26 - 1] = "Guangzhou Guang Xie Cheng Trading",
|
||||
[10][0x27 - 1] = "StarRam International Co Ltd",
|
||||
[10][0x28 - 1] = "Shen Zhen XinShenHua Tech Co Ltd",
|
||||
|
@ -1321,7 +1329,7 @@
|
|||
[10][0x40 - 1] = "Shenzhen Zhongguang Yunhe Trading",
|
||||
[10][0x41 - 1] = "Smart Shine(QingDao) Microelectronics",
|
||||
[10][0x42 - 1] = "Thermaltake Technology Co Ltd",
|
||||
[10][0x43 - 1] = "Shenzhen O’Yang Maile Technology Ltd",
|
||||
[10][0x43 - 1] = "Shenzhen O'Yang Maile Technology Ltd",
|
||||
[10][0x44 - 1] = "UPMEM",
|
||||
[10][0x45 - 1] = "Chun Well Technology Holding Limited",
|
||||
[10][0x46 - 1] = "Astera Labs Inc",
|
||||
|
@ -1397,7 +1405,7 @@
|
|||
[11][0x0e - 1] = "MLOONG",
|
||||
[11][0x0f - 1] = "Quadratica LLC",
|
||||
[11][0x10 - 1] = "Anpec Electronics",
|
||||
[11][0x11 - 1] = "Xi’an Morebeck Semiconductor Tech Co",
|
||||
[11][0x11 - 1] = "Xi'an Morebeck Semiconductor Tech Co",
|
||||
[11][0x12 - 1] = "Kingbank Technology Co Ltd",
|
||||
[11][0x13 - 1] = "ITRenew Inc",
|
||||
[11][0x14 - 1] = "Shenzhen Eaget Innovation Tech Ltd",
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
#ifdef _WIN32
|
||||
PROCESS_INFORMATION proc_info;
|
||||
|
||||
HANDLE aice_pipe_output[2];
|
||||
HANDLE aice_pipe_input[2];
|
||||
static HANDLE aice_pipe_output[2];
|
||||
static HANDLE aice_pipe_input[2];
|
||||
|
||||
static int aice_pipe_write(const void *buffer, int count)
|
||||
{
|
||||
|
@ -158,8 +158,8 @@ static int aice_pipe_open(struct aice_port_param_s *param)
|
|||
|
||||
#else
|
||||
|
||||
int aice_pipe_output[2];
|
||||
int aice_pipe_input[2];
|
||||
static int aice_pipe_output[2];
|
||||
static int aice_pipe_input[2];
|
||||
|
||||
static int aice_pipe_write(const void *buffer, int count)
|
||||
{
|
||||
|
|
|
@ -683,7 +683,7 @@ int aice_write_ctrl(uint32_t address, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_dtr(uint8_t target_id, uint32_t *data)
|
||||
static int aice_read_dtr(uint8_t target_id, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -733,7 +733,7 @@ int aice_read_dtr(uint8_t target_id, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx)
|
||||
static int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -784,7 +784,7 @@ int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_dtr(uint8_t target_id, uint32_t data)
|
||||
static int aice_write_dtr(uint8_t target_id, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -836,7 +836,7 @@ int aice_write_dtr(uint8_t target_id, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx)
|
||||
static int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -887,7 +887,7 @@ int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
static int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -936,7 +936,7 @@ int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
static int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -992,7 +992,7 @@ int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
static int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1042,7 +1042,7 @@ int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_edmsr(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
static int aice_write_edmsr(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1236,7 +1236,7 @@ static int aice_do_execute(uint8_t target_id)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
static int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1290,7 +1290,7 @@ int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
static int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1345,7 +1345,7 @@ int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
static int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1400,7 +1400,7 @@ int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words)
|
||||
static int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1450,7 +1450,7 @@ int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_words)
|
||||
static int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_words)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1506,7 +1506,7 @@ int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_w
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
static int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1556,7 +1556,7 @@ int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
static int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1606,7 +1606,7 @@ int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
static int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1657,7 +1657,7 @@ int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_batch_buffer_read(uint8_t buf_index, uint32_t *word, uint32_t num_of_words)
|
||||
static int aice_batch_buffer_read(uint8_t buf_index, uint32_t *word, uint32_t num_of_words)
|
||||
{
|
||||
int retry_times = 0;
|
||||
|
||||
|
@ -1760,7 +1760,7 @@ int aice_batch_buffer_write(uint8_t buf_index, const uint8_t *word, uint32_t num
|
|||
typedef int (*read_mem_func_t)(uint32_t coreid, uint32_t address, uint32_t *data);
|
||||
typedef int (*write_mem_func_t)(uint32_t coreid, uint32_t address, uint32_t data);
|
||||
|
||||
struct aice_nds32_info core_info[AICE_MAX_NUM_CORE];
|
||||
static struct aice_nds32_info core_info[AICE_MAX_NUM_CORE];
|
||||
static uint8_t total_num_of_core;
|
||||
|
||||
static char *custom_srst_script;
|
||||
|
|
|
@ -209,7 +209,7 @@ unsigned jtag_tap_count_enabled(void)
|
|||
}
|
||||
|
||||
/** Append a new TAP to the chain of all taps. */
|
||||
void jtag_tap_add(struct jtag_tap *t)
|
||||
static void jtag_tap_add(struct jtag_tap *t)
|
||||
{
|
||||
unsigned jtag_num_taps = 0;
|
||||
|
||||
|
@ -953,12 +953,12 @@ int default_interface_jtag_execute_queue(void)
|
|||
|
||||
int result = jtag->jtag_ops->execute_queue();
|
||||
|
||||
#if !BUILD_ZY1000
|
||||
#if !HAVE_JTAG_MINIDRIVER_H
|
||||
/* Only build this if we use a regular driver with a command queue.
|
||||
* Otherwise jtag_command_queue won't be found at compile/link time. Its
|
||||
* definition is in jtag/commands.c, which is only built/linked by
|
||||
* jtag/Makefile.am if MINIDRIVER_DUMMY || !MINIDRIVER, but those variables
|
||||
* aren't accessible here. */
|
||||
* aren't accessible here. Use HAVE_JTAG_MINIDRIVER_H */
|
||||
struct jtag_command *cmd = jtag_command_queue;
|
||||
while (debug_level >= LOG_LVL_DEBUG_IO && cmd) {
|
||||
switch (cmd->type) {
|
||||
|
|
|
@ -170,8 +170,15 @@ endif
|
|||
if OPENJTAG
|
||||
DRIVERFILES += %D%/openjtag.c
|
||||
endif
|
||||
if CMSIS_DAP
|
||||
DRIVERFILES += %D%/cmsis_dap_usb.c
|
||||
if CMSIS_DAP_HID
|
||||
DRIVERFILES += %D%/cmsis_dap_usb_hid.c
|
||||
DRIVERFILES += %D%/cmsis_dap.c
|
||||
endif
|
||||
if CMSIS_DAP_USB
|
||||
DRIVERFILES += %D%/cmsis_dap_usb_bulk.c
|
||||
if !CMSIS_DAP_HID
|
||||
DRIVERFILES += %D%/cmsis_dap.c
|
||||
endif
|
||||
endif
|
||||
if IMX_GPIO
|
||||
DRIVERFILES += %D%/imx_gpio.c
|
||||
|
@ -187,7 +194,9 @@ DRIVERHEADERS = \
|
|||
%D%/bitbang.h \
|
||||
%D%/bitq.h \
|
||||
%D%/jtag_usb_common.h \
|
||||
%D%/libftdi_helper.h \
|
||||
%D%/libusb_helper.h \
|
||||
%D%/cmsis_dap.h \
|
||||
%D%/minidriver_imp.h \
|
||||
%D%/mpsse.h \
|
||||
%D%/rlink.h \
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Mickaël Thomas *
|
||||
* mickael9@gmail.com *
|
||||
* *
|
||||
* Copyright (C) 2016 by Maksym Hilliaka *
|
||||
* oter@frozen-team.com *
|
||||
* *
|
||||
|
@ -38,12 +41,17 @@
|
|||
#include <jtag/commands.h>
|
||||
#include <jtag/tcl.h>
|
||||
|
||||
#include <hidapi.h>
|
||||
#include "cmsis_dap.h"
|
||||
|
||||
/*
|
||||
* See CMSIS-DAP documentation:
|
||||
* Version 0.01 - Beta.
|
||||
*/
|
||||
static const struct cmsis_dap_backend *const cmsis_dap_backends[] = {
|
||||
#if BUILD_CMSIS_DAP_USB == 1
|
||||
&cmsis_dap_usb_backend,
|
||||
#endif
|
||||
|
||||
#if BUILD_CMSIS_DAP_HID == 1
|
||||
&cmsis_dap_hid_backend,
|
||||
#endif
|
||||
};
|
||||
|
||||
/* USB Config */
|
||||
|
||||
|
@ -52,6 +60,7 @@
|
|||
* PID 0xf001: LPC-Link-II CMSIS_DAP
|
||||
* PID 0xf002: OPEN-SDA CMSIS_DAP (Freedom Board)
|
||||
* PID 0x2722: Keil ULINK2 CMSIS-DAP
|
||||
* PID 0x2750: Keil ULINKplus CMSIS-DAP
|
||||
*
|
||||
* VID 0x0d28: mbed Software
|
||||
* PID 0x0204: MBED CMSIS-DAP
|
||||
|
@ -61,10 +70,10 @@
|
|||
/* vid = pid = 0 marks the end of the list */
|
||||
static uint16_t cmsis_dap_vid[MAX_USB_IDS + 1] = { 0 };
|
||||
static uint16_t cmsis_dap_pid[MAX_USB_IDS + 1] = { 0 };
|
||||
static wchar_t *cmsis_dap_serial;
|
||||
static char *cmsis_dap_serial;
|
||||
static int cmsis_dap_backend = -1;
|
||||
static bool swd_mode;
|
||||
|
||||
#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */
|
||||
#define USB_TIMEOUT 1000
|
||||
|
||||
/* CMSIS-DAP General Commands */
|
||||
|
@ -163,14 +172,6 @@ static const char * const info_caps_str[] = {
|
|||
/* max clock speed (kHz) */
|
||||
#define DAP_MAX_CLOCK 5000
|
||||
|
||||
struct cmsis_dap {
|
||||
hid_device *dev_handle;
|
||||
uint16_t packet_size;
|
||||
int packet_count;
|
||||
uint8_t *packet_buffer;
|
||||
uint8_t caps;
|
||||
uint8_t mode;
|
||||
};
|
||||
|
||||
struct pending_transfer_result {
|
||||
uint8_t cmd;
|
||||
|
@ -223,91 +224,10 @@ static uint8_t output_pins = SWJ_PIN_SRST | SWJ_PIN_TRST;
|
|||
|
||||
static struct cmsis_dap *cmsis_dap_handle;
|
||||
|
||||
static int cmsis_dap_usb_open(void)
|
||||
|
||||
static int cmsis_dap_open(void)
|
||||
{
|
||||
hid_device *dev = NULL;
|
||||
int i;
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
unsigned short target_vid, target_pid;
|
||||
bool found = false;
|
||||
|
||||
target_vid = 0;
|
||||
target_pid = 0;
|
||||
|
||||
if (hid_init() != 0) {
|
||||
LOG_ERROR("unable to open HIDAPI");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The CMSIS-DAP specification stipulates:
|
||||
* "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the
|
||||
* debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer."
|
||||
*/
|
||||
devs = hid_enumerate(0x0, 0x0);
|
||||
cur_dev = devs;
|
||||
while (NULL != cur_dev) {
|
||||
if (0 == cmsis_dap_vid[0]) {
|
||||
if (NULL == cur_dev->product_string) {
|
||||
LOG_DEBUG("Cannot read product string of device 0x%x:0x%x",
|
||||
cur_dev->vendor_id, cur_dev->product_id);
|
||||
} else {
|
||||
if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) {
|
||||
/* if the user hasn't specified VID:PID *and*
|
||||
* product string contains "CMSIS-DAP", pick it
|
||||
*/
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* otherwise, exhaustively compare against all VID:PID in list */
|
||||
for (i = 0; cmsis_dap_vid[i] || cmsis_dap_pid[i]; i++) {
|
||||
if ((cmsis_dap_vid[i] == cur_dev->vendor_id) && (cmsis_dap_pid[i] == cur_dev->product_id))
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (cmsis_dap_vid[i] || cmsis_dap_pid[i])
|
||||
found = true;
|
||||
}
|
||||
|
||||
/* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */
|
||||
if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0)
|
||||
found = false;
|
||||
|
||||
if (found) {
|
||||
/* we have found an adapter, so exit further checks */
|
||||
/* check serial number matches if given */
|
||||
if (cmsis_dap_serial != NULL) {
|
||||
if ((cur_dev->serial_number != NULL) && wcscmp(cmsis_dap_serial, cur_dev->serial_number) == 0) {
|
||||
break;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
|
||||
found = false;
|
||||
}
|
||||
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
if (NULL != cur_dev) {
|
||||
target_vid = cur_dev->vendor_id;
|
||||
target_pid = cur_dev->product_id;
|
||||
}
|
||||
|
||||
if (target_vid == 0 && target_pid == 0) {
|
||||
LOG_ERROR("unable to find CMSIS-DAP device");
|
||||
hid_free_enumeration(devs);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dev = hid_open_path(cur_dev->path);
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
if (dev == NULL) {
|
||||
LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
const struct cmsis_dap_backend *backend = NULL;
|
||||
|
||||
struct cmsis_dap *dap = malloc(sizeof(struct cmsis_dap));
|
||||
if (dap == NULL) {
|
||||
|
@ -315,42 +235,55 @@ static int cmsis_dap_usb_open(void)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dap->dev_handle = dev;
|
||||
dap->caps = 0;
|
||||
dap->mode = 0;
|
||||
dap->packet_size = 0; /* initialized by backend */
|
||||
|
||||
cmsis_dap_handle = dap;
|
||||
if (cmsis_dap_backend >= 0) {
|
||||
/* Use forced backend */
|
||||
backend = cmsis_dap_backends[cmsis_dap_backend];
|
||||
if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) != ERROR_OK)
|
||||
backend = NULL;
|
||||
} else {
|
||||
/* Try all backends */
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) {
|
||||
backend = cmsis_dap_backends[i];
|
||||
if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) == ERROR_OK)
|
||||
break;
|
||||
else
|
||||
backend = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate default packet buffer, may be changed later.
|
||||
* currently with HIDAPI we have no way of getting the output report length
|
||||
* without this info we cannot communicate with the adapter.
|
||||
* For the moment we ahve to hard code the packet size */
|
||||
|
||||
int packet_size = PACKET_SIZE;
|
||||
|
||||
/* atmel cmsis-dap uses 512 byte reports */
|
||||
/* except when it doesn't e.g. with mEDBG on SAMD10 Xplained
|
||||
* board */
|
||||
/* TODO: HID report descriptor should be parsed instead of
|
||||
* hardcoding a match by VID */
|
||||
if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175)
|
||||
packet_size = 512 + 1;
|
||||
|
||||
cmsis_dap_handle->packet_buffer = malloc(packet_size);
|
||||
cmsis_dap_handle->packet_size = packet_size;
|
||||
|
||||
if (cmsis_dap_handle->packet_buffer == NULL) {
|
||||
LOG_ERROR("unable to allocate memory");
|
||||
if (backend == NULL) {
|
||||
LOG_ERROR("unable to find a matching CMSIS-DAP device");
|
||||
free(dap);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
assert(dap->packet_size > 0);
|
||||
|
||||
dap->backend = backend;
|
||||
dap->packet_buffer = malloc(dap->packet_size);
|
||||
|
||||
if (dap->packet_buffer == NULL) {
|
||||
LOG_ERROR("unable to allocate memory");
|
||||
dap->backend->close(dap);
|
||||
free(dap);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
cmsis_dap_handle = dap;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void cmsis_dap_usb_close(struct cmsis_dap *dap)
|
||||
static void cmsis_dap_close(struct cmsis_dap *dap)
|
||||
{
|
||||
hid_close(dap->dev_handle);
|
||||
hid_exit();
|
||||
if (dap->backend) {
|
||||
dap->backend->close(dap);
|
||||
dap->backend = NULL;
|
||||
}
|
||||
|
||||
free(cmsis_dap_handle->packet_buffer);
|
||||
free(cmsis_dap_handle);
|
||||
|
@ -364,47 +297,27 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap)
|
|||
}
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen)
|
||||
{
|
||||
#ifdef CMSIS_DAP_JTAG_DEBUG
|
||||
LOG_DEBUG("cmsis-dap usb xfer cmd=%02X", dap->packet_buffer[1]);
|
||||
#endif
|
||||
/* Pad the rest of the TX buffer with 0's */
|
||||
memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen);
|
||||
|
||||
/* write data to device */
|
||||
int retval = hid_write(dap->dev_handle, dap->packet_buffer, dap->packet_size);
|
||||
if (retval == -1) {
|
||||
LOG_ERROR("error writing data: %ls", hid_error(dap->dev_handle));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* Send a message and receive the reply */
|
||||
static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen)
|
||||
static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
|
||||
{
|
||||
if (pending_fifo_block_count) {
|
||||
LOG_ERROR("pending %d blocks, flushing", pending_fifo_block_count);
|
||||
while (pending_fifo_block_count) {
|
||||
hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, 10);
|
||||
dap->backend->read(dap, 10);
|
||||
pending_fifo_block_count--;
|
||||
}
|
||||
pending_fifo_put_idx = 0;
|
||||
pending_fifo_get_idx = 0;
|
||||
}
|
||||
|
||||
int retval = cmsis_dap_usb_write(dap, txlen);
|
||||
if (retval != ERROR_OK)
|
||||
int retval = dap->backend->write(dap, txlen, USB_TIMEOUT);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
/* get reply */
|
||||
retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, USB_TIMEOUT);
|
||||
if (retval == -1 || retval == 0) {
|
||||
LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
retval = dap->backend->read(dap, USB_TIMEOUT);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -422,7 +335,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Pins(uint8_t pins, uint8_t mask, uint32_t delay
|
|||
buffer[5] = (delay >> 8) & 0xff;
|
||||
buffer[6] = (delay >> 16) & 0xff;
|
||||
buffer[7] = (delay >> 24) & 0xff;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 8);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 8);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_PINS failed.");
|
||||
|
@ -448,7 +361,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Clock(uint32_t swj_clock)
|
|||
buffer[3] = (swj_clock >> 8) & 0xff;
|
||||
buffer[4] = (swj_clock >> 16) & 0xff;
|
||||
buffer[5] = (swj_clock >> 24) & 0xff;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 6);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 6);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_CLOCK failed.");
|
||||
|
@ -477,7 +390,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Sequence(uint8_t s_len, const uint8_t *sequence
|
|||
buffer[2] = s_len;
|
||||
bit_copy(&buffer[3], 0, sequence, 0, s_len);
|
||||
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK)
|
||||
return ERROR_FAIL;
|
||||
|
@ -493,7 +406,7 @@ static int cmsis_dap_cmd_DAP_Info(uint8_t info, uint8_t **data)
|
|||
buffer[0] = 0; /* report number */
|
||||
buffer[1] = CMD_DAP_INFO;
|
||||
buffer[2] = info;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 3);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_INFO failed.");
|
||||
|
@ -514,7 +427,7 @@ static int cmsis_dap_cmd_DAP_LED(uint8_t led, uint8_t state)
|
|||
buffer[1] = CMD_DAP_LED;
|
||||
buffer[2] = led;
|
||||
buffer[3] = state;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 4);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != 0x00) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_LED failed.");
|
||||
|
@ -532,7 +445,7 @@ static int cmsis_dap_cmd_DAP_Connect(uint8_t mode)
|
|||
buffer[0] = 0; /* report number */
|
||||
buffer[1] = CMD_DAP_CONNECT;
|
||||
buffer[2] = mode;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 3);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_CONNECT failed.");
|
||||
|
@ -554,7 +467,7 @@ static int cmsis_dap_cmd_DAP_Disconnect(void)
|
|||
|
||||
buffer[0] = 0; /* report number */
|
||||
buffer[1] = CMD_DAP_DISCONNECT;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 2);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 2);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_DISCONNECT failed.");
|
||||
|
@ -576,7 +489,7 @@ static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t retry_count,
|
|||
buffer[4] = (retry_count >> 8) & 0xff;
|
||||
buffer[5] = match_retry & 0xff;
|
||||
buffer[6] = (match_retry >> 8) & 0xff;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 7);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_TFER_Configure failed.");
|
||||
|
@ -594,7 +507,7 @@ static int cmsis_dap_cmd_DAP_SWD_Configure(uint8_t cfg)
|
|||
buffer[0] = 0; /* report number */
|
||||
buffer[1] = CMD_DAP_SWD_CONFIGURE;
|
||||
buffer[2] = cfg;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 3);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_SWD_Configure failed.");
|
||||
|
@ -614,7 +527,7 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us)
|
|||
buffer[1] = CMD_DAP_DELAY;
|
||||
buffer[2] = delay_us & 0xff;
|
||||
buffer[3] = (delay_us >> 8) & 0xff;
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, 4);
|
||||
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_Delay failed.");
|
||||
|
@ -684,9 +597,14 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
|
|||
}
|
||||
}
|
||||
|
||||
queued_retval = cmsis_dap_usb_write(dap, idx);
|
||||
if (queued_retval != ERROR_OK)
|
||||
int retval = dap->backend->write(dap, idx, USB_TIMEOUT);
|
||||
|
||||
if (retval < 0) {
|
||||
queued_retval = retval;
|
||||
goto skip;
|
||||
} else {
|
||||
queued_retval = ERROR_OK;
|
||||
}
|
||||
|
||||
pending_fifo_put_idx = (pending_fifo_put_idx + 1) % dap->packet_count;
|
||||
pending_fifo_block_count++;
|
||||
|
@ -708,12 +626,12 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
|
|||
LOG_ERROR("no pending write");
|
||||
|
||||
/* get reply */
|
||||
int retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms);
|
||||
if (retval == 0 && timeout_ms < USB_TIMEOUT)
|
||||
int retval = dap->backend->read(dap, timeout_ms);
|
||||
if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < USB_TIMEOUT)
|
||||
return;
|
||||
|
||||
if (retval == -1 || retval == 0) {
|
||||
LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle));
|
||||
if (retval <= 0) {
|
||||
LOG_DEBUG("error reading data");
|
||||
queued_retval = ERROR_FAIL;
|
||||
goto skip;
|
||||
}
|
||||
|
@ -969,7 +887,7 @@ static int cmsis_dap_init(void)
|
|||
int retval;
|
||||
uint8_t *data;
|
||||
|
||||
retval = cmsis_dap_usb_open();
|
||||
retval = cmsis_dap_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -1048,7 +966,7 @@ static int cmsis_dap_init(void)
|
|||
LOG_DEBUG("CMSIS-DAP: Packet Count = %d", pkt_cnt);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Allocating FIFO for %d pending HID requests", cmsis_dap_handle->packet_count);
|
||||
LOG_DEBUG("Allocating FIFO for %d pending packets", cmsis_dap_handle->packet_count);
|
||||
for (int i = 0; i < cmsis_dap_handle->packet_count; i++) {
|
||||
pending_fifo[i].transfers = malloc(pending_queue_len * sizeof(struct pending_transfer_result));
|
||||
if (!pending_fifo[i].transfers) {
|
||||
|
@ -1120,7 +1038,7 @@ static int cmsis_dap_quit(void)
|
|||
cmsis_dap_cmd_DAP_LED(LED_ID_RUN, LED_OFF);
|
||||
cmsis_dap_cmd_DAP_LED(LED_ID_CONNECT, LED_OFF);
|
||||
|
||||
cmsis_dap_usb_close(cmsis_dap_handle);
|
||||
cmsis_dap_close(cmsis_dap_handle);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -1261,7 +1179,7 @@ static void cmsis_dap_flush(void)
|
|||
#endif
|
||||
|
||||
/* send command to USB device */
|
||||
int retval = cmsis_dap_usb_xfer(cmsis_dap_handle, queued_seq_buf_end + 3);
|
||||
int retval = cmsis_dap_xfer(cmsis_dap_handle, queued_seq_buf_end + 3);
|
||||
if (retval != ERROR_OK || buffer[1] != DAP_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command CMD_DAP_JTAG_SEQ failed.");
|
||||
exit(-1);
|
||||
|
@ -1668,7 +1586,7 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command)
|
|||
for (i = 0; i < CMD_ARGC; i++)
|
||||
buffer[i + 1] = strtoul(CMD_ARGV[i], NULL, 16);
|
||||
|
||||
retval = cmsis_dap_usb_xfer(cmsis_dap_handle, CMD_ARGC + 1);
|
||||
retval = cmsis_dap_xfer(cmsis_dap_handle, CMD_ARGC + 1);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("CMSIS-DAP command failed.");
|
||||
|
@ -1712,21 +1630,32 @@ COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command)
|
|||
}
|
||||
|
||||
COMMAND_HANDLER(cmsis_dap_handle_serial_command)
|
||||
{
|
||||
if (CMD_ARGC == 1)
|
||||
cmsis_dap_serial = strdup(CMD_ARGV[0]);
|
||||
else
|
||||
LOG_ERROR("expected exactly one argument to cmsis_dap_serial <serial-number>");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(cmsis_dap_handle_backend_command)
|
||||
{
|
||||
if (CMD_ARGC == 1) {
|
||||
size_t len = mbstowcs(NULL, CMD_ARGV[0], 0);
|
||||
cmsis_dap_serial = calloc(len + 1, sizeof(wchar_t));
|
||||
if (cmsis_dap_serial == NULL) {
|
||||
LOG_ERROR("unable to allocate memory");
|
||||
return ERROR_OK;
|
||||
}
|
||||
if (mbstowcs(cmsis_dap_serial, CMD_ARGV[0], len + 1) == (size_t)-1) {
|
||||
free(cmsis_dap_serial);
|
||||
cmsis_dap_serial = NULL;
|
||||
LOG_ERROR("unable to convert serial");
|
||||
if (strcmp(CMD_ARGV[0], "auto") == 0) {
|
||||
cmsis_dap_backend = -1; /* autoselect */
|
||||
} else {
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) {
|
||||
if (strcasecmp(cmsis_dap_backends[i]->name, CMD_ARGV[0]) == 0) {
|
||||
cmsis_dap_backend = i;
|
||||
return ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("invalid backend argument to cmsis_dap_backend <backend>");
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("expected exactly one argument to cmsis_dap_serial <serial-number>");
|
||||
LOG_ERROR("expected exactly one argument to cmsis_dap_backend <backend>");
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
|
@ -1750,6 +1679,7 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = {
|
|||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
||||
static const struct command_registration cmsis_dap_command_handlers[] = {
|
||||
{
|
||||
.name = "cmsis-dap",
|
||||
|
@ -1772,6 +1702,22 @@ static const struct command_registration cmsis_dap_command_handlers[] = {
|
|||
.help = "set the serial number of the adapter",
|
||||
.usage = "serial_string",
|
||||
},
|
||||
{
|
||||
.name = "cmsis_dap_backend",
|
||||
.handler = &cmsis_dap_handle_backend_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "set the communication backend to use (USB bulk or HID).",
|
||||
.usage = "(auto | usb_bulk | hid)",
|
||||
},
|
||||
#if BUILD_CMSIS_DAP_USB
|
||||
{
|
||||
.name = "cmsis_dap_usb",
|
||||
.chain = cmsis_dap_usb_subcommand_handlers,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "USB bulk backend-specific commands",
|
||||
.usage = "<cmd>",
|
||||
},
|
||||
#endif
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H
|
||||
#define OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct cmsis_dap_backend;
|
||||
struct cmsis_dap_backend_data;
|
||||
struct command_registration;
|
||||
|
||||
struct cmsis_dap {
|
||||
struct cmsis_dap_backend_data *bdata;
|
||||
const struct cmsis_dap_backend *backend;
|
||||
uint16_t packet_size;
|
||||
int packet_count;
|
||||
uint8_t *packet_buffer;
|
||||
uint8_t caps;
|
||||
uint8_t mode;
|
||||
};
|
||||
|
||||
struct cmsis_dap_backend {
|
||||
const char *name;
|
||||
int (*open)(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial);
|
||||
void (*close)(struct cmsis_dap *dap);
|
||||
int (*read)(struct cmsis_dap *dap, int timeout_ms);
|
||||
int (*write)(struct cmsis_dap *dap, int len, int timeout_ms);
|
||||
};
|
||||
|
||||
extern const struct cmsis_dap_backend cmsis_dap_hid_backend;
|
||||
extern const struct cmsis_dap_backend cmsis_dap_usb_backend;
|
||||
extern const struct command_registration cmsis_dap_usb_subcommand_handlers[];
|
||||
|
||||
#endif
|
|
@ -0,0 +1,454 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Mickaël Thomas *
|
||||
* mickael9@gmail.com *
|
||||
* *
|
||||
* Copyright (C) 2016 by Maksym Hilliaka *
|
||||
* oter@frozen-team.com *
|
||||
* *
|
||||
* Copyright (C) 2016 by Phillip Pearson *
|
||||
* pp@myelin.co.nz *
|
||||
* *
|
||||
* Copyright (C) 2014 by Paul Fertser *
|
||||
* fercerpav@gmail.com *
|
||||
* *
|
||||
* Copyright (C) 2013 by mike brown *
|
||||
* mike@theshedworks.org.uk *
|
||||
* *
|
||||
* Copyright (C) 2013 by Spencer Oliver *
|
||||
* spen@spen-soft.co.uk *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <libusb.h>
|
||||
#include <helper/log.h>
|
||||
|
||||
#include "cmsis_dap.h"
|
||||
|
||||
struct cmsis_dap_backend_data {
|
||||
libusb_context *usb_ctx;
|
||||
libusb_device_handle *dev_handle;
|
||||
unsigned int ep_out;
|
||||
unsigned int ep_in;
|
||||
int interface;
|
||||
};
|
||||
|
||||
static int cmsis_dap_usb_interface = -1;
|
||||
|
||||
static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial)
|
||||
{
|
||||
int err;
|
||||
libusb_context *ctx;
|
||||
libusb_device **device_list;
|
||||
|
||||
err = libusb_init(&ctx);
|
||||
if (err) {
|
||||
LOG_ERROR("libusb initialization failed: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int num_devices = libusb_get_device_list(ctx, &device_list);
|
||||
if (num_devices < 0) {
|
||||
LOG_ERROR("could not enumerate USB devices: %s", libusb_strerror(num_devices));
|
||||
libusb_exit(ctx);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
libusb_device *dev = device_list[i];
|
||||
struct libusb_device_descriptor dev_desc;
|
||||
|
||||
err = libusb_get_device_descriptor(dev, &dev_desc);
|
||||
if (err) {
|
||||
LOG_ERROR("could not get device descriptor for device %d: %s", i, libusb_strerror(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Match VID/PID */
|
||||
|
||||
bool id_match = false;
|
||||
bool id_filter = vids[0] || pids[0];
|
||||
for (int id = 0; vids[id] || pids[id]; id++) {
|
||||
id_match = !vids[id] || dev_desc.idVendor == vids[id];
|
||||
id_match &= !pids[id] || dev_desc.idProduct == pids[id];
|
||||
|
||||
if (id_match)
|
||||
break;
|
||||
}
|
||||
|
||||
if (id_filter && !id_match)
|
||||
continue;
|
||||
|
||||
/* Don't continue if we asked for a serial number and the device doesn't have one */
|
||||
if (dev_desc.iSerialNumber == 0 && serial && serial[0])
|
||||
continue;
|
||||
|
||||
libusb_device_handle *dev_handle = NULL;
|
||||
err = libusb_open(dev, &dev_handle);
|
||||
if (err) {
|
||||
/* It's to be expected that most USB devices can't be opened
|
||||
* so only report an error if it was explicitly selected
|
||||
*/
|
||||
if (id_filter) {
|
||||
LOG_ERROR("could not open device 0x%04x:0x%04x: %s",
|
||||
dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
|
||||
} else {
|
||||
LOG_DEBUG("could not open device 0x%04x:0x%04x: %s",
|
||||
dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Match serial number */
|
||||
|
||||
bool serial_match = false;
|
||||
char dev_serial[256] = {0};
|
||||
if (dev_desc.iSerialNumber > 0) {
|
||||
err = libusb_get_string_descriptor_ascii(
|
||||
dev_handle, dev_desc.iSerialNumber,
|
||||
(uint8_t *)dev_serial, sizeof(dev_serial));
|
||||
|
||||
if (err < 0) {
|
||||
const char *msg = "could not read serial number for device 0x%04x:0x%04x: %s";
|
||||
if (serial)
|
||||
LOG_WARNING(msg, dev_desc.idVendor, dev_desc.idProduct,
|
||||
libusb_strerror(err));
|
||||
else
|
||||
LOG_DEBUG(msg, dev_desc.idVendor, dev_desc.idProduct,
|
||||
libusb_strerror(err));
|
||||
} else if (serial && strncmp(dev_serial, serial, sizeof(dev_serial)) == 0) {
|
||||
serial_match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (serial && !serial_match) {
|
||||
libusb_close(dev_handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the CMSIS-DAP string in product string */
|
||||
|
||||
bool cmsis_dap_in_product_str = false;
|
||||
char product_string[256] = {0};
|
||||
if (dev_desc.iProduct > 0) {
|
||||
err = libusb_get_string_descriptor_ascii(
|
||||
dev_handle, dev_desc.iProduct,
|
||||
(uint8_t *)product_string, sizeof(product_string));
|
||||
if (err < 0) {
|
||||
LOG_WARNING("could not read product string for device 0x%04x:0x%04x: %s",
|
||||
dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
|
||||
} else if (strstr(product_string, "CMSIS-DAP")) {
|
||||
LOG_DEBUG("found product string of 0x%04x:0x%04x '%s'",
|
||||
dev_desc.idVendor, dev_desc.idProduct, product_string);
|
||||
cmsis_dap_in_product_str = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool device_identified_reliably = cmsis_dap_in_product_str
|
||||
|| serial_match || id_match;
|
||||
|
||||
/* Find the CMSIS-DAP interface */
|
||||
|
||||
for (int config = 0; config < dev_desc.bNumConfigurations; config++) {
|
||||
struct libusb_config_descriptor *config_desc;
|
||||
err = libusb_get_config_descriptor(dev, config, &config_desc);
|
||||
if (err) {
|
||||
LOG_ERROR("could not get configuration descriptor %d for device 0x%04x:0x%04x: %s",
|
||||
config, dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG("enumerating interfaces of 0x%04x:0x%04x",
|
||||
dev_desc.idVendor, dev_desc.idProduct);
|
||||
int config_num = config_desc->bConfigurationValue;
|
||||
const struct libusb_interface_descriptor *intf_desc_candidate = NULL;
|
||||
const struct libusb_interface_descriptor *intf_desc_found = NULL;
|
||||
|
||||
for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) {
|
||||
const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0];
|
||||
int interface_num = intf_desc->bInterfaceNumber;
|
||||
|
||||
/* Skip this interface if another one was requested explicitly */
|
||||
if (cmsis_dap_usb_interface != -1 && cmsis_dap_usb_interface != interface_num)
|
||||
continue;
|
||||
|
||||
/* CMSIS-DAP v2 spec says:
|
||||
*
|
||||
* CMSIS-DAP with default V2 configuration uses WinUSB and is therefore faster.
|
||||
* Optionally support for streaming SWO trace is provided via an additional USB endpoint.
|
||||
*
|
||||
* The WinUSB configuration requires custom class support with the interface setting
|
||||
* Class Code: 0xFF (Vendor specific)
|
||||
* Subclass: 0x00
|
||||
* Protocol code: 0x00
|
||||
*
|
||||
* Depending on the configuration it uses the following USB endpoints which should be configured
|
||||
* in the interface descriptor in this order:
|
||||
* - Endpoint 1: Bulk Out – used for commands received from host PC.
|
||||
* - Endpoint 2: Bulk In – used for responses send to host PC.
|
||||
* - Endpoint 3: Bulk In (optional) – used for streaming SWO trace (if enabled with SWO_STREAM).
|
||||
*/
|
||||
|
||||
/* Search for "CMSIS-DAP" in the interface string */
|
||||
bool cmsis_dap_in_interface_str = false;
|
||||
if (intf_desc->iInterface != 0) {
|
||||
|
||||
char interface_str[256] = {0};
|
||||
|
||||
err = libusb_get_string_descriptor_ascii(
|
||||
dev_handle, intf_desc->iInterface,
|
||||
(uint8_t *)interface_str, sizeof(interface_str));
|
||||
if (err < 0) {
|
||||
LOG_DEBUG("could not read interface string %d for device 0x%04x:0x%04x: %s",
|
||||
intf_desc->iInterface,
|
||||
dev_desc.idVendor, dev_desc.idProduct,
|
||||
libusb_strerror(err));
|
||||
} else if (strstr(interface_str, "CMSIS-DAP")) {
|
||||
cmsis_dap_in_interface_str = true;
|
||||
LOG_DEBUG("found interface %d string '%s'",
|
||||
interface_num, interface_str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bypass the following check if this interface was explicitly requested. */
|
||||
if (cmsis_dap_usb_interface == -1) {
|
||||
if (!cmsis_dap_in_product_str && !cmsis_dap_in_interface_str)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check endpoints */
|
||||
if (intf_desc->bNumEndpoints < 2) {
|
||||
LOG_DEBUG("skipping interface %d, has only %d endpoints",
|
||||
interface_num, intf_desc->bNumEndpoints);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((intf_desc->endpoint[0].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK ||
|
||||
(intf_desc->endpoint[0].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_OUT) {
|
||||
LOG_DEBUG("skipping interface %d, endpoint[0] is not bulk out",
|
||||
interface_num);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((intf_desc->endpoint[1].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK ||
|
||||
(intf_desc->endpoint[1].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_IN) {
|
||||
LOG_DEBUG("skipping interface %d, endpoint[1] is not bulk in",
|
||||
interface_num);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We can rely on the interface is really CMSIS-DAP if
|
||||
* - we've seen CMSIS-DAP in the interface string
|
||||
* - config asked explicitly for an interface number
|
||||
* - the device has only one interface
|
||||
* The later two cases should be honored only if we know
|
||||
* we are on the rigt device */
|
||||
bool intf_identified_reliably = cmsis_dap_in_interface_str
|
||||
|| (device_identified_reliably &&
|
||||
(cmsis_dap_usb_interface != -1
|
||||
|| config_desc->bNumInterfaces == 1));
|
||||
|
||||
if (intf_desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC ||
|
||||
intf_desc->bInterfaceSubClass != 0 || intf_desc->bInterfaceProtocol != 0) {
|
||||
/* If the interface is reliably identified
|
||||
* then we need not insist on setting USB class, subclass and protocol
|
||||
* exactly as the specification requires.
|
||||
* At least KitProg3 uses class 0 contrary to the specification */
|
||||
if (intf_identified_reliably) {
|
||||
LOG_WARNING("Using CMSIS-DAPv2 interface %d with wrong class %" PRId8
|
||||
" subclass %" PRId8 " or protocol %" PRId8,
|
||||
interface_num,
|
||||
intf_desc->bInterfaceClass,
|
||||
intf_desc->bInterfaceSubClass,
|
||||
intf_desc->bInterfaceProtocol);
|
||||
} else {
|
||||
LOG_DEBUG("skipping interface %d, class %" PRId8
|
||||
" subclass %" PRId8 " protocol %" PRId8,
|
||||
interface_num,
|
||||
intf_desc->bInterfaceClass,
|
||||
intf_desc->bInterfaceSubClass,
|
||||
intf_desc->bInterfaceProtocol);
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (intf_identified_reliably) {
|
||||
/* That's the one! */
|
||||
intf_desc_found = intf_desc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!intf_desc_candidate && device_identified_reliably) {
|
||||
/* This interface looks suitable for CMSIS-DAP. Store the pointer to it
|
||||
* and keep searching for another one with CMSIS-DAP in interface string */
|
||||
intf_desc_candidate = intf_desc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intf_desc_found) {
|
||||
/* We were not able to identify reliably which interface is CMSIS-DAP.
|
||||
* Let's use the first suitable if we found one */
|
||||
intf_desc_found = intf_desc_candidate;
|
||||
}
|
||||
|
||||
if (!intf_desc_found) {
|
||||
libusb_free_config_descriptor(config_desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We've chosen an interface, connect to it */
|
||||
int interface_num = intf_desc_found->bInterfaceNumber;
|
||||
int packet_size = intf_desc_found->endpoint[0].wMaxPacketSize;
|
||||
int ep_out = intf_desc_found->endpoint[0].bEndpointAddress;
|
||||
int ep_in = intf_desc_found->endpoint[1].bEndpointAddress;
|
||||
|
||||
libusb_free_config_descriptor(config_desc);
|
||||
libusb_free_device_list(device_list, true);
|
||||
|
||||
LOG_INFO("Using CMSIS-DAPv2 interface with VID:PID=0x%04x:0x%04x, serial=%s",
|
||||
dev_desc.idVendor, dev_desc.idProduct, dev_serial);
|
||||
|
||||
int current_config;
|
||||
err = libusb_get_configuration(dev_handle, ¤t_config);
|
||||
if (err) {
|
||||
LOG_ERROR("could not find current configuration: %s", libusb_strerror(err));
|
||||
libusb_close(dev_handle);
|
||||
libusb_exit(ctx);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (config_num != current_config) {
|
||||
err = libusb_set_configuration(dev_handle, config_num);
|
||||
if (err) {
|
||||
LOG_ERROR("could not set configuration: %s", libusb_strerror(err));
|
||||
libusb_close(dev_handle);
|
||||
libusb_exit(ctx);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
err = libusb_claim_interface(dev_handle, interface_num);
|
||||
if (err)
|
||||
LOG_WARNING("could not claim interface: %s", libusb_strerror(err));
|
||||
|
||||
dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data));
|
||||
if (dap->bdata == NULL) {
|
||||
LOG_ERROR("unable to allocate memory");
|
||||
libusb_release_interface(dev_handle, interface_num);
|
||||
libusb_close(dev_handle);
|
||||
libusb_exit(ctx);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dap->packet_size = packet_size + 1; /* "+ 1" for compatibility with the HID backend */
|
||||
dap->bdata->usb_ctx = ctx;
|
||||
dap->bdata->dev_handle = dev_handle;
|
||||
dap->bdata->ep_out = ep_out;
|
||||
dap->bdata->ep_in = ep_in;
|
||||
dap->bdata->interface = interface_num;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
|
||||
libusb_free_device_list(device_list, true);
|
||||
|
||||
libusb_exit(ctx);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static void cmsis_dap_usb_close(struct cmsis_dap *dap)
|
||||
{
|
||||
libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface);
|
||||
libusb_close(dap->bdata->dev_handle);
|
||||
libusb_exit(dap->bdata->usb_ctx);
|
||||
free(dap->bdata);
|
||||
dap->bdata = NULL;
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms)
|
||||
{
|
||||
int transferred = 0;
|
||||
int err;
|
||||
|
||||
err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in,
|
||||
dap->packet_buffer, dap->packet_size, &transferred, timeout_ms);
|
||||
if (err) {
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else {
|
||||
LOG_ERROR("error reading data: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&dap->packet_buffer[transferred], 0, dap->packet_size - transferred);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms)
|
||||
{
|
||||
int transferred = 0;
|
||||
int err;
|
||||
|
||||
/* skip the first byte that is only used by the HID backend */
|
||||
err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out,
|
||||
dap->packet_buffer + 1, txlen - 1, &transferred, timeout_ms);
|
||||
if (err) {
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else {
|
||||
LOG_ERROR("error writing data: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command)
|
||||
{
|
||||
if (CMD_ARGC == 1)
|
||||
cmsis_dap_usb_interface = strtoul(CMD_ARGV[0], NULL, 10);
|
||||
else
|
||||
LOG_ERROR("expected exactly one argument to cmsis_dap_usb_interface <interface_number>");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
const struct command_registration cmsis_dap_usb_subcommand_handlers[] = {
|
||||
{
|
||||
.name = "interface",
|
||||
.handler = &cmsis_dap_handle_usb_interface_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "set the USB interface number to use (for USB bulk backend only)",
|
||||
.usage = "<interface_number>",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
const struct cmsis_dap_backend cmsis_dap_usb_backend = {
|
||||
.name = "usb_bulk",
|
||||
.open = cmsis_dap_usb_open,
|
||||
.close = cmsis_dap_usb_close,
|
||||
.read = cmsis_dap_usb_read,
|
||||
.write = cmsis_dap_usb_write,
|
||||
};
|
|
@ -0,0 +1,208 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Mickaël Thomas *
|
||||
* mickael9@gmail.com *
|
||||
* *
|
||||
* Copyright (C) 2016 by Maksym Hilliaka *
|
||||
* oter@frozen-team.com *
|
||||
* *
|
||||
* Copyright (C) 2016 by Phillip Pearson *
|
||||
* pp@myelin.co.nz *
|
||||
* *
|
||||
* Copyright (C) 2014 by Paul Fertser *
|
||||
* fercerpav@gmail.com *
|
||||
* *
|
||||
* Copyright (C) 2013 by mike brown *
|
||||
* mike@theshedworks.org.uk *
|
||||
* *
|
||||
* Copyright (C) 2013 by Spencer Oliver *
|
||||
* spen@spen-soft.co.uk *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <hidapi.h>
|
||||
#include <helper/log.h>
|
||||
|
||||
#include "cmsis_dap.h"
|
||||
|
||||
#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */
|
||||
|
||||
struct cmsis_dap_backend_data {
|
||||
hid_device *dev_handle;
|
||||
};
|
||||
|
||||
static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial)
|
||||
{
|
||||
hid_device *dev = NULL;
|
||||
int i;
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
unsigned short target_vid, target_pid;
|
||||
|
||||
target_vid = 0;
|
||||
target_pid = 0;
|
||||
|
||||
if (hid_init() != 0) {
|
||||
LOG_ERROR("unable to open HIDAPI");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The CMSIS-DAP specification stipulates:
|
||||
* "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the
|
||||
* debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer."
|
||||
*/
|
||||
devs = hid_enumerate(0x0, 0x0);
|
||||
cur_dev = devs;
|
||||
while (NULL != cur_dev) {
|
||||
bool found = false;
|
||||
|
||||
if (0 == vids[0]) {
|
||||
if (NULL == cur_dev->product_string) {
|
||||
LOG_DEBUG("Cannot read product string of device 0x%x:0x%x",
|
||||
cur_dev->vendor_id, cur_dev->product_id);
|
||||
} else if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) {
|
||||
/* if the user hasn't specified VID:PID *and*
|
||||
* product string contains "CMSIS-DAP", pick it
|
||||
*/
|
||||
found = true;
|
||||
}
|
||||
} else {
|
||||
/* otherwise, exhaustively compare against all VID:PID in list */
|
||||
for (i = 0; vids[i] || pids[i]; i++) {
|
||||
if ((vids[i] == cur_dev->vendor_id) && (pids[i] == cur_dev->product_id))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */
|
||||
if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0)
|
||||
found = false;
|
||||
|
||||
if (found) {
|
||||
/* check serial number matches if given */
|
||||
if (serial == NULL)
|
||||
break;
|
||||
|
||||
if (cur_dev->serial_number != NULL) {
|
||||
size_t len = (strlen(serial) + 1) * sizeof(wchar_t);
|
||||
wchar_t *wserial = malloc(len);
|
||||
mbstowcs(wserial, serial, len);
|
||||
|
||||
if (wcscmp(wserial, cur_dev->serial_number) == 0) {
|
||||
free(wserial);
|
||||
break;
|
||||
} else {
|
||||
free(wserial);
|
||||
wserial = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
if (NULL != cur_dev) {
|
||||
target_vid = cur_dev->vendor_id;
|
||||
target_pid = cur_dev->product_id;
|
||||
}
|
||||
|
||||
if (target_vid == 0 && target_pid == 0) {
|
||||
hid_free_enumeration(devs);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data));
|
||||
if (dap->bdata == NULL) {
|
||||
LOG_ERROR("unable to allocate memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dev = hid_open_path(cur_dev->path);
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
if (dev == NULL) {
|
||||
LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* allocate default packet buffer, may be changed later.
|
||||
* currently with HIDAPI we have no way of getting the output report length
|
||||
* without this info we cannot communicate with the adapter.
|
||||
* For the moment we have to hard code the packet size */
|
||||
|
||||
dap->packet_size = PACKET_SIZE;
|
||||
|
||||
/* atmel cmsis-dap uses 512 byte reports */
|
||||
/* except when it doesn't e.g. with mEDBG on SAMD10 Xplained
|
||||
* board */
|
||||
/* TODO: HID report descriptor should be parsed instead of
|
||||
* hardcoding a match by VID */
|
||||
if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175)
|
||||
dap->packet_size = 512 + 1;
|
||||
|
||||
dap->bdata->dev_handle = dev;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void cmsis_dap_hid_close(struct cmsis_dap *dap)
|
||||
{
|
||||
hid_close(dap->bdata->dev_handle);
|
||||
hid_exit();
|
||||
free(dap->bdata);
|
||||
dap->bdata = NULL;
|
||||
}
|
||||
|
||||
static int cmsis_dap_hid_read(struct cmsis_dap *dap, int timeout_ms)
|
||||
{
|
||||
int retval = hid_read_timeout(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms);
|
||||
|
||||
if (retval == 0) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else if (retval == -1) {
|
||||
LOG_ERROR("error reading data: %ls", hid_error(dap->bdata->dev_handle));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cmsis_dap_hid_write(struct cmsis_dap *dap, int txlen, int timeout_ms)
|
||||
{
|
||||
(void) timeout_ms;
|
||||
|
||||
/* Pad the rest of the TX buffer with 0's */
|
||||
memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen);
|
||||
|
||||
/* write data to device */
|
||||
int retval = hid_write(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size);
|
||||
if (retval == -1) {
|
||||
LOG_ERROR("error writing data: %ls", hid_error(dap->bdata->dev_handle));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
const struct cmsis_dap_backend cmsis_dap_hid_backend = {
|
||||
.name = "hid",
|
||||
.open = cmsis_dap_hid_open,
|
||||
.close = cmsis_dap_hid_close,
|
||||
.read = cmsis_dap_hid_read,
|
||||
.write = cmsis_dap_hid_write,
|
||||
};
|
|
@ -170,7 +170,7 @@ static int ft232r_send_recv(void)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
void ft232r_increase_buf_size(size_t new_buf_size)
|
||||
static void ft232r_increase_buf_size(size_t new_buf_size)
|
||||
{
|
||||
uint8_t *new_buf_ptr;
|
||||
if (new_buf_size >= ft232r_buf_size) {
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
#endif
|
||||
|
||||
/* configuration */
|
||||
uint16_t gw16012_port;
|
||||
static uint16_t gw16012_port;
|
||||
|
||||
/* interface variables
|
||||
*/
|
||||
|
|
|
@ -1270,7 +1270,7 @@ static bool calculate_swo_prescaler(unsigned int traceclkin_freq,
|
|||
uint32_t trace_freq, uint16_t *prescaler)
|
||||
{
|
||||
unsigned int presc = (traceclkin_freq + trace_freq / 2) / trace_freq;
|
||||
if (presc > TPIU_ACPR_MAX_SWOSCALER)
|
||||
if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1)
|
||||
return false;
|
||||
|
||||
/* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */
|
||||
|
@ -1296,7 +1296,7 @@ static bool detect_swo_freq_and_prescaler(struct jaylink_swo_speed speed,
|
|||
*trace_freq = speed.freq / divider;
|
||||
presc = ((1.0 - SWO_MAX_FREQ_DEV) * traceclkin_freq) / *trace_freq + 1;
|
||||
|
||||
if (presc > TPIU_ACPR_MAX_SWOSCALER)
|
||||
if (presc > TPIU_ACPR_MAX_SWOSCALER + 1)
|
||||
break;
|
||||
|
||||
deviation = fabs(1.0 - ((double)*trace_freq * presc / traceclkin_freq));
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H
|
||||
#define OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H
|
||||
|
||||
#include <ftdi.h>
|
||||
|
||||
#ifndef HAVE_LIBFTDI_TCIOFLUSH
|
||||
/* Backward compatibility with libftdi pre 1.5 */
|
||||
|
||||
static inline int ftdi_tciflush(struct ftdi_context *ftdi)
|
||||
{
|
||||
return ftdi_usb_purge_rx_buffer(ftdi);
|
||||
}
|
||||
|
||||
static inline int ftdi_tcoflush(struct ftdi_context *ftdi)
|
||||
{
|
||||
return ftdi_usb_purge_tx_buffer(ftdi);
|
||||
}
|
||||
|
||||
static inline int ftdi_tcioflush(struct ftdi_context *ftdi)
|
||||
{
|
||||
return ftdi_usb_purge_buffers(ftdi);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H */
|
|
@ -411,7 +411,7 @@ static int nulink_usb_step(void *handle)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int nulink_usb_read_reg(void *handle, int num, uint32_t *val)
|
||||
static int nulink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val)
|
||||
{
|
||||
struct nulink_usb_handle_s *h = handle;
|
||||
|
||||
|
@ -434,7 +434,7 @@ static int nulink_usb_read_reg(void *handle, int num, uint32_t *val)
|
|||
h->cmdbuf[h->cmdidx] = 0;
|
||||
h->cmdidx += 1;
|
||||
/* u32Addr */
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, num);
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, regsel);
|
||||
h->cmdidx += 4;
|
||||
/* u32Data */
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, 0);
|
||||
|
@ -450,7 +450,7 @@ static int nulink_usb_read_reg(void *handle, int num, uint32_t *val)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int nulink_usb_write_reg(void *handle, int num, uint32_t val)
|
||||
static int nulink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val)
|
||||
{
|
||||
struct nulink_usb_handle_s *h = handle;
|
||||
|
||||
|
@ -473,7 +473,7 @@ static int nulink_usb_write_reg(void *handle, int num, uint32_t val)
|
|||
h->cmdbuf[h->cmdidx] = 0;
|
||||
h->cmdidx += 1;
|
||||
/* u32Addr */
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, num);
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, regsel);
|
||||
h->cmdidx += 4;
|
||||
/* u32Data */
|
||||
h_u32_to_le(h->cmdbuf + h->cmdidx, val);
|
||||
|
|
|
@ -144,7 +144,7 @@ static int opendous_usb_write(struct opendous_jtag *opendous_jtag, int out_lengt
|
|||
static int opendous_usb_read(struct opendous_jtag *opendous_jtag);
|
||||
|
||||
/* helper functions */
|
||||
int opendous_get_version_info(void);
|
||||
static int opendous_get_version_info(void);
|
||||
|
||||
#ifdef _DEBUG_USB_COMMS_
|
||||
static void opendous_debug_buffer(uint8_t *buffer, int length);
|
||||
|
@ -544,7 +544,7 @@ int opendous_get_status(void)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int opendous_get_version_info(void)
|
||||
static int opendous_get_version_info(void)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ typedef enum openjtag_tap_state {
|
|||
} openjtag_tap_state_t;
|
||||
|
||||
/* OPENJTAG access library includes */
|
||||
#include <ftdi.h>
|
||||
#include "libftdi_helper.h"
|
||||
|
||||
/* OpenJTAG vid/pid */
|
||||
static uint16_t openjtag_vid = 0x0403;
|
||||
|
@ -436,8 +436,8 @@ static int openjtag_init_standard(void)
|
|||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (ftdi_usb_purge_buffers(&ftdic) < 0) {
|
||||
LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str);
|
||||
if (ftdi_tcioflush(&ftdic) < 0) {
|
||||
LOG_ERROR("ftdi flush: %s", ftdic.error_str);
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "bitq.h"
|
||||
|
||||
/* PRESTO access library includes */
|
||||
#include <ftdi.h>
|
||||
#include "libftdi_helper.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
@ -160,8 +160,8 @@ static int presto_open_libftdi(char *req_serial)
|
|||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (ftdi_usb_purge_buffers(&presto->ftdic) < 0) {
|
||||
LOG_ERROR("unable to purge PRESTO buffers");
|
||||
if (ftdi_tcioflush(&presto->ftdic) < 0) {
|
||||
LOG_ERROR("unable to flush PRESTO buffers");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ static int presto_open_libftdi(char *req_serial)
|
|||
if (presto_read(&presto_data, 1) != ERROR_OK) {
|
||||
LOG_DEBUG("no response from PRESTO, retrying");
|
||||
|
||||
if (ftdi_usb_purge_buffers(&presto->ftdic) < 0)
|
||||
if (ftdi_tcioflush(&presto->ftdic) < 0)
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
|
||||
presto_data = 0xD0;
|
||||
|
|
|
@ -299,6 +299,7 @@ struct stlink_usb_handle_s {
|
|||
|
||||
#define STLINK_TRACE_SIZE 4096
|
||||
#define STLINK_TRACE_MAX_HZ 2000000
|
||||
#define STLINK_V3_TRACE_MAX_HZ 24000000
|
||||
|
||||
#define STLINK_V3_MAX_FREQ_NB 10
|
||||
|
||||
|
@ -323,6 +324,9 @@ struct stlink_usb_handle_s {
|
|||
|
||||
/* aliases */
|
||||
#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE
|
||||
#define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2
|
||||
|
||||
#define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F)
|
||||
|
||||
struct speed_map {
|
||||
int speed;
|
||||
|
@ -358,7 +362,7 @@ static const struct speed_map stlink_khz_to_speed_map_jtag[] = {
|
|||
|
||||
static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
|
||||
static int stlink_swim_status(void *handle);
|
||||
void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
|
||||
static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
|
||||
static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map);
|
||||
static int stlink_speed(void *handle, int khz, bool query);
|
||||
static int stlink_usb_open_ap(void *handle, unsigned short apsel);
|
||||
|
@ -2015,13 +2019,22 @@ static int stlink_usb_read_regs(void *handle)
|
|||
}
|
||||
|
||||
/** */
|
||||
static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
|
||||
static int stlink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val)
|
||||
{
|
||||
int res;
|
||||
struct stlink_usb_handle_s *h = handle;
|
||||
|
||||
assert(handle != NULL);
|
||||
|
||||
if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
|
||||
res = stlink_usb_write_debug_reg(h, DCB_DCRSR, regsel & 0x7f);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
/* FIXME: poll DHCSR.S_REGRDY before read DCRDR */
|
||||
return stlink_usb_v2_read_debug_reg(h, DCB_DCRDR, val);
|
||||
}
|
||||
|
||||
stlink_usb_init_buffer(handle, h->rx_ep, h->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
|
||||
|
||||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
|
||||
|
@ -2029,7 +2042,7 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
|
|||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READREG;
|
||||
else
|
||||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG;
|
||||
h->cmdbuf[h->cmdidx++] = num;
|
||||
h->cmdbuf[h->cmdidx++] = regsel;
|
||||
|
||||
if (h->version.jtag_api == STLINK_JTAG_API_V1) {
|
||||
res = stlink_usb_xfer_noerrcheck(handle, h->databuf, 4);
|
||||
|
@ -2047,12 +2060,21 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
|
|||
}
|
||||
|
||||
/** */
|
||||
static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
|
||||
static int stlink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val)
|
||||
{
|
||||
struct stlink_usb_handle_s *h = handle;
|
||||
|
||||
assert(handle != NULL);
|
||||
|
||||
if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
|
||||
int res = stlink_usb_write_debug_reg(h, DCB_DCRDR, val);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
return stlink_usb_write_debug_reg(h, DCB_DCRSR, DCRSR_WnR | (regsel & 0x7f));
|
||||
/* FIXME: poll DHCSR.S_REGRDY after write DCRSR */
|
||||
}
|
||||
|
||||
stlink_usb_init_buffer(handle, h->rx_ep, 2);
|
||||
|
||||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
|
||||
|
@ -2060,7 +2082,7 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
|
|||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEREG;
|
||||
else
|
||||
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEREG;
|
||||
h->cmdbuf[h->cmdidx++] = num;
|
||||
h->cmdbuf[h->cmdidx++] = regsel;
|
||||
h_u32_to_le(h->cmdbuf+h->cmdidx, val);
|
||||
h->cmdidx += 4;
|
||||
|
||||
|
@ -2577,7 +2599,7 @@ static int stlink_speed_jtag(void *handle, int khz, bool query)
|
|||
return stlink_khz_to_speed_map_jtag[speed_index].speed;
|
||||
}
|
||||
|
||||
void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
|
||||
static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
|
@ -2725,7 +2747,7 @@ static int stlink_usb_close(void *handle)
|
|||
* based on the length (0x1a = 26) we could easily decide if we have to fixup the serial
|
||||
* and then we have just to convert the raw data into printable characters using sprintf
|
||||
*/
|
||||
char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
|
||||
static char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
|
||||
struct libusb_device_descriptor *dev_desc)
|
||||
{
|
||||
int usb_retval;
|
||||
|
@ -2982,13 +3004,12 @@ static int stlink_usb_hl_open(struct hl_interface_param_s *param, void **fd)
|
|||
return stlink_usb_open(param, stlink_get_mode(param->transport), fd);
|
||||
}
|
||||
|
||||
int stlink_config_trace(void *handle, bool enabled,
|
||||
static int stlink_config_trace(void *handle, bool enabled,
|
||||
enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
|
||||
unsigned int *trace_freq, unsigned int traceclkin_freq,
|
||||
uint16_t *prescaler)
|
||||
{
|
||||
struct stlink_usb_handle_s *h = handle;
|
||||
uint16_t presc;
|
||||
|
||||
if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) ||
|
||||
pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
|
||||
|
@ -2996,34 +3017,42 @@ int stlink_config_trace(void *handle, bool enabled,
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
stlink_usb_trace_disable(h);
|
||||
return ERROR_OK;
|
||||
}
|
||||
unsigned int max_trace_freq = (h->version.stlink == 3) ?
|
||||
STLINK_V3_TRACE_MAX_HZ : STLINK_TRACE_MAX_HZ;
|
||||
|
||||
if (*trace_freq > STLINK_TRACE_MAX_HZ) {
|
||||
/* Only concern ourselves with the frequency if the STlink is processing it. */
|
||||
if (enabled && *trace_freq > max_trace_freq) {
|
||||
LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u",
|
||||
STLINK_TRACE_MAX_HZ);
|
||||
max_trace_freq);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
stlink_usb_trace_disable(h);
|
||||
|
||||
if (!*trace_freq)
|
||||
*trace_freq = STLINK_TRACE_MAX_HZ;
|
||||
*trace_freq = max_trace_freq;
|
||||
|
||||
presc = traceclkin_freq / *trace_freq;
|
||||
unsigned int presc = (traceclkin_freq + *trace_freq / 2) / *trace_freq;
|
||||
if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1) {
|
||||
LOG_ERROR("SWO frequency is not suitable. Please choose a different "
|
||||
"frequency.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (traceclkin_freq % *trace_freq > 0)
|
||||
presc++;
|
||||
|
||||
if (presc > TPIU_ACPR_MAX_SWOSCALER) {
|
||||
/* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */
|
||||
unsigned int max_deviation = (traceclkin_freq * 3) / 100;
|
||||
if (presc * *trace_freq < traceclkin_freq - max_deviation ||
|
||||
presc * *trace_freq > traceclkin_freq + max_deviation) {
|
||||
LOG_ERROR("SWO frequency is not suitable. Please choose a different "
|
||||
"frequency.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
*prescaler = presc;
|
||||
|
||||
if (!enabled)
|
||||
return ERROR_OK;
|
||||
|
||||
h->trace.source_hz = *trace_freq;
|
||||
|
||||
return stlink_usb_trace_enable(h);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
#include <target/cortex_m.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include "libusb_helper.h"
|
||||
|
||||
#define ICDI_WRITE_ENDPOINT 0x02
|
||||
#define ICDI_READ_ENDPOINT 0x83
|
||||
|
@ -44,8 +44,7 @@
|
|||
#define PACKET_END "#"
|
||||
|
||||
struct icdi_usb_handle_s {
|
||||
libusb_context *usb_ctx;
|
||||
libusb_device_handle *usb_dev;
|
||||
struct libusb_device_handle *usb_dev;
|
||||
|
||||
char *read_buffer;
|
||||
char *write_buffer;
|
||||
|
@ -476,13 +475,13 @@ static int icdi_usb_read_regs(void *handle)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int icdi_usb_read_reg(void *handle, int num, uint32_t *val)
|
||||
static int icdi_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val)
|
||||
{
|
||||
int result;
|
||||
struct icdi_usb_handle_s *h = handle;
|
||||
char cmd[10];
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "p%x", num);
|
||||
snprintf(cmd, sizeof(cmd), "p%x", regsel);
|
||||
result = icdi_send_cmd(handle, cmd);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
|
@ -505,14 +504,14 @@ static int icdi_usb_read_reg(void *handle, int num, uint32_t *val)
|
|||
return result;
|
||||
}
|
||||
|
||||
static int icdi_usb_write_reg(void *handle, int num, uint32_t val)
|
||||
static int icdi_usb_write_reg(void *handle, unsigned int regsel, uint32_t val)
|
||||
{
|
||||
int result;
|
||||
char cmd[20];
|
||||
uint8_t buf[4];
|
||||
h_u32_to_le(buf, val);
|
||||
|
||||
int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num);
|
||||
int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", regsel);
|
||||
hexify(cmd + cmd_len, buf, 4, sizeof(cmd));
|
||||
|
||||
result = icdi_send_cmd(handle, cmd);
|
||||
|
@ -657,10 +656,7 @@ static int icdi_usb_close(void *handle)
|
|||
return ERROR_OK;
|
||||
|
||||
if (h->usb_dev)
|
||||
libusb_close(h->usb_dev);
|
||||
|
||||
if (h->usb_ctx)
|
||||
libusb_exit(h->usb_ctx);
|
||||
jtag_libusb_close(h->usb_dev);
|
||||
|
||||
free(h->read_buffer);
|
||||
free(h->write_buffer);
|
||||
|
@ -670,6 +666,7 @@ static int icdi_usb_close(void *handle)
|
|||
|
||||
static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
|
||||
{
|
||||
/* TODO: Convert remaining libusb_ calls to jtag_libusb_ */
|
||||
int retval;
|
||||
struct icdi_usb_handle_s *h;
|
||||
|
||||
|
@ -682,19 +679,14 @@ static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
|
||||
param->vid[0], param->pid[0]);
|
||||
for (uint8_t i = 0; param->vid[i] && param->pid[i]; ++i)
|
||||
LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", param->transport,
|
||||
param->vid[i], param->pid[i], param->serial ? param->serial : "");
|
||||
|
||||
/* TODO: convert libusb_ calls to jtag_libusb_ */
|
||||
if (param->vid[1])
|
||||
LOG_WARNING("Bad configuration: 'hla_vid_pid' command does not accept more than one VID PID pair on ti-icdi!");
|
||||
/* TI (Stellaris) ICDI provides its serial number in the USB descriptor;
|
||||
no need to provide a callback here. */
|
||||
jtag_libusb_open(param->vid, param->pid, param->serial, &h->usb_dev, NULL);
|
||||
|
||||
if (libusb_init(&h->usb_ctx) != 0) {
|
||||
LOG_ERROR("libusb init failed");
|
||||
goto error_open;
|
||||
}
|
||||
|
||||
h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid[0], param->pid[0]);
|
||||
if (!h->usb_dev) {
|
||||
LOG_ERROR("open failed");
|
||||
goto error_open;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <jtag/commands.h>
|
||||
#include <target/image.h>
|
||||
#include <libusb.h>
|
||||
#include "libusb_helper.h"
|
||||
#include "OpenULINK/include/msgtypes.h"
|
||||
|
||||
/** USB Vendor ID of ULINK device in unconfigured state (no firmware loaded
|
||||
|
@ -148,6 +149,9 @@ struct ulink {
|
|||
struct libusb_device_handle *usb_device_handle;
|
||||
enum ulink_type type;
|
||||
|
||||
unsigned int ep_in; /**< IN endpoint number */
|
||||
unsigned int ep_out; /**< OUT endpoint number */
|
||||
|
||||
int delay_scan_in; /**< Delay value for SCAN_IN commands */
|
||||
int delay_scan_out; /**< Delay value for SCAN_OUT commands */
|
||||
int delay_scan_io; /**< Delay value for SCAN_IO commands */
|
||||
|
@ -162,34 +166,34 @@ struct ulink {
|
|||
/**************************** Function Prototypes *****************************/
|
||||
|
||||
/* USB helper functions */
|
||||
int ulink_usb_open(struct ulink **device);
|
||||
int ulink_usb_close(struct ulink **device);
|
||||
static int ulink_usb_open(struct ulink **device);
|
||||
static int ulink_usb_close(struct ulink **device);
|
||||
|
||||
/* ULINK MCU (Cypress EZ-USB) specific functions */
|
||||
int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit);
|
||||
int ulink_load_firmware_and_renumerate(struct ulink **device, const char *filename,
|
||||
static int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit);
|
||||
static int ulink_load_firmware_and_renumerate(struct ulink **device, const char *filename,
|
||||
uint32_t delay);
|
||||
int ulink_load_firmware(struct ulink *device, const char *filename);
|
||||
int ulink_write_firmware_section(struct ulink *device,
|
||||
static int ulink_load_firmware(struct ulink *device, const char *filename);
|
||||
static int ulink_write_firmware_section(struct ulink *device,
|
||||
struct image *firmware_image, int section_index);
|
||||
|
||||
/* Generic helper functions */
|
||||
void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals);
|
||||
static void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals);
|
||||
|
||||
/* OpenULINK command generation helper functions */
|
||||
int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
|
||||
static int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
|
||||
enum ulink_payload_direction direction);
|
||||
|
||||
/* OpenULINK command queue helper functions */
|
||||
int ulink_get_queue_size(struct ulink *device,
|
||||
static int ulink_get_queue_size(struct ulink *device,
|
||||
enum ulink_payload_direction direction);
|
||||
void ulink_clear_queue(struct ulink *device);
|
||||
int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd);
|
||||
int ulink_execute_queued_commands(struct ulink *device, int timeout);
|
||||
static void ulink_clear_queue(struct ulink *device);
|
||||
static int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd);
|
||||
static int ulink_execute_queued_commands(struct ulink *device, int timeout);
|
||||
|
||||
static void ulink_print_queue(struct ulink *device);
|
||||
|
||||
int ulink_append_scan_cmd(struct ulink *device,
|
||||
static int ulink_append_scan_cmd(struct ulink *device,
|
||||
enum scan_type scan_type,
|
||||
int scan_size_bits,
|
||||
uint8_t *tdi,
|
||||
|
@ -201,39 +205,39 @@ int ulink_append_scan_cmd(struct ulink *device,
|
|||
uint8_t tms_sequence_end,
|
||||
struct jtag_command *origin,
|
||||
bool postprocess);
|
||||
int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
|
||||
static int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
|
||||
uint8_t sequence);
|
||||
int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count);
|
||||
int ulink_append_get_signals_cmd(struct ulink *device);
|
||||
int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
|
||||
static int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count);
|
||||
static int ulink_append_get_signals_cmd(struct ulink *device);
|
||||
static int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
|
||||
uint8_t high);
|
||||
int ulink_append_sleep_cmd(struct ulink *device, uint32_t us);
|
||||
int ulink_append_configure_tck_cmd(struct ulink *device,
|
||||
static int ulink_append_sleep_cmd(struct ulink *device, uint32_t us);
|
||||
static int ulink_append_configure_tck_cmd(struct ulink *device,
|
||||
int delay_scan_in,
|
||||
int delay_scan_out,
|
||||
int delay_scan_io,
|
||||
int delay_tck,
|
||||
int delay_tms);
|
||||
int ulink_append_led_cmd(struct ulink *device, uint8_t led_state);
|
||||
int ulink_append_test_cmd(struct ulink *device);
|
||||
static int __attribute__((unused)) ulink_append_led_cmd(struct ulink *device, uint8_t led_state);
|
||||
static int ulink_append_test_cmd(struct ulink *device);
|
||||
|
||||
/* OpenULINK TCK frequency helper functions */
|
||||
int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay);
|
||||
static int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay);
|
||||
|
||||
/* Interface between OpenULINK and OpenOCD */
|
||||
static void ulink_set_end_state(tap_state_t endstate);
|
||||
int ulink_queue_statemove(struct ulink *device);
|
||||
static int ulink_queue_statemove(struct ulink *device);
|
||||
|
||||
int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd);
|
||||
int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd);
|
||||
static int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd);
|
||||
|
||||
int ulink_post_process_scan(struct ulink_cmd *ulink_cmd);
|
||||
int ulink_post_process_queue(struct ulink *device);
|
||||
static int ulink_post_process_scan(struct ulink_cmd *ulink_cmd);
|
||||
static int ulink_post_process_queue(struct ulink *device);
|
||||
|
||||
/* adapter driver functions */
|
||||
static int ulink_execute_queue(void);
|
||||
|
@ -245,12 +249,12 @@ static int ulink_quit(void);
|
|||
|
||||
/****************************** Global Variables ******************************/
|
||||
|
||||
struct ulink *ulink_handle;
|
||||
static struct ulink *ulink_handle;
|
||||
|
||||
/**************************** USB helper functions ****************************/
|
||||
|
||||
/**
|
||||
* Opens the ULINK device and claims its USB interface.
|
||||
* Opens the ULINK device
|
||||
*
|
||||
* Currently, only the original ULINK is supported
|
||||
*
|
||||
|
@ -258,7 +262,7 @@ struct ulink *ulink_handle;
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_usb_open(struct ulink **device)
|
||||
static int ulink_usb_open(struct ulink **device)
|
||||
{
|
||||
ssize_t num_devices, i;
|
||||
bool found;
|
||||
|
@ -288,9 +292,6 @@ int ulink_usb_open(struct ulink **device)
|
|||
return ERROR_FAIL;
|
||||
libusb_free_device_list(usb_devices, 1);
|
||||
|
||||
if (libusb_claim_interface(usb_device_handle, 0) != 0)
|
||||
return ERROR_FAIL;
|
||||
|
||||
(*device)->usb_device_handle = usb_device_handle;
|
||||
(*device)->type = ULINK_1;
|
||||
|
||||
|
@ -304,7 +305,7 @@ int ulink_usb_open(struct ulink **device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_usb_close(struct ulink **device)
|
||||
static int ulink_usb_close(struct ulink **device)
|
||||
{
|
||||
if (libusb_release_interface((*device)->usb_device_handle, 0) != 0)
|
||||
return ERROR_FAIL;
|
||||
|
@ -327,7 +328,7 @@ int ulink_usb_close(struct ulink **device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit)
|
||||
static int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -354,7 +355,7 @@ int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_load_firmware_and_renumerate(struct ulink **device,
|
||||
static int ulink_load_firmware_and_renumerate(struct ulink **device,
|
||||
const char *filename, uint32_t delay)
|
||||
{
|
||||
int ret;
|
||||
|
@ -390,10 +391,10 @@ int ulink_load_firmware_and_renumerate(struct ulink **device,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_load_firmware(struct ulink *device, const char *filename)
|
||||
static int ulink_load_firmware(struct ulink *device, const char *filename)
|
||||
{
|
||||
struct image ulink_firmware_image;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
ret = ulink_cpu_reset(device, CPU_RESET);
|
||||
if (ret != ERROR_OK) {
|
||||
|
@ -402,7 +403,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename)
|
|||
}
|
||||
|
||||
ulink_firmware_image.base_address = 0;
|
||||
ulink_firmware_image.base_address_set = 0;
|
||||
ulink_firmware_image.base_address_set = false;
|
||||
|
||||
ret = image_open(&ulink_firmware_image, filename, "ihex");
|
||||
if (ret != ERROR_OK) {
|
||||
|
@ -411,7 +412,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename)
|
|||
}
|
||||
|
||||
/* Download all sections in the image to ULINK */
|
||||
for (i = 0; i < ulink_firmware_image.num_sections; i++) {
|
||||
for (unsigned int i = 0; i < ulink_firmware_image.num_sections; i++) {
|
||||
ret = ulink_write_firmware_section(device, &ulink_firmware_image, i);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
@ -439,7 +440,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_write_firmware_section(struct ulink *device,
|
||||
static int ulink_write_firmware_section(struct ulink *device,
|
||||
struct image *firmware_image, int section_index)
|
||||
{
|
||||
uint16_t addr, size, bytes_remaining, chunk_size;
|
||||
|
@ -499,7 +500,7 @@ int ulink_write_firmware_section(struct ulink *device,
|
|||
* @param input_signals input signal states as returned by CMD_GET_SIGNALS
|
||||
* @param output_signals output signal states as returned by CMD_GET_SIGNALS
|
||||
*/
|
||||
void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals)
|
||||
static void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals)
|
||||
{
|
||||
LOG_INFO("ULINK signal states: TDI: %i, TDO: %i, TMS: %i, TCK: %i, TRST: %i,"
|
||||
" SRST: %i",
|
||||
|
@ -522,7 +523,7 @@ void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
|
||||
static int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
|
||||
enum ulink_payload_direction direction)
|
||||
{
|
||||
uint8_t *payload;
|
||||
|
@ -576,7 +577,7 @@ int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
|
|||
* @return the number of bytes currently stored in the queue for the specified
|
||||
* direction.
|
||||
*/
|
||||
int ulink_get_queue_size(struct ulink *device,
|
||||
static int ulink_get_queue_size(struct ulink *device,
|
||||
enum ulink_payload_direction direction)
|
||||
{
|
||||
struct ulink_cmd *current = device->queue_start;
|
||||
|
@ -605,7 +606,7 @@ int ulink_get_queue_size(struct ulink *device,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
void ulink_clear_queue(struct ulink *device)
|
||||
static void ulink_clear_queue(struct ulink *device)
|
||||
{
|
||||
struct ulink_cmd *current = device->queue_start;
|
||||
struct ulink_cmd *next = NULL;
|
||||
|
@ -647,7 +648,7 @@ void ulink_clear_queue(struct ulink *device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd)
|
||||
static int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd)
|
||||
{
|
||||
int newsize_out, newsize_in;
|
||||
int ret = ERROR_OK;
|
||||
|
@ -698,7 +699,7 @@ int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_execute_queued_commands(struct ulink *device, int timeout)
|
||||
static int ulink_execute_queued_commands(struct ulink *device, int timeout)
|
||||
{
|
||||
struct ulink_cmd *current;
|
||||
int ret, i, index_out, index_in, count_out, count_in, transferred;
|
||||
|
@ -725,7 +726,7 @@ int ulink_execute_queued_commands(struct ulink *device, int timeout)
|
|||
}
|
||||
|
||||
/* Send packet to ULINK */
|
||||
ret = libusb_bulk_transfer(device->usb_device_handle, (2 | LIBUSB_ENDPOINT_OUT),
|
||||
ret = libusb_bulk_transfer(device->usb_device_handle, device->ep_out,
|
||||
(unsigned char *)buffer, count_out, &transferred, timeout);
|
||||
if (ret != 0)
|
||||
return ERROR_FAIL;
|
||||
|
@ -734,7 +735,7 @@ int ulink_execute_queued_commands(struct ulink *device, int timeout)
|
|||
|
||||
/* Wait for response if commands contain IN payload data */
|
||||
if (count_in > 0) {
|
||||
ret = libusb_bulk_transfer(device->usb_device_handle, (2 | LIBUSB_ENDPOINT_IN),
|
||||
ret = libusb_bulk_transfer(device->usb_device_handle, device->ep_in,
|
||||
(unsigned char *)buffer, 64, &transferred, timeout);
|
||||
if (ret != 0)
|
||||
return ERROR_FAIL;
|
||||
|
@ -865,7 +866,7 @@ static void ulink_print_queue(struct ulink *device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
|
||||
static int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
|
||||
int scan_size_bits, uint8_t *tdi, uint8_t *tdo_start, uint8_t *tdo,
|
||||
uint8_t tms_count_start, uint8_t tms_sequence_start, uint8_t tms_count_end,
|
||||
uint8_t tms_sequence_end, struct jtag_command *origin, bool postprocess)
|
||||
|
@ -966,7 +967,7 @@ int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
|
||||
static int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
|
||||
uint8_t sequence)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
|
@ -1003,7 +1004,7 @@ int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count)
|
||||
static int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
int ret;
|
||||
|
@ -1036,7 +1037,7 @@ int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_get_signals_cmd(struct ulink *device)
|
||||
static int ulink_append_get_signals_cmd(struct ulink *device)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
int ret;
|
||||
|
@ -1075,7 +1076,7 @@ int ulink_append_get_signals_cmd(struct ulink *device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
|
||||
static int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
|
||||
uint8_t high)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
|
@ -1108,7 +1109,7 @@ int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_sleep_cmd(struct ulink *device, uint32_t us)
|
||||
static int ulink_append_sleep_cmd(struct ulink *device, uint32_t us)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
int ret;
|
||||
|
@ -1144,7 +1145,7 @@ int ulink_append_sleep_cmd(struct ulink *device, uint32_t us)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in,
|
||||
static int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in,
|
||||
int delay_scan_out, int delay_scan_io, int delay_tck, int delay_tms)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
|
@ -1206,7 +1207,7 @@ int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in,
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_led_cmd(struct ulink *device, uint8_t led_state)
|
||||
static int ulink_append_led_cmd(struct ulink *device, uint8_t led_state)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
int ret;
|
||||
|
@ -1236,7 +1237,7 @@ int ulink_append_led_cmd(struct ulink *device, uint8_t led_state)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_append_test_cmd(struct ulink *device)
|
||||
static int ulink_append_test_cmd(struct ulink *device)
|
||||
{
|
||||
struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
|
||||
int ret;
|
||||
|
@ -1292,7 +1293,7 @@ int ulink_append_test_cmd(struct ulink *device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay)
|
||||
static int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay)
|
||||
{
|
||||
float t, x, x_ceil;
|
||||
|
||||
|
@ -1423,7 +1424,7 @@ static void ulink_set_end_state(tap_state_t endstate)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_statemove(struct ulink *device)
|
||||
static int ulink_queue_statemove(struct ulink *device)
|
||||
{
|
||||
uint8_t tms_sequence, tms_count;
|
||||
int ret;
|
||||
|
@ -1452,7 +1453,7 @@ int ulink_queue_statemove(struct ulink *device)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
uint32_t scan_size_bits, scan_size_bytes, bits_last_scan;
|
||||
uint32_t scans_max_payload, bytecount;
|
||||
|
@ -1631,7 +1632,7 @@ int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -1654,7 +1655,7 @@ int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -1685,7 +1686,7 @@ int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
uint8_t low = 0, high = 0;
|
||||
|
||||
|
@ -1711,7 +1712,7 @@ int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
int ret, i, num_states, batch_size, state_count;
|
||||
tap_state_t *path;
|
||||
|
@ -1770,7 +1771,7 @@ int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
/* IMPORTANT! Due to the time offset in command execution introduced by
|
||||
* command queueing, this needs to be implemented in the ULINK device */
|
||||
|
@ -1783,7 +1784,7 @@ int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd)
|
|||
* @param device pointer to struct ulink identifying ULINK driver instance.
|
||||
* @param cmd pointer to the command that shall be executed.
|
||||
*/
|
||||
int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd)
|
||||
static int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
unsigned num_cycles;
|
||||
|
@ -1828,7 +1829,7 @@ int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_post_process_scan(struct ulink_cmd *ulink_cmd)
|
||||
static int ulink_post_process_scan(struct ulink_cmd *ulink_cmd)
|
||||
{
|
||||
struct jtag_command *cmd = ulink_cmd->cmd_origin;
|
||||
int ret;
|
||||
|
@ -1859,7 +1860,7 @@ int ulink_post_process_scan(struct ulink_cmd *ulink_cmd)
|
|||
* @return on success: ERROR_OK
|
||||
* @return on failure: ERROR_FAIL
|
||||
*/
|
||||
int ulink_post_process_queue(struct ulink *device)
|
||||
static int ulink_post_process_queue(struct ulink *device)
|
||||
{
|
||||
struct ulink_cmd *current;
|
||||
struct jtag_command *openocd_cmd;
|
||||
|
@ -2156,6 +2157,12 @@ static int ulink_init(void)
|
|||
} else
|
||||
LOG_INFO("ULINK device is already running OpenULINK firmware");
|
||||
|
||||
/* Get OpenULINK USB IN/OUT endpoints and claim the interface */
|
||||
ret = jtag_libusb_choose_interface(ulink_handle->usb_device_handle,
|
||||
&ulink_handle->ep_in, &ulink_handle->ep_out, -1, -1, -1, -1);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
/* Initialize OpenULINK command queue */
|
||||
ulink_clear_queue(ulink_handle);
|
||||
|
||||
|
@ -2171,7 +2178,7 @@ static int ulink_init(void)
|
|||
* shut down by the user via Ctrl-C. Try to retrieve this Bulk IN packet. */
|
||||
dummy = calloc(64, sizeof(uint8_t));
|
||||
|
||||
ret = libusb_bulk_transfer(ulink_handle->usb_device_handle, (2 | LIBUSB_ENDPOINT_IN),
|
||||
ret = libusb_bulk_transfer(ulink_handle->usb_device_handle, ulink_handle->ep_in,
|
||||
dummy, 64, &transferred, 200);
|
||||
|
||||
free(dummy);
|
||||
|
|
|
@ -134,7 +134,7 @@ static int load_usb_blaster_firmware(struct libusb_device_handle *libusb_dev,
|
|||
}
|
||||
|
||||
ublast2_firmware_image.base_address = 0;
|
||||
ublast2_firmware_image.base_address_set = 0;
|
||||
ublast2_firmware_image.base_address_set = false;
|
||||
|
||||
int ret = image_open(&ublast2_firmware_image, low->firmware_path, "ihex");
|
||||
if (ret != ERROR_OK) {
|
||||
|
@ -162,7 +162,7 @@ static int load_usb_blaster_firmware(struct libusb_device_handle *libusb_dev,
|
|||
100);
|
||||
|
||||
/* Download all sections in the image to ULINK */
|
||||
for (int i = 0; i < ublast2_firmware_image.num_sections; i++) {
|
||||
for (unsigned int i = 0; i < ublast2_firmware_image.num_sections; i++) {
|
||||
ret = ublast2_write_firmware_section(libusb_dev,
|
||||
&ublast2_firmware_image, i);
|
||||
if (ret != ERROR_OK) {
|
||||
|
|
|
@ -275,7 +275,7 @@ static void ublast_queue_byte(uint8_t abyte)
|
|||
*
|
||||
* Returns pin value (1 means driven high, 0 mean driven low)
|
||||
*/
|
||||
bool ublast_compute_pin(enum gpio_steer steer)
|
||||
static bool ublast_compute_pin(enum gpio_steer steer)
|
||||
{
|
||||
switch (steer) {
|
||||
case FIXED_0:
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "usbtoxxx.h"
|
||||
#include "usbtoxxx_internal.h"
|
||||
|
||||
RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed)
|
||||
static RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed)
|
||||
{
|
||||
struct versaloon_pending_t *pending = (struct versaloon_pending_t *)p;
|
||||
|
||||
|
@ -38,7 +38,7 @@ RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
RESULT usbtoswd_write_callback(void *p, uint8_t *src, uint8_t *processed)
|
||||
static RESULT usbtoswd_write_callback(void *p, uint8_t *src, uint8_t *processed)
|
||||
{
|
||||
struct versaloon_pending_t *pending = (struct versaloon_pending_t *)p;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#define N_A "n/a"
|
||||
|
||||
const char *types_name[96] = {
|
||||
static const char *types_name[96] = {
|
||||
"usbtousart", "usbtospi", "usbtoi2c", "usbtogpio", "usbtocan", "usbtopwm",
|
||||
"usbtoadc", "usbtodac",
|
||||
"usbtomicrowire", "usbtoswim", "usbtodusi", N_A, N_A, N_A, "usbtopower", "usbtodelay",
|
||||
|
@ -55,8 +55,8 @@ static uint16_t usbtoxxx_buffer_index;
|
|||
static uint16_t usbtoxxx_current_cmd_index;
|
||||
static uint8_t *usbtoxxx_buffer;
|
||||
|
||||
uint16_t collect_index;
|
||||
uint8_t collect_cmd;
|
||||
static uint16_t collect_index;
|
||||
static uint8_t collect_cmd;
|
||||
static uint8_t poll_nesting;
|
||||
|
||||
struct usbtoxxx_context_t {
|
||||
|
@ -86,7 +86,7 @@ static void usbtoxxx_pop_context(struct usbtoxxx_context_t *c)
|
|||
versaloon_pending_idx = c->versaloon_pending_idx;
|
||||
}
|
||||
|
||||
RESULT usbtoxxx_validate_current_command_type(void)
|
||||
static RESULT usbtoxxx_validate_current_command_type(void)
|
||||
{
|
||||
if (type_pre > 0) {
|
||||
/* not the first command */
|
||||
|
@ -272,7 +272,7 @@ bool usbtoxxx_interface_supported(uint8_t cmd)
|
|||
return (usbtoxxx_abilities[cmd / 8] & (1 << (cmd % 8))) > 0;
|
||||
}
|
||||
|
||||
RESULT usbtoxxx_ensure_buffer_size(uint16_t cmdlen)
|
||||
static RESULT usbtoxxx_ensure_buffer_size(uint16_t cmdlen)
|
||||
{
|
||||
/* check free space, commit if not enough */
|
||||
if (((usbtoxxx_buffer_index + usbtoxxx_current_cmd_index + cmdlen)
|
||||
|
|
|
@ -38,12 +38,12 @@ uint16_t versaloon_pending_idx;
|
|||
libusb_device_handle *versaloon_usb_device_handle;
|
||||
static uint32_t versaloon_usb_to = VERSALOON_TIMEOUT;
|
||||
|
||||
RESULT versaloon_init(void);
|
||||
RESULT versaloon_fini(void);
|
||||
RESULT versaloon_get_target_voltage(uint16_t *voltage);
|
||||
RESULT versaloon_set_target_voltage(uint16_t voltage);
|
||||
RESULT versaloon_delay_ms(uint16_t ms);
|
||||
RESULT versaloon_delay_us(uint16_t us);
|
||||
static RESULT versaloon_init(void);
|
||||
static RESULT versaloon_fini(void);
|
||||
static RESULT versaloon_get_target_voltage(uint16_t *voltage);
|
||||
static RESULT versaloon_set_target_voltage(uint16_t voltage);
|
||||
static RESULT versaloon_delay_ms(uint16_t ms);
|
||||
static RESULT versaloon_delay_us(uint16_t us);
|
||||
|
||||
struct versaloon_interface_t versaloon_interface = {
|
||||
.init = versaloon_init,
|
||||
|
@ -233,7 +233,7 @@ RESULT versaloon_send_command(uint16_t out_len, uint16_t *inlen)
|
|||
}
|
||||
|
||||
#define VERSALOON_RETRY_CNT 10
|
||||
RESULT versaloon_init(void)
|
||||
static RESULT versaloon_init(void)
|
||||
{
|
||||
uint16_t ret = 0;
|
||||
uint8_t retry;
|
||||
|
@ -291,7 +291,7 @@ RESULT versaloon_init(void)
|
|||
return versaloon_get_target_voltage(&ret);
|
||||
}
|
||||
|
||||
RESULT versaloon_fini(void)
|
||||
static RESULT versaloon_fini(void)
|
||||
{
|
||||
if (versaloon_usb_device_handle != NULL) {
|
||||
usbtoxxx_fini();
|
||||
|
@ -309,7 +309,7 @@ RESULT versaloon_fini(void)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
RESULT versaloon_set_target_voltage(uint16_t voltage)
|
||||
static RESULT versaloon_set_target_voltage(uint16_t voltage)
|
||||
{
|
||||
usbtopwr_init(0);
|
||||
usbtopwr_config(0);
|
||||
|
@ -319,7 +319,7 @@ RESULT versaloon_set_target_voltage(uint16_t voltage)
|
|||
return usbtoxxx_execute_command();
|
||||
}
|
||||
|
||||
RESULT versaloon_get_target_voltage(uint16_t *voltage)
|
||||
static RESULT versaloon_get_target_voltage(uint16_t *voltage)
|
||||
{
|
||||
uint16_t inlen;
|
||||
|
||||
|
@ -345,12 +345,12 @@ RESULT versaloon_get_target_voltage(uint16_t *voltage)
|
|||
}
|
||||
}
|
||||
|
||||
RESULT versaloon_delay_ms(uint16_t ms)
|
||||
static RESULT versaloon_delay_ms(uint16_t ms)
|
||||
{
|
||||
return usbtodelay_delay(ms | 0x8000);
|
||||
}
|
||||
|
||||
RESULT versaloon_delay_us(uint16_t us)
|
||||
static RESULT versaloon_delay_us(uint16_t us)
|
||||
{
|
||||
return usbtodelay_delay(us & 0x7FFF);
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
#define CMD_STABLECLOCKS 4
|
||||
|
||||
/* Array to convert from OpenOCD tap_state_t to XDS JTAG state */
|
||||
const uint32_t xds_jtag_state[] = {
|
||||
static const uint32_t xds_jtag_state[] = {
|
||||
XDS_JTAG_STATE_EXIT2_DR, /* TAP_DREXIT2 = 0x0 */
|
||||
XDS_JTAG_STATE_EXIT1_DR, /* TAP_DREXIT1 = 0x1 */
|
||||
XDS_JTAG_STATE_SHIFT_DR, /* TAP_DRSHIFT = 0x2 */
|
||||
|
|
|
@ -120,7 +120,7 @@ static int xlnx_pcie_xvc_transact(size_t num_bits, uint32_t tms, uint32_t tdi,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd)
|
||||
static int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd)
|
||||
{
|
||||
int tms = tap_get_state() == TAP_RESET ? 1 : 0;
|
||||
size_t left = cmd->cmd.stableclocks->num_cycles;
|
||||
|
|
|
@ -188,7 +188,7 @@ int hl_interface_override_target(const char **targetname)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
static int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq,
|
||||
unsigned int traceclkin_freq, uint16_t *prescaler)
|
||||
{
|
||||
|
@ -203,7 +203,7 @@ int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int hl_interface_poll_trace(uint8_t *buf, size_t *size)
|
||||
static int hl_interface_poll_trace(uint8_t *buf, size_t *size)
|
||||
{
|
||||
if (hl_if.layout->api->poll_trace)
|
||||
return hl_if.layout->api->poll_trace(hl_if.handle, buf, size);
|
||||
|
|
|
@ -51,10 +51,25 @@ struct hl_layout_api_s {
|
|||
int (*step)(void *handle);
|
||||
/** */
|
||||
int (*read_regs)(void *handle);
|
||||
/** */
|
||||
int (*read_reg)(void *handle, int num, uint32_t *val);
|
||||
/** */
|
||||
int (*write_reg)(void *handle, int num, uint32_t val);
|
||||
/**
|
||||
* Read one register from the target
|
||||
*
|
||||
* @param handle A pointer to the device-specific handle
|
||||
* @param regsel Register selection index compatible with all the
|
||||
* values allowed by armv7m DCRSR.REGSEL
|
||||
* @param val A pointer to retrieve the register value
|
||||
* @returns ERROR_OK on success, or an error code on failure.
|
||||
*/
|
||||
int (*read_reg)(void *handle, unsigned int regsel, uint32_t *val);
|
||||
/**
|
||||
* Write one register to the target
|
||||
* @param handle A pointer to the device-specific handle
|
||||
* @param regsel Register selection index compatible with all the
|
||||
* values allowed by armv7m DCRSR.REGSEL
|
||||
* @param val The value to be written in the register
|
||||
* @returns ERROR_OK on success, or an error code on failure.
|
||||
*/
|
||||
int (*write_reg)(void *handle, unsigned int regsel, uint32_t val);
|
||||
/** */
|
||||
int (*read_mem)(void *handle, uint32_t addr, uint32_t size,
|
||||
uint32_t count, uint8_t *buffer);
|
||||
|
|
|
@ -134,7 +134,7 @@ extern struct adapter_driver aice_adapter_driver;
|
|||
#if BUILD_BCM2835GPIO == 1
|
||||
extern struct adapter_driver bcm2835gpio_adapter_driver;
|
||||
#endif
|
||||
#if BUILD_CMSIS_DAP == 1
|
||||
#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1
|
||||
extern struct adapter_driver cmsis_dap_adapter_driver;
|
||||
#endif
|
||||
#if BUILD_KITPROG == 1
|
||||
|
@ -254,7 +254,7 @@ struct adapter_driver *adapter_drivers[] = {
|
|||
#if BUILD_BCM2835GPIO == 1
|
||||
&bcm2835gpio_adapter_driver,
|
||||
#endif
|
||||
#if BUILD_CMSIS_DAP == 1
|
||||
#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1
|
||||
&cmsis_dap_adapter_driver,
|
||||
#endif
|
||||
#if BUILD_KITPROG == 1
|
||||
|
|
|
@ -194,6 +194,11 @@ static int Jim_Command_drscan(Jim_Interp *interp, int argc, Jim_Obj *const *args
|
|||
retval = jtag_execute_queue();
|
||||
if (retval != ERROR_OK) {
|
||||
Jim_SetResultString(interp, "drscan: jtag execute failed", -1);
|
||||
|
||||
for (i = 0; i < field_count; i++)
|
||||
free(fields[i].in_value);
|
||||
free(fields);
|
||||
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,11 @@
|
|||
#include <pld/pld.h>
|
||||
#include <target/arm_cti.h>
|
||||
#include <target/arm_adi_v5.h>
|
||||
#include <rtt/rtt.h>
|
||||
|
||||
#include <server/server.h>
|
||||
#include <server/gdb_server.h>
|
||||
#include <server/rtt_server.h>
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
|
@ -230,10 +232,7 @@ static int openocd_register_commands(struct command_context *cmd_ctx)
|
|||
|
||||
struct command_context *global_cmd_ctx;
|
||||
|
||||
/* NB! this fn can be invoked outside this file for non PC hosted builds
|
||||
* NB! do not change to 'static'!!!!
|
||||
*/
|
||||
struct command_context *setup_command_handler(Jim_Interp *interp)
|
||||
static struct command_context *setup_command_handler(Jim_Interp *interp)
|
||||
{
|
||||
log_init();
|
||||
LOG_DEBUG("log_init: complete");
|
||||
|
@ -247,6 +246,7 @@ struct command_context *setup_command_handler(Jim_Interp *interp)
|
|||
&server_register_commands,
|
||||
&gdb_register_commands,
|
||||
&log_register_commands,
|
||||
&rtt_server_register_commands,
|
||||
&transport_register_commands,
|
||||
&interface_register_commands,
|
||||
&target_register_commands,
|
||||
|
@ -338,6 +338,9 @@ int openocd_main(int argc, char *argv[])
|
|||
if (ioutil_init(cmd_ctx) != ERROR_OK)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (rtt_init() != ERROR_OK)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
LOG_OUTPUT("For bug reports, read\n\t"
|
||||
"http://openocd.org/doc/doxygen/bugs.html"
|
||||
"\n");
|
||||
|
@ -367,6 +370,7 @@ int openocd_main(int argc, char *argv[])
|
|||
/* Shutdown commandline interface */
|
||||
command_exit(cmd_ctx);
|
||||
|
||||
rtt_exit();
|
||||
free_config();
|
||||
|
||||
if (ERROR_FAIL == ret)
|
||||
|
|
|
@ -110,7 +110,7 @@ static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_i
|
|||
{ 16, 0x04, 32 }, /* xPSR */
|
||||
};
|
||||
|
||||
const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = {
|
||||
static const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = {
|
||||
{
|
||||
ARM926EJS_REGISTERS_SIZE_SOLICITED, /* stack_registers_size */
|
||||
-1, /* stack_growth_direction */
|
||||
|
|
|
@ -37,11 +37,11 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
|
|||
struct rtos_reg **reg_list, int *num_regs);
|
||||
static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
|
||||
static int hwthread_smp_init(struct target *target);
|
||||
int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
|
||||
bool hwthread_needs_fake_step(struct target *target, int64_t thread_id);
|
||||
int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
|
||||
static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
|
||||
static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id);
|
||||
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
|
||||
uint32_t size, uint8_t *buffer);
|
||||
int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
|
||||
static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
|
||||
uint32_t size, const uint8_t *buffer);
|
||||
|
||||
#define HW_THREAD_NAME_STR_SIZE (32)
|
||||
|
@ -247,23 +247,35 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
|
|||
if (!target_was_examined(curr))
|
||||
return ERROR_FAIL;
|
||||
|
||||
int reg_list_size;
|
||||
struct reg **reg_list;
|
||||
int retval = target_get_gdb_reg_list(curr, ®_list, rtos_reg_list_size,
|
||||
int retval = target_get_gdb_reg_list(curr, ®_list, ®_list_size,
|
||||
REG_CLASS_GENERAL);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
j++;
|
||||
}
|
||||
*rtos_reg_list_size = j;
|
||||
*rtos_reg_list = calloc(*rtos_reg_list_size, sizeof(struct rtos_reg));
|
||||
if (*rtos_reg_list == NULL) {
|
||||
free(reg_list);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < *rtos_reg_list_size; i++) {
|
||||
(*rtos_reg_list)[i].number = (*reg_list)[i].number;
|
||||
(*rtos_reg_list)[i].size = (*reg_list)[i].size;
|
||||
memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value,
|
||||
j = 0;
|
||||
for (int i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
(*rtos_reg_list)[j].number = (*reg_list)[i].number;
|
||||
(*rtos_reg_list)[j].size = (*reg_list)[i].size;
|
||||
memcpy((*rtos_reg_list)[j].value, (*reg_list)[i].value,
|
||||
((*reg_list)[i].size + 7) / 8);
|
||||
j++;
|
||||
}
|
||||
free(reg_list);
|
||||
|
||||
|
@ -308,7 +320,7 @@ static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
|
||||
static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
|
||||
{
|
||||
if (rtos == NULL)
|
||||
return ERROR_FAIL;
|
||||
|
@ -395,12 +407,12 @@ static int hwthread_create(struct target *target)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool hwthread_needs_fake_step(struct target *target, int64_t thread_id)
|
||||
static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
|
||||
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
|
||||
uint32_t size, uint8_t *buffer)
|
||||
{
|
||||
if (rtos == NULL)
|
||||
|
@ -415,7 +427,7 @@ int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
|
|||
return target_read_buffer(curr, address, size, buffer);
|
||||
}
|
||||
|
||||
int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
|
||||
static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
|
||||
uint32_t size, const uint8_t *buffer)
|
||||
{
|
||||
if (rtos == NULL)
|
||||
|
|
|
@ -92,7 +92,7 @@ struct cpu_context {
|
|||
uint32_t PC;
|
||||
uint32_t preempt_count;
|
||||
};
|
||||
struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
|
||||
static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
|
||||
uint32_t *info_addr);
|
||||
static int insert_into_threadlist(struct target *target, struct threads *t);
|
||||
|
||||
|
@ -144,7 +144,7 @@ static int linux_read_memory(struct target *target,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer)
|
||||
static int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer)
|
||||
{
|
||||
|
||||
if ((addr & 0xfffffffc) != addr)
|
||||
|
@ -155,7 +155,7 @@ int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer)
|
|||
|
||||
}
|
||||
|
||||
uint32_t get_buffer(struct target *target, const uint8_t *buffer)
|
||||
static uint32_t get_buffer(struct target *target, const uint8_t *buffer)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
const uint8_t *value_ptr = buffer;
|
||||
|
@ -293,7 +293,7 @@ int fill_task_pid(struct target *target, struct threads *t)
|
|||
}
|
||||
#endif
|
||||
|
||||
int fill_task(struct target *target, struct threads *t)
|
||||
static int fill_task(struct target *target, struct threads *t)
|
||||
{
|
||||
int retval;
|
||||
uint32_t pid_addr = t->base_addr + PID;
|
||||
|
@ -349,7 +349,7 @@ int fill_task(struct target *target, struct threads *t)
|
|||
return retval;
|
||||
}
|
||||
|
||||
int get_name(struct target *target, struct threads *t)
|
||||
static int get_name(struct target *target, struct threads *t)
|
||||
{
|
||||
int retval;
|
||||
uint32_t full_name[4];
|
||||
|
@ -395,7 +395,7 @@ int get_name(struct target *target, struct threads *t)
|
|||
|
||||
}
|
||||
|
||||
int get_current(struct target *target, int create)
|
||||
static int get_current(struct target *target, int create)
|
||||
{
|
||||
struct target_list *head;
|
||||
head = target->head;
|
||||
|
@ -483,7 +483,7 @@ int get_current(struct target *target, int create)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
|
||||
static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
|
||||
uint32_t *thread_info_addr_old)
|
||||
{
|
||||
struct cpu_context *context = calloc(1, sizeof(struct cpu_context));
|
||||
|
@ -579,7 +579,7 @@ retry:
|
|||
return context;
|
||||
}
|
||||
|
||||
uint32_t next_task(struct target *target, struct threads *t)
|
||||
static uint32_t next_task(struct target *target, struct threads *t)
|
||||
{
|
||||
uint8_t *buffer = calloc(1, 4);
|
||||
uint32_t next_addr = t->base_addr + NEXT;
|
||||
|
@ -598,7 +598,7 @@ uint32_t next_task(struct target *target, struct threads *t)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct current_thread *add_current_thread(struct current_thread *currents,
|
||||
static struct current_thread *add_current_thread(struct current_thread *currents,
|
||||
struct current_thread *ct)
|
||||
{
|
||||
ct->next = NULL;
|
||||
|
@ -617,7 +617,7 @@ struct current_thread *add_current_thread(struct current_thread *currents,
|
|||
}
|
||||
}
|
||||
|
||||
struct threads *liste_del_task(struct threads *task_list, struct threads **t,
|
||||
static struct threads *liste_del_task(struct threads *task_list, struct threads **t,
|
||||
struct threads *prev)
|
||||
{
|
||||
LOG_INFO("del task %" PRId64, (*t)->threadid);
|
||||
|
@ -634,7 +634,7 @@ struct threads *liste_del_task(struct threads *task_list, struct threads **t,
|
|||
return task_list;
|
||||
}
|
||||
|
||||
struct threads *liste_add_task(struct threads *task_list, struct threads *t,
|
||||
static struct threads *liste_add_task(struct threads *task_list, struct threads *t,
|
||||
struct threads **last)
|
||||
{
|
||||
t->next = NULL;
|
||||
|
@ -683,7 +683,7 @@ static int current_base_addr(struct linux_os *linux_os, uint32_t base_addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int linux_get_tasks(struct target *target, int context)
|
||||
static int linux_get_tasks(struct target *target, int context)
|
||||
{
|
||||
int loop = 0;
|
||||
int retval = 0;
|
||||
|
@ -1033,7 +1033,7 @@ static int linux_task_update(struct target *target, int context)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int linux_gdb_thread_packet(struct target *target,
|
||||
static int linux_gdb_thread_packet(struct target *target,
|
||||
struct connection *connection, char const *packet,
|
||||
int packet_size)
|
||||
{
|
||||
|
@ -1070,7 +1070,7 @@ int linux_gdb_thread_packet(struct target *target,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int linux_gdb_thread_update(struct target *target,
|
||||
static int linux_gdb_thread_update(struct target *target,
|
||||
struct connection *connection, char const *packet,
|
||||
int packet_size)
|
||||
{
|
||||
|
@ -1117,7 +1117,7 @@ int linux_gdb_thread_update(struct target *target,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int linux_thread_extra_info(struct target *target,
|
||||
static int linux_thread_extra_info(struct target *target,
|
||||
struct connection *connection, char const *packet,
|
||||
int packet_size)
|
||||
{
|
||||
|
@ -1163,7 +1163,7 @@ int linux_thread_extra_info(struct target *target,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int linux_gdb_T_packet(struct connection *connection,
|
||||
static int linux_gdb_T_packet(struct connection *connection,
|
||||
struct target *target, char const *packet, int packet_size)
|
||||
{
|
||||
int64_t threadid;
|
||||
|
@ -1223,7 +1223,7 @@ int linux_gdb_T_packet(struct connection *connection,
|
|||
return retval;
|
||||
}
|
||||
|
||||
int linux_gdb_h_packet(struct connection *connection,
|
||||
static int linux_gdb_h_packet(struct connection *connection,
|
||||
struct target *target, char const *packet, int packet_size)
|
||||
{
|
||||
struct linux_os *linux_os = (struct linux_os *)
|
||||
|
|
|
@ -72,7 +72,7 @@ struct tcb {
|
|||
uint8_t dat[512];
|
||||
};
|
||||
|
||||
struct {
|
||||
static struct {
|
||||
uint32_t addr;
|
||||
uint32_t prio;
|
||||
} g_tasklist[TASK_QUEUE_NUM];
|
||||
|
|
|
@ -59,6 +59,8 @@ static struct rtos_type *rtos_types[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static int rtos_try_next(struct target *target);
|
||||
|
||||
int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
|
||||
|
||||
int rtos_smp_init(struct target *target)
|
||||
|
@ -632,7 +634,7 @@ int rtos_generic_stack_read(struct target *target,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtos_try_next(struct target *target)
|
||||
static int rtos_try_next(struct target *target)
|
||||
{
|
||||
struct rtos *os = target->rtos;
|
||||
struct rtos_type **type = rtos_types;
|
||||
|
|
|
@ -134,7 +134,6 @@ int rtos_generic_stack_read(struct target *target,
|
|||
int64_t stack_ptr,
|
||||
struct rtos_reg **reg_list,
|
||||
int *num_regs);
|
||||
int rtos_try_next(struct target *target);
|
||||
int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size);
|
||||
int rtos_get_gdb_reg(struct connection *connection, int reg_num);
|
||||
int rtos_get_gdb_reg_list(struct connection *connection);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
noinst_LTLIBRARIES += %D%/librtt.la
|
||||
%C%_librtt_la_SOURCES = %D%/rtt.c %D%/rtt.h %D%/tcl.c
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <helper/log.h>
|
||||
#include <helper/list.h>
|
||||
#include <target/target.h>
|
||||
#include <target/rtt.h>
|
||||
|
||||
#include "rtt.h"
|
||||
|
||||
static struct {
|
||||
struct rtt_source source;
|
||||
/** Control block. */
|
||||
struct rtt_control ctrl;
|
||||
struct target *target;
|
||||
/** Start address to search for the control block. */
|
||||
target_addr_t addr;
|
||||
/** Size of the control block search area. */
|
||||
size_t size;
|
||||
/** Control block identifier. */
|
||||
char id[RTT_CB_MAX_ID_LENGTH];
|
||||
/** Whether RTT is configured. */
|
||||
bool configured;
|
||||
/** Whether RTT is started. */
|
||||
bool started;
|
||||
/** Whether configuration changed. */
|
||||
bool changed;
|
||||
/** Whether the control block was found. */
|
||||
bool found_cb;
|
||||
|
||||
struct rtt_sink_list **sink_list;
|
||||
size_t sink_list_length;
|
||||
|
||||
unsigned int polling_interval;
|
||||
} rtt;
|
||||
|
||||
int rtt_init(void)
|
||||
{
|
||||
rtt.sink_list_length = 1;
|
||||
rtt.sink_list = calloc(rtt.sink_list_length,
|
||||
sizeof(struct rtt_sink_list *));
|
||||
|
||||
if (!rtt.sink_list)
|
||||
return ERROR_FAIL;
|
||||
|
||||
rtt.sink_list[0] = NULL;
|
||||
rtt.started = false;
|
||||
|
||||
rtt.polling_interval = 100;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_exit(void)
|
||||
{
|
||||
free(rtt.sink_list);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int read_channel_callback(void *user_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtt.source.read(rtt.target, &rtt.ctrl, rtt.sink_list,
|
||||
rtt.sink_list_length, NULL);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
target_unregister_timer_callback(&read_channel_callback, NULL);
|
||||
rtt.source.stop(rtt.target, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_setup(target_addr_t address, size_t size, const char *id)
|
||||
{
|
||||
size_t id_length = strlen(id);
|
||||
|
||||
if (!id_length || id_length >= RTT_CB_MAX_ID_LENGTH) {
|
||||
LOG_ERROR("rtt: Invalid control block ID");
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
rtt.addr = address;
|
||||
rtt.size = size;
|
||||
strncpy(rtt.id, id, id_length + 1);
|
||||
rtt.changed = true;
|
||||
rtt.configured = true;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_register_source(const struct rtt_source source,
|
||||
struct target *target)
|
||||
{
|
||||
if (!source.find_cb || !source.read_cb || !source.read_channel_info)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (!source.start || !source.stop)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (!source.read || !source.write)
|
||||
return ERROR_FAIL;
|
||||
|
||||
rtt.source = source;
|
||||
rtt.target = target;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_start(void)
|
||||
{
|
||||
int ret;
|
||||
target_addr_t addr = rtt.addr;
|
||||
|
||||
if (rtt.started)
|
||||
return ERROR_OK;
|
||||
|
||||
if (!rtt.found_cb || rtt.changed) {
|
||||
rtt.source.find_cb(rtt.target, &addr, rtt.size, rtt.id,
|
||||
&rtt.found_cb, NULL);
|
||||
|
||||
rtt.changed = false;
|
||||
|
||||
if (rtt.found_cb) {
|
||||
LOG_INFO("rtt: Control block found at 0x%" TARGET_PRIxADDR,
|
||||
addr);
|
||||
rtt.ctrl.address = addr;
|
||||
} else {
|
||||
LOG_INFO("rtt: No control block found");
|
||||
return ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rtt.source.read_cb(rtt.target, rtt.ctrl.address, &rtt.ctrl, NULL);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
ret = rtt.source.start(rtt.target, &rtt.ctrl, NULL);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
target_register_timer_callback(&read_channel_callback,
|
||||
rtt.polling_interval, 1, NULL);
|
||||
rtt.started = true;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_stop(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rtt.configured) {
|
||||
LOG_ERROR("rtt: Not configured");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
target_unregister_timer_callback(&read_channel_callback, NULL);
|
||||
rtt.started = false;
|
||||
|
||||
ret = rtt.source.stop(rtt.target, NULL);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int adjust_sink_list(size_t length)
|
||||
{
|
||||
struct rtt_sink_list **tmp;
|
||||
|
||||
if (length <= rtt.sink_list_length)
|
||||
return ERROR_OK;
|
||||
|
||||
tmp = realloc(rtt.sink_list, sizeof(struct rtt_sink_list *) * length);
|
||||
|
||||
if (!tmp)
|
||||
return ERROR_FAIL;
|
||||
|
||||
for (size_t i = rtt.sink_list_length; i < length; i++)
|
||||
tmp[i] = NULL;
|
||||
|
||||
rtt.sink_list = tmp;
|
||||
rtt.sink_list_length = length;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_register_sink(unsigned int channel_index, rtt_sink_read read,
|
||||
void *user_data)
|
||||
{
|
||||
struct rtt_sink_list *tmp;
|
||||
|
||||
if (channel_index >= rtt.sink_list_length) {
|
||||
if (adjust_sink_list(channel_index + 1) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
LOG_DEBUG("rtt: Registering sink for channel %u", channel_index);
|
||||
|
||||
tmp = malloc(sizeof(struct rtt_sink_list));
|
||||
|
||||
if (!tmp)
|
||||
return ERROR_FAIL;
|
||||
|
||||
tmp->read = read;
|
||||
tmp->user_data = user_data;
|
||||
tmp->next = rtt.sink_list[channel_index];
|
||||
|
||||
rtt.sink_list[channel_index] = tmp;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read,
|
||||
void *user_data)
|
||||
{
|
||||
struct rtt_sink_list *prev_sink;
|
||||
|
||||
LOG_DEBUG("rtt: Unregistering sink for channel %u", channel_index);
|
||||
|
||||
if (channel_index >= rtt.sink_list_length)
|
||||
return ERROR_FAIL;
|
||||
|
||||
prev_sink = rtt.sink_list[channel_index];
|
||||
|
||||
for (struct rtt_sink_list *sink = rtt.sink_list[channel_index]; sink;
|
||||
prev_sink = sink, sink = sink->next) {
|
||||
if (sink->read == read && sink->user_data == user_data) {
|
||||
|
||||
if (sink == rtt.sink_list[channel_index])
|
||||
rtt.sink_list[channel_index] = sink->next;
|
||||
else
|
||||
prev_sink->next = sink->next;
|
||||
|
||||
free(sink);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_get_polling_interval(unsigned int *interval)
|
||||
{
|
||||
if (!interval)
|
||||
return ERROR_FAIL;
|
||||
|
||||
*interval = rtt.polling_interval;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_set_polling_interval(unsigned int interval)
|
||||
{
|
||||
if (!interval)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (rtt.polling_interval != interval) {
|
||||
target_unregister_timer_callback(&read_channel_callback, NULL);
|
||||
target_register_timer_callback(&read_channel_callback, interval, 1,
|
||||
NULL);
|
||||
}
|
||||
|
||||
rtt.polling_interval = interval;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer,
|
||||
size_t *length)
|
||||
{
|
||||
if (channel_index >= rtt.ctrl.num_up_channels) {
|
||||
LOG_WARNING("rtt: Down-channel %u is not available", channel_index);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
return rtt.source.write(rtt.target, &rtt.ctrl, channel_index, buffer,
|
||||
length, NULL);
|
||||
}
|
||||
|
||||
bool rtt_started(void)
|
||||
{
|
||||
return rtt.started;
|
||||
}
|
||||
|
||||
bool rtt_configured(void)
|
||||
{
|
||||
return rtt.configured;
|
||||
}
|
||||
|
||||
bool rtt_found_cb(void)
|
||||
{
|
||||
return rtt.found_cb;
|
||||
}
|
||||
|
||||
const struct rtt_control *rtt_get_control(void)
|
||||
{
|
||||
return &rtt.ctrl;
|
||||
}
|
||||
|
||||
int rtt_read_channel_info(unsigned int channel_index,
|
||||
enum rtt_channel_type type, struct rtt_channel_info *info)
|
||||
{
|
||||
return rtt.source.read_channel_info(rtt.target, &rtt.ctrl,
|
||||
channel_index, type, info, NULL);
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPENOCD_RTT_RTT_H
|
||||
#define OPENOCD_RTT_RTT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <helper/command.h>
|
||||
#include <target/target.h>
|
||||
|
||||
/**
|
||||
* Control block ID length in bytes, including the trailing null-terminator.
|
||||
*/
|
||||
#define RTT_CB_MAX_ID_LENGTH 16
|
||||
|
||||
/* Control block size in bytes. */
|
||||
#define RTT_CB_SIZE (RTT_CB_MAX_ID_LENGTH + 2 * sizeof(uint32_t))
|
||||
|
||||
/* Channel structure size in bytes. */
|
||||
#define RTT_CHANNEL_SIZE 24
|
||||
|
||||
/* Minimal channel buffer size in bytes. */
|
||||
#define RTT_CHANNEL_BUFFER_MIN_SIZE 2
|
||||
|
||||
/** RTT control block. */
|
||||
struct rtt_control {
|
||||
/** Control block address on the target. */
|
||||
target_addr_t address;
|
||||
/** Control block identifier, including trailing null-terminator. */
|
||||
char id[RTT_CB_MAX_ID_LENGTH];
|
||||
/** Maximum number of up-channels. */
|
||||
uint32_t num_up_channels;
|
||||
/** Maximum number of down-channels. */
|
||||
uint32_t num_down_channels;
|
||||
};
|
||||
|
||||
/** RTT channel. */
|
||||
struct rtt_channel {
|
||||
/** Channel structure address on the target. */
|
||||
target_addr_t address;
|
||||
/** Channel name address on the target. */
|
||||
uint32_t name_addr;
|
||||
/** Buffer address on the target. */
|
||||
uint32_t buffer_addr;
|
||||
/** Channel buffer size in bytes. */
|
||||
uint32_t size;
|
||||
/** Write position within the buffer in bytes. */
|
||||
uint32_t write_pos;
|
||||
/** Read position within the buffer in bytes. */
|
||||
uint32_t read_pos;
|
||||
/**
|
||||
* Buffer flags.
|
||||
*
|
||||
* @note: Not used at the moment.
|
||||
*/
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/** RTT channel information. */
|
||||
struct rtt_channel_info {
|
||||
/** Channel name. */
|
||||
char *name;
|
||||
/** Length of the name in bytes, including the trailing null-terminator. */
|
||||
size_t name_length;
|
||||
/** Buffer size in bytes. */
|
||||
uint32_t size;
|
||||
/**
|
||||
* Buffer flags.
|
||||
*
|
||||
* @note: Not used at the moment.
|
||||
*/
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
typedef int (*rtt_sink_read)(unsigned int channel, const uint8_t *buffer,
|
||||
size_t length, void *user_data);
|
||||
|
||||
struct rtt_sink_list {
|
||||
rtt_sink_read read;
|
||||
void *user_data;
|
||||
|
||||
struct rtt_sink_list *next;
|
||||
};
|
||||
|
||||
/** Channel type. */
|
||||
enum rtt_channel_type {
|
||||
/** Up channel (target to host). */
|
||||
RTT_CHANNEL_TYPE_UP,
|
||||
/** Down channel (host to target). */
|
||||
RTT_CHANNEL_TYPE_DOWN
|
||||
};
|
||||
|
||||
typedef int (*rtt_source_find_ctrl_block)(struct target *target,
|
||||
target_addr_t *address, size_t size, const char *id, bool *found,
|
||||
void *user_data);
|
||||
typedef int (*rtt_source_read_ctrl_block)(struct target *target,
|
||||
target_addr_t address, struct rtt_control *ctrl_block,
|
||||
void *user_data);
|
||||
typedef int (*rtt_source_read_channel_info)(struct target *target,
|
||||
const struct rtt_control *ctrl, unsigned int channel,
|
||||
enum rtt_channel_type type, struct rtt_channel_info *info,
|
||||
void *user_data);
|
||||
typedef int (*rtt_source_start)(struct target *target,
|
||||
const struct rtt_control *ctrl, void *user_data);
|
||||
typedef int (*rtt_source_stop)(struct target *target, void *user_data);
|
||||
typedef int (*rtt_source_read)(struct target *target,
|
||||
const struct rtt_control *ctrl, struct rtt_sink_list **sinks,
|
||||
size_t num_channels, void *user_data);
|
||||
typedef int (*rtt_source_write)(struct target *target,
|
||||
struct rtt_control *ctrl, unsigned int channel,
|
||||
const uint8_t *buffer, size_t *length, void *user_data);
|
||||
|
||||
/** RTT source. */
|
||||
struct rtt_source {
|
||||
rtt_source_find_ctrl_block find_cb;
|
||||
rtt_source_read_ctrl_block read_cb;
|
||||
rtt_source_read_channel_info read_channel_info;
|
||||
rtt_source_start start;
|
||||
rtt_source_stop stop;
|
||||
rtt_source_read read;
|
||||
rtt_source_write write;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Real-Time Transfer (RTT).
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_init(void);
|
||||
|
||||
/**
|
||||
* Shutdown Real-Time Transfer (RTT).
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_exit(void);
|
||||
|
||||
/**
|
||||
* Register an RTT source for a target.
|
||||
*
|
||||
* @param[in] source RTT source.
|
||||
* @param[in,out] target Target.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_register_source(const struct rtt_source source,
|
||||
struct target *target);
|
||||
|
||||
/**
|
||||
* Setup RTT.
|
||||
*
|
||||
* @param[in] address Start address to search for the control block.
|
||||
* @param[in] size Size of the control block search area.
|
||||
* @param[in] id Identifier of the control block. Must be null-terminated.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_setup(target_addr_t address, size_t size, const char *id);
|
||||
|
||||
/**
|
||||
* Start Real-Time Transfer (RTT).
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_start(void);
|
||||
|
||||
/**
|
||||
* Stop Real-Time Transfer (RTT).
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_stop(void);
|
||||
|
||||
/**
|
||||
* Get the polling interval.
|
||||
*
|
||||
* @param[out] interval Polling interval in milliseconds.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_get_polling_interval(unsigned int *interval);
|
||||
|
||||
/**
|
||||
* Set the polling interval.
|
||||
*
|
||||
* @param[in] interval Polling interval in milliseconds.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_set_polling_interval(unsigned int interval);
|
||||
|
||||
/**
|
||||
* Get whether RTT is started.
|
||||
*
|
||||
* @returns Whether RTT is started.
|
||||
*/
|
||||
bool rtt_started(void);
|
||||
|
||||
/**
|
||||
* Get whether RTT is configured.
|
||||
*
|
||||
* @returns Whether RTT is configured.
|
||||
*/
|
||||
bool rtt_configured(void);
|
||||
|
||||
/**
|
||||
* Get whether RTT control block was found.
|
||||
*
|
||||
* @returns Whether RTT was found.
|
||||
*/
|
||||
bool rtt_found_cb(void);
|
||||
|
||||
/**
|
||||
* Get the RTT control block.
|
||||
*
|
||||
* @returns The RTT control block.
|
||||
*/
|
||||
const struct rtt_control *rtt_get_control(void);
|
||||
|
||||
/**
|
||||
* Read channel information.
|
||||
*
|
||||
* @param[in] channel_index Channel index.
|
||||
* @param[in] channel_type Channel type.
|
||||
* @param[out] info Channel information.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_read_channel_info(unsigned int channel_index,
|
||||
enum rtt_channel_type type, struct rtt_channel_info *info);
|
||||
|
||||
/**
|
||||
* Register an RTT sink.
|
||||
*
|
||||
* @param[in] channel_index Channel index.
|
||||
* @param[in] read Read callback function.
|
||||
* @param[in,out] user_data User data to be passed to the callback function.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_register_sink(unsigned int channel_index, rtt_sink_read read,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* Unregister an RTT sink.
|
||||
*
|
||||
* @param[in] channel_index Channel index.
|
||||
* @param[in] read Read callback function.
|
||||
* @param[in,out] user_data User data to be passed to the callback function.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* Write to an RTT channel.
|
||||
*
|
||||
* @param[in] channel_index Channel index.
|
||||
* @param[in] buffer Buffer with data that should be written to the channel.
|
||||
* @param[in,out] length Number of bytes to write. On success, the argument gets
|
||||
* updated with the actual number of written bytes.
|
||||
*
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer,
|
||||
size_t *length);
|
||||
|
||||
extern const struct command_registration rtt_target_command_handlers[];
|
||||
|
||||
#endif /* OPENOCD_RTT_RTT_H */
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright (C) 2019-2020 by Marc Schink <dev@zapb.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <helper/log.h>
|
||||
#include <target/rtt.h>
|
||||
|
||||
#include "rtt.h"
|
||||
|
||||
#define CHANNEL_NAME_SIZE 128
|
||||
|
||||
COMMAND_HANDLER(handle_rtt_setup_command)
|
||||
{
|
||||
struct rtt_source source;
|
||||
|
||||
if (CMD_ARGC != 3)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
source.find_cb = &target_rtt_find_control_block;
|
||||
source.read_cb = &target_rtt_read_control_block;
|
||||
source.start = &target_rtt_start;
|
||||
source.stop = &target_rtt_stop;
|
||||
source.read = &target_rtt_read_callback;
|
||||
source.write = &target_rtt_write_callback;
|
||||
source.read_channel_info = &target_rtt_read_channel_info;
|
||||
|
||||
target_addr_t address;
|
||||
uint32_t size;
|
||||
|
||||
COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], address);
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
|
||||
|
||||
rtt_register_source(source, get_current_target(CMD_CTX));
|
||||
|
||||
if (rtt_setup(address, size, CMD_ARGV[2]) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_rtt_start_command)
|
||||
{
|
||||
if (CMD_ARGC > 0)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
if (!rtt_configured()) {
|
||||
command_print(CMD, "RTT is not configured");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return rtt_start();
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_rtt_stop_command)
|
||||
{
|
||||
if (CMD_ARGC > 0)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
return rtt_stop();
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_rtt_polling_interval_command)
|
||||
{
|
||||
if (CMD_ARGC == 0) {
|
||||
int ret;
|
||||
unsigned int interval;
|
||||
|
||||
ret = rtt_get_polling_interval(&interval);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
command_print(CMD, "Failed to get polling interval");
|
||||
return ret;
|
||||
}
|
||||
|
||||
command_print(CMD, "%u ms", interval);
|
||||
} else if (CMD_ARGC == 1) {
|
||||
int ret;
|
||||
unsigned int interval;
|
||||
|
||||
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], interval);
|
||||
ret = rtt_set_polling_interval(interval);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
command_print(CMD, "Failed to set polling interval");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_rtt_channels_command)
|
||||
{
|
||||
int ret;
|
||||
char channel_name[CHANNEL_NAME_SIZE];
|
||||
const struct rtt_control *ctrl;
|
||||
struct rtt_channel_info info;
|
||||
|
||||
if (!rtt_found_cb()) {
|
||||
command_print(CMD, "rtt: Control block not available");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
ctrl = rtt_get_control();
|
||||
|
||||
command_print(CMD, "Channels: up=%u, down=%u", ctrl->num_up_channels,
|
||||
ctrl->num_down_channels);
|
||||
|
||||
command_print(CMD, "Up-channels:");
|
||||
|
||||
info.name = channel_name;
|
||||
info.name_length = sizeof(channel_name);
|
||||
|
||||
for (unsigned int i = 0; i < ctrl->num_up_channels; i++) {
|
||||
ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
if (!info.size)
|
||||
continue;
|
||||
|
||||
command_print(CMD, "%u: %s %u %u", i, info.name, info.size,
|
||||
info.flags);
|
||||
}
|
||||
|
||||
command_print(CMD, "Down-channels:");
|
||||
|
||||
for (unsigned int i = 0; i < ctrl->num_down_channels; i++) {
|
||||
ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
if (!info.size)
|
||||
continue;
|
||||
|
||||
command_print(CMD, "%u: %s %u %u", i, info.name, info.size,
|
||||
info.flags);
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int jim_channel_list(Jim_Interp *interp, int argc,
|
||||
Jim_Obj * const *argv)
|
||||
{
|
||||
Jim_Obj *list;
|
||||
Jim_Obj *channel_list;
|
||||
char channel_name[CHANNEL_NAME_SIZE];
|
||||
const struct rtt_control *ctrl;
|
||||
struct rtt_channel_info info;
|
||||
|
||||
if (!rtt_found_cb()) {
|
||||
Jim_SetResultFormatted(interp, "rtt: Control block not available");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
ctrl = rtt_get_control();
|
||||
|
||||
info.name = channel_name;
|
||||
info.name_length = sizeof(channel_name);
|
||||
|
||||
list = Jim_NewListObj(interp, NULL, 0);
|
||||
channel_list = Jim_NewListObj(interp, NULL, 0);
|
||||
|
||||
for (unsigned int i = 0; i < ctrl->num_up_channels; i++) {
|
||||
int ret;
|
||||
Jim_Obj *tmp;
|
||||
|
||||
ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
if (!info.size)
|
||||
continue;
|
||||
|
||||
tmp = Jim_NewListObj(interp, NULL, 0);
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"name", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
info.name, -1));
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"size", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp,
|
||||
info.size));
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"flags", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp,
|
||||
info.flags));
|
||||
|
||||
Jim_ListAppendElement(interp, channel_list, tmp);
|
||||
}
|
||||
|
||||
Jim_ListAppendElement(interp, list, channel_list);
|
||||
|
||||
channel_list = Jim_NewListObj(interp, NULL, 0);
|
||||
|
||||
for (unsigned int i = 0; i < ctrl->num_down_channels; i++) {
|
||||
int ret;
|
||||
Jim_Obj *tmp;
|
||||
|
||||
ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info);
|
||||
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
if (!info.size)
|
||||
continue;
|
||||
|
||||
tmp = Jim_NewListObj(interp, NULL, 0);
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"name", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
info.name, -1));
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"size", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp,
|
||||
info.size));
|
||||
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp,
|
||||
"flags", -1));
|
||||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp,
|
||||
info.flags));
|
||||
|
||||
Jim_ListAppendElement(interp, channel_list, tmp);
|
||||
}
|
||||
|
||||
Jim_ListAppendElement(interp, list, channel_list);
|
||||
Jim_SetResult(interp, list);
|
||||
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration rtt_subcommand_handlers[] = {
|
||||
{
|
||||
.name = "setup",
|
||||
.handler = handle_rtt_setup_command,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "setup RTT",
|
||||
.usage = "<address> <size> <ID>"
|
||||
},
|
||||
{
|
||||
.name = "start",
|
||||
.handler = handle_rtt_start_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "start RTT",
|
||||
.usage = ""
|
||||
},
|
||||
{
|
||||
.name = "stop",
|
||||
.handler = handle_rtt_stop_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "stop RTT",
|
||||
.usage = ""
|
||||
},
|
||||
{
|
||||
.name = "polling_interval",
|
||||
.handler = handle_rtt_polling_interval_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "show or set polling interval in ms",
|
||||
.usage = "[interval]"
|
||||
},
|
||||
{
|
||||
.name = "channels",
|
||||
.handler = handle_rtt_channels_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "list available channels",
|
||||
.usage = ""
|
||||
},
|
||||
{
|
||||
.name = "channellist",
|
||||
.jim_handler = jim_channel_list,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "list available channels",
|
||||
.usage = ""
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
const struct command_registration rtt_target_command_handlers[] = {
|
||||
{
|
||||
.name = "rtt",
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "RTT target commands",
|
||||
.usage = "",
|
||||
.chain = rtt_subcommand_handlers
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
|
@ -8,7 +8,9 @@ noinst_LTLIBRARIES += %D%/libserver.la
|
|||
%D%/gdb_server.h \
|
||||
%D%/server_stubs.c \
|
||||
%D%/tcl_server.c \
|
||||
%D%/tcl_server.h
|
||||
%D%/tcl_server.h \
|
||||
%D%/rtt_server.c \
|
||||
%D%/rtt_server.h
|
||||
|
||||
%C%_libserver_la_CFLAGS = $(AM_CFLAGS)
|
||||
if IS_MINGW
|
||||
|
|
|
@ -967,15 +967,6 @@ static int gdb_new_connection(struct connection *connection)
|
|||
breakpoint_clear_target(target);
|
||||
watchpoint_clear_target(target);
|
||||
|
||||
if (target->rtos) {
|
||||
/* clean previous rtos session if supported*/
|
||||
if (target->rtos->type->clean)
|
||||
target->rtos->type->clean(target);
|
||||
|
||||
/* update threads */
|
||||
rtos_update_threads(target);
|
||||
}
|
||||
|
||||
/* remove the initial ACK from the incoming buffer */
|
||||
retval = gdb_get_char(connection, &initial_ack);
|
||||
if (retval != ERROR_OK)
|
||||
|
@ -988,6 +979,15 @@ static int gdb_new_connection(struct connection *connection)
|
|||
gdb_putback_char(connection, initial_ack);
|
||||
target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH);
|
||||
|
||||
if (target->rtos) {
|
||||
/* clean previous rtos session if supported*/
|
||||
if (target->rtos->type->clean)
|
||||
target->rtos->type->clean(target);
|
||||
|
||||
/* update threads */
|
||||
rtos_update_threads(target);
|
||||
}
|
||||
|
||||
if (gdb_use_memory_map) {
|
||||
/* Connect must fail if the memory map can't be set up correctly.
|
||||
*
|
||||
|
@ -1187,7 +1187,7 @@ static int gdb_get_registers_packet(struct connection *connection,
|
|||
return gdb_error(connection, retval);
|
||||
|
||||
for (i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false)
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
|
||||
}
|
||||
|
@ -1201,7 +1201,7 @@ static int gdb_get_registers_packet(struct connection *connection,
|
|||
reg_packet_p = reg_packet;
|
||||
|
||||
for (i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false)
|
||||
if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
if (!reg_list[i]->valid) {
|
||||
retval = reg_list[i]->type->get(reg_list[i]);
|
||||
|
@ -1330,7 +1330,7 @@ static int gdb_get_register_packet(struct connection *connection,
|
|||
}
|
||||
}
|
||||
|
||||
reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */
|
||||
reg_packet = calloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1, 1); /* plus one for string termination null */
|
||||
|
||||
gdb_str_to_target(target, reg_packet, reg_list[reg_num]);
|
||||
|
||||
|
@ -2187,7 +2187,7 @@ static int get_reg_features_list(struct target *target, char const **feature_lis
|
|||
*feature_list = calloc(1, sizeof(char *));
|
||||
|
||||
for (int i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i]->exist == false)
|
||||
if (reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
|
||||
if (reg_list[i]->feature != NULL
|
||||
|
@ -2353,7 +2353,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
|
|||
int i;
|
||||
for (i = 0; i < reg_list_size; i++) {
|
||||
|
||||
if (reg_list[i]->exist == false)
|
||||
if (reg_list[i]->exist == false || reg_list[i]->hidden)
|
||||
continue;
|
||||
|
||||
if (strcmp(reg_list[i]->feature->name, features[current_feature]))
|
||||
|
@ -2600,7 +2600,7 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
|
|||
transfer_type = 'l';
|
||||
|
||||
*chunk = malloc(length + 2 + 3);
|
||||
/* Allocating extra 3 bytes prevents false positive valgrind report
|
||||
/* Allocating extra 3 bytes prevents false positive valgrind report
|
||||
* of strlen(chunk) word access:
|
||||
* Invalid read of size 4
|
||||
* Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */
|
||||
|
@ -3614,8 +3614,8 @@ static int gdb_target_start(struct target *target, const char *port)
|
|||
target->gdb_service = gdb_service;
|
||||
|
||||
ret = add_service("gdb",
|
||||
port, 1, &gdb_new_connection, &gdb_input,
|
||||
&gdb_connection_closed, gdb_service);
|
||||
port, target->gdb_max_connections, &gdb_new_connection, &gdb_input,
|
||||
&gdb_connection_closed, gdb_service, NULL);
|
||||
/* initialize all targets gdb service with the same pointer */
|
||||
{
|
||||
struct target_list *head;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue