Merge branch 'riscv' into riscv-compliance

riscv-compliance-dev
Megan Wachs 2018-04-12 16:03:54 -07:00
commit adf7dd7b5e
220 changed files with 9692 additions and 1951 deletions

View File

@ -30,6 +30,7 @@ matrix:
env: env:
- BUILD=i686-linux-gnu - BUILD=i686-linux-gnu
- CFLAGS=-m32 - CFLAGS=-m32
- CONFIGURE_ARGS="--disable-target64"
- EXECUTABLE=openocd - EXECUTABLE=openocd
compiler: clang compiler: clang
addons: addons:

13
HACKING
View File

@ -1,13 +1,20 @@
// This file is part of the Doxygen Developer Manual // This file is part of the Doxygen Developer Manual
/** @page patchguide Patch Guidelines /** @page patchguide Patch Guidelines
\attention If you're behind a corporate wall with http only access to the
world, you can still use these instructions!
\attention You can't send patches to the mailing list anymore at all. Nowadays \attention You can't send patches to the mailing list anymore at all. Nowadays
you are expected to send patches to the OpenOCD Gerrit GIT server for a you are expected to send patches to the OpenOCD Gerrit GIT server for a
review. review.
\attention If you already have a Gerrit account and want to try a
different sign in method, please first sign in as usually, press your
name in the upper-right corner, go to @a Settings, select @a
Identities pane, press <em>Link Another Identity</em> button. In case
you already have duplicated accounts, ask administrators for manual
merging.
\attention If you're behind a corporate wall with http only access to the
world, you can still use these instructions!
@section gerrit Submitting patches to the OpenOCD Gerrit server @section gerrit Submitting patches to the OpenOCD Gerrit server
OpenOCD is to some extent a "self service" open source project, so to OpenOCD is to some extent a "self service" open source project, so to

View File

@ -114,6 +114,7 @@ m4_define([USB1_ADAPTERS],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]], [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]], [[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
[[usb_blaster_2], [Altera USB-Blaster II Compatible], [USB_BLASTER_2]], [[usb_blaster_2], [Altera USB-Blaster II Compatible], [USB_BLASTER_2]],
[[ft232r], [Bitbang mode of FT232R based devices], [FT232R]],
[[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]]]) [[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]]])
m4_define([USB_ADAPTERS], m4_define([USB_ADAPTERS],

View File

@ -0,0 +1,27 @@
BIN2C = ../../../../src/helper/bin2char.sh
CROSS_COMPILE ?= arm-none-eabi-
CC=$(CROSS_COMPILE)gcc
OBJCOPY=$(CROSS_COMPILE)objcopy
OBJDUMP=$(CROSS_COMPILE)objdump
CFLAGS = -c -mthumb -mcpu=cortex-m0 -O3 -g
all: bluenrg-x_write.inc
.PHONY: clean
.INTERMEDIATE: bluenrg-x_write.o
%.o: %.c
$(CC) $(CFLAGS) -Wall -Wextra -Wa,-adhln=$*.lst $< -o $@
%.bin: %.o
$(OBJCOPY) -Obinary $< $@
%.inc: %.bin
$(BIN2C) < $< > $@
clean:
-rm -f *.o *.lst *.bin *.inc

View File

@ -0,0 +1,132 @@
/* To be built with arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m0 -O3 bluenrgx.c */
/* Then postprocess output of command "arm-none-eabi-objdump -d bluenrgx.o" to make a C array of bytes */
#include <stdint.h>
/* Status Values ----------------------------------------------------------*/
#define SUCCESS 0
#define ERR_UNALIGNED 1
#define ERR_INVALID_ADDRESS 2
#define ERR_INVALID_TYPE 3
#define ERR_WRITE_PROTECTED 4
#define ERR_WRITE_FAILED 5
#define ERR_ERASE_REQUIRED 6
#define ERR_VERIFY_FAILED 7
/* Flash Controller defines ---------------------------------------------------*/
#define FLASH_REG_COMMAND ((volatile uint32_t *)0x40100000)
#define FLASH_REG_CONFIG ((volatile uint32_t *)0x40100004)
#define FLASH_REG_IRQSTAT ((volatile uint32_t *)0x40100008)
#define FLASH_REG_IRQMASK ((volatile uint32_t *)0x4010000C)
#define FLASH_REG_IRQRAW ((volatile uint32_t *)0x40100010)
#define FLASH_REG_ADDRESS ((volatile uint32_t *)0x40100018)
#define FLASH_REG_UNLOCKM ((volatile uint32_t *)0x4010001C)
#define FLASH_REG_UNLOCKL ((volatile uint32_t *)0x40100020)
#define FLASH_REG_DATA0 ((volatile uint32_t *)0x40100040)
#define FLASH_REG_DATA1 ((volatile uint32_t *)0x40100044)
#define FLASH_REG_DATA2 ((volatile uint32_t *)0x40100048)
#define FLASH_REG_DATA3 ((volatile uint32_t *)0x4010004C)
#define FLASH_SIZE_REG 0x40100014
#define MFB_MASS_ERASE 0x01
#define MFB_PAGE_ERASE 0x02
#define DO_ERASE 0x0100
#define DO_VERIFY 0x0200
#define FLASH_CMD_ERASE_PAGE 0x11
#define FLASH_CMD_MASSERASE 0x22
#define FLASH_CMD_WRITE 0x33
#define FLASH_CMD_BURSTWRITE 0xCC
#define FLASH_INT_CMDDONE 0x01
#define MFB_BOTTOM (0x10040000)
#define MFB_SIZE_B ((16 * (((*(uint32_t *) FLASH_SIZE_REG) + 1) >> 12)) * 1024)
#define MFB_SIZE_W (MFB_SIZE_B/4)
#define MFB_TOP (MFB_BOTTOM+MFB_SIZE_B-1)
#define MFB_PAGE_SIZE_B (2048)
#define MFB_PAGE_SIZE_W (MFB_PAGE_SIZE_B/4)
#define AREA_ERROR 0x01
#define AREA_MFB 0x04
#define FLASH_WORD_LEN 4
typedef struct {
volatile uint8_t *wp;
uint8_t *rp;
} work_area_t;
/* Flash Commands --------------------------------------------------------*/
static inline __attribute__((always_inline)) uint32_t flashWrite(uint32_t address, uint8_t **data,
uint32_t writeLength)
{
uint32_t index, flash_word[4];
uint8_t i;
*FLASH_REG_IRQMASK = 0;
for (index = 0; index < writeLength; index += (FLASH_WORD_LEN*4)) {
for (i = 0; i < 4; i++)
flash_word[i] = (*(uint32_t *) (*data + i*4));
/* Clear the IRQ flags */
*FLASH_REG_IRQRAW = 0x0000003F;
/* Load the flash address to write */
*FLASH_REG_ADDRESS = (uint16_t)((address + index) >> 2);
/* Prepare and load the data to flash */
*FLASH_REG_DATA0 = flash_word[0];
*FLASH_REG_DATA1 = flash_word[1];
*FLASH_REG_DATA2 = flash_word[2];
*FLASH_REG_DATA3 = flash_word[3];
/* Flash write command */
*FLASH_REG_COMMAND = FLASH_CMD_BURSTWRITE;
/* Wait the end of the flash write command */
while ((*FLASH_REG_IRQRAW & FLASH_INT_CMDDONE) == 0)
;
*data += (FLASH_WORD_LEN * 4);
}
return SUCCESS;
}
__attribute__((naked)) __attribute__((noreturn)) void write(uint8_t *work_area_p,
uint8_t *fifo_end,
uint8_t *target_address,
uint32_t count)
{
uint32_t retval;
volatile work_area_t *work_area = (work_area_t *) work_area_p;
uint8_t *fifo_start = (uint8_t *) work_area->rp;
while (count) {
volatile int32_t fifo_linear_size;
/* Wait for some data in the FIFO */
while (work_area->rp == work_area->wp)
;
if (work_area->wp == 0) {
/* Aborted by other party */
break;
}
if (work_area->rp > work_area->wp) {
fifo_linear_size = fifo_end-work_area->rp;
} else {
fifo_linear_size = (work_area->wp - work_area->rp);
if (fifo_linear_size < 0)
fifo_linear_size = 0;
}
if (fifo_linear_size < 16) {
/* We should never get here */
continue;
}
retval = flashWrite((uint32_t) target_address, (uint8_t **) &work_area->rp, fifo_linear_size);
if (retval != SUCCESS) {
work_area->rp = (uint8_t *)retval;
break;
}
target_address += fifo_linear_size;
if (work_area->rp >= fifo_end)
work_area->rp = fifo_start;
count -= fifo_linear_size;
}
__asm("bkpt 0");
}

View File

@ -0,0 +1,18 @@
/* Autogenerated with ../../../../src/helper/bin2char.sh */
0x05,0x93,0x43,0x68,0x05,0x00,0x07,0x93,0x05,0x9b,0x06,0x91,0x03,0x92,0x35,0x4c,
0x00,0x2b,0x5c,0xd0,0x6a,0x68,0x2b,0x68,0x9a,0x42,0xfb,0xd0,0x2b,0x68,0x00,0x2b,
0x55,0xd0,0x6a,0x68,0x2b,0x68,0x9a,0x42,0x52,0xd9,0x6b,0x68,0x06,0x9a,0xd3,0x1a,
0x09,0x93,0x09,0x9b,0x0f,0x2b,0xed,0xdd,0x00,0x21,0x09,0x9b,0x04,0x93,0x1a,0x1e,
0x29,0x4b,0x19,0x60,0x32,0xd0,0x29,0x4b,0x00,0x20,0x98,0x46,0x28,0x4b,0x6a,0x68,
0x9c,0x46,0x28,0x4b,0x28,0x4e,0x9b,0x46,0x28,0x4b,0x9a,0x46,0x28,0x4b,0x99,0x46,
0x01,0x23,0x51,0x68,0x17,0x68,0x00,0x91,0x91,0x68,0x01,0x91,0xd1,0x68,0x02,0x91,
0x3f,0x21,0x21,0x60,0x03,0x99,0x09,0x18,0x89,0x03,0x09,0x0c,0x31,0x60,0x41,0x46,
0x0f,0x60,0x67,0x46,0x00,0x99,0x39,0x60,0x5f,0x46,0x01,0x99,0x39,0x60,0x57,0x46,
0x02,0x99,0x39,0x60,0x49,0x46,0xcc,0x27,0x0f,0x60,0x21,0x68,0x0b,0x42,0xfc,0xd0,
0x04,0x99,0x10,0x32,0x10,0x30,0x6a,0x60,0x81,0x42,0xda,0xd8,0x03,0x9a,0x09,0x9b,
0x94,0x46,0x9c,0x44,0x63,0x46,0x06,0x9a,0x03,0x93,0x6b,0x68,0x9a,0x42,0x01,0xd8,
0x07,0x9b,0x6b,0x60,0x05,0x9a,0x09,0x9b,0xd3,0x1a,0x05,0x93,0xa2,0xd1,0x00,0xbe,
0x2b,0x68,0x6a,0x68,0x9b,0x1a,0x09,0x93,0x09,0x9b,0x00,0x2b,0xa9,0xda,0x00,0x23,
0x09,0x93,0xa6,0xe7,0x10,0x00,0x10,0x40,0x0c,0x00,0x10,0x40,0x40,0x00,0x10,0x40,
0x44,0x00,0x10,0x40,0x48,0x00,0x10,0x40,0x18,0x00,0x10,0x40,0x4c,0x00,0x10,0x40,
0x00,0x00,0x10,0x40,

View File

@ -1595,8 +1595,11 @@ proc enable_fast_clock @{@} @{
proc init_board @{@} @{ proc init_board @{@} @{
reset_config trst_and_srst trst_pulls_srst reset_config trst_and_srst trst_pulls_srst
$_TARGETNAME configure -event reset-start @{
adapter_khz 100
@}
$_TARGETNAME configure -event reset-init @{ $_TARGETNAME configure -event reset-init @{
adapter_khz 1
enable_fast_clock enable_fast_clock
adapter_khz 10000 adapter_khz 10000
@} @}
@ -2197,6 +2200,13 @@ The default behaviour is @option{disable};
use @option{enable} see these errors reported. use @option{enable} see these errors reported.
@end deffn @end deffn
@deffn {Config Command} gdb_report_register_access_error (@option{enable}|@option{disable})
Specifies whether register accesses requested by GDB register read/write
packets report errors or not.
The default behaviour is @option{disable};
use @option{enable} see these errors reported.
@end deffn
@deffn {Config Command} gdb_target_description (@option{enable}|@option{disable}) @deffn {Config Command} gdb_target_description (@option{enable}|@option{disable})
Set to @option{enable} to cause OpenOCD to send the target descriptions to gdb via qXfer:features:read packet. Set to @option{enable} to cause OpenOCD to send the target descriptions to gdb via qXfer:features:read packet.
The default behaviour is @option{enable}. The default behaviour is @option{enable}.
@ -2556,6 +2566,36 @@ For example adapter definitions, see the configuration files shipped in the
@end deffn @end deffn
@deffn {Interface Driver} {ft232r}
This driver is implementing synchronous bitbang mode of an FTDI FT232R
USB UART bridge IC.
List of connections (pin numbers for SSOP):
@itemize @minus
@item RXD(5) - TDI
@item TXD(1) - TCK
@item RTS(3) - TDO
@item CTS(11) - TMS
@item DTR(2) - TRST
@item DCD(10) - SRST
@end itemize
These interfaces have several commands, used to configure the driver
before initializing the JTAG scan chain:
@deffn {Config Command} {ft232r_vid_pid} @var{vid} @var{pid}
The vendor ID and product ID of the adapter. If not specified, default
0x0403:0x6001 is used.
@end deffn
@deffn {Config Command} {ft232r_serial_desc} @var{serial}
Specifies the @var{serial} of the adapter to use, in case the
vendor provides unique IDs and more than one adapter is connected to
the host. If not specified, serial numbers are not considered.
@end deffn
@end deffn
@deffn {Interface Driver} {remote_bitbang} @deffn {Interface Driver} {remote_bitbang}
Drive JTAG from a remote process. This sets up a UNIX or TCP socket connection Drive JTAG from a remote process. This sets up a UNIX or TCP socket connection
with a remote process and sends ASCII encoded bitbang requests to that process with a remote process and sends ASCII encoded bitbang requests to that process
@ -3969,6 +4009,84 @@ with these TAPs, any targets associated with them, and any on-chip
resources; then a @file{board.cfg} with off-chip resources, clocking, resources; then a @file{board.cfg} with off-chip resources, clocking,
and so forth. and so forth.
@anchor{dapdeclaration}
@section DAP declaration (ARMv7 and ARMv8 targets)
@cindex DAP declaration
Since OpenOCD version 0.11.0, the Debug Access Port (DAP) is
no longer implicitly created together with the target. It must be
explicitly declared using the @command{dap create} command. For all
ARMv7 and ARMv8 targets, the option "@option{-dap} @var{dap_name}" has to be used
instead of "@option{-chain-position} @var{dotted.name}" when the target is created.
The @command{dap} command group supports the following sub-commands:
@deffn Command {dap create} dap_name @option{-chain-position} dotted.name
Declare a DAP instance named @var{dap_name} linked to the JTAG tap
@var{dotted.name}. This also creates a new command (@command{dap_name})
which is used for various purposes including additional configuration.
There can only be one DAP for each JTAG tap in the system.
@end deffn
@deffn Command {dap names}
This command returns a list of all registered DAP objects. It it useful mainly
for TCL scripting.
@end deffn
@deffn Command {dap info} [num]
Displays the ROM table for MEM-AP @var{num},
defaulting to the currently selected AP of the currently selected target.
@end deffn
@deffn Command {dap init}
Initialize all registered DAPs. This command is used internally
during initialization. It can be issued at any time after the
initialization, too.
@end deffn
The following commands exist as subcommands of DAP instances:
@deffn Command {$dap_name info} [num]
Displays the ROM table for MEM-AP @var{num},
defaulting to the currently selected AP.
@end deffn
@deffn Command {$dap_name apid} [num]
Displays ID register from AP @var{num}, defaulting to the currently selected AP.
@end deffn
@deffn Command {$dap_name apreg} ap_num reg [value]
Displays content of a register @var{reg} from AP @var{ap_num}
or set a new value @var{value}.
@var{reg} is byte address of a word register, 0, 4, 8 ... 0xfc.
@end deffn
@deffn Command {$dap_name apsel} [num]
Select AP @var{num}, defaulting to 0.
@end deffn
@deffn Command {$dap_name baseaddr} [num]
Displays debug base address from MEM-AP @var{num},
defaulting to the currently selected AP.
@end deffn
@deffn Command {$dap_name memaccess} [value]
Displays the number of extra tck cycles in the JTAG idle to use for MEM-AP
memory bus access [0-255], giving additional time to respond to reads.
If @var{value} is defined, first assigns that.
@end deffn
@deffn Command {$dap_name apcsw} [0 / 1]
fix CSW_SPROT from register AP_REG_CSW on selected dap.
Defaulting to 0.
@end deffn
@deffn Command {$dap_name ti_be_32_quirks} [@option{enable}]
Set/get quirks mode for TI TMS450/TMS570 processors
Disabled by default
@end deffn
@node CPU Configuration @node CPU Configuration
@chapter CPU Configuration @chapter CPU Configuration
@cindex GDB target @cindex GDB target
@ -4135,10 +4253,11 @@ to be much more board-specific.
The key steps you use might look something like this The key steps you use might look something like this
@example @example
target create MyTarget cortex_m -chain-position mychip.cpu dap create mychip.dap -chain-position mychip.cpu
$MyTarget configure -work-area-phys 0x08000 -work-area-size 8096 target create MyTarget cortex_m -dap mychip.dap
$MyTarget configure -event reset-deassert-pre @{ jtag_rclk 5 @} MyTarget configure -work-area-phys 0x08000 -work-area-size 8096
$MyTarget configure -event reset-init @{ myboard_reinit @} MyTarget configure -event reset-deassert-pre @{ jtag_rclk 5 @}
MyTarget configure -event reset-init @{ myboard_reinit @}
@end example @end example
You should specify a working area if you can; typically it uses some You should specify a working area if you can; typically it uses some
@ -4188,7 +4307,8 @@ and in other places the target needs to be identified.
@command{$target_name configure} are permitted. @command{$target_name configure} are permitted.
If the target is big-endian, set it here with @code{-endian big}. If the target is big-endian, set it here with @code{-endian big}.
You @emph{must} set the @code{-chain-position @var{dotted.name}} here. You @emph{must} set the @code{-chain-position @var{dotted.name}} or
@code{-dap @var{dap_name}} here.
@end itemize @end itemize
@end deffn @end deffn
@ -4207,6 +4327,10 @@ and changing its endianness.
@item @code{-chain-position} @var{dotted.name} -- names the TAP @item @code{-chain-position} @var{dotted.name} -- names the TAP
used to access this target. used to access this target.
@item @code{-dap} @var{dap_name} -- names the DAP used to access
this target. @xref{dapdeclaration,,DAP declaration}, on how to
create and manage DAP instances.
@item @code{-endian} (@option{big}|@option{little}) -- specifies @item @code{-endian} (@option{big}|@option{little}) -- specifies
whether the CPU uses big or little endian conventions whether the CPU uses big or little endian conventions
@ -4217,6 +4341,9 @@ Calling this twice with two different event names assigns
two different handlers, but calling it twice with the two different handlers, but calling it twice with the
same event name assigns only one handler. same event name assigns only one handler.
Current target is temporarily overridden to the event issuing target
before handler code starts and switched back after handler is done.
@item @code{-work-area-backup} (@option{0}|@option{1}) -- says @item @code{-work-area-backup} (@option{0}|@option{1}) -- says
whether the work area gets backed up; by default, whether the work area gets backed up; by default,
@emph{it is not backed up.} @emph{it is not backed up.}
@ -4254,9 +4381,11 @@ access the target for debugging.
Use this option with systems where multiple, independent cores are connected Use this option with systems where multiple, independent cores are connected
to separate access ports of the same DAP. to separate access ports of the same DAP.
@item @code{-ctibase} @var{address} -- set base address of Cross-Trigger interface (CTI) connected @item @code{-cti} @var{cti_name} -- set Cross-Trigger Interface (CTI) connected
to the target. Currently, only the @code{aarch64} target makes use of this option, where it is to the target. Currently, only the @code{aarch64} target makes use of this option,
a mandatory configuration for the target run control. where it is a mandatory configuration for the target run control.
@xref{armcrosstrigger,,ARM Cross-Trigger Interface},
for instruction on how to declare and control a CTI instance.
@end itemize @end itemize
@end deffn @end deffn
@ -4427,16 +4556,14 @@ buttons and events. The two examples below act the same, but one creates
and invokes a small procedure while the other inlines it. and invokes a small procedure while the other inlines it.
@example @example
proc my_attach_proc @{ @} @{ proc my_init_proc @{ @} @{
echo "Reset..." echo "Disabling watchdog..."
reset halt mww 0xfffffd44 0x00008000
@} @}
mychip.cpu configure -event gdb-attach my_attach_proc mychip.cpu configure -event reset-init my_init_proc
mychip.cpu configure -event gdb-attach @{ mychip.cpu configure -event reset-init @{
echo "Reset..." echo "Disabling watchdog..."
# To make flash probe and gdb load to flash work mww 0xfffffd44 0x00008000
# we need a reset init.
reset init
@} @}
@end example @end example
@ -4446,7 +4573,7 @@ The following target events are defined:
@item @b{debug-halted} @item @b{debug-halted}
@* The target has halted for debug reasons (i.e.: breakpoint) @* The target has halted for debug reasons (i.e.: breakpoint)
@item @b{debug-resumed} @item @b{debug-resumed}
@* The target has resumed (i.e.: gdb said run) @* The target has resumed (i.e.: GDB said run)
@item @b{early-halted} @item @b{early-halted}
@* Occurs early in the halt process @* Occurs early in the halt process
@item @b{examine-start} @item @b{examine-start}
@ -4454,11 +4581,17 @@ The following target events are defined:
@item @b{examine-end} @item @b{examine-end}
@* After target examine is called with no errors. @* After target examine is called with no errors.
@item @b{gdb-attach} @item @b{gdb-attach}
@* When GDB connects. This is before any communication with the target, so this @* When GDB connects. Issued before any GDB communication with the target
can be used to set up the target so it is possible to probe flash. Probing flash starts. GDB expects the target is halted during attachment.
is necessary during gdb connect if gdb load is to write the image to flash. Another @xref{gdbmeminspect,,GDB as a non-intrusive memory inspector}, how to
use of the flash memory map is for GDB to automatically hardware/software breakpoints connect GDB to running target.
depending on whether the breakpoint is in RAM or read only memory. The event can be also used to set up the target so it is possible to probe flash.
Probing flash is necessary during GDB connect if you want to use
@pxref{programmingusinggdb,,programming using GDB}.
Another use of the flash memory map is for GDB to automatically choose
hardware or software breakpoints depending on whether the breakpoint
is in RAM or read only memory.
Default is @code{halt}
@item @b{gdb-detach} @item @b{gdb-detach}
@* When GDB disconnects @* When GDB disconnects
@item @b{gdb-end} @item @b{gdb-end}
@ -4473,13 +4606,13 @@ depending on whether the breakpoint is in RAM or read only memory.
@item @b{gdb-flash-write-end} @item @b{gdb-flash-write-end}
@* After GDB writes to the flash (default is @code{reset halt}) @* After GDB writes to the flash (default is @code{reset halt})
@item @b{gdb-start} @item @b{gdb-start}
@* Before the target steps, gdb is trying to start/resume the target @* Before the target steps, GDB is trying to start/resume the target
@item @b{halted} @item @b{halted}
@* The target has halted @* The target has halted
@item @b{reset-assert-pre} @item @b{reset-assert-pre}
@* Issued as part of @command{reset} processing @* Issued as part of @command{reset} processing
after @command{reset_init} was triggered after @command{reset-start} was triggered
but before either SRST alone is re-asserted on the scan chain, but before either SRST alone is asserted on the scan chain,
or @code{reset-assert} is triggered. or @code{reset-assert} is triggered.
@item @b{reset-assert} @item @b{reset-assert}
@* Issued as part of @command{reset} processing @* Issued as part of @command{reset} processing
@ -4513,8 +4646,8 @@ multiplexing, and so on.
(You may be able to switch to a fast JTAG clock rate here, after (You may be able to switch to a fast JTAG clock rate here, after
the target clocks are fully set up.) the target clocks are fully set up.)
@item @b{reset-start} @item @b{reset-start}
@* Issued as part of @command{reset} processing @* Issued as the first step in @command{reset} processing
before @command{reset_init} is called. before @command{reset-assert-pre} is called.
This is the most robust place to use @command{jtag_rclk} This is the most robust place to use @command{jtag_rclk}
or @command{adapter_khz} to switch to a low JTAG clock rate, or @command{adapter_khz} to switch to a low JTAG clock rate,
@ -5193,6 +5326,26 @@ and prepares reset vector catch in case of reset halt.
Command is used internally in event event reset-deassert-post. Command is used internally in event event reset-deassert-post.
@end deffn @end deffn
@deffn Command {at91samd nvmuserrow}
Writes or reads the entire 64 bit wide NVM user row register which is located at
0x804000. This register includes various fuses lock-bits and factory calibration
data. Reading the register is done by invoking this command without any
arguments. Writing is possible by giving 1 or 2 hex values. The first argument
is the register value to be written and the second one is an optional changemask.
Every bit which value in changemask is 0 will stay unchanged. The lock- and
reserved-bits are masked out and cannot be changed.
@example
# Read user row
>at91samd nvmuserrow
NVMUSERROW: 0xFFFFFC5DD8E0C788
# Write 0xFFFFFC5DD8E0C788 to user row
>at91samd nvmuserrow 0xFFFFFC5DD8E0C788
# Write 0x12300 to user row but leave other bits and low byte unchanged
>at91samd nvmuserrow 0x12345 0xFFF00
@end example
@end deffn
@end deffn @end deffn
@anchor{at91sam3} @anchor{at91sam3}
@ -5341,6 +5494,30 @@ The AVR 8-bit microcontrollers from Atmel integrate flash memory.
@comment - defines mass_erase ... pointless given flash_erase_address @comment - defines mass_erase ... pointless given flash_erase_address
@end deffn @end deffn
@deffn {Flash Driver} bluenrg-x
STMicroelectronics BlueNRG-1 and BlueNRG-2 Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0 core and internal flash memory.
The driver automatically recognizes these chips using
the chip identification registers, and autoconfigures itself.
@example
flash bank $_FLASHNAME bluenrg-x 0 0 0 0 $_TARGETNAME
@end example
Note that when users ask to erase all the sectors of the flash, a mass erase command is used which is faster than erasing
each single sector one by one.
@example
flash erase_sector 0 0 79 # It will perform a mass erase on BlueNRG-1
@end example
@example
flash erase_sector 0 0 127 # It will perform a mass erase on BlueNRG-2
@end example
Triggering a mass erase is also useful when users want to disable readout protection.
@end deffn
@deffn {Flash Driver} efm32 @deffn {Flash Driver} efm32
All members of the EFM32 microcontroller family from Energy Micro include All members of the EFM32 microcontroller family from Energy Micro include
internal flash and use ARM Cortex-M3 cores. The driver automatically recognizes internal flash and use ARM Cortex-M3 cores. The driver automatically recognizes
@ -5891,6 +6068,62 @@ The @var{num} parameter is a value shown by @command{flash banks}.
@end deffn @end deffn
@end deffn @end deffn
@deffn {Flash Driver} psoc6
Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers.
PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
the same Flash/RAM/MMIO address space.
Flash in PSoC6 is split into three regions:
@itemize @bullet
@item Main Flash - this is the main storage for user application.
Total size varies among devices, sector size: 256 kBytes, row size:
512 bytes. Supports erase operation on individual rows.
@item Work Flash - intended to be used as storage for user data
(e.g. EEPROM emulation). Total size: 32 KBytes, sector size: 32 KBytes,
row size: 512 bytes.
@item Supervisory Flash - special region which contains device-specific
service data. This region does not support erase operation. Only few rows can
be programmed by the user, most of the rows are read only. Programming
operation will erase row automatically.
@end itemize
All three flash regions are supported by the driver. Flash geometry is detected
automatically by parsing data in SPCIF_GEOMETRY register.
PSoC6 is equipped with NOR Flash so erased Flash reads as 0x00.
@example
flash bank main_flash_cm0 psoc6 0x10000000 0 0 0 $@{TARGET@}.cm0
flash bank work_flash_cm0 psoc6 0x14000000 0 0 0 $@{TARGET@}.cm0
flash bank super_flash_user_cm0 psoc6 0x16000800 0 0 0 $@{TARGET@}.cm0
flash bank super_flash_nar_cm0 psoc6 0x16001A00 0 0 0 $@{TARGET@}.cm0
flash bank super_flash_key_cm0 psoc6 0x16005A00 0 0 0 $@{TARGET@}.cm0
flash bank super_flash_toc2_cm0 psoc6 0x16007C00 0 0 0 $@{TARGET@}.cm0
flash bank main_flash_cm4 psoc6 0x10000000 0 0 0 $@{TARGET@}.cm4
flash bank work_flash_cm4 psoc6 0x14000000 0 0 0 $@{TARGET@}.cm4
flash bank super_flash_user_cm4 psoc6 0x16000800 0 0 0 $@{TARGET@}.cm4
flash bank super_flash_nar_cm4 psoc6 0x16001A00 0 0 0 $@{TARGET@}.cm4
flash bank super_flash_key_cm4 psoc6 0x16005A00 0 0 0 $@{TARGET@}.cm4
flash bank super_flash_toc2_cm4 psoc6 0x16007C00 0 0 0 $@{TARGET@}.cm4
@end example
psoc6-specific commands
@deffn Command {psoc6 reset_halt}
Command can be used to simulate broken Vector Catch from gdbinit or tcl scripts.
When invoked for CM0+ target, it will set break point at application entry point
and issue SYSRESETREQ. This will reset both cores and all peripherals. CM0+ will
reset CM4 during boot anyway so this is safe. On CM4 target, VECTRESET is used
instead of SYSRESETREQ to avoid unwanted reset of CM0+;
@end deffn
@deffn Command {psoc6 mass_erase} num
Erases the contents given flash bank. The @var{num} parameter is a value shown
by @command{flash banks}.
Note: only Main and Work flash regions support Erase operation.
@end deffn
@end deffn
@deffn {Flash Driver} sim3x @deffn {Flash Driver} sim3x
All members of the SiM3 microcontroller family from Silicon Laboratories All members of the SiM3 microcontroller family from Silicon Laboratories
include internal flash and use ARM Cortex-M3 cores. It supports both JTAG include internal flash and use ARM Cortex-M3 cores. It supports both JTAG
@ -7012,9 +7245,11 @@ the initial log output channel is stderr.
Add @var{directory} to the file/script search path. Add @var{directory} to the file/script search path.
@end deffn @end deffn
@deffn Command bindto [name] @deffn Command bindto [@var{name}]
Specify address by name on which to listen for incoming TCP/IP connections. Specify hostname or IPv4 address on which to listen for incoming
By default, OpenOCD will listen on all available interfaces. TCP/IP connections. By default, OpenOCD will listen on the loopback
interface only. If your network environment is safe, @code{bindto
0.0.0.0} can be used to cover all available interfaces.
@end deffn @end deffn
@anchor{targetstatehandling} @anchor{targetstatehandling}
@ -7659,6 +7894,50 @@ Reports whether the capture clock is locked or not.
@end deffn @end deffn
@end deffn @end deffn
@anchor{armcrosstrigger}
@section ARM Cross-Trigger Interface
@cindex CTI
The ARM Cross-Trigger Interface (CTI) is a generic CoreSight component
that connects event sources like tracing components or CPU cores with each
other through a common trigger matrix (CTM). For ARMv8 architecture, a
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
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
new command @command{$cti_name} which is used for various purposes
including additional configuration.
@end deffn
@deffn Command {$cti_name enable} @option{on|off}
Enable (@option{on}) or disable (@option{off}) the CTI.
@end deffn
@deffn Command {$cti_name dump}
Displays a register dump of the CTI.
@end deffn
@deffn Command {$cti_name write } @var{reg_name} @var{value}
Write @var{value} to the CTI register with the symbolic name @var{reg_name}.
@end deffn
@deffn Command {$cti_name read} @var{reg_name}
Print the value read from the CTI register with the symbolic name @var{reg_name}.
@end deffn
@deffn Command {$cti_name testmode} @option{on|off}
Enable (@option{on}) or disable (@option{off}) the integration test mode
of the CTI.
@end deffn
@deffn Command {cti names}
Prints a list of names of all CTI objects created. This command is mainly
useful in TCL scripting.
@end deffn
@section Generic ARM @section Generic ARM
@cindex ARM @cindex ARM
@ -8138,55 +8417,6 @@ cores @emph{except the ARM1176} use the same six bits.
@cindex ARMv7 @cindex ARMv7
@cindex ARMv8 @cindex ARMv8
@subsection ARMv7 and ARMv8 Debug Access Port (DAP) specific commands
@cindex Debug Access Port
@cindex DAP
These commands are specific to ARM architecture v7 and v8 Debug Access Port (DAP),
included on Cortex-M and Cortex-A systems.
They are available in addition to other core-specific commands that may be available.
@deffn Command {dap apid} [num]
Displays ID register from AP @var{num},
defaulting to the currently selected AP.
@end deffn
@deffn Command {dap apreg} ap_num reg [value]
Displays content of a register @var{reg} from AP @var{ap_num}
or set a new value @var{value}.
@var{reg} is byte address of a word register, 0, 4, 8 ... 0xfc.
@end deffn
@deffn Command {dap apsel} [num]
Select AP @var{num}, defaulting to 0.
@end deffn
@deffn Command {dap baseaddr} [num]
Displays debug base address from MEM-AP @var{num},
defaulting to the currently selected AP.
@end deffn
@deffn Command {dap info} [num]
Displays the ROM table for MEM-AP @var{num},
defaulting to the currently selected AP.
@end deffn
@deffn Command {dap memaccess} [value]
Displays the number of extra tck cycles in the JTAG idle to use for MEM-AP
memory bus access [0-255], giving additional time to respond to reads.
If @var{value} is defined, first assigns that.
@end deffn
@deffn Command {dap apcsw} [0 / 1]
fix CSW_SPROT from register AP_REG_CSW on selected dap.
Defaulting to 0.
@end deffn
@deffn Command {dap ti_be_32_quirks} [@option{enable}]
Set/get quirks mode for TI TMS450/TMS570 processors
Disabled by default
@end deffn
@subsection ARMv7-A specific commands @subsection ARMv7-A specific commands
@cindex Cortex-A @cindex Cortex-A
@ -9095,19 +9325,6 @@ With that particular hardware (Cortex-M3) the hardware breakpoints
only work for code running from flash memory. Most other ARM systems only work for code running from flash memory. Most other ARM systems
do not have such restrictions. do not have such restrictions.
Another example of useful GDB configuration came from a user who
found that single stepping his Cortex-M3 didn't work well with IRQs
and an RTOS until he told GDB to disable the IRQs while stepping:
@example
define hook-step
mon cortex_m maskisr on
end
define hookpost-step
mon cortex_m maskisr off
end
@end example
Rather than typing such commands interactively, you may prefer to Rather than typing such commands interactively, you may prefer to
save them in a file and have GDB execute them as it starts, perhaps save them in a file and have GDB execute them as it starts, perhaps
using a @file{.gdbinit} in your project directory or starting GDB using a @file{.gdbinit} in your project directory or starting GDB
@ -9147,14 +9364,60 @@ GDB will look at the target memory map when a load command is given, if any
areas to be programmed lie within the target flash area the vFlash packets areas to be programmed lie within the target flash area the vFlash packets
will be used. will be used.
If the target needs configuring before GDB programming, an event If the target needs configuring before GDB programming, set target
script can be executed: event gdb-flash-erase-start:
@example @example
$_TARGETNAME configure -event EVENTNAME BODY $_TARGETNAME configure -event gdb-flash-erase-start BODY
@end example @end example
@xref{targetevents,,Target Events}, for other GDB programming related events.
To verify any flash programming the GDB command @option{compare-sections} To verify any flash programming the GDB command @option{compare-sections}
can be used. can be used.
@section Using GDB as a non-intrusive memory inspector
@cindex Using GDB as a non-intrusive memory inspector
@anchor{gdbmeminspect}
If your project controls more than a blinking LED, let's say a heavy industrial
robot or an experimental nuclear reactor, stopping the controlling process
just because you want to attach GDB is not a good option.
OpenOCD does not support GDB non-stop mode (might be implemented in the future).
Though there is a possible setup where the target does not get stopped
and GDB treats it as it were running.
If the target supports background access to memory while it is running,
you can use GDB in this mode to inspect memory (mainly global variables)
without any intrusion of the target process.
Remove default setting of gdb-attach event. @xref{targetevents,,Target Events}.
Place following command after target configuration:
@example
$_TARGETNAME configure -event gdb-attach @{@}
@end example
If any of installed flash banks does not support probe on running target,
switch off gdb_memory_map:
@example
gdb_memory_map disable
@end example
Ensure GDB is configured without interrupt-on-connect.
Some GDB versions set it by default, some does not.
@example
set remote interrupt-on-connect off
@end example
If you switched gdb_memory_map off, you may want to setup GDB memory map
manually or issue @command{set mem inaccessible-by-default off}
Now you can issue GDB command @command{target remote ...} and inspect memory
of a running target. Do not use GDB commands @command{continue},
@command{step} or @command{next} as they synchronize GDB with your target
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.
@anchor{usingopenocdsmpwithgdb} @anchor{usingopenocdsmpwithgdb}
@section Using OpenOCD SMP with GDB @section Using OpenOCD SMP with GDB
@cindex SMP @cindex SMP

View File

@ -18,6 +18,7 @@ NOR_DRIVERS = \
%D%/ath79.c \ %D%/ath79.c \
%D%/atsamv.c \ %D%/atsamv.c \
%D%/avrf.c \ %D%/avrf.c \
%D%/bluenrg-x.c \
%D%/cfi.c \ %D%/cfi.c \
%D%/dsp5680xx_flash.c \ %D%/dsp5680xx_flash.c \
%D%/efm32.c \ %D%/efm32.c \
@ -42,6 +43,7 @@ NOR_DRIVERS = \
%D%/ocl.c \ %D%/ocl.c \
%D%/pic32mx.c \ %D%/pic32mx.c \
%D%/psoc4.c \ %D%/psoc4.c \
%D%/psoc6.c \
%D%/sim3x.c \ %D%/sim3x.c \
%D%/spi.c \ %D%/spi.c \
%D%/stmsmi.c \ %D%/stmsmi.c \

View File

@ -901,4 +901,5 @@ struct flash_driver ambiqmicro_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = ambiqmicro_protect_check, .protect_check = ambiqmicro_protect_check,
.info = get_ambiqmicro_info, .info = get_ambiqmicro_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -83,6 +83,9 @@
#define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F)) #define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F))
#define SAMD_GET_DEVSEL(id) (id & 0xFF) #define SAMD_GET_DEVSEL(id) (id & 0xFF)
/* Bits to mask out lockbits in user row */
#define NVMUSERROW_LOCKBIT_MASK ((uint64_t)0x0000FFFFFFFFFFFF)
struct samd_part { struct samd_part {
uint8_t id; uint8_t id;
const char *name; const char *name;
@ -159,23 +162,22 @@ static const struct samd_part samd21_parts[] = {
{ 0xC, "SAMD21E16A", 64, 8 }, { 0xC, "SAMD21E16A", 64, 8 },
{ 0xD, "SAMD21E15A", 32, 4 }, { 0xD, "SAMD21E15A", 32, 4 },
{ 0xE, "SAMD21E14A", 16, 2 }, { 0xE, "SAMD21E14A", 16, 2 },
/* Below are B Variants (Table 3-7 from rev I of datasheet) */
{ 0x20, "SAMD21J16B", 64, 8 },
{ 0x21, "SAMD21J15B", 32, 4 },
{ 0x23, "SAMD21G16B", 64, 8 },
{ 0x24, "SAMD21G15B", 32, 4 },
{ 0x26, "SAMD21E16B", 64, 8 },
{ 0x27, "SAMD21E15B", 32, 4 },
};
/* Known SAMR21 parts. */ /* SAMR21 parts have integrated SAMD21 with a radio */
static const struct samd_part samr21_parts[] = {
{ 0x19, "SAMR21G18A", 256, 32 }, { 0x19, "SAMR21G18A", 256, 32 },
{ 0x1A, "SAMR21G17A", 128, 32 }, { 0x1A, "SAMR21G17A", 128, 32 },
{ 0x1B, "SAMR21G16A", 64, 32 }, { 0x1B, "SAMR21G16A", 64, 32 },
{ 0x1C, "SAMR21E18A", 256, 32 }, { 0x1C, "SAMR21E18A", 256, 32 },
{ 0x1D, "SAMR21E17A", 128, 32 }, { 0x1D, "SAMR21E17A", 128, 32 },
{ 0x1E, "SAMR21E16A", 64, 32 }, { 0x1E, "SAMR21E16A", 64, 32 },
/* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */
{ 0x20, "SAMD21J16B", 64, 8 },
{ 0x21, "SAMD21J15B", 32, 4 },
{ 0x23, "SAMD21G16B", 64, 8 },
{ 0x24, "SAMD21G15B", 32, 4 },
{ 0x26, "SAMD21E16B", 64, 8 },
{ 0x27, "SAMD21E15B", 32, 4 },
}; };
/* Known SAML21 parts. */ /* Known SAML21 parts. */
@ -200,6 +202,10 @@ static const struct samd_part saml21_parts[] = {
{ 0x1A, "SAML21E17B", 128, 16 }, { 0x1A, "SAML21E17B", 128, 16 },
{ 0x1B, "SAML21E16B", 64, 8 }, { 0x1B, "SAML21E16B", 64, 8 },
{ 0x1C, "SAML21E15B", 32, 4 }, { 0x1C, "SAML21E15B", 32, 4 },
/* SAMR30 parts have integrated SAML21 with a radio */
{ 0x1E, "SAMR30G18A", 256, 32 },
{ 0x1F, "SAMR30E18A", 256, 32 },
}; };
/* Known SAML22 parts. */ /* Known SAML22 parts. */
@ -256,30 +262,38 @@ struct samd_family {
uint8_t series; uint8_t series;
const struct samd_part *parts; const struct samd_part *parts;
size_t num_parts; size_t num_parts;
uint64_t nvm_userrow_res_mask; /* protect bits which are reserved, 0 -> protect */
}; };
/* Known SAMD families */ /* Known SAMD families */
static const struct samd_family samd_families[] = { static const struct samd_family samd_families[] = {
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20,
samd20_parts, ARRAY_SIZE(samd20_parts) }, samd20_parts, ARRAY_SIZE(samd20_parts),
(uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
samd21_parts, ARRAY_SIZE(samd21_parts) }, samd21_parts, ARRAY_SIZE(samd21_parts),
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, (uint64_t)0xFFFF01FFFE01FF77 },
samr21_parts, ARRAY_SIZE(samr21_parts) },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09,
samd09_parts, ARRAY_SIZE(samd09_parts) }, samd09_parts, ARRAY_SIZE(samd09_parts),
(uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10,
samd10_parts, ARRAY_SIZE(samd10_parts) }, samd10_parts, ARRAY_SIZE(samd10_parts),
(uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11,
samd11_parts, ARRAY_SIZE(samd11_parts) }, samd11_parts, ARRAY_SIZE(samd11_parts),
(uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21, { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21,
saml21_parts, ARRAY_SIZE(saml21_parts) }, saml21_parts, ARRAY_SIZE(saml21_parts),
(uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22, { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22,
saml22_parts, ARRAY_SIZE(saml22_parts) }, saml22_parts, ARRAY_SIZE(saml22_parts),
(uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20,
samc20_parts, ARRAY_SIZE(samc20_parts) }, samc20_parts, ARRAY_SIZE(samc20_parts),
(uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21,
samc21_parts, ARRAY_SIZE(samc21_parts) }, samc21_parts, ARRAY_SIZE(samc21_parts),
(uint64_t)0xFFFF03FFFC01FF77 },
}; };
struct samd_info { struct samd_info {
@ -295,24 +309,42 @@ struct samd_info {
static struct samd_info *samd_chips; static struct samd_info *samd_chips;
/**
* Gives the family structure to specific device id.
static const struct samd_part *samd_find_part(uint32_t id) * @param id The id of the device.
* @return On failure NULL, otherwise a pointer to the structure.
*/
static const struct samd_family *samd_find_family(uint32_t id)
{ {
uint8_t processor = SAMD_GET_PROCESSOR(id); uint8_t processor = SAMD_GET_PROCESSOR(id);
uint8_t family = SAMD_GET_FAMILY(id); uint8_t family = SAMD_GET_FAMILY(id);
uint8_t series = SAMD_GET_SERIES(id); uint8_t series = SAMD_GET_SERIES(id);
uint8_t devsel = SAMD_GET_DEVSEL(id);
for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) { for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) {
if (samd_families[i].processor == processor && if (samd_families[i].processor == processor &&
samd_families[i].series == series && samd_families[i].series == series &&
samd_families[i].family == family) { samd_families[i].family == family)
for (unsigned j = 0; j < samd_families[i].num_parts; j++) { return &samd_families[i];
if (samd_families[i].parts[j].id == devsel) }
return &samd_families[i].parts[j];
} return NULL;
} }
/**
* Gives the part structure to specific device id.
* @param id The id of the device.
* @return On failure NULL, otherwise a pointer to the structure.
*/
static const struct samd_part *samd_find_part(uint32_t id)
{
uint8_t devsel = SAMD_GET_DEVSEL(id);
const struct samd_family *family = samd_find_family(id);
if (family == NULL)
return NULL;
for (unsigned i = 0; i < family->num_parts; i++) {
if (family->parts[i].id == devsel)
return &family->parts[i];
} }
return NULL; return NULL;
@ -483,6 +515,12 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
return samd_check_error(target); return samd_check_error(target);
} }
/**
* Erases a flash-row at the given address.
* @param target Pointer to the target structure.
* @param address The address of the row.
* @return On success ERROR_OK, on failure an errorcode.
*/
static int samd_erase_row(struct target *target, uint32_t address) static int samd_erase_row(struct target *target, uint32_t address)
{ {
int res; int res;
@ -504,49 +542,62 @@ static int samd_erase_row(struct target *target, uint32_t address)
return ERROR_OK; return ERROR_OK;
} }
static bool is_user_row_reserved_bit(uint8_t bit) /**
* Returns the bitmask of reserved bits in register.
* @param target Pointer to the target structure.
* @param mask Bitmask, 0 -> value stays untouched.
* @return On success ERROR_OK, on failure an errorcode.
*/
static int samd_get_reservedmask(struct target *target, uint64_t *mask)
{ {
/* See Table 9-3 in the SAMD20 datasheet for more information. */ int res;
switch (bit) { /* Get the devicetype */
/* Reserved bits */ uint32_t id;
case 3: res = target_read_u32(target, SAMD_DSU + SAMD_DSU_DID, &id);
case 7: if (res != ERROR_OK) {
/* Voltage regulator internal configuration with default value of 0x70, LOG_ERROR("Couldn't read Device ID register");
* may not be changed. */ return res;
case 17 ... 24:
/* 41 is voltage regulator internal configuration and must not be
* changed. 42 through 47 are reserved. */
case 41 ... 47:
return true;
default:
break;
} }
const struct samd_family *family;
return false; family = samd_find_family(id);
if (family == NULL) {
LOG_ERROR("Couldn't determine device family");
return ERROR_FAIL;
}
*mask = family->nvm_userrow_res_mask;
return ERROR_OK;
} }
/* Modify the contents of the User Row in Flash. These are described in Table static int read_userrow(struct target *target, uint64_t *userrow)
* 9-3 of the SAMD20 datasheet. The User Row itself has a size of one page {
* and contains a combination of "fuses" and calibration data in bits 24:17. int res;
* We therefore try not to erase the row's contents unless we absolutely have uint8_t buffer[8];
* to and we don't permit modifying reserved bits. */
static int samd_modify_user_row(struct target *target, uint32_t value, res = target_read_memory(target, SAMD_USER_ROW, 4, 2, buffer);
uint8_t startb, uint8_t endb) if (res != ERROR_OK)
return res;
*userrow = target_buffer_get_u64(target, buffer);
return ERROR_OK;
}
/**
* Modify the contents of the User Row in Flash. The User Row itself
* has a size of one page and contains a combination of "fuses" and
* calibration data. Bits which have a value of zero in the mask will
* not be changed. Up to now devices only use the first 64 bits.
* @param target Pointer to the target structure.
* @param value_input The value to write.
* @param value_mask Bitmask, 0 -> value stays untouched.
* @return On success ERROR_OK, on failure an errorcode.
*/
static int samd_modify_user_row_masked(struct target *target,
uint64_t value_input, uint64_t value_mask)
{ {
int res; int res;
uint32_t nvm_ctrlb; uint32_t nvm_ctrlb;
bool manual_wp = true; bool manual_wp = true;
if (is_user_row_reserved_bit(startb) || is_user_row_reserved_bit(endb)) {
LOG_ERROR("Can't modify bits in the requested range");
return ERROR_FAIL;
}
/* Check if we need to do manual page write commands */
res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
if (res == ERROR_OK)
manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
/* Retrieve the MCU's page size, in bytes. This is also the size of the /* Retrieve the MCU's page size, in bytes. This is also the size of the
* entire User Row. */ * entire User Row. */
uint32_t page_size; uint32_t page_size;
@ -556,44 +607,49 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
return res; return res;
} }
/* Make sure the size is sane before we allocate. */ /* Make sure the size is sane. */
assert(page_size > 0 && page_size <= SAMD_PAGE_SIZE_MAX); assert(page_size <= SAMD_PAGE_SIZE_MAX &&
page_size >= sizeof(value_input));
/* Make sure we're within the single page that comprises the User Row. */
if (startb >= (page_size * 8) || endb >= (page_size * 8)) {
LOG_ERROR("Can't modify bits outside the User Row page range");
return ERROR_FAIL;
}
uint8_t *buf = malloc(page_size);
if (!buf)
return ERROR_FAIL;
uint8_t buf[SAMD_PAGE_SIZE_MAX];
/* Read the user row (comprising one page) by words. */ /* Read the user row (comprising one page) by words. */
res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
if (res != ERROR_OK) if (res != ERROR_OK)
goto out_user_row; return res;
uint64_t value_device;
res = read_userrow(target, &value_device);
if (res != ERROR_OK)
return res;
uint64_t value_new = (value_input & value_mask) | (value_device & ~value_mask);
/* We will need to erase before writing if the new value needs a '1' in any /* We will need to erase before writing if the new value needs a '1' in any
* position for which the current value had a '0'. Otherwise we can avoid * position for which the current value had a '0'. Otherwise we can avoid
* erasing. */ * erasing. */
uint32_t cur = buf_get_u32(buf, startb, endb - startb + 1); if ((~value_device) & value_new) {
if ((~cur) & value) {
res = samd_erase_row(target, SAMD_USER_ROW); res = samd_erase_row(target, SAMD_USER_ROW);
if (res != ERROR_OK) { if (res != ERROR_OK) {
LOG_ERROR("Couldn't erase user row"); LOG_ERROR("Couldn't erase user row");
goto out_user_row; return res;
} }
} }
/* Modify */ /* Modify */
buf_set_u32(buf, startb, endb - startb + 1, value); target_buffer_set_u64(target, buf, value_new);
/* Write the page buffer back out to the target. */ /* Write the page buffer back out to the target. */
res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
if (res != ERROR_OK) if (res != ERROR_OK)
goto out_user_row; return res;
/* Check if we need to do manual page write commands */
res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
if (res == ERROR_OK)
manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
else {
LOG_ERROR("Read of NVM register CTRKB failed.");
return ERROR_FAIL;
}
if (manual_wp) { if (manual_wp) {
/* Trigger flash write */ /* Trigger flash write */
res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP); res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP);
@ -601,12 +657,28 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
res = samd_check_error(target); res = samd_check_error(target);
} }
out_user_row:
free(buf);
return res; return res;
} }
/**
* Modifies the user row register to the given value.
* @param target Pointer to the target structure.
* @param value The value to write.
* @param startb The bit-offset by which the given value is shifted.
* @param endb The bit-offset of the last bit in value to write.
* @return On success ERROR_OK, on failure an errorcode.
*/
static int samd_modify_user_row(struct target *target, uint64_t value,
uint8_t startb, uint8_t endb)
{
uint64_t mask = 0;
int i;
for (i = startb ; i <= endb ; i++)
mask |= ((uint64_t)1) << i;
return samd_modify_user_row_masked(target, value << startb, mask);
}
static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl) static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl)
{ {
int res = ERROR_OK; int res = ERROR_OK;
@ -643,7 +715,8 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int
* corresponding to Sector 15. A '1' means unlocked and a '0' means * corresponding to Sector 15. A '1' means unlocked and a '0' means
* locked. See Table 9-3 in the SAMD20 datasheet for more details. */ * locked. See Table 9-3 in the SAMD20 datasheet for more details. */
res = samd_modify_user_row(bank->target, set ? 0x0000 : 0xFFFF, res = samd_modify_user_row(bank->target,
set ? (uint64_t)0 : (uint64_t)UINT64_MAX,
48 + first_prot_bl, 48 + last_prot_bl); 48 + first_prot_bl, 48 + last_prot_bl);
if (res != ERROR_OK) if (res != ERROR_OK)
LOG_WARNING("SAMD: protect settings were not made persistent!"); LOG_WARNING("SAMD: protect settings were not made persistent!");
@ -944,6 +1017,83 @@ COMMAND_HANDLER(samd_handle_eeprom_command)
return res; return res;
} }
static COMMAND_HELPER(get_u64_from_hexarg, unsigned int num, uint64_t *value)
{
if (num >= CMD_ARGC) {
command_print(CMD_CTX, "Too few Arguments.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (strlen(CMD_ARGV[num]) >= 3 &&
CMD_ARGV[num][0] == '0' &&
CMD_ARGV[num][1] == 'x') {
char *check = NULL;
*value = strtoull(&(CMD_ARGV[num][2]), &check, 16);
if ((value == 0 && errno == ERANGE) ||
check == NULL || *check != 0) {
command_print(CMD_CTX, "Invalid 64-bit hex value in argument %d.",
num + 1);
return ERROR_COMMAND_SYNTAX_ERROR;
}
} else {
command_print(CMD_CTX, "Argument %d needs to be a hex value.", num + 1);
return ERROR_COMMAND_SYNTAX_ERROR;
}
return ERROR_OK;
}
COMMAND_HANDLER(samd_handle_nvmuserrow_command)
{
int res = ERROR_OK;
struct target *target = get_current_target(CMD_CTX);
if (target) {
if (CMD_ARGC > 2) {
command_print(CMD_CTX, "Too much Arguments given.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (CMD_ARGC > 0) {
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted.");
return ERROR_TARGET_NOT_HALTED;
}
uint64_t mask;
res = samd_get_reservedmask(target, &mask);
if (res != ERROR_OK) {
LOG_ERROR("Couldn't determine the mask for reserved bits.");
return ERROR_FAIL;
}
mask &= NVMUSERROW_LOCKBIT_MASK;
uint64_t value;
res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 0, &value);
if (res != ERROR_OK)
return res;
if (CMD_ARGC == 2) {
uint64_t mask_temp;
res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 1, &mask_temp);
if (res != ERROR_OK)
return res;
mask &= mask_temp;
}
res = samd_modify_user_row_masked(target, value, mask);
if (res != ERROR_OK)
return res;
}
/* read register */
uint64_t value;
res = read_userrow(target, &value);
if (res == ERROR_OK)
command_print(CMD_CTX, "NVMUSERROW: 0x%016"PRIX64, value);
else
LOG_ERROR("NVMUSERROW could not be read.");
}
return res;
}
COMMAND_HANDLER(samd_handle_bootloader_command) COMMAND_HANDLER(samd_handle_bootloader_command)
{ {
int res = ERROR_OK; int res = ERROR_OK;
@ -1049,29 +1199,29 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.name = "dsu_reset_deassert", .name = "dsu_reset_deassert",
.handler = samd_handle_reset_deassert, .handler = samd_handle_reset_deassert,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "deasert internal reset held by DSU" .help = "Deasert internal reset held by DSU."
}, },
{ {
.name = "info", .name = "info",
.handler = samd_handle_info_command, .handler = samd_handle_info_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "Print information about the current at91samd chip" .help = "Print information about the current at91samd chip "
"and its flash configuration.", "and its flash configuration.",
}, },
{ {
.name = "chip-erase", .name = "chip-erase",
.handler = samd_handle_chip_erase_command, .handler = samd_handle_chip_erase_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "Erase the entire Flash by using the Chip" .help = "Erase the entire Flash by using the Chip-"
"Erase feature in the Device Service Unit (DSU).", "Erase feature in the Device Service Unit (DSU).",
}, },
{ {
.name = "set-security", .name = "set-security",
.handler = samd_handle_set_security_command, .handler = samd_handle_set_security_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "Secure the chip's Flash by setting the Security Bit." .help = "Secure the chip's Flash by setting the Security Bit. "
"This makes it impossible to read the Flash contents." "This makes it impossible to read the Flash contents. "
"The only way to undo this is to issue the chip-erase" "The only way to undo this is to issue the chip-erase "
"command.", "command.",
}, },
{ {
@ -1079,9 +1229,9 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.usage = "[size_in_bytes]", .usage = "[size_in_bytes]",
.handler = samd_handle_eeprom_command, .handler = samd_handle_eeprom_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "Show or set the EEPROM size setting, stored in the User Row." .help = "Show or set the EEPROM size setting, stored in the User Row. "
"Please see Table 20-3 of the SAMD20 datasheet for allowed values." "Please see Table 20-3 of the SAMD20 datasheet for allowed values. "
"Changes are stored immediately but take affect after the MCU is" "Changes are stored immediately but take affect after the MCU is "
"reset.", "reset.",
}, },
{ {
@ -1089,11 +1239,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.usage = "[size_in_bytes]", .usage = "[size_in_bytes]",
.handler = samd_handle_bootloader_command, .handler = samd_handle_bootloader_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.help = "Show or set the bootloader size, stored in the User Row." .help = "Show or set the bootloader size, stored in the User Row. "
"Please see Table 20-2 of the SAMD20 datasheet for allowed values." "Please see Table 20-2 of the SAMD20 datasheet for allowed values. "
"Changes are stored immediately but take affect after the MCU is" "Changes are stored immediately but take affect after the MCU is "
"reset.", "reset.",
}, },
{
.name = "nvmuserrow",
.usage = "[value] [mask]",
.handler = samd_handle_nvmuserrow_command,
.mode = COMMAND_EXEC,
.help = "Show or set the nvmuserrow register. It is 64 bit wide "
"and located at address 0x804000. Use the optional mask argument "
"to prevent changes at positions where the bitvalue is zero. "
"For security reasons the lock- and reserved-bits are masked out "
"in background and therefore cannot be changed.",
},
COMMAND_REGISTRATION_DONE COMMAND_REGISTRATION_DONE
}; };

View File

@ -898,4 +898,5 @@ struct flash_driver ath79_flash = {
.erase_check = ath79_flash_blank_check, .erase_check = ath79_flash_blank_check,
.protect_check = ath79_protect_check, .protect_check = ath79_protect_check,
.info = get_ath79_info, .info = get_ath79_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -739,4 +739,5 @@ struct flash_driver atsamv_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = samv_protect_check, .protect_check = samv_protect_check,
.info = samv_get_info, .info = samv_get_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -487,4 +487,5 @@ struct flash_driver avr_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = avrf_protect_check, .protect_check = avrf_protect_check,
.info = avrf_info, .info = avrf_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

554
src/flash/nor/bluenrg-x.c Normal file
View File

@ -0,0 +1,554 @@
/***************************************************************************
* Copyright (C) 2017 by Michele Sardo *
* msmttchr@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <target/algorithm.h>
#include <target/armv7m.h>
#include <target/cortex_m.h>
#include "imp.h"
#define FLASH_SIZE_REG (0x40100014)
#define DIE_ID_REG (0x4090001C)
#define JTAG_IDCODE_REG (0x40900028)
#define BLUENRG2_IDCODE (0x0200A041)
#define FLASH_BASE (0x10040000)
#define FLASH_PAGE_SIZE (2048)
#define FLASH_REG_COMMAND (0x40100000)
#define FLASH_REG_IRQRAW (0x40100010)
#define FLASH_REG_ADDRESS (0x40100018)
#define FLASH_REG_DATA (0x40100040)
#define FLASH_CMD_ERASE_PAGE 0x11
#define FLASH_CMD_MASSERASE 0x22
#define FLASH_CMD_WRITE 0x33
#define FLASH_CMD_BURSTWRITE 0xCC
#define FLASH_INT_CMDDONE 0x01
#define FLASH_WORD_LEN 4
struct bluenrgx_flash_bank {
int probed;
uint32_t idcode;
uint32_t die_id;
};
static int bluenrgx_protect_check(struct flash_bank *bank)
{
/* Nothing to do. Protection is only handled in SW. */
return ERROR_OK;
}
/* flash_bank bluenrg-x 0 0 0 0 <target#> */
FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command)
{
struct bluenrgx_flash_bank *bluenrgx_info;
/* Create the bank structure */
bluenrgx_info = calloc(1, sizeof(*bluenrgx_info));
/* Check allocation */
if (bluenrgx_info == NULL) {
LOG_ERROR("failed to allocate bank structure");
return ERROR_FAIL;
}
bank->driver_priv = bluenrgx_info;
bluenrgx_info->probed = 0;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
return ERROR_OK;
}
static int bluenrgx_erase(struct flash_bank *bank, int first, int last)
{
int retval = ERROR_OK;
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
int num_sectors = (last - first + 1);
int mass_erase = (num_sectors == bank->num_sectors);
struct target *target = bank->target;
uint32_t address, command;
/* check preconditions */
if (bluenrgx_info->probed == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Disable blue module */
if (target_write_u32(target, 0x200000c0, 0) != ERROR_OK) {
LOG_ERROR("Blue disable failed");
return ERROR_FAIL;
}
if (mass_erase) {
command = FLASH_CMD_MASSERASE;
address = bank->base;
if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
for (int i = 0; i < 100; i++) {
uint32_t value;
if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (value & FLASH_INT_CMDDONE)
break;
if (i == 99) {
LOG_ERROR("Mass erase command failed (timeout)");
retval = ERROR_FAIL;
}
}
} else {
command = FLASH_CMD_ERASE_PAGE;
for (int i = first; i <= last; i++) {
address = bank->base+i*FLASH_PAGE_SIZE;
if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
LOG_ERROR("Failed");
return ERROR_FAIL;
}
for (int j = 0; j < 100; j++) {
uint32_t value;
if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
LOG_ERROR("Register write failed");
return ERROR_FAIL;
}
if (value & FLASH_INT_CMDDONE)
break;
if (j == 99) {
LOG_ERROR("Erase command failed (timeout)");
retval = ERROR_FAIL;
}
}
}
}
return retval;
}
static int bluenrgx_protect(struct flash_bank *bank, int set, int first, int last)
{
/* Protection is only handled in software: no hardware write protection
available in BlueNRG-x devices */
int sector;
for (sector = first; sector <= last; sector++)
bank->sectors[sector].is_protected = set;
return ERROR_OK;
}
static int bluenrgx_write_word(struct target *target, uint32_t address_base, uint8_t *values, uint32_t count)
{
int retval = ERROR_OK;
retval = target_write_u32(target, FLASH_REG_IRQRAW, 0x3f);
if (retval != ERROR_OK) {
LOG_ERROR("Register write failed, error code: %d", retval);
return retval;
}
for (uint32_t i = 0; i < count; i++) {
uint32_t address = address_base + i * FLASH_WORD_LEN;
retval = target_write_u32(target, FLASH_REG_ADDRESS, address >> 2);
if (retval != ERROR_OK) {
LOG_ERROR("Register write failed, error code: %d", retval);
return retval;
}
retval = target_write_buffer(target, FLASH_REG_DATA, FLASH_WORD_LEN, values + i * FLASH_WORD_LEN);
if (retval != ERROR_OK) {
LOG_ERROR("Register write failed, error code: %d", retval);
return retval;
}
retval = target_write_u32(target, FLASH_REG_COMMAND, FLASH_CMD_WRITE);
if (retval != ERROR_OK) {
LOG_ERROR("Register write failed, error code: %d", retval);
return retval;
}
for (int j = 0; j < 100; j++) {
uint32_t reg_value;
retval = target_read_u32(target, FLASH_REG_IRQRAW, &reg_value);
if (retval != ERROR_OK) {
LOG_ERROR("Register read failed, error code: %d", retval);
return retval;
}
if (reg_value & FLASH_INT_CMDDONE)
break;
if (j == 99) {
LOG_ERROR("Write command failed (timeout)");
return ERROR_FAIL;
}
}
}
return retval;
}
static int bluenrgx_write_bytes(struct target *target, uint32_t address_base, uint8_t *buffer, uint32_t count)
{
int retval = ERROR_OK;
uint8_t *new_buffer = NULL;
uint32_t pre_bytes = 0, post_bytes = 0, pre_word, post_word, pre_address, post_address;
if (count == 0) {
/* Just return if there are no bytes to write */
return retval;
}
if (address_base & 3) {
pre_bytes = address_base & 3;
pre_address = address_base - pre_bytes;
}
if ((count + pre_bytes) & 3) {
post_bytes = ((count + pre_bytes + 3) & ~3) - (count + pre_bytes);
post_address = (address_base + count) & ~3;
}
if (pre_bytes || post_bytes) {
uint32_t old_count = count;
count = old_count + pre_bytes + post_bytes;
new_buffer = malloc(count);
if (new_buffer == NULL) {
LOG_ERROR("odd number of bytes to write and no memory "
"for padding buffer");
return ERROR_FAIL;
}
LOG_INFO("Requested number of bytes to write and/or address not word aligned (%" PRIu32 "), extending to %"
PRIu32 " ", old_count, count);
if (pre_bytes) {
if (target_read_u32(target, pre_address, &pre_word)) {
LOG_ERROR("Memory read failed");
free(new_buffer);
return ERROR_FAIL;
}
}
if (post_bytes) {
if (target_read_u32(target, post_address, &post_word)) {
LOG_ERROR("Memory read failed");
free(new_buffer);
return ERROR_FAIL;
}
}
memcpy(new_buffer, &pre_word, pre_bytes);
memcpy((new_buffer+((pre_bytes+old_count) & ~3)), &post_word, 4);
memcpy(new_buffer+pre_bytes, buffer, old_count);
buffer = new_buffer;
}
retval = bluenrgx_write_word(target, address_base - pre_bytes, buffer, count/4);
if (new_buffer)
free(new_buffer);
return retval;
}
static int bluenrgx_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 16384 + 8;
struct working_area *write_algorithm;
struct working_area *write_algorithm_sp;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[5];
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
uint32_t pre_size = 0, fast_size = 0, post_size = 0;
uint32_t pre_offset = 0, fast_offset = 0, post_offset = 0;
/* See contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c for source and
* hints how to generate the data!
*/
static const uint8_t bluenrgx_flash_write_code[] = {
#include "../../../contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc"
};
if ((offset + count) > bank->size) {
LOG_ERROR("Requested write past beyond of flash size: (offset+count) = %d, size=%d",
(offset + count),
bank->size);
return ERROR_FLASH_DST_OUT_OF_BANK;
}
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* We are good here and we need to compute pre_size, fast_size, post_size */
pre_size = MIN(count, ((offset+0xF) & ~0xF) - offset);
pre_offset = offset;
fast_size = 16*((count - pre_size) / 16);
fast_offset = offset + pre_size;
post_size = (count-pre_size-fast_size) % 16;
post_offset = fast_offset + fast_size;
LOG_DEBUG("pre_size = %08x, pre_offset=%08x", pre_size, pre_offset);
LOG_DEBUG("fast_size = %08x, fast_offset=%08x", fast_size, fast_offset);
LOG_DEBUG("post_size = %08x, post_offset=%08x", post_size, post_offset);
/* Program initial chunk not 16 bytes aligned */
retval = bluenrgx_write_bytes(target, bank->base+pre_offset, (uint8_t *) buffer, pre_size);
if (retval) {
LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
return ERROR_FAIL;
}
/* Program chunk 16 bytes aligned in fast mode */
if (fast_size) {
if (target_alloc_working_area(target, sizeof(bluenrgx_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
retval = target_write_buffer(target, write_algorithm->address,
sizeof(bluenrgx_flash_write_code),
bluenrgx_flash_write_code);
if (retval != ERROR_OK)
return retval;
/* memory buffer */
if (target_alloc_working_area(target, buffer_size, &source)) {
LOG_WARNING("no large enough working area available");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
/* Stack pointer area */
if (target_alloc_working_area(target, 64,
&write_algorithm_sp) != ERROR_OK) {
LOG_DEBUG("no working area for write code stack pointer");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
init_reg_param(&reg_params[4], "sp", 32, PARAM_OUT);
/* FIFO start address (first two words used for write and read pointers) */
buf_set_u32(reg_params[0].value, 0, 32, source->address);
/* FIFO end address (first two words used for write and read pointers) */
buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
/* Flash memory address */
buf_set_u32(reg_params[2].value, 0, 32, address+pre_size);
/* Number of bytes */
buf_set_u32(reg_params[3].value, 0, 32, fast_size);
/* Stack pointer for program working area */
buf_set_u32(reg_params[4].value, 0, 32, write_algorithm_sp->address);
LOG_DEBUG("source->address = %08" TARGET_PRIxADDR, source->address);
LOG_DEBUG("source->address+ source->size = %08" TARGET_PRIxADDR, source->address+source->size);
LOG_DEBUG("write_algorithm_sp->address = %08" TARGET_PRIxADDR, write_algorithm_sp->address);
LOG_DEBUG("address = %08x", address+pre_size);
LOG_DEBUG("count = %08x", count);
retval = target_run_flash_async_algorithm(target,
buffer+pre_size,
fast_size/16,
16, /* Block size: we write in block of 16 bytes to enjoy burstwrite speed */
0,
NULL,
5,
reg_params,
source->address,
source->size,
write_algorithm->address,
0,
&armv7m_info);
if (retval == ERROR_FLASH_OPERATION_FAILED) {
LOG_ERROR("error executing bluenrg-x flash write algorithm");
uint32_t error = buf_get_u32(reg_params[0].value, 0, 32);
if (error != 0)
LOG_ERROR("flash write failed = %08" PRIx32, error);
}
if (retval == ERROR_OK) {
uint32_t rp;
/* Read back rp and check that is valid */
retval = target_read_u32(target, source->address+4, &rp);
if (retval == ERROR_OK) {
if ((rp < source->address+8) || (rp > (source->address + source->size))) {
LOG_ERROR("flash write failed = %08" PRIx32, rp);
retval = ERROR_FLASH_OPERATION_FAILED;
}
}
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
target_free_working_area(target, write_algorithm_sp);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
if (retval != ERROR_OK)
return retval;
}
/* Program chunk at end, not addressable by fast burst write algorithm */
retval = bluenrgx_write_bytes(target, bank->base+post_offset, (uint8_t *) (buffer+pre_size+fast_size), post_size);
if (retval) {
LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
return ERROR_FAIL;
}
return retval;
}
static int bluenrgx_probe(struct flash_bank *bank)
{
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
uint32_t idcode, size_info, die_id;
int i;
int retval = target_read_u32(bank->target, JTAG_IDCODE_REG, &idcode);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(bank->target, FLASH_SIZE_REG, &size_info);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(bank->target, DIE_ID_REG, &die_id);
if (retval != ERROR_OK)
return retval;
bank->size = (size_info + 1) * 4;
bank->base = FLASH_BASE;
bank->num_sectors = bank->size/FLASH_PAGE_SIZE;
bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector) * bank->num_sectors);
for (i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = i * FLASH_PAGE_SIZE;
bank->sectors[i].size = FLASH_PAGE_SIZE;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 0;
}
bluenrgx_info->probed = 1;
bluenrgx_info->die_id = die_id;
bluenrgx_info->idcode = idcode;
return ERROR_OK;
}
static int bluenrgx_auto_probe(struct flash_bank *bank)
{
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
if (bluenrgx_info->probed)
return ERROR_OK;
return bluenrgx_probe(bank);
}
/* This method must return a string displaying information about the bank */
static int bluenrgx_get_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
int mask_number, cut_number;
char *part_name;
if (!bluenrgx_info->probed) {
int retval = bluenrgx_probe(bank);
if (retval != ERROR_OK) {
snprintf(buf, buf_size,
"Unable to find bank information.");
return retval;
}
}
if (bluenrgx_info->idcode == BLUENRG2_IDCODE)
part_name = "BLUENRG-2";
else
part_name = "BLUENRG-1";
mask_number = (bluenrgx_info->die_id >> 4) & 0xF;
cut_number = bluenrgx_info->die_id & 0xF;
snprintf(buf, buf_size,
"%s - Rev: %d.%d", part_name, mask_number, cut_number);
return ERROR_OK;
}
struct flash_driver bluenrgx_flash = {
.name = "bluenrg-x",
.flash_bank_command = bluenrgx_flash_bank_command,
.erase = bluenrgx_erase,
.protect = bluenrgx_protect,
.write = bluenrgx_write,
.read = default_flash_read,
.probe = bluenrgx_probe,
.erase_check = default_flash_blank_check,
.protect_check = bluenrgx_protect_check,
.auto_probe = bluenrgx_auto_probe,
.info = bluenrgx_get_info,
};

View File

@ -3128,4 +3128,5 @@ struct flash_driver cfi_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = cfi_protect_check, .protect_check = cfi_protect_check,
.info = get_cfi_info, .info = get_cfi_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -171,6 +171,31 @@ int flash_get_bank_count(void)
return i; return i;
} }
void default_flash_free_driver_priv(struct flash_bank *bank)
{
free(bank->driver_priv);
bank->driver_priv = NULL;
}
void flash_free_all_banks(void)
{
struct flash_bank *bank = flash_banks;
while (bank) {
struct flash_bank *next = bank->next;
if (bank->driver->free_driver_priv)
bank->driver->free_driver_priv(bank);
else
LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name);
free(bank->name);
free(bank->sectors);
free(bank->prot_blocks);
free(bank);
bank = next;
}
flash_banks = NULL;
}
struct flash_bank *get_flash_bank_by_name_noprobe(const char *name) struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
{ {
unsigned requested = get_flash_name_index(name); unsigned requested = get_flash_name_index(name);
@ -399,18 +424,21 @@ static int flash_iterate_address_range_inner(struct target *target,
return ERROR_FLASH_DST_BREAKS_ALIGNMENT; return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
} }
addr -= c->base; if (c->prot_blocks == NULL || c->num_prot_blocks == 0) {
last_addr -= c->base; /* flash driver does not define protect blocks, use sectors instead */
iterate_protect_blocks = false;
}
if (iterate_protect_blocks && c->prot_blocks && c->num_prot_blocks) { if (iterate_protect_blocks) {
block_array = c->prot_blocks; block_array = c->prot_blocks;
num_blocks = c->num_prot_blocks; num_blocks = c->num_prot_blocks;
} else { } else {
block_array = c->sectors; block_array = c->sectors;
num_blocks = c->num_sectors; num_blocks = c->num_sectors;
iterate_protect_blocks = false;
} }
addr -= c->base;
last_addr -= c->base;
for (i = 0; i < num_blocks; i++) { for (i = 0; i < num_blocks; i++) {
struct flash_sector *f = &block_array[i]; struct flash_sector *f = &block_array[i];
@ -601,7 +629,7 @@ int flash_write_unlock(struct target *target, struct image *image,
uint32_t buffer_size; uint32_t buffer_size;
uint8_t *buffer; uint8_t *buffer;
int section_last; int section_last;
uint32_t run_address = sections[section]->base_address + section_offset; target_addr_t run_address = sections[section]->base_address + section_offset;
uint32_t run_size = sections[section]->size - section_offset; uint32_t run_size = sections[section]->size - section_offset;
int pad_bytes = 0; int pad_bytes = 0;
@ -617,7 +645,7 @@ int flash_write_unlock(struct target *target, struct image *image,
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto done; goto done;
if (c == NULL) { if (c == NULL) {
LOG_WARNING("no flash bank found for address %" PRIx32, run_address); LOG_WARNING("no flash bank found for address " TARGET_ADDR_FMT, run_address);
section++; /* and skip it */ section++; /* and skip it */
section_offset = 0; section_offset = 0;
continue; continue;
@ -652,7 +680,18 @@ int flash_write_unlock(struct target *target, struct image *image,
/* if we have multiple sections within our image, /* if we have multiple sections within our image,
* flash programming could fail due to alignment issues * flash programming could fail due to alignment issues
* attempt to rebuild a consecutive buffer for the flash loader */ * attempt to rebuild a consecutive buffer for the flash loader */
pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size); target_addr_t run_next_addr = run_address + run_size;
if (sections[section_last + 1]->base_address < run_next_addr) {
LOG_ERROR("Section at " TARGET_ADDR_FMT
" overlaps section ending at " TARGET_ADDR_FMT,
sections[section_last + 1]->base_address,
run_next_addr);
LOG_ERROR("Flash write aborted.");
retval = ERROR_FAIL;
goto done;
}
pad_bytes = sections[section_last + 1]->base_address - run_next_addr;
padding[section_last] = pad_bytes; padding[section_last] = pad_bytes;
run_size += sections[++section_last]->size; run_size += sections[++section_last]->size;
run_size += pad_bytes; run_size += pad_bytes;

View File

@ -76,7 +76,7 @@ struct flash_sector {
* per-bank basis, if required. * per-bank basis, if required.
*/ */
struct flash_bank { struct flash_bank {
const char *name; char *name;
struct target *target; /**< Target to which this bank belongs. */ struct target *target; /**< Target to which this bank belongs. */
@ -153,8 +153,15 @@ int flash_write(struct target *target,
* This routine must be called when the system may modify the status. * This routine must be called when the system may modify the status.
*/ */
void flash_set_dirty(void); void flash_set_dirty(void);
/** @returns The number of flash banks currently defined. */ /** @returns The number of flash banks currently defined. */
int flash_get_bank_count(void); int flash_get_bank_count(void);
/** Deallocates bank->driver_priv */
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. * Provides default read implementation for flash memory.
* @param bank The bank to read. * @param bank The bank to read.

View File

@ -209,6 +209,14 @@ struct flash_driver {
* @returns ERROR_OK if successful; otherwise, an error code. * @returns ERROR_OK if successful; otherwise, an error code.
*/ */
int (*auto_probe)(struct flash_bank *bank); int (*auto_probe)(struct flash_bank *bank);
/**
* Deallocates private driver structures.
* Use default_flash_free_driver_priv() to simply free(bank->driver_priv)
*
* @param bank - the bank being destroyed
*/
void (*free_driver_priv)(struct flash_bank *bank);
}; };
#define FLASH_BANK_COMMAND_HANDLER(name) \ #define FLASH_BANK_COMMAND_HANDLER(name) \

View File

@ -31,6 +31,7 @@ extern struct flash_driver at91samd_flash;
extern struct flash_driver ath79_flash; extern struct flash_driver ath79_flash;
extern struct flash_driver atsamv_flash; extern struct flash_driver atsamv_flash;
extern struct flash_driver avr_flash; extern struct flash_driver avr_flash;
extern struct flash_driver bluenrgx_flash;
extern struct flash_driver cfi_flash; extern struct flash_driver cfi_flash;
extern struct flash_driver dsp5680xx_flash; extern struct flash_driver dsp5680xx_flash;
extern struct flash_driver efm32_flash; extern struct flash_driver efm32_flash;
@ -55,6 +56,7 @@ extern struct flash_driver numicro_flash;
extern struct flash_driver ocl_flash; extern struct flash_driver ocl_flash;
extern struct flash_driver pic32mx_flash; extern struct flash_driver pic32mx_flash;
extern struct flash_driver psoc4_flash; extern struct flash_driver psoc4_flash;
extern struct flash_driver psoc6_flash;
extern struct flash_driver sim3x_flash; extern struct flash_driver sim3x_flash;
extern struct flash_driver stellaris_flash; extern struct flash_driver stellaris_flash;
extern struct flash_driver stm32f1x_flash; extern struct flash_driver stm32f1x_flash;
@ -88,6 +90,7 @@ static struct flash_driver *flash_drivers[] = {
&ath79_flash, &ath79_flash,
&atsamv_flash, &atsamv_flash,
&avr_flash, &avr_flash,
&bluenrgx_flash,
&cfi_flash, &cfi_flash,
&dsp5680xx_flash, &dsp5680xx_flash,
&efm32_flash, &efm32_flash,
@ -112,6 +115,7 @@ static struct flash_driver *flash_drivers[] = {
&ocl_flash, &ocl_flash,
&pic32mx_flash, &pic32mx_flash,
&psoc4_flash, &psoc4_flash,
&psoc6_flash,
&sim3x_flash, &sim3x_flash,
&stellaris_flash, &stellaris_flash,
&stm32f1x_flash, &stm32f1x_flash,

View File

@ -38,19 +38,8 @@
#include <target/armv7m.h> #include <target/armv7m.h>
#include <target/cortex_m.h> #include <target/cortex_m.h>
/* keep family IDs in decimal */
#define EFM_FAMILY_ID_GECKO 71
#define EFM_FAMILY_ID_GIANT_GECKO 72 #define EFM_FAMILY_ID_GIANT_GECKO 72
#define EFM_FAMILY_ID_TINY_GECKO 73
#define EFM_FAMILY_ID_LEOPARD_GECKO 74 #define EFM_FAMILY_ID_LEOPARD_GECKO 74
#define EFM_FAMILY_ID_WONDER_GECKO 75
#define EFM_FAMILY_ID_ZERO_GECKO 76
#define EFM_FAMILY_ID_HAPPY_GECKO 77
#define EZR_FAMILY_ID_WONDER_GECKO 120
#define EZR_FAMILY_ID_LEOPARD_GECKO 121
#define EZR_FAMILY_ID_HAPPY_GECKO 122
#define EFR_FAMILY_ID_MIGHTY_GECKO 16
#define EFR_FAMILY_ID_BLUE_GECKO 20
#define EFM32_FLASH_ERASE_TMO 100 #define EFM32_FLASH_ERASE_TMO 100
#define EFM32_FLASH_WDATAREADY_TMO 100 #define EFM32_FLASH_WDATAREADY_TMO 100
@ -65,7 +54,7 @@
#define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000) #define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000)
#define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000) #define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000)
/* PAGE_SIZE is only present in Leopard, Giant and Wonder Gecko MCUs */ /* PAGE_SIZE is not present in Zero, Happy and the original Gecko MCU */
#define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7) #define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7)
#define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8) #define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8)
#define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa) #define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa)
@ -74,7 +63,7 @@
#define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff) #define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff)
#define EFM32_MSC_REGBASE 0x400c0000 #define EFM32_MSC_REGBASE 0x400c0000
#define EFR32_MSC_REGBASE 0x400e0000 #define EFM32_MSC_REGBASE_SERIES1 0x400e0000
#define EFM32_MSC_REG_WRITECTRL 0x008 #define EFM32_MSC_REG_WRITECTRL 0x008
#define EFM32_MSC_WRITECTRL_WREN_MASK 0x1 #define EFM32_MSC_WRITECTRL_WREN_MASK 0x1
#define EFM32_MSC_REG_WRITECMD 0x00c #define EFM32_MSC_REG_WRITECMD 0x00c
@ -91,9 +80,24 @@
#define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10 #define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10
#define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20 #define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20
#define EFM32_MSC_REG_LOCK 0x03c #define EFM32_MSC_REG_LOCK 0x03c
#define EFR32_MSC_REG_LOCK 0x040 #define EFM32_MSC_REG_LOCK_SERIES1 0x040
#define EFM32_MSC_LOCK_LOCKKEY 0x1b71 #define EFM32_MSC_LOCK_LOCKKEY 0x1b71
struct efm32_family_data {
int family_id;
const char *name;
/* EFM32 series (EFM32LG995F is the "old" series 0, while EFR32MG12P132
is the "new" series 1). Determines location of MSC registers. */
int series;
/* Page size in bytes, or 0 to read from EFM32_MSC_DI_PAGE_SIZE */
int page_size;
/* MSC register base address, or 0 to use default */
uint32_t msc_regbase;
};
struct efm32x_flash_bank { struct efm32x_flash_bank {
int probed; int probed;
uint32_t lb_page[LOCKBITS_PAGE_SZ/4]; uint32_t lb_page[LOCKBITS_PAGE_SZ/4];
@ -102,6 +106,7 @@ struct efm32x_flash_bank {
}; };
struct efm32_info { struct efm32_info {
const struct efm32_family_data *family_data;
uint16_t flash_sz_kib; uint16_t flash_sz_kib;
uint16_t ram_sz_kib; uint16_t ram_sz_kib;
uint16_t part_num; uint16_t part_num;
@ -110,6 +115,64 @@ struct efm32_info {
uint16_t page_size; uint16_t page_size;
}; };
static const struct efm32_family_data efm32_families[] = {
{ 16, "EFR32MG1P Mighty", .series = 1 },
{ 17, "EFR32MG1B Mighty", .series = 1 },
{ 18, "EFR32MG1V Mighty", .series = 1 },
{ 19, "EFR32MG1P Blue", .series = 1 },
{ 20, "EFR32MG1B Blue", .series = 1 },
{ 21, "EFR32MG1V Blue", .series = 1 },
{ 25, "EFR32FG1P Flex", .series = 1 },
{ 26, "EFR32FG1B Flex", .series = 1 },
{ 27, "EFR32FG1V Flex", .series = 1 },
{ 28, "EFR32MG2P Mighty", .series = 1 },
{ 29, "EFR32MG2B Mighty", .series = 1 },
{ 30, "EFR32MG2V Mighty", .series = 1 },
{ 31, "EFR32BG12P Blue", .series = 1 },
{ 32, "EFR32BG12B Blue", .series = 1 },
{ 33, "EFR32BG12V Blue", .series = 1 },
{ 37, "EFR32FG12P Flex", .series = 1 },
{ 38, "EFR32FG12B Flex", .series = 1 },
{ 39, "EFR32FG12V Flex", .series = 1 },
{ 40, "EFR32MG13P Mighty", .series = 1 },
{ 41, "EFR32MG13B Mighty", .series = 1 },
{ 42, "EFR32MG13V Mighty", .series = 1 },
{ 43, "EFR32BG13P Blue", .series = 1 },
{ 44, "EFR32BG13B Blue", .series = 1 },
{ 45, "EFR32BG13V Blue", .series = 1 },
{ 49, "EFR32FG13P Flex", .series = 1 },
{ 50, "EFR32FG13B Flex", .series = 1 },
{ 51, "EFR32FG13V Flex", .series = 1 },
{ 52, "EFR32MG14P Mighty", .series = 1 },
{ 53, "EFR32MG14B Mighty", .series = 1 },
{ 54, "EFR32MG14V Mighty", .series = 1 },
{ 55, "EFR32BG14P Blue", .series = 1 },
{ 56, "EFR32BG14B Blue", .series = 1 },
{ 57, "EFR32BG14V Blue", .series = 1 },
{ 61, "EFR32FG14P Flex", .series = 1 },
{ 62, "EFR32FG14B Flex", .series = 1 },
{ 63, "EFR32FG14V Flex", .series = 1 },
{ 71, "EFM32G", .series = 0, .page_size = 512 },
{ 72, "EFM32GG Giant", .series = 0 },
{ 73, "EFM32TG Tiny", .series = 0, .page_size = 512 },
{ 74, "EFM32LG Leopard", .series = 0 },
{ 75, "EFM32WG Wonder", .series = 0 },
{ 76, "EFM32ZG Zero", .series = 0, .page_size = 1024 },
{ 77, "EFM32HG Happy", .series = 0, .page_size = 1024 },
{ 81, "EFM32PG1B Pearl", .series = 1 },
{ 83, "EFM32JG1B Jade", .series = 1 },
{ 85, "EFM32PG12B Pearl", .series = 1 },
{ 87, "EFM32JG12B Jade", .series = 1 },
{ 89, "EFM32PG13B Pearl", .series = 1 },
{ 91, "EFM32JG13B Jade", .series = 1 },
{ 100, "EFM32GG11B Giant", .series = 1, .msc_regbase = 0x40000000 },
{ 103, "EFM32TG11B Tiny", .series = 1 },
{ 120, "EZR32WG Wonder", .series = 0 },
{ 121, "EZR32LG Leopard", .series = 0 },
{ 122, "EZR32HG Happy", .series = 0, .page_size = 1024 },
};
static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count); uint32_t offset, uint32_t count);
@ -200,51 +263,33 @@ static int efm32x_read_info(struct flash_bank *bank,
if (ERROR_OK != ret) if (ERROR_OK != ret)
return ret; return ret;
if (EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family || for (size_t i = 0; i < ARRAY_SIZE(efm32_families); i++) {
EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) { if (efm32_families[i].family_id == efm32_info->part_family)
efm32x_info->reg_base = EFR32_MSC_REGBASE; efm32_info->family_data = &efm32_families[i];
efm32x_info->reg_lock = EFR32_MSC_REG_LOCK;
} else {
efm32x_info->reg_base = EFM32_MSC_REGBASE;
efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
} }
if (EFM_FAMILY_ID_GECKO == efm32_info->part_family || if (efm32_info->family_data == NULL) {
EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family) LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
efm32_info->page_size = 512; return ERROR_FAIL;
else if (EFM_FAMILY_ID_ZERO_GECKO == efm32_info->part_family || }
EFM_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family ||
EZR_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family)
efm32_info->page_size = 1024;
else if (EFM_FAMILY_ID_GIANT_GECKO == efm32_info->part_family ||
EFM_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) {
if (efm32_info->prod_rev >= 18) {
uint8_t pg_size = 0;
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
&pg_size);
if (ERROR_OK != ret)
return ret;
efm32_info->page_size = (1 << ((pg_size+10) & 0xff)); switch (efm32_info->family_data->series) {
} else { case 0:
/* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid efm32x_info->reg_base = EFM32_MSC_REGBASE;
for MCUs with PROD_REV < 18 */ efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
if (efm32_info->flash_sz_kib < 512) break;
efm32_info->page_size = 2048; case 1:
else efm32x_info->reg_base = EFM32_MSC_REGBASE_SERIES1;
efm32_info->page_size = 4096; efm32x_info->reg_lock = EFM32_MSC_REG_LOCK_SERIES1;
} break;
}
if ((2048 != efm32_info->page_size) && if (efm32_info->family_data->msc_regbase != 0)
(4096 != efm32_info->page_size)) { efm32x_info->reg_base = efm32_info->family_data->msc_regbase;
LOG_ERROR("Invalid page size %u", efm32_info->page_size);
return ERROR_FAIL; if (efm32_info->family_data->page_size != 0) {
} efm32_info->page_size = efm32_info->family_data->page_size;
} else if (EFM_FAMILY_ID_WONDER_GECKO == efm32_info->part_family || } else {
EZR_FAMILY_ID_WONDER_GECKO == efm32_info->part_family ||
EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family ||
EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family ||
EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) {
uint8_t pg_size = 0; uint8_t pg_size = 0;
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE, ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
&pg_size); &pg_size);
@ -252,13 +297,25 @@ static int efm32x_read_info(struct flash_bank *bank,
return ret; return ret;
efm32_info->page_size = (1 << ((pg_size+10) & 0xff)); efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
if (2048 != efm32_info->page_size) {
if (efm32_info->part_family == EFM_FAMILY_ID_GIANT_GECKO ||
efm32_info->part_family == EFM_FAMILY_ID_LEOPARD_GECKO) {
/* Giant or Leopard Gecko */
if (efm32_info->prod_rev < 18) {
/* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
for MCUs with PROD_REV < 18 */
if (efm32_info->flash_sz_kib < 512)
efm32_info->page_size = 2048;
else
efm32_info->page_size = 4096;
}
}
if ((efm32_info->page_size != 2048) &&
(efm32_info->page_size != 4096)) {
LOG_ERROR("Invalid page size %u", efm32_info->page_size); LOG_ERROR("Invalid page size %u", efm32_info->page_size);
return ERROR_FAIL; return ERROR_FAIL;
} }
} else {
LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
return ERROR_FAIL;
} }
return ERROR_OK; return ERROR_OK;
@ -270,71 +327,10 @@ static int efm32x_read_info(struct flash_bank *bank,
static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size) static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size)
{ {
int printed = 0; int printed = 0;
printed = snprintf(buf, buf_size, "%s Gecko, rev %d",
info->family_data->name, info->prod_rev);
switch (info->part_family) { if (printed >= buf_size)
case EZR_FAMILY_ID_WONDER_GECKO:
case EZR_FAMILY_ID_LEOPARD_GECKO:
case EZR_FAMILY_ID_HAPPY_GECKO:
printed = snprintf(buf, buf_size, "EZR32 ");
break;
case EFR_FAMILY_ID_MIGHTY_GECKO:
case EFR_FAMILY_ID_BLUE_GECKO:
printed = snprintf(buf, buf_size, "EFR32 ");
break;
default:
printed = snprintf(buf, buf_size, "EFM32 ");
}
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL;
switch (info->part_family) {
case EFM_FAMILY_ID_GECKO:
printed = snprintf(buf, buf_size, "Gecko");
break;
case EFM_FAMILY_ID_GIANT_GECKO:
printed = snprintf(buf, buf_size, "Giant Gecko");
break;
case EFM_FAMILY_ID_TINY_GECKO:
printed = snprintf(buf, buf_size, "Tiny Gecko");
break;
case EFM_FAMILY_ID_LEOPARD_GECKO:
case EZR_FAMILY_ID_LEOPARD_GECKO:
printed = snprintf(buf, buf_size, "Leopard Gecko");
break;
case EFM_FAMILY_ID_WONDER_GECKO:
case EZR_FAMILY_ID_WONDER_GECKO:
printed = snprintf(buf, buf_size, "Wonder Gecko");
break;
case EFM_FAMILY_ID_ZERO_GECKO:
printed = snprintf(buf, buf_size, "Zero Gecko");
break;
case EFM_FAMILY_ID_HAPPY_GECKO:
case EZR_FAMILY_ID_HAPPY_GECKO:
printed = snprintf(buf, buf_size, "Happy Gecko");
break;
case EFR_FAMILY_ID_BLUE_GECKO:
printed = snprintf(buf, buf_size, "Blue Gecko");
break;
case EFR_FAMILY_ID_MIGHTY_GECKO:
printed = snprintf(buf, buf_size, "Mighty Gecko");
break;
}
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL;
printed = snprintf(buf, buf_size, " - Rev: %d", info->prod_rev);
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL; return ERROR_BUF_TOO_SMALL;
return ERROR_OK; return ERROR_OK;
@ -522,7 +518,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
} }
} }
/* also, read ULW, DLW and MLW */ /* also, read ULW, DLW, MLW, ALW and CLW words */
/* ULW, word 126 */ /* ULW, word 126 */
ptr = efm32x_info->lb_page + 126; ptr = efm32x_info->lb_page + 126;
@ -540,7 +536,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
return ret; return ret;
} }
/* MLW, word 125, present in GG and LG */ /* MLW, word 125, present in GG, LG, PG, JG, EFR32 */
ptr = efm32x_info->lb_page + 125; ptr = efm32x_info->lb_page + 125;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr); ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr);
if (ERROR_OK != ret) { if (ERROR_OK != ret) {
@ -548,6 +544,30 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
return ret; return ret;
} }
/* ALW, word 124, present in GG, LG, PG, JG, EFR32 */
ptr = efm32x_info->lb_page + 124;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+124*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read ALW");
return ret;
}
/* CLW1, word 123, present in EFR32 */
ptr = efm32x_info->lb_page + 123;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+123*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read CLW1");
return ret;
}
/* CLW0, word 122, present in GG, LG, PG, JG, EFR32 */
ptr = efm32x_info->lb_page + 122;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+122*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read CLW0");
return ret;
}
return ERROR_OK; return ERROR_OK;
} }
@ -1113,4 +1133,5 @@ struct flash_driver efm32_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = efm32x_protect_check, .protect_check = efm32x_protect_check,
.info = get_efm32x_info, .info = get_efm32x_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -941,4 +941,5 @@ struct flash_driver em357_flash = {
.auto_probe = em357_auto_probe, .auto_probe = em357_auto_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = em357_protect_check, .protect_check = em357_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -136,5 +136,6 @@ struct flash_driver faux_flash = {
.auto_probe = faux_probe, .auto_probe = faux_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = faux_protect_check, .protect_check = faux_protect_check,
.info = faux_info .info = faux_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -997,4 +997,5 @@ struct flash_driver fm3_flash = {
.probe = fm3_probe, .probe = fm3_probe,
.auto_probe = fm3_auto_probe, .auto_probe = fm3_auto_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -719,4 +719,5 @@ struct flash_driver fm4_flash = {
.erase = fm4_flash_erase, .erase = fm4_flash_erase,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.write = fm4_flash_write, .write = fm4_flash_write,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -432,5 +432,6 @@ struct flash_driver jtagspi_flash = {
.auto_probe = jtagspi_probe, .auto_probe = jtagspi_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = jtagspi_protect_check, .protect_check = jtagspi_protect_check,
.info = jtagspi_info .info = jtagspi_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -478,14 +478,13 @@ int kinetis_ke_stop_watchdog(struct target *target)
watchdog_algorithm->address, 0, 100000, &armv7m_info); watchdog_algorithm->address, 0, 100000, &armv7m_info);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
LOG_ERROR("Error executing Kinetis KE watchdog algorithm"); LOG_ERROR("Error executing Kinetis KE watchdog algorithm");
retval = ERROR_FAIL;
} else { } else {
LOG_INFO("Watchdog stopped"); LOG_INFO("Watchdog stopped");
} }
target_free_working_area(target, watchdog_algorithm); target_free_working_area(target, watchdog_algorithm);
return ERROR_OK; return retval;
} }
COMMAND_HANDLER(kinetis_ke_disable_wdog_handler) COMMAND_HANDLER(kinetis_ke_disable_wdog_handler)
@ -1311,4 +1310,5 @@ struct flash_driver kinetis_ke_flash = {
.erase_check = kinetis_ke_blank_check, .erase_check = kinetis_ke_blank_check,
.protect_check = kinetis_ke_protect_check, .protect_check = kinetis_ke_protect_check,
.info = kinetis_ke_info, .info = kinetis_ke_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1579,4 +1579,5 @@ struct flash_driver lpc2000_flash = {
.erase_check = lpc2000_erase_check, .erase_check = lpc2000_erase_check,
.protect_check = lpc2000_protect_check, .protect_check = lpc2000_protect_check,
.info = get_lpc2000_info, .info = get_lpc2000_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -433,4 +433,5 @@ struct flash_driver lpc288x_flash = {
.auto_probe = lpc288x_probe, .auto_probe = lpc288x_probe,
.erase_check = lpc288x_erase_check, .erase_check = lpc288x_erase_check,
.protect_check = lpc288x_protect_check, .protect_check = lpc288x_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1598,4 +1598,5 @@ struct flash_driver lpc2900_flash = {
.auto_probe = lpc2900_probe, .auto_probe = lpc2900_probe,
.erase_check = lpc2900_erase_check, .erase_check = lpc2900_erase_check,
.protect_check = lpc2900_protect_check, .protect_check = lpc2900_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -942,4 +942,5 @@ struct flash_driver lpcspifi_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = lpcspifi_protect_check, .protect_check = lpcspifi_protect_check,
.info = get_lpcspifi_info, .info = get_lpcspifi_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -633,4 +633,5 @@ struct flash_driver mdr_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = mdr_protect_check, .protect_check = mdr_protect_check,
.info = get_mdr_info, .info = get_mdr_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -955,4 +955,5 @@ struct flash_driver mrvlqspi_flash = {
.erase_check = mrvlqspi_flash_erase_check, .erase_check = mrvlqspi_flash_erase_check,
.protect_check = mrvlqspi_protect_check, .protect_check = mrvlqspi_protect_check,
.info = mrvlqspi_get_info, .info = mrvlqspi_get_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1741,4 +1741,5 @@ struct flash_driver niietcm4_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = niietcm4_protect_check, .protect_check = niietcm4_protect_check,
.info = get_niietcm4_info, .info = get_niietcm4_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -155,6 +155,11 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256), NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256),
NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256), NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256),
/* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
with built-in jlink seem to use engineering samples not listed
in the nRF51 Series Compatibility Matrix V1.0. */
NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
/* nRF51822 Devices (IC rev 2). */ /* nRF51822 Devices (IC rev 2). */
NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256), NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256),
NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256), NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256),
@ -175,6 +180,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128), NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128),
NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256), NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256),
NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256), NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256),
NRF5_DEVICE_DEF(0x008F, "51822", "QFAA", "H1", 256),
/* nRF51422 Devices (IC rev 1). */ /* nRF51422 Devices (IC rev 1). */
NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256), NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256),
@ -198,11 +204,6 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
/* nRF52832 Devices */ /* nRF52832 Devices */
NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512), NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512),
/* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
with built-in jlink seem to use engineering samples not listed
in the nRF51 Series Compatibility Matrix V1.0. */
NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
}; };
static int nrf5_bank_is_probed(struct flash_bank *bank) static int nrf5_bank_is_probed(struct flash_bank *bank)

View File

@ -1880,4 +1880,5 @@ struct flash_driver numicro_flash = {
.auto_probe = numicro_auto_probe, .auto_probe = numicro_auto_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = numicro_protect_check, .protect_check = numicro_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -340,4 +340,5 @@ struct flash_driver ocl_flash = {
.erase_check = ocl_erase_check, .erase_check = ocl_erase_check,
.protect_check = ocl_protect_check, .protect_check = ocl_protect_check,
.auto_probe = ocl_auto_probe, .auto_probe = ocl_auto_probe,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -980,4 +980,5 @@ struct flash_driver pic32mx_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = pic32mx_protect_check, .protect_check = pic32mx_protect_check,
.info = pic32mx_info, .info = pic32mx_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -41,24 +41,67 @@
Document Number: 001-87197 Rev. *B Revised August 29, 2013 Document Number: 001-87197 Rev. *B Revised August 29, 2013
PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM
Document No. 001-85634 Rev. *C March 25, 2014 Document No. 001-85634 Rev. *E June 28, 2016
PSoC(R) 4 Registers TRM Spec. PSoC(R) 4 Registers TRM Spec.
Document No. 001-85847 Rev. *A June 25, 2013 Document No. 001-85847 Rev. *A June 25, 2013
PSoC 4000 Family PSoC(R) 4 Technical Reference Manual
Document No. 001-89309 Rev. *B May 9, 2016
PSoC 41XX_BLE/42XX_BLE Family PSoC 4 BLE Architecture TRM
Document No. 001-92738 Rev. *C February 12, 2016
PSoC 4200L Family PSoC 4 Architecture TRM
Document No. 001-97952 Rev. *A December 15, 2015
PSoC 4200L Family PSoC 4 Registers TRM
Document No. 001-98126 Rev. *A December 16, 2015
PSoC 4100M/4200M Family PSoC 4 Architecture TRM
Document No. 001-95223 Rev. *B July 29, 2015
PSoC 4100S Family PSoC 4 Architecture TRM
Document No. 002-10621 Rev. *A July 29, 2016
PSoC 4100S Family PSoC 4 Registers TRM
Document No. 002-10523 Rev. *A July 20, 2016
PSoC Analog Coprocessor Architecture TRM
Document No. 002-10404 Rev. ** December 18, 2015
CY8C4Axx PSoC Analog Coprocessor Registers TRM
Document No. 002-10405 Rev. ** December 18, 2015
CY8C41xx, CY8C42xx Programming Specifications CY8C41xx, CY8C42xx Programming Specifications
Document No. 001-81799 Rev. *C March 4, 2014 Document No. 001-81799 Rev. *C March 4, 2014
CYBL10x6x, CY8C4127_BL, CY8C4247_BL Programming Specifications
Document No. 001-91508 Rev. *B September 22, 2014
http://dmitry.gr/index.php?r=05.Projects&proj=24.%20PSoC4%20confidential
*/ */
/* register locations */ /* register locations */
#define PSOC4_CPUSS_SYSREQ 0x40000004 #define PSOC4_SFLASH_MACRO0 0x0FFFF000
#define PSOC4_CPUSS_SYSARG 0x40000008
#define PSOC4_TEST_MODE 0x40030014 #define PSOC4_CPUSS_SYSREQ_LEGACY 0x40000004
#define PSOC4_SPCIF_GEOMETRY 0x400E0000 #define PSOC4_CPUSS_SYSARG_LEGACY 0x40000008
#define PSOC4_SPCIF_GEOMETRY_LEGACY 0x400E0000
#define PSOC4_CPUSS_SYSREQ_NEW 0x40100004
#define PSOC4_CPUSS_SYSARG_NEW 0x40100008
#define PSOC4_SPCIF_GEOMETRY_NEW 0x40110000
#define PSOC4_TEST_MODE 0x40030014
#define PSOC4_ROMTABLE_PID0 0xF0000FE0
#define PSOC4_SFLASH_MACRO 0x0ffff000
/* constants */ /* constants */
#define PSOC4_SFLASH_MACRO_SIZE 0x800
#define PSOC4_ROWS_PER_MACRO 512
#define PSOC4_SROM_KEY1 0xb6 #define PSOC4_SROM_KEY1 0xb6
#define PSOC4_SROM_KEY2 0xd3 #define PSOC4_SROM_KEY2 0xd3
#define PSOC4_SROM_SYSREQ_BIT (1<<31) #define PSOC4_SROM_SYSREQ_BIT (1<<31)
@ -66,6 +109,10 @@
#define PSOC4_SROM_PRIVILEGED_BIT (1<<28) #define PSOC4_SROM_PRIVILEGED_BIT (1<<28)
#define PSOC4_SROM_STATUS_SUCCEEDED 0xa0000000 #define PSOC4_SROM_STATUS_SUCCEEDED 0xa0000000
#define PSOC4_SROM_STATUS_FAILED 0xf0000000 #define PSOC4_SROM_STATUS_FAILED 0xf0000000
#define PSOC4_SROM_STATUS_MASK 0xf0000000
/* not documented in any TRM */
#define PSOC4_SROM_ERR_IMO_NOT_IMPLEM 0xf0000013
#define PSOC4_CMD_GET_SILICON_ID 0 #define PSOC4_CMD_GET_SILICON_ID 0
#define PSOC4_CMD_LOAD_LATCH 4 #define PSOC4_CMD_LOAD_LATCH 4
@ -74,76 +121,60 @@
#define PSOC4_CMD_ERASE_ALL 0xa #define PSOC4_CMD_ERASE_ALL 0xa
#define PSOC4_CMD_CHECKSUM 0xb #define PSOC4_CMD_CHECKSUM 0xb
#define PSOC4_CMD_WRITE_PROTECTION 0xd #define PSOC4_CMD_WRITE_PROTECTION 0xd
#define PSOC4_CMD_SET_IMO48 0x15
#define PSOC4_CMD_WRITE_SFLASH_ROW 0x18
#define PSOC4_CHIP_PROT_VIRGIN 0x0 #define PSOC4_CHIP_PROT_VIRGIN 0x0
#define PSOC4_CHIP_PROT_OPEN 0x1 #define PSOC4_CHIP_PROT_OPEN 0x1
#define PSOC4_CHIP_PROT_PROTECTED 0x2 #define PSOC4_CHIP_PROT_PROTECTED 0x2
#define PSOC4_CHIP_PROT_KILL 0x4 #define PSOC4_CHIP_PROT_KILL 0x4
#define PSOC4_ROMTABLE_DESIGNER_CHECK 0xb4
struct psoc4_chip_details { #define PSOC4_FAMILY_FLAG_LEGACY 1
struct psoc4_chip_family {
uint16_t id; uint16_t id;
const char *type; const char *name;
const char *package; uint32_t flags;
uint32_t flash_size_in_kb;
}; };
/* list of PSoC 4 chips const struct psoc4_chip_family psoc4_families[] = {
* flash_size_in_kb is not necessary as it can be decoded from SPCIF_GEOMETRY { 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY },
*/ { 0x9A, "PSoC4000", .flags = 0 },
const struct psoc4_chip_details psoc4_devices[] = { { 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 },
/* 4200 series */ { 0xA0, "PSoC4200L", .flags = 0 },
{ 0x04A6, "CY8C4245PVI-482", "SSOP-28", .flash_size_in_kb = 32 }, { 0xA1, "PSoC4100M/4200M", .flags = 0 },
{ 0x04B6, "CY8C4245LQI-483", "QFN-40", .flash_size_in_kb = 32 }, { 0xA3, "PSoC/PRoC BLE (11A3)", .flags = 0 },
{ 0x04C8, "CY8C4245AXI-483", "TQFP-44", .flash_size_in_kb = 32 }, { 0xA9, "PSoC4000S", .flags = 0 },
{ 0x04FB, "CY8C4245AXI-473", "TQFP-44", .flash_size_in_kb = 32 }, { 0xAA, "PSoC/PRoC BLE (11AA)", .flags = 0 },
{ 0x04F0, "CY8C4244PVI-432", "SSOP-28", .flash_size_in_kb = 16 }, { 0xAB, "PSoC4100S", .flags = 0 },
{ 0x04F1, "CY8C4244PVI-442", "SSOP-28", .flash_size_in_kb = 16 }, { 0xAC, "PSoC Analog Coprocessor", .flags = 0 },
{ 0x04F6, "CY8C4244LQI-443", "QFN-40", .flash_size_in_kb = 16 }, { 0, "Unknown", .flags = 0 }
{ 0x04FA, "CY8C4244AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
/* 4100 series */
{ 0x0410, "CY8C4124PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
{ 0x0411, "CY8C4124PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
{ 0x041C, "CY8C4124LQI-443", "QFN-40", .flash_size_in_kb = 16 },
{ 0x041A, "CY8C4124AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
{ 0x041B, "CY8C4125AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
{ 0x0412, "CY8C4125PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
{ 0x0417, "CY8C4125LQI-483", "QFN-40", .flash_size_in_kb = 32 },
{ 0x0416, "CY8C4125AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
/* CCG1 series */
{ 0x0490, "CYPD1103-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
{ 0x0489, "CYPD1121-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
{ 0x048A, "CYPD1122-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
{ 0x0491, "CYPD1131-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
{ 0x0498, "CYPD1132-16SXI", "SOIC-16", .flash_size_in_kb = 32 },
{ 0x0481, "CYPD1134-28PVXI", "SSOP-28", .flash_size_in_kb = 32 },
{ 0x048B, "CYPD1134-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
}; };
struct psoc4_flash_bank { struct psoc4_flash_bank {
uint32_t row_size; uint32_t row_size;
uint32_t user_bank_size; uint32_t user_bank_size;
int probed; int num_macros;
uint32_t silicon_id; bool probed;
uint8_t chip_protection;
uint8_t cmd_program_row; uint8_t cmd_program_row;
uint16_t family_id;
bool legacy_family;
uint32_t cpuss_sysreq_addr;
uint32_t cpuss_sysarg_addr;
uint32_t spcif_geometry_addr;
}; };
static const struct psoc4_chip_details *psoc4_details_by_id(uint32_t silicon_id) static const struct psoc4_chip_family *psoc4_family_by_id(uint16_t family_id)
{ {
const struct psoc4_chip_details *p = psoc4_devices; const struct psoc4_chip_family *p = psoc4_families;
unsigned int i; while (p->id && p->id != family_id)
uint16_t id = silicon_id >> 16; /* ignore die revision */ p++;
for (i = 0; i < sizeof(psoc4_devices)/sizeof(psoc4_devices[0]); i++, p++) {
if (p->id == id) return p;
return p;
}
LOG_DEBUG("Unknown PSoC 4 device silicon id 0x%08" PRIx32 ".", silicon_id);
return NULL;
} }
static const char *psoc4_decode_chip_protection(uint8_t protection) static const char *psoc4_decode_chip_protection(uint8_t protection)
@ -176,7 +207,9 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank)); psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank));
bank->driver_priv = psoc4_info; bank->driver_priv = psoc4_info;
bank->default_padded_value = bank->erased_value = 0x00;
psoc4_info->user_bank_size = bank->size; psoc4_info->user_bank_size = bank->size;
psoc4_info->cmd_program_row = PSOC4_CMD_WRITE_ROW;
return ERROR_OK; return ERROR_OK;
} }
@ -189,9 +222,14 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
* Otherwise address of memory parameter block is set in CPUSS_SYSARG * Otherwise address of memory parameter block is set in CPUSS_SYSARG
* and the first parameter is written to the first word of parameter block * and the first parameter is written to the first word of parameter block
*/ */
static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, static int psoc4_sysreq(struct flash_bank *bank, uint8_t cmd,
uint32_t *sysreq_params, uint32_t sysreq_params_size) uint16_t cmd_param,
uint32_t *sysreq_params, uint32_t sysreq_params_size,
uint32_t *sysarg_out)
{ {
struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
struct working_area *sysreq_wait_algorithm; struct working_area *sysreq_wait_algorithm;
struct working_area *sysreq_mem; struct working_area *sysreq_mem;
@ -212,8 +250,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4; const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4;
/* stack must be aligned */ /* stack must be aligned */
const int stack_size = 196; const int stack_size = 256;
/* tested stack sizes on PSoC 4: /* tested stack sizes on PSoC4200:
ERASE_ALL 144 ERASE_ALL 144
PROGRAM_ROW 112 PROGRAM_ROW 112
other sysreq 68 other sysreq 68
@ -238,6 +276,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
} }
if (sysreq_params_size) { if (sysreq_params_size) {
LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32 " size %" PRIu32,
cmd, cmd_param, param1, sysreq_params_size);
/* Allocate memory for sysreq_params */ /* Allocate memory for sysreq_params */
retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem); retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
@ -250,21 +290,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
} }
/* Write sysreq_params */ /* Write sysreq_params */
sysreq_params[0] = param1; target_buffer_set_u32(target, (uint8_t *)sysreq_params, param1);
retval = target_write_buffer(target, sysreq_mem->address, retval = target_write_buffer(target, sysreq_mem->address,
sysreq_params_size, (uint8_t *)sysreq_params); sysreq_params_size, (uint8_t *)sysreq_params);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup_mem; goto cleanup_mem;
/* Set address of sysreq parameters block */ /* Set address of sysreq parameters block */
retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, sysreq_mem->address); retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, sysreq_mem->address);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup_mem; goto cleanup_mem;
} else { } else {
/* Sysreq without memory block of parameters */ /* Sysreq without memory block of parameters */
LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32,
cmd, cmd_param, param1);
/* Set register parameter */ /* Set register parameter */
retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, param1); retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, param1);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup_mem; goto cleanup_mem;
} }
@ -279,14 +321,14 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
struct armv7m_common *armv7m = target_to_armv7m(target); struct armv7m_common *armv7m = target_to_armv7m(target);
if (armv7m == NULL) { if (armv7m == NULL) {
/* something is very wrong if armv7m is NULL */ /* something is very wrong if armv7m is NULL */
LOG_ERROR("unable to get armv7m target"); LOG_ERROR("unable to get armv7m target");
retval = ERROR_FAIL;
goto cleanup; goto cleanup;
} }
/* Set SROM request */ /* Set SROM request */
retval = target_write_u32(target, PSOC4_CPUSS_SYSREQ, retval = target_write_u32(target, psoc4_info->cpuss_sysreq_addr,
PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd); PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup; goto cleanup;
@ -295,9 +337,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
retval = target_run_algorithm(target, 0, NULL, retval = target_run_algorithm(target, 0, NULL,
sizeof(reg_params) / sizeof(*reg_params), reg_params, sizeof(reg_params) / sizeof(*reg_params), reg_params,
sysreq_wait_algorithm->address, 0, 1000, &armv7m_info); sysreq_wait_algorithm->address, 0, 1000, &armv7m_info);
if (retval != ERROR_OK) if (retval != ERROR_OK) {
LOG_ERROR("sysreq wait code execution failed"); LOG_ERROR("sysreq wait code execution failed");
goto cleanup;
}
uint32_t sysarg_out_tmp;
retval = target_read_u32(target, psoc4_info->cpuss_sysarg_addr, &sysarg_out_tmp);
if (retval != ERROR_OK)
goto cleanup;
if (sysarg_out) {
*sysarg_out = sysarg_out_tmp;
/* If result is an error, do not show now, let caller to decide */
} else if ((sysarg_out_tmp & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
LOG_ERROR("sysreq error 0x%" PRIx32, sysarg_out_tmp);
retval = ERROR_FAIL;
}
cleanup: cleanup:
destroy_reg_param(&reg_params[0]); destroy_reg_param(&reg_params[0]);
@ -313,42 +369,123 @@ cleanup_algo:
/* helper routine to get silicon ID from a PSoC 4 chip */ /* helper routine to get silicon ID from a PSoC 4 chip */
static int psoc4_get_silicon_id(struct target *target, uint32_t *silicon_id, uint8_t *protection) static int psoc4_get_silicon_id(struct flash_bank *bank, uint32_t *silicon_id, uint16_t *family_id, uint8_t *protection)
{ {
uint32_t params = PSOC4_SROM_KEY1 struct target *target = bank->target;
| ((PSOC4_SROM_KEY2 + PSOC4_CMD_GET_SILICON_ID) << 8); struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
uint32_t part0, part1; uint32_t part0, part1;
int retval = psoc4_sysreq(target, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0); int retval = psoc4_sysreq(bank, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0, &part0);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
retval = target_read_u32(target, PSOC4_CPUSS_SYSARG, &part0); if ((part0 & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
if (retval != ERROR_OK) LOG_ERROR("sysreq error 0x%" PRIx32, part0);
return retval;
if (part0 == params) {
LOG_ERROR("sysreq silicon id request not served");
return ERROR_FAIL; return ERROR_FAIL;
} }
retval = target_read_u32(target, PSOC4_CPUSS_SYSREQ, &part1); retval = target_read_u32(target, psoc4_info->cpuss_sysreq_addr, &part1);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
uint32_t silicon = ((part0 & 0xffff) << 16) /* build ID as Cypress sw does:
| (((part0 >> 16) & 0xff) << 8) * bit 31..16 silicon ID
| (part1 & 0xff); * bit 15..8 revision ID (so far 0x11 for all devices)
uint8_t prot = (part1 >> 12) & 0xff; * bit 7..0 family ID (lowes 8 bits)
*/
if (silicon_id) if (silicon_id)
*silicon_id = silicon; *silicon_id = ((part0 & 0x0000ffff) << 16)
if (protection) | ((part0 & 0x00ff0000) >> 8)
*protection = prot; | (part1 & 0x000000ff);
LOG_DEBUG("silicon id: 0x%08" PRIx32 "", silicon); if (family_id)
LOG_DEBUG("protection: 0x%02" PRIx8 "", prot); *family_id = part1 & 0x0fff;
return retval;
if (protection)
*protection = (part1 >> 12) & 0x0f;
return ERROR_OK;
}
static int psoc4_get_family(struct target *target, uint16_t *family_id)
{
int retval, i;
uint32_t pidbf[3];
uint8_t pid[3];
retval = target_read_memory(target, PSOC4_ROMTABLE_PID0, 4, 3, (uint8_t *)pidbf);
if (retval != ERROR_OK)
return retval;
for (i = 0; i < 3; i++) {
uint32_t tmp = target_buffer_get_u32(target, (uint8_t *)(pidbf + i));
if (tmp & 0xffffff00) {
LOG_ERROR("Unexpected data in ROMTABLE");
return ERROR_FAIL;
}
pid[i] = tmp & 0xff;
}
uint16_t family = pid[0] | ((pid[1] & 0xf) << 8);
uint32_t designer = ((pid[1] & 0xf0) >> 4) | ((pid[2] & 0xf) << 4);
if (designer != PSOC4_ROMTABLE_DESIGNER_CHECK) {
LOG_ERROR("ROMTABLE designer is not Cypress");
return ERROR_FAIL;
}
*family_id = family;
return ERROR_OK;
}
static int psoc4_flash_prepare(struct flash_bank *bank)
{
struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
uint16_t family_id;
int retval;
/* get family ID from SROM call */
retval = psoc4_get_silicon_id(bank, NULL, &family_id, NULL);
if (retval != ERROR_OK)
return retval;
/* and check with family ID from ROMTABLE */
if (family_id != psoc4_info->family_id) {
LOG_ERROR("Family mismatch");
return ERROR_FAIL;
}
if (!psoc4_info->legacy_family) {
uint32_t sysreq_status;
retval = psoc4_sysreq(bank, PSOC4_CMD_SET_IMO48, 0, NULL, 0, &sysreq_status);
if (retval != ERROR_OK)
return retval;
if ((sysreq_status & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
/* This undocumented error code is returned probably when
* PSOC4_CMD_SET_IMO48 command is not implemented.
* Can be safely ignored, programming works.
*/
if (sysreq_status == PSOC4_SROM_ERR_IMO_NOT_IMPLEM)
LOG_INFO("PSOC4_CMD_SET_IMO48 is not implemented on this device.");
else {
LOG_ERROR("sysreq error 0x%" PRIx32, sysreq_status);
return ERROR_FAIL;
}
}
}
return ERROR_OK;
} }
@ -357,48 +494,37 @@ static int psoc4_protect_check(struct flash_bank *bank)
struct target *target = bank->target; struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
uint32_t prot_addr = PSOC4_SFLASH_MACRO; uint32_t prot_addr = PSOC4_SFLASH_MACRO0;
uint32_t protection; int retval;
int i, s; int s = 0;
int num_bits; int m, i;
int retval = ERROR_OK; uint8_t bf[PSOC4_ROWS_PER_MACRO/8];
num_bits = bank->num_sectors; for (m = 0; m < psoc4_info->num_macros; m++, prot_addr += PSOC4_SFLASH_MACRO_SIZE) {
retval = target_read_memory(target, prot_addr, 4, PSOC4_ROWS_PER_MACRO/32, bf);
for (i = 0; i < num_bits; i += 32) {
retval = target_read_u32(target, prot_addr, &protection);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
prot_addr += 4; for (i = 0; i < PSOC4_ROWS_PER_MACRO && s < bank->num_sectors; i++, s++)
bank->sectors[s].is_protected = bf[i/8] & (1 << (i%8)) ? 1 : 0;
for (s = 0; s < 32; s++) {
if (i + s >= num_bits)
break;
bank->sectors[i + s].is_protected = (protection & (1 << s)) ? 1 : 0;
}
} }
retval = psoc4_get_silicon_id(target, NULL, &(psoc4_info->chip_protection)); return ERROR_OK;
return retval;
} }
static int psoc4_mass_erase(struct flash_bank *bank) static int psoc4_mass_erase(struct flash_bank *bank)
{ {
struct target *target = bank->target;
int i; int i;
int retval = psoc4_flash_prepare(bank);
if (bank->target->state != TARGET_HALTED) { if (retval != ERROR_OK)
LOG_ERROR("Target not halted"); return retval;
return ERROR_TARGET_NOT_HALTED;
}
/* Call "Erase All" system ROM API */ /* Call "Erase All" system ROM API */
uint32_t param; uint32_t param = 0;
int retval = psoc4_sysreq(target, PSOC4_CMD_ERASE_ALL, retval = psoc4_sysreq(bank, PSOC4_CMD_ERASE_ALL,
0, 0,
&param, sizeof(param)); &param, sizeof(param), NULL);
if (retval == ERROR_OK) if (retval == ERROR_OK)
/* set all sectors as erased */ /* set all sectors as erased */
@ -420,7 +546,7 @@ static int psoc4_erase(struct flash_bank *bank, int first, int last)
if ((first == 0) && (last == (bank->num_sectors - 1))) if ((first == 0) && (last == (bank->num_sectors - 1)))
return psoc4_mass_erase(bank); return psoc4_mass_erase(bank);
LOG_ERROR("Only mass erase available"); LOG_ERROR("Only mass erase available! Consider using 'psoc4 flash_autoerase 0 on'");
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -431,56 +557,63 @@ static int psoc4_protect(struct flash_bank *bank, int set, int first, int last)
struct target *target = bank->target; struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
if (psoc4_info->probed == 0) if (!psoc4_info->probed)
return ERROR_FAIL; return ERROR_FAIL;
if (target->state != TARGET_HALTED) { int retval = psoc4_flash_prepare(bank);
LOG_ERROR("Target not halted"); if (retval != ERROR_OK)
return ERROR_TARGET_NOT_HALTED; return retval;
}
uint32_t *sysrq_buffer = NULL; uint32_t *sysrq_buffer = NULL;
int retval;
int num_bits = bank->num_sectors;
const int param_sz = 8; const int param_sz = 8;
int prot_sz = num_bits / 8;
int chip_prot = PSOC4_CHIP_PROT_OPEN; int chip_prot = PSOC4_CHIP_PROT_OPEN;
int flash_macro = 0; /* PSoC 42xx has only macro 0 */ int i, m, sect;
int i; int num_bits = bank->num_sectors;
sysrq_buffer = calloc(1, param_sz + prot_sz); if (num_bits > PSOC4_ROWS_PER_MACRO)
num_bits = PSOC4_ROWS_PER_MACRO;
int prot_sz = num_bits / 8;
sysrq_buffer = malloc(param_sz + prot_sz);
if (sysrq_buffer == NULL) { if (sysrq_buffer == NULL) {
LOG_ERROR("no memory for row buffer"); LOG_ERROR("no memory for row buffer");
return ERROR_FAIL; return ERROR_FAIL;
} }
for (i = first; i < num_bits && i <= last; i++) for (i = first; i <= last && i < bank->num_sectors; i++)
bank->sectors[i].is_protected = set; bank->sectors[i].is_protected = set;
uint32_t *p = sysrq_buffer + 2; for (m = 0, sect = 0; m < psoc4_info->num_macros; m++) {
for (i = 0; i < num_bits; i++) { uint8_t *p = (uint8_t *)(sysrq_buffer + 2);
if (bank->sectors[i].is_protected) memset(p, 0, prot_sz);
p[i / 32] |= 1 << (i % 32); for (i = 0; i < num_bits && sect < bank->num_sectors; i++, sect++) {
if (bank->sectors[sect].is_protected)
p[i/8] |= 1 << (i%8);
}
/* Call "Load Latch" system ROM API */
target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
prot_sz - 1);
retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
0 /* Byte number in latch from what to write */
| (m << 8), /* flash macro index */
sysrq_buffer, param_sz + prot_sz,
NULL);
if (retval != ERROR_OK)
break;
/* Call "Write Protection" system ROM API */
retval = psoc4_sysreq(bank, PSOC4_CMD_WRITE_PROTECTION,
chip_prot | (m << 8), NULL, 0, NULL);
if (retval != ERROR_OK)
break;
} }
/* Call "Load Latch" system ROM API */
sysrq_buffer[1] = prot_sz - 1;
retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
0, /* Byte number in latch from what to write */
sysrq_buffer, param_sz + psoc4_info->row_size);
if (retval != ERROR_OK)
goto cleanup;
/* Call "Write Protection" system ROM API */
retval = psoc4_sysreq(target, PSOC4_CMD_WRITE_PROTECTION,
chip_prot | (flash_macro << 8), NULL, 0);
cleanup:
if (retval != ERROR_OK)
psoc4_protect_check(bank);
if (sysrq_buffer) if (sysrq_buffer)
free(sysrq_buffer); free(sysrq_buffer);
psoc4_protect_check(bank);
return retval; return retval;
} }
@ -516,21 +649,14 @@ COMMAND_HANDLER(psoc4_handle_flash_autoerase_command)
static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer, static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count) uint32_t offset, uint32_t count)
{ {
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
struct target *target = bank->target; struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
uint32_t *sysrq_buffer = NULL; uint32_t *sysrq_buffer = NULL;
int retval = ERROR_OK;
const int param_sz = 8; const int param_sz = 8;
if (bank->target->state != TARGET_HALTED) { int retval = psoc4_flash_prepare(bank);
LOG_ERROR("Target not halted"); if (retval != ERROR_OK)
return ERROR_TARGET_NOT_HALTED; return retval;
}
if (offset & 0x1) {
LOG_ERROR("offset 0x%08" PRIx32 " breaks required 2-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
sysrq_buffer = malloc(param_sz + psoc4_info->row_size); sysrq_buffer = malloc(param_sz + psoc4_info->row_size);
if (sysrq_buffer == NULL) { if (sysrq_buffer == NULL) {
@ -542,7 +668,7 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t row_num = offset / psoc4_info->row_size; uint32_t row_num = offset / psoc4_info->row_size;
uint32_t row_offset = offset - row_num * psoc4_info->row_size; uint32_t row_offset = offset - row_num * psoc4_info->row_size;
if (row_offset) if (row_offset)
memset(row_buffer, 0, row_offset); memset(row_buffer, bank->default_padded_value, row_offset);
bool save_poll = jtag_poll_get_enabled(); bool save_poll = jtag_poll_get_enabled();
jtag_poll_set_enabled(false); jtag_poll_set_enabled(false);
@ -551,25 +677,31 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t chunk_size = psoc4_info->row_size - row_offset; uint32_t chunk_size = psoc4_info->row_size - row_offset;
if (chunk_size > count) { if (chunk_size > count) {
chunk_size = count; chunk_size = count;
memset(row_buffer + chunk_size, 0, psoc4_info->row_size - chunk_size); memset(row_buffer + chunk_size, bank->default_padded_value, psoc4_info->row_size - chunk_size);
} }
memcpy(row_buffer + row_offset, buffer, chunk_size); memcpy(row_buffer + row_offset, buffer, chunk_size);
LOG_DEBUG("offset / row: 0x%08" PRIx32 " / %" PRIu32 ", size %" PRIu32 "", LOG_DEBUG("offset / row: 0x%08" PRIx32 " / %" PRIu32 ", size %" PRIu32 "",
offset, row_offset, chunk_size); offset, row_offset, chunk_size);
uint32_t macro_idx = row_num / PSOC4_ROWS_PER_MACRO;
/* Call "Load Latch" system ROM API */ /* Call "Load Latch" system ROM API */
sysrq_buffer[1] = psoc4_info->row_size - 1; target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH, psoc4_info->row_size - 1);
0, /* Byte number in latch from what to write */ retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
sysrq_buffer, param_sz + psoc4_info->row_size); 0 /* Byte number in latch from what to write */
| (macro_idx << 8),
sysrq_buffer, param_sz + psoc4_info->row_size,
NULL);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup; goto cleanup;
/* Call "Program Row" or "Write Row" system ROM API */ /* Call "Program Row" or "Write Row" system ROM API */
uint32_t sysrq_param; uint32_t sysrq_param;
retval = psoc4_sysreq(target, psoc4_info->cmd_program_row, retval = psoc4_sysreq(bank, psoc4_info->cmd_program_row,
row_num & 0xffff, row_num & 0xffff,
&sysrq_param, sizeof(sysrq_param)); &sysrq_param, sizeof(sysrq_param),
NULL);
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto cleanup; goto cleanup;
@ -589,84 +721,82 @@ cleanup:
} }
/* Due to Cypress's method of market segmentation some devices
* have accessible only 1/2, 1/4 or 1/8 of SPCIF described flash */
static int psoc4_test_flash_wounding(struct target *target, uint32_t flash_size)
{
int retval, i;
for (i = 3; i >= 1; i--) {
uint32_t addr = flash_size >> i;
uint32_t dummy;
retval = target_read_u32(target, addr, &dummy);
if (retval != ERROR_OK)
return i;
}
return 0;
}
static int psoc4_probe(struct flash_bank *bank) static int psoc4_probe(struct flash_bank *bank)
{ {
struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
struct target *target = bank->target; struct target *target = bank->target;
uint32_t flash_size_in_kb = 0;
uint32_t max_flash_size_in_kb;
uint32_t cpu_id;
uint32_t silicon_id;
uint32_t row_size;
uint32_t base_address = 0x00000000;
uint8_t protection;
if (target->state != TARGET_HALTED) { int retval;
LOG_ERROR("Target not halted"); uint16_t family_id;
return ERROR_TARGET_NOT_HALTED;
}
psoc4_info->probed = 0; psoc4_info->probed = false;
psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW;
/* Get the CPUID from the ARM Core retval = psoc4_get_family(target, &family_id);
* http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */
int retval = target_read_u32(target, 0xE000ED00, &cpu_id);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
LOG_DEBUG("cpu id = 0x%08" PRIx32 "", cpu_id); const struct psoc4_chip_family *family = psoc4_family_by_id(family_id);
/* set page size, protection granularity and max flash size depending on family */ if (family->id == 0) {
switch ((cpu_id >> 4) & 0xFFF) { LOG_ERROR("Cannot identify PSoC 4 family.");
case 0xc20: /* M0 -> PSoC4 */
row_size = 128;
max_flash_size_in_kb = 32;
break;
default:
LOG_WARNING("Cannot identify target as a PSoC 4 family.");
return ERROR_FAIL; return ERROR_FAIL;
} }
uint32_t spcif_geometry; if (family->flags & PSOC4_FAMILY_FLAG_LEGACY) {
retval = target_read_u32(target, PSOC4_SPCIF_GEOMETRY, &spcif_geometry); LOG_INFO("%s legacy family detected.", family->name);
if (retval == ERROR_OK) { psoc4_info->legacy_family = true;
row_size = 128 * ((spcif_geometry >> 22) & 3); psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_LEGACY;
flash_size_in_kb = (spcif_geometry & 0xffff) * 256 / 1024; psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_LEGACY;
LOG_INFO("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.", psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_LEGACY;
flash_size_in_kb, row_size); } else {
LOG_INFO("%s family detected.", family->name);
psoc4_info->legacy_family = false;
psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_NEW;
psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_NEW;
psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_NEW;
} }
/* Early revisions of ST-Link v2 have some problem reading PSOC4_SPCIF_GEOMETRY uint32_t spcif_geometry;
and an error is reported late. Dummy read gets this error. */ retval = target_read_u32(target, psoc4_info->spcif_geometry_addr, &spcif_geometry);
uint32_t dummy;
target_read_u32(target, PSOC4_CPUSS_SYSREQ, &dummy);
/* get silicon ID from target. */
retval = psoc4_get_silicon_id(target, &silicon_id, &protection);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
const struct psoc4_chip_details *details = psoc4_details_by_id(silicon_id); uint32_t flash_size_in_kb = spcif_geometry & 0x3fff;
if (details) { /* TRM of legacy, M and L version describes FLASH field as 16-bit.
LOG_INFO("%s device detected.", details->type); * S-series and PSoC Analog Coprocessor changes spec to 14-bit only.
if (flash_size_in_kb == 0) * Impose PSoC Analog Coprocessor limit to all devices as it
flash_size_in_kb = details->flash_size_in_kb; * does not make any harm: flash size is safely below 4 MByte limit
else if (flash_size_in_kb != details->flash_size_in_kb) */
LOG_ERROR("Flash size mismatch"); uint32_t row_size = (spcif_geometry >> 22) & 3;
uint32_t num_macros = (spcif_geometry >> 20) & 3;
if (psoc4_info->legacy_family) {
flash_size_in_kb = flash_size_in_kb * 256 / 1024;
row_size *= 128;
} else {
flash_size_in_kb = (flash_size_in_kb + 1) * 256 / 1024;
row_size = 64 * (row_size + 1);
num_macros++;
} }
psoc4_info->row_size = row_size; LOG_DEBUG("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.",
psoc4_info->silicon_id = silicon_id; flash_size_in_kb, row_size);
psoc4_info->chip_protection = protection;
/* failed reading flash size or flash size invalid (early silicon),
* default to max target family */
if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
LOG_WARNING("PSoC 4 flash size failed, probe inaccurate - assuming %" PRIu32 " k flash",
max_flash_size_in_kb);
flash_size_in_kb = max_flash_size_in_kb;
}
/* if the user sets the size manually then ignore the probed value /* if the user sets the size manually then ignore the probed value
* this allows us to work around devices that have a invalid flash size register value */ * this allows us to work around devices that have a invalid flash size register value */
@ -675,37 +805,44 @@ static int psoc4_probe(struct flash_bank *bank)
flash_size_in_kb = psoc4_info->user_bank_size / 1024; flash_size_in_kb = psoc4_info->user_bank_size / 1024;
} }
LOG_INFO("flash size = %" PRIu32 " kbytes", flash_size_in_kb); char macros_txt[20] = "";
if (num_macros > 1)
snprintf(macros_txt, sizeof(macros_txt), " in %" PRIu32 " macros", num_macros);
/* did we assign flash size? */ LOG_INFO("flash size = %" PRIu32 " kbytes%s", flash_size_in_kb, macros_txt);
assert(flash_size_in_kb != 0xffff);
/* calculate numbers of pages */ /* calculate number of pages */
uint32_t num_rows = flash_size_in_kb * 1024 / row_size; uint32_t num_rows = flash_size_in_kb * 1024 / row_size;
/* check that calculation result makes sense */ /* check number of flash macros */
assert(num_rows > 0); if (num_macros != (num_rows + PSOC4_ROWS_PER_MACRO - 1) / PSOC4_ROWS_PER_MACRO)
LOG_WARNING("Number of macros does not correspond with flash size!");
if (!psoc4_info->legacy_family) {
int wounding = psoc4_test_flash_wounding(target, num_rows * row_size);
if (wounding > 0) {
flash_size_in_kb = flash_size_in_kb >> wounding;
num_rows = num_rows >> wounding;
LOG_INFO("WOUNDING detected: accessible flash size %" PRIu32 " kbytes", flash_size_in_kb);
}
}
if (bank->sectors) { if (bank->sectors) {
free(bank->sectors); free(bank->sectors);
bank->sectors = NULL;
} }
bank->base = base_address; psoc4_info->family_id = family_id;
psoc4_info->num_macros = num_macros;
psoc4_info->row_size = row_size;
bank->base = 0x00000000;
bank->size = num_rows * row_size; bank->size = num_rows * row_size;
bank->num_sectors = num_rows; bank->num_sectors = num_rows;
bank->sectors = malloc(sizeof(struct flash_sector) * num_rows); bank->sectors = alloc_block_array(0, row_size, num_rows);
if (bank->sectors == NULL)
return ERROR_FAIL;
uint32_t i; LOG_DEBUG("flash bank set %" PRIu32 " rows", num_rows);
for (i = 0; i < num_rows; i++) { psoc4_info->probed = true;
bank->sectors[i].offset = i * row_size;
bank->sectors[i].size = row_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
LOG_INFO("flash bank set %" PRIu32 " rows", num_rows);
psoc4_info->probed = 1;
return ERROR_OK; return ERROR_OK;
} }
@ -721,28 +858,45 @@ static int psoc4_auto_probe(struct flash_bank *bank)
static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size) static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size)
{ {
struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
int printed = 0;
if (psoc4_info->probed == 0) if (!psoc4_info->probed)
return ERROR_FAIL; return ERROR_FAIL;
const struct psoc4_chip_details *details = psoc4_details_by_id(psoc4_info->silicon_id); const struct psoc4_chip_family *family = psoc4_family_by_id(psoc4_info->family_id);
uint32_t size_in_kb = bank->size / 1024;
if (details) { if (target->state != TARGET_HALTED) {
uint32_t chip_revision = psoc4_info->silicon_id & 0xffff; snprintf(buf, buf_size, "%s, flash %" PRIu32 " kb"
printed = snprintf(buf, buf_size, "PSoC 4 %s rev 0x%04" PRIx32 " package %s", " (halt target to see details)", family->name, size_in_kb);
details->type, chip_revision, details->package); return ERROR_OK;
} else }
printed = snprintf(buf, buf_size, "PSoC 4 silicon id 0x%08" PRIx32 "",
psoc4_info->silicon_id); int retval;
int printed = 0;
uint32_t silicon_id;
uint16_t family_id;
uint8_t protection;
retval = psoc4_get_silicon_id(bank, &silicon_id, &family_id, &protection);
if (retval != ERROR_OK)
return retval;
if (family_id != psoc4_info->family_id)
printed = snprintf(buf, buf_size, "Family id mismatch 0x%02" PRIx16
"/0x%02" PRIx16 ", silicon id 0x%08" PRIx32,
psoc4_info->family_id, family_id, silicon_id);
else {
printed = snprintf(buf, buf_size, "%s silicon id 0x%08" PRIx32 "",
family->name, silicon_id);
}
buf += printed; buf += printed;
buf_size -= printed; buf_size -= printed;
const char *prot_txt = psoc4_decode_chip_protection(psoc4_info->chip_protection); const char *prot_txt = psoc4_decode_chip_protection(protection);
uint32_t size_in_kb = bank->size / 1024; snprintf(buf, buf_size, ", flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
snprintf(buf, buf_size, " flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
return ERROR_OK; return ERROR_OK;
} }
@ -809,4 +963,5 @@ struct flash_driver psoc4_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = psoc4_protect_check, .protect_check = psoc4_protect_check,
.info = get_psoc4_info, .info = get_psoc4_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

986
src/flash/nor/psoc6.c Normal file
View File

@ -0,0 +1,986 @@
/***************************************************************************
* *
* Copyright (C) 2017 by Bohdan Tymkiv *
* bohdan.tymkiv@cypress.com bohdan200@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <time.h>
#include "imp.h"
#include "target/target.h"
#include "target/cortex_m.h"
#include "target/breakpoints.h"
#include "target/target_type.h"
#include "time_support.h"
#include "target/algorithm.h"
/**************************************************************************************************
* PSoC6 device definitions
*************************************************************************************************/
#define MFLASH_SECTOR_SIZE (256u * 1024u)
#define WFLASH_SECTOR_SIZE (32u * 1024u)
#define MEM_BASE_MFLASH 0x10000000u
#define MEM_BASE_WFLASH 0x14000000u
#define MEM_WFLASH_SIZE 32768u
#define MEM_BASE_SFLASH 0x16000000u
#define RAM_STACK_WA_SIZE 2048u
#define PSOC6_SPCIF_GEOMETRY 0x4025F00Cu
#define PROTECTION_UNKNOWN 0x00u
#define PROTECTION_VIRGIN 0x01u
#define PROTECTION_NORMAL 0x02u
#define PROTECTION_SECURE 0x03u
#define PROTECTION_DEAD 0x04u
#define MEM_BASE_IPC 0x40230000u
#define IPC_STRUCT_SIZE 0x20u
#define MEM_IPC(n) (MEM_BASE_IPC + (n) * IPC_STRUCT_SIZE)
#define MEM_IPC_ACQUIRE(n) (MEM_IPC(n) + 0x00u)
#define MEM_IPC_NOTIFY(n) (MEM_IPC(n) + 0x08u)
#define MEM_IPC_DATA(n) (MEM_IPC(n) + 0x0Cu)
#define MEM_IPC_LOCK_STATUS(n) (MEM_IPC(n) + 0x10u)
#define MEM_BASE_IPC_INTR 0x40231000u
#define IPC_INTR_STRUCT_SIZE 0x20u
#define MEM_IPC_INTR(n) (MEM_BASE_IPC_INTR + (n) * IPC_INTR_STRUCT_SIZE)
#define MEM_IPC_INTR_MASK(n) (MEM_IPC_INTR(n) + 0x08u)
#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u
#define IPC_LOCK_ACQUIRED_MSK 0x80000000u
#define IPC_ID 2u
#define IPC_INTR_ID 0u
#define IPC_TIMEOUT_MS 1000
#define SROMAPI_SIID_REQ 0x00000001u
#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u)
#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u)
#define SROMAPI_WRITEROW_REQ 0x05000100u
#define SROMAPI_PROGRAMROW_REQ 0x06000100u
#define SROMAPI_ERASESECTOR_REQ 0x14000100u
#define SROMAPI_ERASEALL_REQ 0x0A000100u
#define SROMAPI_ERASEROW_REQ 0x1C000100u
#define SROMAPI_STATUS_MSK 0xF0000000u
#define SROMAPI_STAT_SUCCESS 0xA0000000u
#define SROMAPI_DATA_LOCATION_MSK 0x00000001u
#define SROMAPI_CALL_TIMEOUT_MS 1500
struct psoc6_target_info {
uint32_t silicon_id;
uint8_t protection;
uint32_t main_flash_sz;
uint32_t row_sz;
bool is_probed;
};
struct timeout {
int64_t start_time;
long timeout_ms;
};
struct row_region {
uint32_t addr;
size_t size;
};
static struct row_region safe_sflash_regions[] = {
{0x16000800, 0x800}, /* SFLASH: User Data */
{0x16001A00, 0x200}, /* SFLASH: NAR */
{0x16005A00, 0xC00}, /* SFLASH: Public Key */
{0x16007C00, 0x400}, /* SFLASH: TOC2 */
};
#define SFLASH_NUM_REGIONS (sizeof(safe_sflash_regions) / sizeof(safe_sflash_regions[0]))
static struct working_area *g_stack_area;
/**************************************************************************************************
* Initializes timeout_s structure with given timeout in milliseconds
*************************************************************************************************/
static void timeout_init(struct timeout *to, long timeout_ms)
{
to->start_time = timeval_ms();
to->timeout_ms = timeout_ms;
}
/**************************************************************************************************
* Returns true if given timeout_s object has expired
*************************************************************************************************/
static bool timeout_expired(struct timeout *to)
{
return (timeval_ms() - to->start_time) > to->timeout_ms;
}
/**************************************************************************************************
* Prepares PSoC6 for running pseudo flash algorithm. This function allocates Working Area for
* the algorithm and for CPU Stack.
*************************************************************************************************/
static int sromalgo_prepare(struct target *target)
{
int hr;
/* Initialize Vector Table Offset register (in case FW modified it) */
hr = target_write_u32(target, 0xE000ED08, 0x00000000);
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)
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)
goto exit_free_wa;
return ERROR_OK;
exit_free_wa:
/* Something went wrong, free allocated area */
if (g_stack_area) {
target_free_working_area(target, g_stack_area);
g_stack_area = NULL;
}
return hr;
}
/**************************************************************************************************
* Releases working area
*************************************************************************************************/
static int sromalgo_release(struct target *target)
{
int hr = ERROR_OK;
/* Free Stack/Flash algorithm working area */
if (g_stack_area) {
hr = target_free_working_area(target, g_stack_area);
g_stack_area = NULL;
}
return hr;
}
/**************************************************************************************************
* Runs pseudo flash algorithm. Algorithm itself consist of couple of NOPs followed by BKPT
* instruction. The trick here is that NMI has already been posted to CM0 via IPC structure
* prior to calling this function. CM0 will immediately jump to NMI handler and execute
* SROM API code.
* This approach is borrowed from PSoC4 Flash Driver.
*************************************************************************************************/
static int sromalgo_run(struct target *target)
{
int hr;
struct armv7m_algorithm armv7m_info;
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
struct reg_param reg_params;
init_reg_param(&reg_params, "sp", 32, PARAM_OUT);
buf_set_u32(reg_params.value, 0, 32, g_stack_area->address + g_stack_area->size);
/* mov r8, r8; mov r8, r8 */
hr = target_write_u32(target, g_stack_area->address + 0, 0x46C046C0);
if (hr != ERROR_OK)
return hr;
/* mov r8, r8; bkpt #0 */
hr = target_write_u32(target, g_stack_area->address + 4, 0xBE0046C0);
if (hr != ERROR_OK)
return hr;
hr = target_run_algorithm(target, 0, NULL, 1, &reg_params, g_stack_area->address,
0, SROMAPI_CALL_TIMEOUT_MS, &armv7m_info);
destroy_reg_param(&reg_params);
return hr;
}
/**************************************************************************************************
* Waits for expected IPC lock status.
* PSoC6 uses IPC structures for inter-core communication. Same IPCs are used to invoke SROM API.
* IPC structure must be locked prior to invoking any SROM API. This ensures nothing else in the
* system will use same IPC thus corrupting our data. Locking is performed by ipc_acquire(), this
* function ensures that IPC is actually in expected state
*************************************************************************************************/
static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_expected)
{
int hr;
uint32_t reg_val;
struct timeout to;
timeout_init(&to, IPC_TIMEOUT_MS);
while (!timeout_expired(&to)) {
/* Process any server requests */
keep_alive();
/* Read IPC Lock status */
hr = target_read_u32(target, MEM_IPC_LOCK_STATUS(ipc_id), &reg_val);
if (hr != ERROR_OK) {
LOG_ERROR("Unable to read IPC Lock Status register");
return hr;
}
bool is_locked = (reg_val & IPC_LOCK_ACQUIRED_MSK) != 0;
if (lock_expected == is_locked)
return ERROR_OK;
}
if (target->coreid) {
LOG_WARNING("SROM API calls via CM4 target are supported on single-core PSoC6 devices only. "
"Please perform all Flash-related operations via CM0+ target on dual-core devices.");
}
LOG_ERROR("Timeout polling IPC Lock Status");
return ERROR_TARGET_TIMEOUT;
}
/**************************************************************************************************
* Acquires IPC structure
* PSoC6 uses IPC structures for inter-core communication. Same IPCs are used to invoke SROM API.
* IPC structure must be locked prior to invoking any SROM API. This ensures nothing else in the
* system will use same IPC thus corrupting our data. This function locks the IPC.
*************************************************************************************************/
static int ipc_acquire(struct target *target, char ipc_id)
{
int hr = ERROR_OK;
bool is_acquired = false;
uint32_t reg_val;
struct timeout to;
timeout_init(&to, IPC_TIMEOUT_MS);
while (!timeout_expired(&to)) {
keep_alive();
hr = target_write_u32(target, MEM_IPC_ACQUIRE(ipc_id), IPC_ACQUIRE_SUCCESS_MSK);
if (hr != ERROR_OK) {
LOG_ERROR("Unable to write to IPC Acquire register");
return hr;
}
/* Check if data is written on first step */
hr = target_read_u32(target, MEM_IPC_ACQUIRE(ipc_id), &reg_val);
if (hr != ERROR_OK) {
LOG_ERROR("Unable to read IPC Acquire register");
return hr;
}
is_acquired = (reg_val & IPC_ACQUIRE_SUCCESS_MSK) != 0;
if (is_acquired) {
/* If IPC structure is acquired, the lock status should be set */
hr = ipc_poll_lock_stat(target, ipc_id, true);
break;
}
}
if (!is_acquired)
LOG_ERROR("Timeout acquiring IPC structure");
return hr;
}
/**************************************************************************************************
* Invokes SROM API functions which are responsible for Flash operations
*************************************************************************************************/
static int call_sromapi(struct target *target,
uint32_t req_and_params,
uint32_t working_area,
uint32_t *data_out)
{
int hr;
bool is_data_in_ram = (req_and_params & SROMAPI_DATA_LOCATION_MSK) == 0;
hr = ipc_acquire(target, IPC_ID);
if (hr != ERROR_OK)
return hr;
if (is_data_in_ram)
hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), working_area);
else
hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), req_and_params);
if (hr != ERROR_OK)
return hr;
/* Enable notification interrupt of IPC_INTR_STRUCT0(CM0+) for IPC_STRUCT2 */
hr = target_write_u32(target, MEM_IPC_INTR_MASK(IPC_INTR_ID), 1u << (16 + IPC_ID));
if (hr != ERROR_OK)
return hr;
hr = target_write_u32(target, MEM_IPC_NOTIFY(IPC_ID), 1);
if (hr != ERROR_OK)
return hr;
hr = sromalgo_run(target);
if (hr != ERROR_OK)
return hr;
/* Poll lock status */
hr = ipc_poll_lock_stat(target, IPC_ID, false);
if (hr != ERROR_OK)
return hr;
/* Poll Data byte */
if (is_data_in_ram)
hr = target_read_u32(target, working_area, data_out);
else
hr = target_read_u32(target, MEM_IPC_DATA(IPC_ID), data_out);
if (hr != ERROR_OK) {
LOG_ERROR("Error reading SROM API Status location");
return hr;
}
bool is_success = (*data_out & SROMAPI_STATUS_MSK) == SROMAPI_STAT_SUCCESS;
if (!is_success) {
LOG_ERROR("SROM API execution failed. Status: 0x%08X", (uint32_t)*data_out);
return ERROR_TARGET_FAILURE;
}
return ERROR_OK;
}
/**************************************************************************************************
* Retrieves SiliconID and Protection status of the target device
*************************************************************************************************/
static int get_silicon_id(struct target *target, uint32_t *si_id, uint8_t *protection)
{
int hr;
uint32_t family_rev, siid_prot;
hr = sromalgo_prepare(target);
if (hr != ERROR_OK)
return hr;
/* Read FamilyID and Revision */
hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev);
if (hr != ERROR_OK)
return hr;
/* Read SiliconID and Protection */
hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot);
if (hr != ERROR_OK)
return hr;
*si_id = (siid_prot & 0x0000FFFF) << 16;
*si_id |= (family_rev & 0x00FF0000) >> 8;
*si_id |= (family_rev & 0x000000FF) >> 0;
*protection = (siid_prot & 0x000F0000) >> 0x10;
hr = sromalgo_release(target);
return hr;
}
/**************************************************************************************************
* Translates Protection status to openocd-friendly boolean value
*************************************************************************************************/
static int psoc6_protect_check(struct flash_bank *bank)
{
int is_protected;
struct psoc6_target_info *psoc6_info = bank->driver_priv;
int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
if (hr != ERROR_OK)
return hr;
switch (psoc6_info->protection) {
case PROTECTION_VIRGIN:
case PROTECTION_NORMAL:
is_protected = 0;
break;
case PROTECTION_UNKNOWN:
case PROTECTION_SECURE:
case PROTECTION_DEAD:
default:
is_protected = 1;
break;
}
for (int i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_protected = is_protected;
return ERROR_OK;
}
/**************************************************************************************************
* Life Cycle transition is not currently supported
*************************************************************************************************/
static int psoc6_protect(struct flash_bank *bank, int set, int first, int last)
{
(void)bank;
(void)set;
(void)first;
(void)last;
LOG_WARNING("Life Cycle transition for PSoC6 is not supported");
return ERROR_OK;
}
/**************************************************************************************************
* Translates Protection status to string
*************************************************************************************************/
static const char *protection_to_str(uint8_t protection)
{
switch (protection) {
case PROTECTION_VIRGIN:
return "VIRGIN";
break;
case PROTECTION_NORMAL:
return "NORMAL";
break;
case PROTECTION_SECURE:
return "SECURE";
break;
case PROTECTION_DEAD:
return "DEAD";
break;
case PROTECTION_UNKNOWN:
default:
return "UNKNOWN";
break;
}
}
/**************************************************************************************************
* Displays human-readable information about acquired device
*************************************************************************************************/
static int psoc6_get_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct psoc6_target_info *psoc6_info = bank->driver_priv;
if (psoc6_info->is_probed == false)
return ERROR_FAIL;
int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
if (hr != ERROR_OK)
return hr;
snprintf(buf, buf_size,
"PSoC6 Silicon ID: 0x%08X\n"
"Protection: %s\n"
"Main Flash size: %d kB\n"
"Work Flash size: 32 kB\n",
psoc6_info->silicon_id,
protection_to_str(psoc6_info->protection),
psoc6_info->main_flash_sz / 1024);
return ERROR_OK;
}
/**************************************************************************************************
* Returns true if flash bank name represents Supervisory Flash
*************************************************************************************************/
static bool is_sflash_bank(struct flash_bank *bank)
{
for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
if (bank->base == safe_sflash_regions[i].addr)
return true;
}
return false;
}
/**************************************************************************************************
* Returns true if flash bank name represents Work Flash
*************************************************************************************************/
static inline bool is_wflash_bank(struct flash_bank *bank)
{
return (bank->base == MEM_BASE_WFLASH);
}
/**************************************************************************************************
* Returns true if flash bank name represents Main Flash
*************************************************************************************************/
static inline bool is_mflash_bank(struct flash_bank *bank)
{
return (bank->base == MEM_BASE_MFLASH);
}
/**************************************************************************************************
* Probes the device and populates related data structures with target flash geometry data.
* This is done in non-intrusive way, no SROM API calls are involved so GDB can safely attach to a
* running target.
* Function assumes that size of Work Flash is 32kB (true for all current part numbers)
*************************************************************************************************/
static int psoc6_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct psoc6_target_info *psoc6_info = bank->driver_priv;
int hr = ERROR_OK;
/* Retrieve data from SPCIF_GEOMATRY */
uint32_t geom;
target_read_u32(target, PSOC6_SPCIF_GEOMETRY, &geom);
uint32_t row_sz_lg2 = (geom & 0xF0) >> 4;
uint32_t row_sz = (0x01 << row_sz_lg2);
uint32_t row_cnt = 1 + ((geom & 0x00FFFF00) >> 8);
uint32_t bank_cnt = 1 + ((geom & 0xFF000000) >> 24);
/* Calculate size of Main Flash*/
uint32_t flash_sz_bytes = bank_cnt * row_cnt * row_sz;
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
size_t bank_size = 0;
if (is_mflash_bank(bank))
bank_size = flash_sz_bytes;
else if (is_wflash_bank(bank))
bank_size = MEM_WFLASH_SIZE;
else if (is_sflash_bank(bank)) {
for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
if (safe_sflash_regions[i].addr == bank->base) {
bank_size = safe_sflash_regions[i].size;
break;
}
}
}
if (bank_size == 0) {
LOG_ERROR("Invalid Flash Bank base address in config file");
return ERROR_FLASH_BANK_INVALID;
}
size_t num_sectors = bank_size / row_sz;
bank->size = bank_size;
bank->chip_width = 4;
bank->bus_width = 4;
bank->erased_value = 0;
bank->default_padded_value = 0;
bank->num_sectors = num_sectors;
bank->sectors = calloc(num_sectors, sizeof(struct flash_sector));
for (size_t i = 0; i < num_sectors; i++) {
bank->sectors[i].size = row_sz;
bank->sectors[i].offset = i * row_sz;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = -1;
}
psoc6_info->is_probed = true;
psoc6_info->main_flash_sz = flash_sz_bytes;
psoc6_info->row_sz = row_sz;
return hr;
}
/**************************************************************************************************
* Probes target device only if it hasn't been probed yet
*************************************************************************************************/
static int psoc6_auto_probe(struct flash_bank *bank)
{
struct psoc6_target_info *psoc6_info = bank->driver_priv;
int hr;
if (psoc6_info->is_probed)
hr = ERROR_OK;
else
hr = psoc6_probe(bank);
return hr;
}
/**************************************************************************************************
* Erases single sector (256k) on target device
*************************************************************************************************/
static int psoc6_erase_sector(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
{
struct target *target = bank->target;
LOG_DEBUG("Erasing SECTOR @%08X", addr);
int hr = target_write_u32(target, wa->address, SROMAPI_ERASESECTOR_REQ);
if (hr != ERROR_OK)
return hr;
hr = target_write_u32(target, wa->address + 0x04, addr);
if (hr != ERROR_OK)
return hr;
uint32_t data_out;
hr = call_sromapi(target, SROMAPI_ERASESECTOR_REQ, wa->address, &data_out);
if (hr != ERROR_OK)
LOG_ERROR("SECTOR @%08X not erased!", addr);
return hr;
}
/**************************************************************************************************
* Erases single row (512b) on target device
*************************************************************************************************/
static int psoc6_erase_row(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
{
struct target *target = bank->target;
LOG_DEBUG("Erasing ROW @%08X", addr);
int hr = target_write_u32(target, wa->address, SROMAPI_ERASEROW_REQ);
if (hr != ERROR_OK)
return hr;
hr = target_write_u32(target, wa->address + 0x04, addr);
if (hr != ERROR_OK)
return hr;
uint32_t data_out;
hr = call_sromapi(target, SROMAPI_ERASEROW_REQ, wa->address, &data_out);
if (hr != ERROR_OK)
LOG_ERROR("ROW @%08X not erased!", addr);
return hr;
}
/**************************************************************************************************
* Performs Erase operation.
* Function will try to use biggest erase block possible to speedup the operation
*************************************************************************************************/
static int psoc6_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
struct psoc6_target_info *psoc6_info = bank->driver_priv;
const uint32_t sector_size = is_wflash_bank(bank) ? WFLASH_SECTOR_SIZE : MFLASH_SECTOR_SIZE;
int hr;
struct working_area *wa;
if (is_sflash_bank(bank)) {
LOG_INFO("Erase operation on Supervisory Flash is not required, skipping");
return ERROR_OK;
}
hr = sromalgo_prepare(target);
if (hr != ERROR_OK)
return hr;
hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
if (hr != ERROR_OK)
goto exit;
/* Number of rows in single sector */
const int rows_in_sector = sector_size / psoc6_info->row_sz;
while (last >= first) {
/* Erase Sector if we are on sector boundary and erase size covers whole sector */
if ((first % rows_in_sector) == 0 &&
(last - first + 1) >= rows_in_sector) {
hr = psoc6_erase_sector(bank, wa, bank->base + first * psoc6_info->row_sz);
if (hr != ERROR_OK)
goto exit_free_wa;
for (int i = first; i < first + rows_in_sector; i++)
bank->sectors[i].is_erased = 1;
first += rows_in_sector;
} else {
/* Perform Row Erase otherwise */
hr = psoc6_erase_row(bank, wa, bank->base + first * psoc6_info->row_sz);
if (hr != ERROR_OK)
goto exit_free_wa;
bank->sectors[first].is_erased = 1;
first += 1;
}
}
exit_free_wa:
target_free_working_area(target, wa);
exit:
sromalgo_release(target);
return hr;
}
/**************************************************************************************************
* Programs single Flash Row
*************************************************************************************************/
static int psoc6_program_row(struct flash_bank *bank,
uint32_t addr,
const uint8_t *buffer,
bool is_sflash)
{
struct target *target = bank->target;
struct psoc6_target_info *psoc6_info = bank->driver_priv;
struct working_area *wa;
const uint32_t sromapi_req = is_sflash ? SROMAPI_WRITEROW_REQ : SROMAPI_PROGRAMROW_REQ;
uint32_t data_out;
int hr = ERROR_OK;
LOG_DEBUG("Programming ROW @%08X", addr);
hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
if (hr != ERROR_OK)
goto exit;
hr = target_write_u32(target, wa->address, sromapi_req);
if (hr != ERROR_OK)
goto exit_free_wa;
hr = target_write_u32(target,
wa->address + 0x04,
0x106);
if (hr != ERROR_OK)
goto exit_free_wa;
hr = target_write_u32(target, wa->address + 0x08, addr);
if (hr != ERROR_OK)
goto exit_free_wa;
hr = target_write_u32(target, wa->address + 0x0C, wa->address + 0x10);
if (hr != ERROR_OK)
goto exit_free_wa;
hr = target_write_buffer(target, wa->address + 0x10, psoc6_info->row_sz, buffer);
if (hr != ERROR_OK)
goto exit_free_wa;
hr = call_sromapi(target, sromapi_req, wa->address, &data_out);
exit_free_wa:
target_free_working_area(target, wa);
exit:
return hr;
}
/**************************************************************************************************
* Programs set of Rows
*************************************************************************************************/
static int psoc6_program(struct flash_bank *bank,
const uint8_t *buffer,
uint32_t offset,
uint32_t count)
{
struct target *target = bank->target;
struct psoc6_target_info *psoc6_info = bank->driver_priv;
const bool is_sflash = is_sflash_bank(bank);
int hr;
hr = sromalgo_prepare(target);
if (hr != ERROR_OK)
return hr;
uint8_t page_buf[psoc6_info->row_sz];
while (count) {
uint32_t row_offset = offset % psoc6_info->row_sz;
uint32_t aligned_addr = bank->base + offset - row_offset;
uint32_t row_bytes = MIN(psoc6_info->row_sz - row_offset, count);
memset(page_buf, 0, sizeof(page_buf));
memcpy(&page_buf[row_offset], buffer, row_bytes);
hr = psoc6_program_row(bank, aligned_addr, page_buf, is_sflash);
if (hr != ERROR_OK) {
LOG_ERROR("Failed to program Flash at address 0x%08X", aligned_addr);
break;
}
buffer += row_bytes;
offset += row_bytes;
count -= row_bytes;
}
hr = sromalgo_release(target);
return hr;
}
/**************************************************************************************************
* Performs Mass Erase of given flash bank
* Syntax: psoc6 mass_erase bank_id
*************************************************************************************************/
COMMAND_HANDLER(psoc6_handle_mass_erase_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (hr != ERROR_OK)
return hr;
hr = psoc6_erase(bank, 0, bank->num_sectors - 1);
return hr;
}
/**************************************************************************************************
* Simulates broken Vector Catch
* Function will try to determine entry point of user application. If it succeeds it will set HW
* breakpoint at that address, issue SW Reset and remove the breakpoint afterwards.
* In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will
* reset CM4 anyway, so using SYSRESETREQ is safe here.
* In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core.
*************************************************************************************************/
int handle_reset_halt(struct target *target)
{
int hr;
uint32_t reset_addr;
bool is_cm0 = (target->coreid == 0);
/* Halt target device */
if (target->state != TARGET_HALTED) {
hr = target_halt(target);
if (hr != ERROR_OK)
return hr;
target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
if (hr != ERROR_OK)
return hr;
}
/* Read Vector Offset register */
uint32_t vt_base;
const uint32_t vt_offset_reg = is_cm0 ? 0x402102B0 : 0x402102C0;
hr = target_read_u32(target, vt_offset_reg, &vt_base);
if (hr != ERROR_OK)
return ERROR_OK;
/* Invalid value means flash is empty */
vt_base &= 0xFFFFFF00;
if ((vt_base == 0) || (vt_base == 0xFFFFFF00))
return ERROR_OK;
/* Read Reset Vector value*/
hr = target_read_u32(target, vt_base + 4, &reset_addr);
if (hr != ERROR_OK)
return hr;
/* Invalid value means flash is empty */
if ((reset_addr == 0) || (reset_addr == 0xFFFFFF00))
return ERROR_OK;
/* Set breakpoint at User Application entry point */
hr = breakpoint_add(target, reset_addr, 2, BKPT_HARD);
if (hr != ERROR_OK)
return hr;
const struct armv7m_common *cm = target_to_armv7m(target);
if (is_cm0) {
/* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */
LOG_INFO("psoc6.cm0: bkpt @0x%08X, issuing SYSRESETREQ", reset_addr);
hr = mem_ap_write_atomic_u32(cm->debug_ap,
NVIC_AIRCR,
AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
/* Wait for bootcode and initialize DAP */
usleep(3000);
dap_dp_init(cm->debug_ap->dap);
} else {
LOG_INFO("psoc6.cm4: bkpt @0x%08X, issuing VECTRESET", reset_addr);
hr = mem_ap_write_atomic_u32(cm->debug_ap,
NVIC_AIRCR,
AIRCR_VECTKEY | AIRCR_VECTRESET);
if (hr != ERROR_OK)
return hr;
}
target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
/* Remove the break point */
breakpoint_remove(target, reset_addr);
return hr;
}
COMMAND_HANDLER(psoc6_handle_reset_halt)
{
if (CMD_ARGC)
return ERROR_COMMAND_SYNTAX_ERROR;
struct target *target = get_current_target(CMD_CTX);
return handle_reset_halt(target);
}
FLASH_BANK_COMMAND_HANDLER(psoc6_flash_bank_command)
{
struct psoc6_target_info *psoc6_info;
int hr = ERROR_OK;
if (CMD_ARGC < 6)
hr = ERROR_COMMAND_SYNTAX_ERROR;
else {
psoc6_info = calloc(1, sizeof(struct psoc6_target_info));
psoc6_info->is_probed = false;
bank->driver_priv = psoc6_info;
}
return hr;
}
static const struct command_registration psoc6_exec_command_handlers[] = {
{
.name = "mass_erase",
.handler = psoc6_handle_mass_erase_command,
.mode = COMMAND_EXEC,
.usage = NULL,
.help = "Erases entire Main Flash",
},
{
.name = "reset_halt",
.handler = psoc6_handle_reset_halt,
.mode = COMMAND_EXEC,
.usage = NULL,
.help = "Tries to simulate broken Vector Catch",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration psoc6_command_handlers[] = {
{
.name = "psoc6",
.mode = COMMAND_ANY,
.help = "PSoC 6 flash command group",
.usage = "",
.chain = psoc6_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver psoc6_flash = {
.name = "psoc6",
.commands = psoc6_command_handlers,
.flash_bank_command = psoc6_flash_bank_command,
.erase = psoc6_erase,
.protect = psoc6_protect,
.write = psoc6_program,
.read = default_flash_read,
.probe = psoc6_probe,
.auto_probe = psoc6_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = psoc6_protect_check,
.info = psoc6_get_info,
.free_driver_priv = default_flash_free_driver_priv,
};

View File

@ -1122,5 +1122,6 @@ struct flash_driver sim3x_flash = {
.auto_probe = sim3x_auto_probe, .auto_probe = sim3x_auto_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = sim3x_flash_protect_check, .protect_check = sim3x_flash_protect_check,
.info = sim3x_flash_info .info = sim3x_flash_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1452,4 +1452,5 @@ struct flash_driver stellaris_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stellaris_protect_check, .protect_check = stellaris_protect_check,
.info = get_stellaris_info, .info = get_stellaris_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1647,4 +1647,5 @@ struct flash_driver stm32f1x_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check, .protect_check = stm32x_protect_check,
.info = get_stm32x_info, .info = get_stm32x_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1634,4 +1634,5 @@ struct flash_driver stm32f2x_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check, .protect_check = stm32x_protect_check,
.info = get_stm32x_info, .info = get_stm32x_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1180,4 +1180,5 @@ struct flash_driver stm32h7x_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check, .protect_check = stm32x_protect_check,
.info = stm32x_get_info, .info = stm32x_get_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -953,4 +953,5 @@ struct flash_driver stm32l4x_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stm32l4_protect_check, .protect_check = stm32l4_protect_check,
.info = get_stm32l4_info, .info = get_stm32l4_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -965,6 +965,7 @@ struct flash_driver stm32lx_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stm32lx_protect_check, .protect_check = stm32lx_protect_check,
.info = stm32lx_get_info, .info = stm32lx_get_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };
/* Static methods implementation */ /* Static methods implementation */

View File

@ -654,4 +654,5 @@ struct flash_driver stmsmi_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = stmsmi_protect_check, .protect_check = stmsmi_protect_check,
.info = get_stmsmi_info, .info = get_stmsmi_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -812,4 +812,5 @@ struct flash_driver str7x_flash = {
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = str7x_protect_check, .protect_check = str7x_protect_check,
.info = get_str7x_info, .info = get_str7x_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -679,4 +679,5 @@ struct flash_driver str9x_flash = {
.auto_probe = str9x_probe, .auto_probe = str9x_probe,
.erase_check = default_flash_blank_check, .erase_check = default_flash_blank_check,
.protect_check = str9x_protect_check, .protect_check = str9x_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1207,4 +1207,5 @@ struct flash_driver str9xpec_flash = {
.auto_probe = str9xpec_probe, .auto_probe = str9xpec_probe,
.erase_check = str9xpec_erase_check, .erase_check = str9xpec_erase_check,
.protect_check = str9xpec_protect_check, .protect_check = str9xpec_protect_check,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1186,4 +1186,5 @@ struct flash_driver tms470_flash = {
.erase_check = tms470_erase_check, .erase_check = tms470_erase_check,
.protect_check = tms470_protect_check, .protect_check = tms470_protect_check,
.info = get_tms470_info, .info = get_tms470_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -231,4 +231,5 @@ struct flash_driver virtual_flash = {
.erase_check = virtual_blank_check, .erase_check = virtual_blank_check,
.protect_check = virtual_protect_check, .protect_check = virtual_protect_check,
.info = virtual_info, .info = virtual_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -636,6 +636,7 @@ static int xcf_probe(struct flash_bank *bank)
fill_sector_table(bank); fill_sector_table(bank);
priv->probed = true; priv->probed = true;
/* REVISIT: Why is unchanged bank->driver_priv rewritten by same value? */
bank->driver_priv = priv; bank->driver_priv = priv;
LOG_INFO("product name: %s", product_name(bank)); LOG_INFO("product name: %s", product_name(bank));
@ -893,5 +894,6 @@ struct flash_driver xcf_flash = {
.auto_probe = xcf_auto_probe, .auto_probe = xcf_auto_probe,
.erase_check = xcf_erase_check, .erase_check = xcf_erase_check,
.protect_check = xcf_protect_check, .protect_check = xcf_protect_check,
.info = xcf_info .info = xcf_info,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -546,4 +546,5 @@ struct flash_driver xmc1xxx_flash = {
.erase = xmc1xxx_erase, .erase = xmc1xxx_erase,
.erase_check = xmc1xxx_erase_check, .erase_check = xmc1xxx_erase_check,
.write = xmc1xxx_write, .write = xmc1xxx_write,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -1356,4 +1356,5 @@ struct flash_driver xmc4xxx_flash = {
.info = xmc4xxx_get_info_command, .info = xmc4xxx_get_info_command,
.protect_check = xmc4xxx_protect_check, .protect_check = xmc4xxx_protect_check,
.protect = xmc4xxx_protect, .protect = xmc4xxx_protect,
.free_driver_priv = default_flash_free_driver_priv,
}; };

View File

@ -608,7 +608,23 @@ static int run_command(struct command_context *context,
.argc = num_words - 1, .argc = num_words - 1,
.argv = words + 1, .argv = words + 1,
}; };
/* Black magic of overridden current target:
* If the command we are going to handle has a target prefix,
* override the current target temporarily for the time
* of processing the command.
* current_target_override is used also for event handlers
* therefore we prevent touching it if command has no prefix.
* Previous override is saved and restored back to ensure
* correct work when run_command() is re-entered. */
struct target *saved_target_override = context->current_target_override;
if (c->jim_handler_data)
context->current_target_override = c->jim_handler_data;
int retval = c->handler(&cmd); int retval = c->handler(&cmd);
if (c->jim_handler_data)
context->current_target_override = saved_target_override;
if (retval == ERROR_COMMAND_SYNTAX_ERROR) { if (retval == ERROR_COMMAND_SYNTAX_ERROR) {
/* Print help for command */ /* Print help for command */
char *full_name = command_name(c, ' '); char *full_name = command_name(c, ' ');
@ -643,6 +659,8 @@ int command_run_line(struct command_context *context, char *line)
* happen when the Jim Tcl interpreter is provided by eCos for * happen when the Jim Tcl interpreter is provided by eCos for
* instance. * instance.
*/ */
context->current_target_override = NULL;
Jim_Interp *interp = context->interp; Jim_Interp *interp = context->interp;
Jim_DeleteAssocData(interp, "context"); Jim_DeleteAssocData(interp, "context");
retcode = Jim_SetAssocData(interp, "context", NULL, context); retcode = Jim_SetAssocData(interp, "context", NULL, context);
@ -1269,14 +1287,10 @@ static const struct command_registration command_builtin_handlers[] = {
struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp) struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp)
{ {
struct command_context *context = malloc(sizeof(struct command_context)); struct command_context *context = calloc(1, sizeof(struct command_context));
const char *HostOs; const char *HostOs;
context->mode = COMMAND_EXEC; context->mode = COMMAND_EXEC;
context->commands = NULL;
context->current_target = 0;
context->output_handler = NULL;
context->output_handler_priv = NULL;
/* Create a jim interpreter if we were not handed one */ /* Create a jim interpreter if we were not handed one */
if (interp == NULL) { if (interp == NULL) {

View File

@ -22,8 +22,12 @@
#ifndef OPENOCD_HELPER_COMMAND_H #ifndef OPENOCD_HELPER_COMMAND_H
#define OPENOCD_HELPER_COMMAND_H #define OPENOCD_HELPER_COMMAND_H
#include <stdint.h>
#include <stdbool.h>
#include <jim-nvp.h> #include <jim-nvp.h>
#include <helper/types.h>
/* To achieve C99 printf compatibility in MinGW, gnu_printf should be /* To achieve C99 printf compatibility in MinGW, gnu_printf should be
* used for __attribute__((format( ... ))), with GCC v4.4 or later * used for __attribute__((format( ... ))), with GCC v4.4 or later
*/ */
@ -49,7 +53,15 @@ struct command_context {
Jim_Interp *interp; Jim_Interp *interp;
enum command_mode mode; enum command_mode mode;
struct command *commands; struct command *commands;
int current_target; struct target *current_target;
/* The target set by 'targets xx' command or the latest created */
struct target *current_target_override;
/* If set overrides current_target
* It happens during processing of
* 1) a target prefixed command
* 2) an event handler
* Pay attention to reentrancy when setting override.
*/
command_output_handler_t output_handler; command_output_handler_t output_handler;
void *output_handler_priv; void *output_handler_priv;
}; };
@ -168,6 +180,11 @@ struct command {
command_handler_t handler; command_handler_t handler;
Jim_CmdProc *jim_handler; Jim_CmdProc *jim_handler;
void *jim_handler_data; void *jim_handler_data;
/* Currently used only for target of target-prefixed cmd.
* Native OpenOCD commands use jim_handler_data exclusively
* as a target override.
* Jim handlers outside of target cmd tree can use
* jim_handler_data for any handler specific data */
enum command_mode mode; enum command_mode mode;
struct command *next; struct command *next;
}; };

View File

@ -51,6 +51,21 @@ void add_config_command(const char *cfg)
config_file_names[num_config_files] = NULL; config_file_names[num_config_files] = NULL;
} }
void free_config(void)
{
while (num_config_files)
free(config_file_names[--num_config_files]);
free(config_file_names);
config_file_names = NULL;
while (num_script_dirs)
free(script_search_dirs[--num_script_dirs]);
free(script_search_dirs);
script_search_dirs = NULL;
}
/* return full path or NULL according to search rules */ /* return full path or NULL according to search rules */
char *find_file(const char *file) char *find_file(const char *file)
{ {

View File

@ -32,6 +32,8 @@ void add_config_command(const char *cfg);
void add_script_search_dir(const char *dir); void add_script_search_dir(const char *dir);
void free_config(void);
int configuration_output_handler(struct command_context *cmd_ctx, int configuration_output_handler(struct command_context *cmd_ctx,
const char *line); const char *line);

View File

@ -153,6 +153,11 @@ int fileio_close(struct fileio *fileio)
return retval; return retval;
} }
int fileio_feof(struct fileio *fileio)
{
return feof(fileio->file);
}
int fileio_seek(struct fileio *fileio, size_t position) int fileio_seek(struct fileio *fileio, size_t position)
{ {
int retval; int retval;

View File

@ -46,6 +46,7 @@ struct fileio;
int fileio_open(struct fileio **fileio, const char *url, int fileio_open(struct fileio **fileio, const char *url,
enum fileio_access access_type, enum fileio_type type); enum fileio_access access_type, enum fileio_type type);
int fileio_close(struct fileio *fileio); int fileio_close(struct fileio *fileio);
int fileio_feof(struct fileio *fileio);
int fileio_seek(struct fileio *fileio, size_t position); int fileio_seek(struct fileio *fileio, size_t position);
int fileio_fgets(struct fileio *fileio, size_t size, void *buffer); int fileio_fgets(struct fileio *fileio, size_t size, void *buffer);

View File

@ -25,6 +25,8 @@
#ifndef OPENOCD_HELPER_REPLACEMENTS_H #ifndef OPENOCD_HELPER_REPLACEMENTS_H
#define OPENOCD_HELPER_REPLACEMENTS_H #define OPENOCD_HELPER_REPLACEMENTS_H
#include <stdint.h>
/* MIN,MAX macros */ /* MIN,MAX macros */
#ifndef MIN #ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))

View File

@ -22,7 +22,12 @@
#ifndef OPENOCD_HELPER_TYPES_H #ifndef OPENOCD_HELPER_TYPES_H
#define OPENOCD_HELPER_TYPES_H #define OPENOCD_HELPER_TYPES_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stddef.h> #include <stddef.h>
#include <assert.h>
#ifdef HAVE_SYS_TYPES_H #ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> #include <sys/types.h>
#endif #endif
@ -123,17 +128,17 @@ static inline uint64_t le_to_h_u64(const uint8_t *buf)
static inline uint32_t le_to_h_u32(const uint8_t* buf) static inline uint32_t le_to_h_u32(const uint8_t* buf)
{ {
return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24); return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24);
} }
static inline uint32_t le_to_h_u24(const uint8_t* buf) static inline uint32_t le_to_h_u24(const uint8_t* buf)
{ {
return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16); return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16);
} }
static inline uint16_t le_to_h_u16(const uint8_t* buf) static inline uint16_t le_to_h_u16(const uint8_t* buf)
{ {
return (uint16_t)(buf[0] | buf[1] << 8); return (uint16_t)((uint16_t)buf[0] | (uint16_t)buf[1] << 8);
} }
static inline uint64_t be_to_h_u64(const uint8_t *buf) static inline uint64_t be_to_h_u64(const uint8_t *buf)
@ -150,17 +155,17 @@ static inline uint64_t be_to_h_u64(const uint8_t *buf)
static inline uint32_t be_to_h_u32(const uint8_t* buf) static inline uint32_t be_to_h_u32(const uint8_t* buf)
{ {
return (uint32_t)(buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24); return (uint32_t)((uint32_t)buf[3] | (uint32_t)buf[2] << 8 | (uint32_t)buf[1] << 16 | (uint32_t)buf[0] << 24);
} }
static inline uint32_t be_to_h_u24(const uint8_t* buf) static inline uint32_t be_to_h_u24(const uint8_t* buf)
{ {
return (uint32_t)(buf[2] | buf[1] << 8 | buf[0] << 16); return (uint32_t)((uint32_t)buf[2] | (uint32_t)buf[1] << 8 | (uint32_t)buf[0] << 16);
} }
static inline uint16_t be_to_h_u16(const uint8_t* buf) static inline uint16_t be_to_h_u16(const uint8_t* buf)
{ {
return (uint16_t)(buf[1] | buf[0] << 8); return (uint16_t)((uint16_t)buf[1] | (uint16_t)buf[0] << 8);
} }
static inline void h_u64_to_le(uint8_t *buf, int64_t val) static inline void h_u64_to_le(uint8_t *buf, int64_t val)

View File

@ -239,6 +239,30 @@ static int aice_khz(int khz, int *jtag_speed)
return ERROR_OK; return ERROR_OK;
} }
int aice_scan_jtag_chain(void)
{
LOG_DEBUG("=== %s ===", __func__);
uint8_t num_of_idcode = 0;
struct target *target;
int res = aice_port->api->idcode(aice_target_id_codes, &num_of_idcode);
if (res != ERROR_OK) {
LOG_ERROR("<-- TARGET ERROR! Failed to identify AndesCore "
"JTAG Manufacture ID in the JTAG scan chain. "
"Failed to access EDM registers. -->");
return res;
}
for (uint32_t i = 0; i < num_of_idcode; i++)
LOG_DEBUG("id_codes[%d] = 0x%x", i, aice_target_id_codes[i]);
/* Update tap idcode */
for (target = all_targets; target; target = target->next)
target->tap->idcode = aice_target_id_codes[target->tap->abs_chain_position];
return ERROR_OK;
}
/***************************************************************************/ /***************************************************************************/
/* Command handlers */ /* Command handlers */
COMMAND_HANDLER(aice_handle_aice_info_command) COMMAND_HANDLER(aice_handle_aice_info_command)

View File

@ -31,5 +31,6 @@ struct aice_interface_param_s {
}; };
int aice_init_targets(void); int aice_init_targets(void);
int aice_scan_jtag_chain(void);
#endif /* OPENOCD_JTAG_AICE_AICE_INTERFACE_H */ #endif /* OPENOCD_JTAG_AICE_AICE_INTERFACE_H */

View File

@ -158,6 +158,59 @@ COMMAND_HANDLER(handle_aice_init_command)
return jtag_init(CMD_CTX); return jtag_init(CMD_CTX);
} }
COMMAND_HANDLER(handle_scan_chain_command)
{
struct jtag_tap *tap;
char expected_id[12];
aice_scan_jtag_chain();
tap = jtag_all_taps();
command_print(CMD_CTX,
" TapName Enabled IdCode Expected IrLen IrCap IrMask");
command_print(CMD_CTX,
"-- ------------------- -------- ---------- ---------- ----- ----- ------");
while (tap) {
uint32_t expected, expected_mask, ii;
snprintf(expected_id, sizeof expected_id, "0x%08x",
(unsigned)((tap->expected_ids_cnt > 0)
? tap->expected_ids[0]
: 0));
if (tap->ignore_version)
expected_id[2] = '*';
expected = buf_get_u32(tap->expected, 0, tap->ir_length);
expected_mask = buf_get_u32(tap->expected_mask, 0, tap->ir_length);
command_print(CMD_CTX,
"%2d %-18s %c 0x%08x %s %5d 0x%02x 0x%02x",
tap->abs_chain_position,
tap->dotted_name,
tap->enabled ? 'Y' : 'n',
(unsigned int)(tap->idcode),
expected_id,
(unsigned int)(tap->ir_length),
(unsigned int)(expected),
(unsigned int)(expected_mask));
for (ii = 1; ii < tap->expected_ids_cnt; ii++) {
snprintf(expected_id, sizeof expected_id, "0x%08x",
(unsigned) tap->expected_ids[ii]);
if (tap->ignore_version)
expected_id[2] = '*';
command_print(CMD_CTX,
" %s",
expected_id);
}
tap = tap->next_tap;
}
return ERROR_OK;
}
static int jim_aice_arp_init(Jim_Interp *interp, int argc, Jim_Obj * const *argv) static int jim_aice_arp_init(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{ {
LOG_DEBUG("No implement: jim_aice_arp_init"); LOG_DEBUG("No implement: jim_aice_arp_init");
@ -307,6 +360,13 @@ aice_transport_jtag_subcommand_handlers[] = {
.jim_handler = jim_aice_names, .jim_handler = jim_aice_names,
.help = "Returns list of all JTAG tap names.", .help = "Returns list of all JTAG tap names.",
}, },
{
.name = "scan_chain",
.handler = handle_scan_chain_command,
.mode = COMMAND_ANY,
.help = "print current scan chain configuration",
.usage = ""
},
COMMAND_REGISTRATION_DONE COMMAND_REGISTRATION_DONE
}; };

View File

@ -836,68 +836,7 @@ int default_interface_jtag_execute_queue(void)
return ERROR_FAIL; return ERROR_FAIL;
} }
int result = jtag->execute_queue(); return jtag->execute_queue();
struct jtag_command *cmd = jtag_command_queue;
while (debug_level >= LOG_LVL_DEBUG && cmd) {
switch (cmd->type) {
case JTAG_SCAN:
DEBUG_JTAG_IO("JTAG %s SCAN to %s",
cmd->cmd.scan->ir_scan ? "IR" : "DR",
tap_state_name(cmd->cmd.scan->end_state));
for (int i = 0; i < cmd->cmd.scan->num_fields; i++) {
struct scan_field *field = cmd->cmd.scan->fields + i;
if (field->out_value) {
char *str = buf_to_str(field->out_value, field->num_bits, 16);
DEBUG_JTAG_IO(" %db out: %s", field->num_bits, str);
free(str);
}
if (field->in_value) {
char *str = buf_to_str(field->in_value, field->num_bits, 16);
DEBUG_JTAG_IO(" %db in: %s", field->num_bits, str);
free(str);
}
}
break;
case JTAG_TLR_RESET:
DEBUG_JTAG_IO("JTAG TLR RESET to %s",
tap_state_name(cmd->cmd.statemove->end_state));
break;
case JTAG_RUNTEST:
DEBUG_JTAG_IO("JTAG RUNTEST %d cycles to %s",
cmd->cmd.runtest->num_cycles,
tap_state_name(cmd->cmd.runtest->end_state));
break;
case JTAG_RESET:
{
const char *reset_str[3] = {
"leave", "deassert", "assert"
};
DEBUG_JTAG_IO("JTAG RESET %s TRST, %s SRST",
reset_str[cmd->cmd.reset->trst + 1],
reset_str[cmd->cmd.reset->srst + 1]);
}
break;
case JTAG_PATHMOVE:
DEBUG_JTAG_IO("JTAG PATHMOVE (TODO)");
break;
case JTAG_SLEEP:
DEBUG_JTAG_IO("JTAG SLEEP (TODO)");
break;
case JTAG_STABLECLOCKS:
DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
break;
case JTAG_TMS:
DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
break;
default:
LOG_ERROR("Unknown JTAG command: %d", cmd->type);
break;
}
cmd = cmd->next;
}
return result;
} }
void jtag_execute_queue_noclear(void) void jtag_execute_queue_noclear(void)
@ -1168,8 +1107,7 @@ static int jtag_examine_chain(void)
if ((idcode & 1) == 0) { if ((idcode & 1) == 0) {
/* Zero for LSB indicates a device in bypass */ /* Zero for LSB indicates a device in bypass */
LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%x)", LOG_INFO("TAP %s does not have IDCODE", tap->dotted_name);
tap->dotted_name, idcode);
tap->hasidcode = false; tap->hasidcode = false;
tap->idcode = 0; tap->idcode = 0;
@ -1370,6 +1308,14 @@ void jtag_tap_free(struct jtag_tap *tap)
{ {
jtag_unregister_event_callback(&jtag_reset_callback, tap); jtag_unregister_event_callback(&jtag_reset_callback, tap);
struct jtag_tap_event_action *jteap = tap->event_action;
while (jteap) {
struct jtag_tap_event_action *next = jteap->next;
Jim_DecrRefCount(jteap->interp, jteap->body);
free(jteap);
jteap = next;
}
free(tap->expected); free(tap->expected);
free(tap->expected_mask); free(tap->expected_mask);
free(tap->expected_ids); free(tap->expected_ids);
@ -1534,13 +1480,21 @@ int jtag_init_inner(struct command_context *cmd_ctx)
int adapter_quit(void) int adapter_quit(void)
{ {
if (!jtag || !jtag->quit) if (jtag && jtag->quit) {
return ERROR_OK; /* close the JTAG interface */
int result = jtag->quit();
if (ERROR_OK != result)
LOG_ERROR("failed: %d", result);
}
/* close the JTAG interface */ struct jtag_tap *t = jtag_all_taps();
int result = jtag->quit(); while (t) {
if (ERROR_OK != result) struct jtag_tap *n = t->next_tap;
LOG_ERROR("failed: %d", result); jtag_tap_free(t);
t = n;
}
dap_cleanup_all();
return ERROR_OK; return ERROR_OK;
} }

View File

@ -80,6 +80,9 @@ if USB_BLASTER_DRIVER
%C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la %C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la
include %D%/usb_blaster/Makefile.am include %D%/usb_blaster/Makefile.am
endif endif
if FT232R
DRIVERFILES += %D%/ft232r.c
endif
if AMTJTAGACCEL if AMTJTAGACCEL
DRIVERFILES += %D%/amt_jtagaccel.c DRIVERFILES += %D%/amt_jtagaccel.c
endif endif

View File

@ -22,6 +22,7 @@
#endif #endif
#include <jtag/interface.h> #include <jtag/interface.h>
#include <jtag/swd.h>
#include <jtag/commands.h> #include <jtag/commands.h>
#include <termios.h> #include <termios.h>
@ -48,9 +49,21 @@ static void buspirate_stableclocks(int num_cycles);
#define CMD_READ_ADCS 0x03 #define CMD_READ_ADCS 0x03
/*#define CMD_TAP_SHIFT 0x04 // old protocol */ /*#define CMD_TAP_SHIFT 0x04 // old protocol */
#define CMD_TAP_SHIFT 0x05 #define CMD_TAP_SHIFT 0x05
#define CMD_ENTER_RWIRE 0x05
#define CMD_ENTER_OOCD 0x06 #define CMD_ENTER_OOCD 0x06
#define CMD_UART_SPEED 0x07 #define CMD_UART_SPEED 0x07
#define CMD_JTAG_SPEED 0x08 #define CMD_JTAG_SPEED 0x08
#define CMD_RAW_PERIPH 0x40
#define CMD_RAW_SPEED 0x60
#define CMD_RAW_MODE 0x80
/* raw-wire mode configuration */
#define CMD_RAW_CONFIG_HIZ 0x00
#define CMD_RAW_CONFIG_3V3 0x08
#define CMD_RAW_CONFIG_2W 0x00
#define CMD_RAW_CONFIG_3W 0x04
#define CMD_RAW_CONFIG_MSB 0x00
#define CMD_RAW_CONFIG_LSB 0x02
/* Not all OSes have this speed defined */ /* Not all OSes have this speed defined */
#if !defined(B1000000) #if !defined(B1000000)
@ -81,6 +94,18 @@ enum {
SERIAL_FAST = 1 SERIAL_FAST = 1
}; };
enum {
SPEED_RAW_5_KHZ = 0x0,
SPEED_RAW_50_KHZ = 0x1,
SPEED_RAW_100_KHZ = 0x2,
SPEED_RAW_400_KHZ = 0x3
};
/* SWD mode specific */
static bool swd_mode;
static int queued_retval;
static char swd_features;
static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */ static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */
static const cc_t NORMAL_TIMEOUT = 10; static const cc_t NORMAL_TIMEOUT = 10;
@ -93,6 +118,12 @@ static char *buspirate_port;
static enum tap_state last_tap_state = TAP_RESET; static enum tap_state last_tap_state = TAP_RESET;
/* SWD interface */
static int buspirate_swd_init(void);
static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk);
static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk);
static int buspirate_swd_switch_seq(enum swd_special_seq seq);
static int buspirate_swd_run_queue(void);
/* TAP interface */ /* TAP interface */
static void buspirate_tap_init(void); static void buspirate_tap_init(void);
@ -103,23 +134,31 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
static void buspirate_tap_make_space(int scan, int bits); static void buspirate_tap_make_space(int scan, int bits);
static void buspirate_reset(int trst, int srst); static void buspirate_reset(int trst, int srst);
static void buspirate_set_feature(int, char, char);
static void buspirate_set_mode(int, char);
static void buspirate_set_speed(int, char);
/* low level interface */ /* low level interface */
static void buspirate_bbio_enable(int);
static void buspirate_jtag_reset(int); static void buspirate_jtag_reset(int);
static void buspirate_jtag_enable(int); static unsigned char buspirate_jtag_command(int, uint8_t *, int);
static unsigned char buspirate_jtag_command(int, char *, int);
static void buspirate_jtag_set_speed(int, char); static void buspirate_jtag_set_speed(int, char);
static void buspirate_jtag_set_mode(int, char); static void buspirate_jtag_set_mode(int, char);
static void buspirate_jtag_set_feature(int, char, char); static void buspirate_jtag_set_feature(int, char, char);
static void buspirate_jtag_get_adcs(int); static void buspirate_jtag_get_adcs(int);
/* low level two-wire interface */
static void buspirate_swd_set_speed(int, char);
static void buspirate_swd_set_feature(int, char, char);
static void buspirate_swd_set_mode(int, char);
/* low level HW communication interface */ /* low level HW communication interface */
static int buspirate_serial_open(char *port); static int buspirate_serial_open(char *port);
static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout); static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout);
static int buspirate_serial_write(int fd, char *buf, int size); static int buspirate_serial_write(int fd, uint8_t *buf, int size);
static int buspirate_serial_read(int fd, char *buf, int size); static int buspirate_serial_read(int fd, uint8_t *buf, int size);
static void buspirate_serial_close(int fd); static void buspirate_serial_close(int fd);
static void buspirate_print_buffer(char *buf, int size); static void buspirate_print_buffer(uint8_t *buf, int size);
static int buspirate_execute_queue(void) static int buspirate_execute_queue(void)
{ {
@ -216,7 +255,7 @@ static bool read_and_discard_all_data(const int fd)
bool was_msg_already_printed = false; bool was_msg_already_printed = false;
for ( ; ; ) { for ( ; ; ) {
char buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */ uint8_t buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */
const ssize_t read_count = read(fd, buffer, sizeof(buffer)); const ssize_t read_count = read(fd, buffer, sizeof(buffer));
@ -295,18 +334,20 @@ static int buspirate_init(void)
return ERROR_JTAG_INIT_FAILED; return ERROR_JTAG_INIT_FAILED;
} }
buspirate_jtag_enable(buspirate_fd); buspirate_bbio_enable(buspirate_fd);
if (buspirate_baudrate != SERIAL_NORMAL) if (swd_mode || buspirate_baudrate != SERIAL_NORMAL)
buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST); buspirate_set_speed(buspirate_fd, SERIAL_FAST);
LOG_INFO("Buspirate Interface ready!"); LOG_INFO("Buspirate %s Interface ready!", swd_mode ? "SWD" : "JTAG");
buspirate_tap_init(); if (!swd_mode)
buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode); buspirate_tap_init();
buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG,
buspirate_set_mode(buspirate_fd, buspirate_pinmode);
buspirate_set_feature(buspirate_fd, FEATURE_VREG,
(buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE); (buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE);
buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP, buspirate_set_feature(buspirate_fd, FEATURE_PULLUP,
(buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE); (buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE);
buspirate_reset(0, 0); buspirate_reset(0, 0);
@ -316,9 +357,9 @@ static int buspirate_init(void)
static int buspirate_quit(void) static int buspirate_quit(void)
{ {
LOG_INFO("Shutting down buspirate."); LOG_INFO("Shutting down buspirate.");
buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ); buspirate_set_mode(buspirate_fd, MODE_HIZ);
buspirate_set_speed(buspirate_fd, SERIAL_NORMAL);
buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL);
buspirate_jtag_reset(buspirate_fd); buspirate_jtag_reset(buspirate_fd);
buspirate_serial_close(buspirate_fd); buspirate_serial_close(buspirate_fd);
@ -336,6 +377,10 @@ COMMAND_HANDLER(buspirate_handle_adc_command)
if (buspirate_fd == -1) if (buspirate_fd == -1)
return ERROR_OK; return ERROR_OK;
/* unavailable in SWD mode */
if (swd_mode)
return ERROR_OK;
/* send the command */ /* send the command */
buspirate_jtag_get_adcs(buspirate_fd); buspirate_jtag_get_adcs(buspirate_fd);
@ -382,11 +427,11 @@ COMMAND_HANDLER(buspirate_handle_led_command)
if (atoi(CMD_ARGV[0]) == 1) { if (atoi(CMD_ARGV[0]) == 1) {
/* enable led */ /* enable led */
buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, buspirate_set_feature(buspirate_fd, FEATURE_LED,
ACTION_ENABLE); ACTION_ENABLE);
} else if (atoi(CMD_ARGV[0]) == 0) { } else if (atoi(CMD_ARGV[0]) == 0) {
/* disable led */ /* disable led */
buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, buspirate_set_feature(buspirate_fd, FEATURE_LED,
ACTION_DISABLE); ACTION_DISABLE);
} else { } else {
LOG_ERROR("usage: buspirate_led <1|0>"); LOG_ERROR("usage: buspirate_led <1|0>");
@ -492,10 +537,22 @@ static const struct command_registration buspirate_command_handlers[] = {
COMMAND_REGISTRATION_DONE COMMAND_REGISTRATION_DONE
}; };
static const struct swd_driver buspirate_swd = {
.init = buspirate_swd_init,
.switch_seq = buspirate_swd_switch_seq,
.read_reg = buspirate_swd_read_reg,
.write_reg = buspirate_swd_write_reg,
.run = buspirate_swd_run_queue,
};
static const char * const buspirate_transports[] = { "jtag", "swd", NULL };
struct jtag_interface buspirate_interface = { struct jtag_interface buspirate_interface = {
.name = "buspirate", .name = "buspirate",
.execute_queue = buspirate_execute_queue, .execute_queue = buspirate_execute_queue,
.commands = buspirate_command_handlers, .commands = buspirate_command_handlers,
.transports = buspirate_transports,
.swd = &buspirate_swd,
.init = buspirate_init, .init = buspirate_init,
.quit = buspirate_quit .quit = buspirate_quit
}; };
@ -633,8 +690,8 @@ static void buspirate_stableclocks(int num_cycles)
make it incompatible with the Bus Pirate firmware. */ make it incompatible with the Bus Pirate firmware. */
#define BUSPIRATE_MAX_PENDING_SCANS 128 #define BUSPIRATE_MAX_PENDING_SCANS 128
static char tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ static uint8_t tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
static char tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ static uint8_t tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
static int tap_chain_index; static int tap_chain_index;
struct pending_scan_result /* this was stolen from arm-jtag-ew */ struct pending_scan_result /* this was stolen from arm-jtag-ew */
@ -659,7 +716,7 @@ static int buspirate_tap_execute(void)
{ {
static const int CMD_TAP_SHIFT_HEADER_LEN = 3; static const int CMD_TAP_SHIFT_HEADER_LEN = 3;
char tmp[4096]; uint8_t tmp[4096];
uint8_t *in_buf; uint8_t *in_buf;
int i; int i;
int fill_index = 0; int fill_index = 0;
@ -675,8 +732,8 @@ static int buspirate_tap_execute(void)
bytes_to_send = DIV_ROUND_UP(tap_chain_index, 8); bytes_to_send = DIV_ROUND_UP(tap_chain_index, 8);
tmp[0] = CMD_TAP_SHIFT; /* this command expects number of bits */ tmp[0] = CMD_TAP_SHIFT; /* this command expects number of bits */
tmp[1] = (char)(tap_chain_index >> 8); /* high */ tmp[1] = tap_chain_index >> 8; /* high */
tmp[2] = (char)(tap_chain_index); /* low */ tmp[2] = tap_chain_index; /* low */
fill_index = CMD_TAP_SHIFT_HEADER_LEN; fill_index = CMD_TAP_SHIFT_HEADER_LEN;
for (i = 0; i < bytes_to_send; i++) { for (i = 0; i < bytes_to_send; i++) {
@ -799,7 +856,7 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
tap_pending_scans_num++; tap_pending_scans_num++;
} }
/*************** jtag wrapper functions *********************/ /*************** wrapper functions *********************/
/* (1) assert or (0) deassert reset lines */ /* (1) assert or (0) deassert reset lines */
static void buspirate_reset(int trst, int srst) static void buspirate_reset(int trst, int srst)
@ -807,33 +864,148 @@ static void buspirate_reset(int trst, int srst)
LOG_DEBUG("trst: %i, srst: %i", trst, srst); LOG_DEBUG("trst: %i, srst: %i", trst, srst);
if (trst) if (trst)
buspirate_jtag_set_feature(buspirate_fd, buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE);
FEATURE_TRST, ACTION_DISABLE);
else else
buspirate_jtag_set_feature(buspirate_fd, buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_ENABLE);
FEATURE_TRST, ACTION_ENABLE);
if (srst) if (srst)
buspirate_jtag_set_feature(buspirate_fd, buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_DISABLE);
FEATURE_SRST, ACTION_DISABLE);
else else
buspirate_jtag_set_feature(buspirate_fd, buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_ENABLE);
FEATURE_SRST, ACTION_ENABLE); }
static void buspirate_set_feature(int fd, char feat, char action)
{
if (swd_mode)
buspirate_swd_set_feature(fd, feat, action);
else
buspirate_jtag_set_feature(fd, feat, action);
}
static void buspirate_set_mode(int fd, char mode)
{
if (swd_mode)
buspirate_swd_set_mode(fd, mode);
else
buspirate_jtag_set_mode(fd, mode);
}
static void buspirate_set_speed(int fd, char speed)
{
if (swd_mode)
buspirate_swd_set_speed(fd, speed);
else
buspirate_jtag_set_speed(fd, speed);
}
/*************** swd lowlevel functions ********************/
static void buspirate_swd_set_speed(int fd, char speed)
{
int ret;
uint8_t tmp[1];
LOG_DEBUG("Buspirate speed setting in SWD mode defaults to 400 kHz");
/* speed settings */
tmp[0] = CMD_RAW_SPEED | SPEED_RAW_400_KHZ;
buspirate_serial_write(fd, tmp, 1);
ret = buspirate_serial_read(fd, tmp, 1);
if (ret != 1) {
LOG_ERROR("Buspirate did not answer correctly");
exit(-1);
}
if (tmp[0] != 1) {
LOG_ERROR("Buspirate did not reply as expected to the speed change command");
exit(-1);
}
}
static void buspirate_swd_set_mode(int fd, char mode)
{
int ret;
uint8_t tmp[1];
/* raw-wire mode configuration */
if (mode == MODE_HIZ)
tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB;
else
tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB | CMD_RAW_CONFIG_3V3;
buspirate_serial_write(fd, tmp, 1);
ret = buspirate_serial_read(fd, tmp, 1);
if (ret != 1) {
LOG_ERROR("Buspirate did not answer correctly");
exit(-1);
}
if (tmp[0] != 1) {
LOG_ERROR("Buspirate did not reply as expected to the configure command");
exit(-1);
}
}
static void buspirate_swd_set_feature(int fd, char feat, char action)
{
int ret;
uint8_t tmp[1];
switch (feat) {
case FEATURE_TRST:
LOG_DEBUG("Buspirate TRST feature not available in SWD mode");
return;
case FEATURE_LED:
LOG_ERROR("Buspirate LED feature not available in SWD mode");
return;
case FEATURE_SRST:
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x02 : swd_features & 0x0D;
break;
case FEATURE_PULLUP:
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x04 : swd_features & 0x0B;
break;
case FEATURE_VREG:
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x08 : swd_features & 0x07;
break;
default:
LOG_DEBUG("Buspirate unknown feature %d", feat);
return;
}
tmp[0] = CMD_RAW_PERIPH | swd_features;
buspirate_serial_write(fd, tmp, 1);
ret = buspirate_serial_read(fd, tmp, 1);
if (ret != 1) {
LOG_DEBUG("Buspirate feature %d not supported in SWD mode", feat);
} else if (tmp[0] != 1) {
LOG_ERROR("Buspirate did not reply as expected to the configure command");
exit(-1);
}
} }
/*************** jtag lowlevel functions ********************/ /*************** jtag lowlevel functions ********************/
static void buspirate_jtag_enable(int fd) static void buspirate_bbio_enable(int fd)
{ {
int ret; int ret;
char tmp[21] = { [0 ... 20] = 0x00 }; char command;
const char *mode_answers[2] = { "OCD1", "RAW1" };
const char *correct_ans = NULL;
uint8_t tmp[21] = { [0 ... 20] = 0x00 };
int done = 0; int done = 0;
int cmd_sent = 0; int cmd_sent = 0;
LOG_DEBUG("Entering binary mode"); if (swd_mode) {
command = CMD_ENTER_RWIRE;
correct_ans = mode_answers[1];
} else {
command = CMD_ENTER_OOCD;
correct_ans = mode_answers[0];
}
LOG_DEBUG("Entering binary mode, that is %s", correct_ans);
buspirate_serial_write(fd, tmp, 20); buspirate_serial_write(fd, tmp, 20);
usleep(10000); usleep(10000);
/* reads 1 to n "BBIO1"s and one "OCD1" */ /* reads 1 to n "BBIO1"s and one "OCD1" or "RAW1" */
while (!done) { while (!done) {
ret = buspirate_serial_read(fd, tmp, 4); ret = buspirate_serial_read(fd, tmp, 4);
if (ret != 4) { if (ret != 4) {
@ -841,7 +1013,7 @@ static void buspirate_jtag_enable(int fd)
"/OpenOCD support enabled?"); "/OpenOCD support enabled?");
exit(-1); exit(-1);
} }
if (strncmp(tmp, "BBIO", 4) == 0) { if (strncmp((char *)tmp, "BBIO", 4) == 0) {
ret = buspirate_serial_read(fd, tmp, 1); ret = buspirate_serial_read(fd, tmp, 1);
if (ret != 1) { if (ret != 1) {
LOG_ERROR("Buspirate did not answer correctly! " LOG_ERROR("Buspirate did not answer correctly! "
@ -854,14 +1026,14 @@ static void buspirate_jtag_enable(int fd)
} }
if (cmd_sent == 0) { if (cmd_sent == 0) {
cmd_sent = 1; cmd_sent = 1;
tmp[0] = CMD_ENTER_OOCD; tmp[0] = command;
ret = buspirate_serial_write(fd, tmp, 1); ret = buspirate_serial_write(fd, tmp, 1);
if (ret != 1) { if (ret != 1) {
LOG_ERROR("error reading"); LOG_ERROR("error reading");
exit(-1); exit(-1);
} }
} }
} else if (strncmp(tmp, "OCD1", 4) == 0) } else if (strncmp((char *)tmp, correct_ans, 4) == 0)
done = 1; done = 1;
else { else {
LOG_ERROR("Buspirate did not answer correctly! " LOG_ERROR("Buspirate did not answer correctly! "
@ -874,14 +1046,14 @@ static void buspirate_jtag_enable(int fd)
static void buspirate_jtag_reset(int fd) static void buspirate_jtag_reset(int fd)
{ {
char tmp[5]; uint8_t tmp[5];
tmp[0] = 0x00; /* exit OCD1 mode */ tmp[0] = 0x00; /* exit OCD1 mode */
buspirate_serial_write(fd, tmp, 1); buspirate_serial_write(fd, tmp, 1);
usleep(10000); usleep(10000);
/* We ignore the return value here purposly, nothing we can do */ /* We ignore the return value here purposly, nothing we can do */
buspirate_serial_read(fd, tmp, 5); buspirate_serial_read(fd, tmp, 5);
if (strncmp(tmp, "BBIO1", 5) == 0) { if (strncmp((char *)tmp, "BBIO1", 5) == 0) {
tmp[0] = 0x0F; /* reset BP */ tmp[0] = 0x0F; /* reset BP */
buspirate_serial_write(fd, tmp, 1); buspirate_serial_write(fd, tmp, 1);
} else } else
@ -891,8 +1063,8 @@ static void buspirate_jtag_reset(int fd)
static void buspirate_jtag_set_speed(int fd, char speed) static void buspirate_jtag_set_speed(int fd, char speed)
{ {
int ret; int ret;
char tmp[2]; uint8_t tmp[2];
char ack[2]; uint8_t ack[2];
ack[0] = 0xAA; ack[0] = 0xAA;
ack[1] = 0x55; ack[1] = 0x55;
@ -924,7 +1096,7 @@ static void buspirate_jtag_set_speed(int fd, char speed)
static void buspirate_jtag_set_mode(int fd, char mode) static void buspirate_jtag_set_mode(int fd, char mode)
{ {
char tmp[2]; uint8_t tmp[2];
tmp[0] = CMD_PORT_MODE; tmp[0] = CMD_PORT_MODE;
tmp[1] = mode; tmp[1] = mode;
buspirate_jtag_command(fd, tmp, 2); buspirate_jtag_command(fd, tmp, 2);
@ -932,7 +1104,7 @@ static void buspirate_jtag_set_mode(int fd, char mode)
static void buspirate_jtag_set_feature(int fd, char feat, char action) static void buspirate_jtag_set_feature(int fd, char feat, char action)
{ {
char tmp[3]; uint8_t tmp[3];
tmp[0] = CMD_FEATURE; tmp[0] = CMD_FEATURE;
tmp[1] = feat; /* what */ tmp[1] = feat; /* what */
tmp[2] = action; /* action */ tmp[2] = action; /* action */
@ -944,7 +1116,7 @@ static void buspirate_jtag_get_adcs(int fd)
uint8_t tmp[10]; uint8_t tmp[10];
uint16_t a, b, c, d; uint16_t a, b, c, d;
tmp[0] = CMD_READ_ADCS; tmp[0] = CMD_READ_ADCS;
buspirate_jtag_command(fd, (char *)tmp, 1); buspirate_jtag_command(fd, tmp, 1);
a = tmp[2] << 8 | tmp[3]; a = tmp[2] << 8 | tmp[3];
b = tmp[4] << 8 | tmp[5]; b = tmp[4] << 8 | tmp[5];
c = tmp[6] << 8 | tmp[7]; c = tmp[6] << 8 | tmp[7];
@ -957,7 +1129,7 @@ static void buspirate_jtag_get_adcs(int fd)
} }
static unsigned char buspirate_jtag_command(int fd, static unsigned char buspirate_jtag_command(int fd,
char *cmd, int cmdlen) uint8_t *cmd, int cmdlen)
{ {
int res; int res;
int len = 0; int len = 0;
@ -1048,7 +1220,7 @@ static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout)
return 0; return 0;
} }
static int buspirate_serial_write(int fd, char *buf, int size) static int buspirate_serial_write(int fd, uint8_t *buf, int size)
{ {
int ret = 0; int ret = 0;
@ -1063,7 +1235,7 @@ static int buspirate_serial_write(int fd, char *buf, int size)
return ret; return ret;
} }
static int buspirate_serial_read(int fd, char *buf, int size) static int buspirate_serial_read(int fd, uint8_t *buf, int size)
{ {
int len = 0; int len = 0;
int ret = 0; int ret = 0;
@ -1102,7 +1274,7 @@ static void buspirate_serial_close(int fd)
#define LINE_SIZE 81 #define LINE_SIZE 81
#define BYTES_PER_LINE 16 #define BYTES_PER_LINE 16
static void buspirate_print_buffer(char *buf, int size) static void buspirate_print_buffer(uint8_t *buf, int size)
{ {
char line[LINE_SIZE]; char line[LINE_SIZE];
char tmp[10]; char tmp[10];
@ -1124,3 +1296,240 @@ static void buspirate_print_buffer(char *buf, int size)
if (line[0] != 0) if (line[0] != 0)
LOG_DEBUG("%s", line); LOG_DEBUG("%s", line);
} }
/************************* SWD related stuff **********/
static int buspirate_swd_init(void)
{
LOG_INFO("Buspirate SWD mode enabled");
swd_mode = true;
return ERROR_OK;
}
static int buspirate_swd_switch_seq(enum swd_special_seq seq)
{
const uint8_t *sequence;
int sequence_len;
uint8_t tmp[64];
switch (seq) {
case LINE_RESET:
LOG_DEBUG("SWD line reset");
sequence = swd_seq_line_reset;
sequence_len = DIV_ROUND_UP(swd_seq_line_reset_len, 8);
break;
case JTAG_TO_SWD:
LOG_DEBUG("JTAG-to-SWD");
sequence = swd_seq_jtag_to_swd;
sequence_len = DIV_ROUND_UP(swd_seq_jtag_to_swd_len, 8);
break;
case SWD_TO_JTAG:
LOG_DEBUG("SWD-to-JTAG");
sequence = swd_seq_swd_to_jtag;
sequence_len = DIV_ROUND_UP(swd_seq_swd_to_jtag_len, 8);
break;
default:
LOG_ERROR("Sequence %d not supported", seq);
return ERROR_FAIL;
}
/* FIXME: all above sequences fit into one pirate command for now
* but it may cause trouble later
*/
tmp[0] = 0x10 + ((sequence_len - 1) & 0x0F);
memcpy(tmp + 1, sequence, sequence_len);
buspirate_serial_write(buspirate_fd, tmp, sequence_len + 1);
buspirate_serial_read(buspirate_fd, tmp, sequence_len + 1);
return ERROR_OK;
}
static uint8_t buspirate_swd_write_header(uint8_t cmd)
{
uint8_t tmp[8];
int to_send;
tmp[0] = 0x10; /* bus pirate: send 1 byte */
tmp[1] = cmd; /* swd cmd */
tmp[2] = 0x07; /* ack __x */
tmp[3] = 0x07; /* ack _x_ */
tmp[4] = 0x07; /* ack x__ */
tmp[5] = 0x07; /* write mode trn_1 */
tmp[6] = 0x07; /* write mode trn_2 */
to_send = ((cmd & SWD_CMD_RnW) == 0) ? 7 : 5;
buspirate_serial_write(buspirate_fd, tmp, to_send);
/* read ack */
buspirate_serial_read(buspirate_fd, tmp, 2); /* drop pirate command ret vals */
buspirate_serial_read(buspirate_fd, tmp, to_send - 2); /* ack bits */
return tmp[2] << 2 | tmp[1] << 1 | tmp[0];
}
static void buspirate_swd_idle_clocks(uint32_t no_bits)
{
uint32_t no_bytes;
uint8_t tmp[20];
no_bytes = (no_bits + 7) / 8;
memset(tmp + 1, 0x00, sizeof(tmp) - 1);
/* unfortunately bus pirate misbehaves when clocks are sent in parts
* so we need to limit at 128 clock cycles
*/
if (no_bytes > 16)
no_bytes = 16;
while (no_bytes) {
uint8_t to_send = no_bytes > 16 ? 16 : no_bytes;
tmp[0] = 0x10 + ((to_send - 1) & 0x0F);
buspirate_serial_write(buspirate_fd, tmp, to_send + 1);
buspirate_serial_read(buspirate_fd, tmp, to_send + 1);
no_bytes -= to_send;
}
}
static void buspirate_swd_clear_sticky_errors(void)
{
buspirate_swd_write_reg(swd_cmd(false, false, DP_ABORT),
STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
}
static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
{
uint8_t tmp[16];
LOG_DEBUG("buspirate_swd_read_reg");
assert(cmd & SWD_CMD_RnW);
if (queued_retval != ERROR_OK) {
LOG_DEBUG("Skip buspirate_swd_read_reg because queued_retval=%d", queued_retval);
return;
}
cmd |= SWD_CMD_START | SWD_CMD_PARK;
uint8_t ack = buspirate_swd_write_header(cmd);
/* do a read transaction */
tmp[0] = 0x06; /* 4 data bytes */
tmp[1] = 0x06;
tmp[2] = 0x06;
tmp[3] = 0x06;
tmp[4] = 0x07; /* parity bit */
tmp[5] = 0x21; /* 2 turnaround clocks */
buspirate_serial_write(buspirate_fd, tmp, 6);
buspirate_serial_read(buspirate_fd, tmp, 6);
/* store the data and parity */
uint32_t data = (uint8_t) tmp[0];
data |= (uint8_t) tmp[1] << 8;
data |= (uint8_t) tmp[2] << 16;
data |= (uint8_t) tmp[3] << 24;
int parity = tmp[4] ? 0x01 : 0x00;
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
cmd & SWD_CMD_APnDP ? "AP" : "DP",
cmd & SWD_CMD_RnW ? "read" : "write",
(cmd & SWD_CMD_A32) >> 1,
data);
switch (ack) {
case SWD_ACK_OK:
if (parity != parity_u32(data)) {
LOG_DEBUG("Read data parity mismatch %x %x", parity, parity_u32(data));
queued_retval = ERROR_FAIL;
return;
}
if (value)
*value = data;
if (cmd & SWD_CMD_APnDP)
buspirate_swd_idle_clocks(ap_delay_clk);
return;
case SWD_ACK_WAIT:
LOG_DEBUG("SWD_ACK_WAIT");
buspirate_swd_clear_sticky_errors();
return;
case SWD_ACK_FAULT:
LOG_DEBUG("SWD_ACK_FAULT");
queued_retval = ack;
return;
default:
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
queued_retval = ack;
return;
}
}
static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
{
uint8_t tmp[16];
LOG_DEBUG("buspirate_swd_write_reg");
assert(!(cmd & SWD_CMD_RnW));
if (queued_retval != ERROR_OK) {
LOG_DEBUG("Skip buspirate_swd_write_reg because queued_retval=%d", queued_retval);
return;
}
cmd |= SWD_CMD_START | SWD_CMD_PARK;
uint8_t ack = buspirate_swd_write_header(cmd);
/* do a write transaction */
tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */
buf_set_u32((uint8_t *) tmp + 1, 0, 32, value);
/* write sequence ends with parity bit and 7 idle ticks */
tmp[5] = parity_u32(value) ? 0x01 : 0x00;
buspirate_serial_write(buspirate_fd, tmp, 6);
buspirate_serial_read(buspirate_fd, tmp, 6);
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
cmd & SWD_CMD_APnDP ? "AP" : "DP",
cmd & SWD_CMD_RnW ? "read" : "write",
(cmd & SWD_CMD_A32) >> 1,
value);
switch (ack) {
case SWD_ACK_OK:
if (cmd & SWD_CMD_APnDP)
buspirate_swd_idle_clocks(ap_delay_clk);
return;
case SWD_ACK_WAIT:
LOG_DEBUG("SWD_ACK_WAIT");
buspirate_swd_clear_sticky_errors();
return;
case SWD_ACK_FAULT:
LOG_DEBUG("SWD_ACK_FAULT");
queued_retval = ack;
return;
default:
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
queued_retval = ack;
return;
}
}
static int buspirate_swd_run_queue(void)
{
LOG_DEBUG("buspirate_swd_run_queue");
/* A transaction must be followed by another transaction or at least 8 idle cycles to
* ensure that data is clocked through the AP. */
buspirate_swd_idle_clocks(8);
int retval = queued_retval;
queued_retval = ERROR_OK;
LOG_DEBUG("SWD queue return value: %02x", retval);
return retval;
}

669
src/jtag/drivers/ft232r.c Normal file
View File

@ -0,0 +1,669 @@
/***************************************************************************
* Copyright (C) 2010 Serge Vakulenko *
* serge@vak.ru *
* *
* 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
#if IS_CYGWIN == 1
#include "windows.h"
#undef LOG_ERROR
#endif
/* project specific includes */
#include <jtag/interface.h>
#include <jtag/commands.h>
#include <helper/time_support.h>
#include "libusb1_common.h"
/* system includes */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
/*
* Bit 7 (0x80, pin 6, RI ): unused.
* Bit 6 (0x40, pin 10,DCD): /SYSRST output.
* Bit 5 (0x20, pin 9, DSR): unused.
* Bit 4 (0x10, pin 2, DTR): /TRST output.
* Bit 3 (0x08, pin 11,CTS): TMS output.
* Bit 2 (0x04, pin 3, RTS): TDO input.
* Bit 1 (0x02, pin 5, RXD): TDI output.
* Bit 0 (0x01, pin 1, TXD): TCK output.
*
* Sync bit bang mode is implemented as described in FTDI Application
* Note AN232R-01: "Bit Bang Modes for the FT232R and FT245R".
*/
#define TCK (1 << 0)
#define TDI (1 << 1)
#define READ_TDO (1 << 2)
#define TMS (1 << 3)
#define NTRST (1 << 4)
#define NSYSRST (1 << 6)
/*
* USB endpoints.
*/
#define IN_EP 0x02
#define OUT_EP 0x81
/* Requests */
#define SIO_RESET 0 /* Reset the port */
#define SIO_MODEM_CTRL 1 /* Set the modem control register */
#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */
#define SIO_SET_BAUD_RATE 3 /* Set baud rate */
#define SIO_SET_DATA 4 /* Set the data characteristics of the port */
#define SIO_POLL_MODEM_STATUS 5
#define SIO_SET_EVENT_CHAR 6
#define SIO_SET_ERROR_CHAR 7
#define SIO_SET_LATENCY_TIMER 9
#define SIO_GET_LATENCY_TIMER 10
#define SIO_SET_BITMODE 11
#define SIO_READ_PINS 12
#define SIO_READ_EEPROM 0x90
#define SIO_WRITE_EEPROM 0x91
#define SIO_ERASE_EEPROM 0x92
#define FT232R_BUF_SIZE 4000
static char *ft232r_serial_desc;
static uint16_t ft232r_vid = 0x0403; /* FTDI */
static uint16_t ft232r_pid = 0x6001; /* FT232R */
static jtag_libusb_device_handle *adapter;
static uint8_t *ft232r_output;
static size_t ft232r_output_len;
/**
* Perform sync bitbang output/input transaction.
* Before call, an array ft232r_output[] should be filled with data to send.
* Counter ft232r_output_len contains the number of bytes to send.
* On return, received data is put back to array ft232r_output[].
*/
static int ft232r_send_recv(void)
{
/* FIFO TX buffer has 128 bytes.
* FIFO RX buffer has 256 bytes.
* First two bytes of received packet contain contain modem
* and line status and are ignored.
* Unfortunately, transfer sizes bigger than 64 bytes
* frequently cause hang ups. */
assert(ft232r_output_len > 0);
size_t total_written = 0;
size_t total_read = 0;
int rxfifo_free = 128;
while (total_read < ft232r_output_len) {
/* Write */
int bytes_to_write = ft232r_output_len - total_written;
if (bytes_to_write > 64)
bytes_to_write = 64;
if (bytes_to_write > rxfifo_free)
bytes_to_write = rxfifo_free;
if (bytes_to_write) {
int n = jtag_libusb_bulk_write(adapter, IN_EP,
(char *) ft232r_output + total_written,
bytes_to_write, 1000);
if (n == 0) {
LOG_ERROR("usb bulk write failed");
return ERROR_JTAG_DEVICE_ERROR;
}
total_written += n;
rxfifo_free -= n;
}
/* Read */
uint8_t reply[64];
int n = jtag_libusb_bulk_read(adapter, OUT_EP,
(char *) reply,
sizeof(reply), 1000);
if (n == 0) {
LOG_ERROR("usb bulk read failed");
return ERROR_JTAG_DEVICE_ERROR;
}
if (n > 2) {
/* Copy data, ignoring first 2 bytes. */
memcpy(ft232r_output + total_read, reply + 2, n - 2);
int bytes_read = n - 2;
total_read += bytes_read;
rxfifo_free += bytes_read;
if (total_read > total_written) {
LOG_ERROR("read more bytes than wrote");
return ERROR_JTAG_DEVICE_ERROR;
}
}
}
ft232r_output_len = 0;
return ERROR_OK;
}
/**
* Add one TCK/TMS/TDI sample to send buffer.
*/
static void ft232r_write(int tck, int tms, int tdi)
{
unsigned out_value = NTRST | NSYSRST;
if (tck)
out_value |= TCK;
if (tms)
out_value |= TMS;
if (tdi)
out_value |= TDI;
if (ft232r_output_len >= FT232R_BUF_SIZE) {
/* FIXME: should we just execute queue here? */
LOG_ERROR("ft232r_write: buffer overflow");
return;
}
ft232r_output[ft232r_output_len++] = out_value;
}
/**
* Control /TRST and /SYSRST pins.
* Perform immediate bitbang transaction.
*/
static void ft232r_reset(int trst, int srst)
{
unsigned out_value = NTRST | NSYSRST;
LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst);
if (trst == 1)
out_value &= ~NTRST; /* switch /TRST low */
else if (trst == 0)
out_value |= NTRST; /* switch /TRST high */
if (srst == 1)
out_value &= ~NSYSRST; /* switch /SYSRST low */
else if (srst == 0)
out_value |= NSYSRST; /* switch /SYSRST high */
if (ft232r_output_len >= FT232R_BUF_SIZE) {
/* FIXME: should we just execute queue here? */
LOG_ERROR("ft232r_write: buffer overflow");
return;
}
ft232r_output[ft232r_output_len++] = out_value;
ft232r_send_recv();
}
static int ft232r_speed(int divisor)
{
int baud = (divisor == 0) ? 3000000 :
(divisor == 1) ? 2000000 :
3000000 / divisor;
LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud);
if (jtag_libusb_control_transfer(adapter,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) {
LOG_ERROR("cannot set baud rate");
return ERROR_JTAG_DEVICE_ERROR;
}
return ERROR_OK;
}
static int ft232r_init(void)
{
uint16_t avids[] = {ft232r_vid, 0};
uint16_t apids[] = {ft232r_pid, 0};
if (jtag_libusb_open(avids, apids, ft232r_serial_desc, &adapter)) {
LOG_ERROR("ft232r not found: vid=%04x, pid=%04x, serial=%s\n",
ft232r_vid, ft232r_pid, (ft232r_serial_desc == NULL) ? "[any]" : ft232r_serial_desc);
return ERROR_JTAG_INIT_FAILED;
}
libusb_detach_kernel_driver(adapter, 0);
if (jtag_libusb_claim_interface(adapter, 0)) {
LOG_ERROR("unable to claim interface");
return ERROR_JTAG_INIT_FAILED;
}
/* Reset the device. */
if (jtag_libusb_control_transfer(adapter,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
SIO_RESET, 0, 0, 0, 0, 1000) != 0) {
LOG_ERROR("unable to reset device");
return ERROR_JTAG_INIT_FAILED;
}
/* Sync bit bang mode. */
if (jtag_libusb_control_transfer(adapter,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
SIO_SET_BITMODE, TCK | TDI | TMS | NTRST | NSYSRST | 0x400,
0, 0, 0, 1000) != 0) {
LOG_ERROR("cannot set sync bitbang mode");
return ERROR_JTAG_INIT_FAILED;
}
/* Exactly 500 nsec between updates. */
unsigned divisor = 1;
unsigned char latency_timer = 1;
/* Frequency divisor is 14-bit non-zero value. */
if (jtag_libusb_control_transfer(adapter,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
SIO_SET_BAUD_RATE, divisor,
0, 0, 0, 1000) != 0) {
LOG_ERROR("cannot set baud rate");
return ERROR_JTAG_INIT_FAILED;
}
if (jtag_libusb_control_transfer(adapter,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) {
LOG_ERROR("unable to set latency timer");
return ERROR_JTAG_INIT_FAILED;
}
ft232r_output = malloc(FT232R_BUF_SIZE);
if (ft232r_output == NULL) {
LOG_ERROR("Unable to allocate memory for the buffer");
return ERROR_JTAG_INIT_FAILED;
}
return ERROR_OK;
}
static int ft232r_quit(void)
{
if (jtag_libusb_release_interface(adapter, 0) != 0)
LOG_ERROR("usb release interface failed");
jtag_libusb_close(adapter);
free(ft232r_output);
return ERROR_OK;
}
static int ft232r_speed_div(int divisor, int *khz)
{
/* Maximum 3 Mbaud for bit bang mode. */
if (divisor == 0)
*khz = 3000;
else if (divisor == 1)
*khz = 2000;
else
*khz = 3000 / divisor;
return ERROR_OK;
}
static int ft232r_khz(int khz, int *divisor)
{
if (khz == 0) {
LOG_DEBUG("RCLK not supported");
return ERROR_FAIL;
}
/* Calculate frequency divisor. */
if (khz > 2500)
*divisor = 0; /* Special case: 3 MHz */
else if (khz > 1700)
*divisor = 1; /* Special case: 2 MHz */
else {
*divisor = (2*3000 / khz + 1) / 2;
if (*divisor > 0x3FFF)
*divisor = 0x3FFF;
}
return ERROR_OK;
}
COMMAND_HANDLER(ft232r_handle_serial_desc_command)
{
if (CMD_ARGC == 1)
ft232r_serial_desc = strdup(CMD_ARGV[0]);
else
LOG_ERROR("require exactly one argument to "
"ft232r_serial_desc <serial>");
return ERROR_OK;
}
COMMAND_HANDLER(ft232r_handle_vid_pid_command)
{
if (CMD_ARGC > 2) {
LOG_WARNING("ignoring extra IDs in ft232r_vid_pid "
"(maximum is 1 pair)");
CMD_ARGC = 2;
}
if (CMD_ARGC == 2) {
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ft232r_vid);
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ft232r_pid);
} else
LOG_WARNING("incomplete ft232r_vid_pid configuration");
return ERROR_OK;
}
static const struct command_registration ft232r_command_handlers[] = {
{
.name = "ft232r_serial_desc",
.handler = ft232r_handle_serial_desc_command,
.mode = COMMAND_CONFIG,
.help = "USB serial descriptor of the adapter",
.usage = "serial string",
},
{
.name = "ft232r_vid_pid",
.handler = ft232r_handle_vid_pid_command,
.mode = COMMAND_CONFIG,
.help = "USB VID and PID of the adapter",
.usage = "vid pid",
},
COMMAND_REGISTRATION_DONE
};
/*
* Synchronous bitbang protocol implementation.
*/
static void syncbb_end_state(tap_state_t state)
{
if (tap_is_state_stable(state))
tap_set_end_state(state);
else {
LOG_ERROR("BUG: %i is not a valid end state", state);
exit(-1);
}
}
static void syncbb_state_move(int skip)
{
int i = 0, tms = 0;
uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state());
int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state());
for (i = skip; i < tms_count; i++) {
tms = (tms_scan >> i) & 1;
ft232r_write(0, tms, 0);
ft232r_write(1, tms, 0);
}
ft232r_write(0, tms, 0);
tap_set_state(tap_get_end_state());
}
/**
* Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG
* (or SWD) state machine.
*/
static int syncbb_execute_tms(struct jtag_command *cmd)
{
unsigned num_bits = cmd->cmd.tms->num_bits;
const uint8_t *bits = cmd->cmd.tms->bits;
DEBUG_JTAG_IO("TMS: %d bits", num_bits);
int tms = 0;
for (unsigned i = 0; i < num_bits; i++) {
tms = ((bits[i/8] >> (i % 8)) & 1);
ft232r_write(0, tms, 0);
ft232r_write(1, tms, 0);
}
ft232r_write(0, tms, 0);
return ERROR_OK;
}
static void syncbb_path_move(struct pathmove_command *cmd)
{
int num_states = cmd->num_states;
int state_count;
int tms = 0;
state_count = 0;
while (num_states) {
if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) {
tms = 0;
} else if (tap_state_transition(tap_get_state(), true) == cmd->path[state_count]) {
tms = 1;
} else {
LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition",
tap_state_name(tap_get_state()),
tap_state_name(cmd->path[state_count]));
exit(-1);
}
ft232r_write(0, tms, 0);
ft232r_write(1, tms, 0);
tap_set_state(cmd->path[state_count]);
state_count++;
num_states--;
}
ft232r_write(0, tms, 0);
tap_set_end_state(tap_get_state());
}
static void syncbb_runtest(int num_cycles)
{
int i;
tap_state_t saved_end_state = tap_get_end_state();
/* only do a state_move when we're not already in IDLE */
if (tap_get_state() != TAP_IDLE) {
syncbb_end_state(TAP_IDLE);
syncbb_state_move(0);
}
/* execute num_cycles */
for (i = 0; i < num_cycles; i++) {
ft232r_write(0, 0, 0);
ft232r_write(1, 0, 0);
}
ft232r_write(0, 0, 0);
/* finish in end_state */
syncbb_end_state(saved_end_state);
if (tap_get_state() != tap_get_end_state())
syncbb_state_move(0);
}
/**
* Function syncbb_stableclocks
* issues a number of clock cycles while staying in a stable state.
* Because the TMS value required to stay in the RESET state is a 1, whereas
* the TMS value required to stay in any of the other stable states is a 0,
* this function checks the current stable state to decide on the value of TMS
* to use.
*/
static void syncbb_stableclocks(int num_cycles)
{
int tms = (tap_get_state() == TAP_RESET ? 1 : 0);
int i;
/* send num_cycles clocks onto the cable */
for (i = 0; i < num_cycles; i++) {
ft232r_write(1, tms, 0);
ft232r_write(0, tms, 0);
}
}
static void syncbb_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size)
{
tap_state_t saved_end_state = tap_get_end_state();
int bit_cnt, bit0_index;
if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) {
if (ir_scan)
syncbb_end_state(TAP_IRSHIFT);
else
syncbb_end_state(TAP_DRSHIFT);
syncbb_state_move(0);
syncbb_end_state(saved_end_state);
}
bit0_index = ft232r_output_len;
for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
int tms = (bit_cnt == scan_size-1) ? 1 : 0;
int tdi;
int bytec = bit_cnt/8;
int bcval = 1 << (bit_cnt % 8);
/* if we're just reading the scan, but don't care about the output
* default to outputting 'low', this also makes valgrind traces more readable,
* as it removes the dependency on an uninitialised value
*/
tdi = 0;
if ((type != SCAN_IN) && (buffer[bytec] & bcval))
tdi = 1;
ft232r_write(0, tms, tdi);
ft232r_write(1, tms, tdi);
}
if (tap_get_state() != tap_get_end_state()) {
/* we *KNOW* the above loop transitioned out of
* the shift state, so we skip the first state
* and move directly to the end state.
*/
syncbb_state_move(1);
}
ft232r_send_recv();
if (type != SCAN_OUT)
for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
int bytec = bit_cnt/8;
int bcval = 1 << (bit_cnt % 8);
int val = ft232r_output[bit0_index + bit_cnt*2 + 1];
if (val & READ_TDO)
buffer[bytec] |= bcval;
else
buffer[bytec] &= ~bcval;
}
}
static int syncbb_execute_queue(void)
{
struct jtag_command *cmd = jtag_command_queue; /* currently processed command */
int scan_size;
enum scan_type type;
uint8_t *buffer;
int retval;
/* return ERROR_OK, unless a jtag_read_buffer returns a failed check
* that wasn't handled by a caller-provided error handler
*/
retval = ERROR_OK;
/* ft232r_blink(1);*/
while (cmd) {
switch (cmd->type) {
case JTAG_RESET:
LOG_DEBUG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst);
if ((cmd->cmd.reset->trst == 1) ||
(cmd->cmd.reset->srst &&
(jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) {
tap_set_state(TAP_RESET);
}
ft232r_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
break;
case JTAG_RUNTEST:
LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles,
tap_state_name(cmd->cmd.runtest->end_state));
syncbb_end_state(cmd->cmd.runtest->end_state);
syncbb_runtest(cmd->cmd.runtest->num_cycles);
break;
case JTAG_STABLECLOCKS:
/* this is only allowed while in a stable state. A check for a stable
* state was done in jtag_add_clocks()
*/
syncbb_stableclocks(cmd->cmd.stableclocks->num_cycles);
break;
case JTAG_TLR_RESET: /* renamed from JTAG_STATEMOVE */
LOG_DEBUG_IO("statemove end in %s", tap_state_name(cmd->cmd.statemove->end_state));
syncbb_end_state(cmd->cmd.statemove->end_state);
syncbb_state_move(0);
break;
case JTAG_PATHMOVE:
LOG_DEBUG_IO("pathmove: %i states, end in %s", cmd->cmd.pathmove->num_states,
tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]));
syncbb_path_move(cmd->cmd.pathmove);
break;
case JTAG_SCAN:
LOG_DEBUG_IO("%s scan end in %s", (cmd->cmd.scan->ir_scan) ? "IR" : "DR",
tap_state_name(cmd->cmd.scan->end_state));
syncbb_end_state(cmd->cmd.scan->end_state);
scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
type = jtag_scan_type(cmd->cmd.scan);
syncbb_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size);
if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
retval = ERROR_JTAG_QUEUE_FAILED;
if (buffer)
free(buffer);
break;
case JTAG_SLEEP:
LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us);
jtag_sleep(cmd->cmd.sleep->us);
break;
case JTAG_TMS:
retval = syncbb_execute_tms(cmd);
break;
default:
LOG_ERROR("BUG: unknown JTAG command type encountered");
exit(-1);
}
if (ft232r_output_len > 0)
ft232r_send_recv();
cmd = cmd->next;
}
/* ft232r_blink(0);*/
return retval;
}
struct jtag_interface ft232r_interface = {
.name = "ft232r",
.commands = ft232r_command_handlers,
.transports = jtag_only,
.supported = DEBUG_CAP_TMS_SEQ,
.execute_queue = syncbb_execute_queue,
.speed = ft232r_speed,
.init = ft232r_init,
.quit = ft232r_quit,
.speed_div = ft232r_speed_div,
.khz = ft232r_khz,
};

View File

@ -694,6 +694,18 @@ static int ftdi_quit(void)
{ {
mpsse_close(mpsse_ctx); mpsse_close(mpsse_ctx);
struct signal *sig = signals;
while (sig) {
struct signal *next = sig->next;
free((void *)sig->name);
free(sig);
sig = next;
}
free(ftdi_device_desc);
free(ftdi_serial);
free(ftdi_location);
free(swd_cmd_queue); free(swd_cmd_queue);
return ERROR_OK; return ERROR_OK;

View File

@ -741,12 +741,22 @@ static int kitprog_swd_run_queue(void)
break; break;
} }
/* We use the maximum buffer size here because the KitProg sometimes /* KitProg firmware does not send a zero length packet
* doesn't like bulk reads of fewer than 62 bytes. (?!?!) * after the bulk-in transmission of a length divisible by bulk packet
* size (64 bytes) as required by the USB specification.
* Therefore libusb would wait for continuation of transmission.
* Workaround: Limit bulk read size to expected number of bytes
* for problematic tranfer sizes. Otherwise use the maximum buffer
* size here because the KitProg sometimes doesn't like bulk reads
* of fewer than 62 bytes. (?!?!)
*/ */
size_t read_count_workaround = SWD_MAX_BUFFER_LENGTH;
if (read_count % 64 == 0)
read_count_workaround = read_count;
ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle, ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle,
BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer, BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer,
SWD_MAX_BUFFER_LENGTH, 0); read_count_workaround, 1000);
if (ret > 0) { if (ret > 0) {
/* Handle garbage data by offsetting the initial read index */ /* Handle garbage data by offsetting the initial read index */
if ((unsigned int)ret > read_count) if ((unsigned int)ret > read_count)

View File

@ -335,7 +335,13 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
ctx->write_size = 16384; ctx->write_size = 16384;
ctx->read_chunk = malloc(ctx->read_chunk_size); ctx->read_chunk = malloc(ctx->read_chunk_size);
ctx->read_buffer = malloc(ctx->read_size); ctx->read_buffer = malloc(ctx->read_size);
ctx->write_buffer = malloc(ctx->write_size);
/* Use calloc to make valgrind happy: buffer_write() sets payload
* on bit basis, so some bits can be left uninitialized in write_buffer.
* Although this is perfectly ok with MPSSE, valgrind reports
* Syscall param ioctl(USBDEVFS_SUBMITURB).buffer points to uninitialised byte(s) */
ctx->write_buffer = calloc(1, ctx->write_size);
if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer) if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer)
goto error; goto error;

View File

@ -457,8 +457,8 @@ static int stlink_usb_error_check(void *handle)
LOG_DEBUG("Write error"); LOG_DEBUG("Write error");
return ERROR_FAIL; return ERROR_FAIL;
case STLINK_JTAG_WRITE_VERIF_ERROR: case STLINK_JTAG_WRITE_VERIF_ERROR:
LOG_DEBUG("Verify error"); LOG_DEBUG("Write verify error, ignoring");
return ERROR_FAIL; return ERROR_OK;
case STLINK_SWD_AP_FAULT: case STLINK_SWD_AP_FAULT:
/* git://git.ac6.fr/openocd commit 657e3e885b9ee10 /* git://git.ac6.fr/openocd commit 657e3e885b9ee10
* returns ERROR_OK with the comment: * returns ERROR_OK with the comment:

View File

@ -119,6 +119,11 @@ static int hl_interface_quit(void)
if (hl_if.layout->api->close) if (hl_if.layout->api->close)
hl_if.layout->api->close(hl_if.handle); hl_if.layout->api->close(hl_if.handle);
jtag_command_queue_reset();
free((void *)hl_if.param.device_desc);
free((void *)hl_if.param.serial);
return ERROR_OK; return ERROR_OK;
} }

View File

@ -39,20 +39,15 @@ static int jim_newtap_expected_id(Jim_Nvp *n, Jim_GetOptInfo *goi,
return e; return e;
} }
unsigned expected_len = sizeof(uint32_t) * pTap->expected_ids_cnt; uint32_t *p = realloc(pTap->expected_ids,
uint32_t *new_expected_ids = malloc(expected_len + sizeof(uint32_t)); (pTap->expected_ids_cnt + 1) * sizeof(uint32_t));
if (new_expected_ids == NULL) { if (!p) {
Jim_SetResultFormatted(goi->interp, "no memory"); Jim_SetResultFormatted(goi->interp, "no memory");
return JIM_ERR; return JIM_ERR;
} }
memcpy(new_expected_ids, pTap->expected_ids, expected_len); pTap->expected_ids = p;
pTap->expected_ids[pTap->expected_ids_cnt++] = w;
new_expected_ids[pTap->expected_ids_cnt] = w;
free(pTap->expected_ids);
pTap->expected_ids = new_expected_ids;
pTap->expected_ids_cnt++;
return JIM_OK; return JIM_OK;
} }

View File

@ -233,3 +233,11 @@ static void hl_constructor(void)
transport_register(&hl_jtag_transport); transport_register(&hl_jtag_transport);
transport_register(&stlink_swim_transport); transport_register(&stlink_swim_transport);
} }
bool transport_is_hla(void)
{
struct transport *t;
t = get_current_transport();
return t == &hl_swd_transport || t == &hl_jtag_transport
|| t == &stlink_swim_transport;
}

View File

@ -60,6 +60,9 @@ extern struct jtag_interface usb_blaster_interface;
#if BUILD_JTAG_VPI == 1 #if BUILD_JTAG_VPI == 1
extern struct jtag_interface jtag_vpi_interface; extern struct jtag_interface jtag_vpi_interface;
#endif #endif
#if BUILD_FT232R == 1
extern struct jtag_interface ft232r_interface;
#endif
#if BUILD_AMTJTAGACCEL == 1 #if BUILD_AMTJTAGACCEL == 1
extern struct jtag_interface amt_jtagaccel_interface; extern struct jtag_interface amt_jtagaccel_interface;
#endif #endif
@ -159,6 +162,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_JTAG_VPI == 1 #if BUILD_JTAG_VPI == 1
&jtag_vpi_interface, &jtag_vpi_interface,
#endif #endif
#if BUILD_FT232R == 1
&ft232r_interface,
#endif
#if BUILD_AMTJTAGACCEL == 1 #if BUILD_AMTJTAGACCEL == 1
&amt_jtagaccel_interface, &amt_jtagaccel_interface,
#endif #endif

View File

@ -153,8 +153,6 @@ struct jtag_tap {
struct jtag_tap_event_action *event_action; struct jtag_tap_event_action *event_action;
struct jtag_tap *next_tap; struct jtag_tap *next_tap;
/* dap instance if some null if no instance , initialized to 0 by calloc*/
struct adiv5_dap *dap;
/* private pointer to support none-jtag specific functions */ /* private pointer to support none-jtag specific functions */
void *priv; void *priv;
}; };
@ -642,8 +640,6 @@ void jtag_poll_set_enabled(bool value);
* level APIs that are used in inner loops. */ * level APIs that are used in inner loops. */
#include <jtag/minidriver.h> #include <jtag/minidriver.h>
bool transport_is_jtag(void);
int jim_jtag_newtap(Jim_Interp *interp, int argc, Jim_Obj *const *argv); int jim_jtag_newtap(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
#endif /* OPENOCD_JTAG_JTAG_H */ #endif /* OPENOCD_JTAG_JTAG_H */

View File

@ -211,6 +211,4 @@ struct swd_driver {
int swd_init_reset(struct command_context *cmd_ctx); int swd_init_reset(struct command_context *cmd_ctx);
void swd_add_reset(int req_srst); void swd_add_reset(int req_srst);
bool transport_is_swd(void);
#endif /* OPENOCD_JTAG_SWD_H */ #endif /* OPENOCD_JTAG_SWD_H */

View File

@ -42,6 +42,7 @@
#endif #endif
#include <helper/time_support.h> #include <helper/time_support.h>
#include "transport/transport.h"
/** /**
* @file * @file

View File

@ -37,6 +37,8 @@
#include <flash/nand/core.h> #include <flash/nand/core.h>
#include <pld/pld.h> #include <pld/pld.h>
#include <flash/mflash.h> #include <flash/mflash.h>
#include <target/arm_cti.h>
#include <target/arm_adi_v5.h>
#include <server/server.h> #include <server/server.h>
#include <server/gdb_server.h> #include <server/gdb_server.h>
@ -85,13 +87,13 @@ static int log_target_callback_event_handler(struct target *target,
{ {
switch (event) { switch (event) {
case TARGET_EVENT_GDB_START: case TARGET_EVENT_GDB_START:
target->display = 0; target->verbose_halt_msg = false;
break; break;
case TARGET_EVENT_GDB_END: case TARGET_EVENT_GDB_END:
target->display = 1; target->verbose_halt_msg = true;
break; break;
case TARGET_EVENT_HALTED: case TARGET_EVENT_HALTED:
if (target->display) { if (target->verbose_halt_msg) {
/* do not display information when debugger caused the halt */ /* do not display information when debugger caused the halt */
target_arch_state(target); target_arch_state(target);
} }
@ -150,6 +152,10 @@ COMMAND_HANDLER(handle_init_command)
if (ERROR_OK != retval) if (ERROR_OK != retval)
return ERROR_FAIL; return ERROR_FAIL;
retval = command_run_line(CMD_CTX, "dap init");
if (ERROR_OK != retval)
return ERROR_FAIL;
LOG_DEBUG("Examining targets..."); LOG_DEBUG("Examining targets...");
if (target_examine() != ERROR_OK) if (target_examine() != ERROR_OK)
LOG_DEBUG("target examination failed"); LOG_DEBUG("target examination failed");
@ -252,6 +258,8 @@ struct command_context *setup_command_handler(Jim_Interp *interp)
&nand_register_commands, &nand_register_commands,
&pld_register_commands, &pld_register_commands,
&mflash_register_commands, &mflash_register_commands,
&cti_register_commands,
&dap_register_commands,
NULL NULL
}; };
for (unsigned i = 0; NULL != command_registrants[i]; i++) { for (unsigned i = 0; NULL != command_registrants[i]; i++) {
@ -286,10 +294,13 @@ static int openocd_thread(int argc, char *argv[], struct command_context *cmd_ct
return ERROR_FAIL; return ERROR_FAIL;
ret = parse_config_file(cmd_ctx); ret = parse_config_file(cmd_ctx);
if (ret == ERROR_COMMAND_CLOSE_CONNECTION) if (ret == ERROR_COMMAND_CLOSE_CONNECTION) {
server_quit(); /* gdb server may be initialized by -c init */
return ERROR_OK; return ERROR_OK;
else if (ret != ERROR_OK) } else if (ret != ERROR_OK) {
server_quit(); /* gdb server may be initialized by -c init */
return ERROR_FAIL; return ERROR_FAIL;
}
ret = server_init(cmd_ctx); ret = server_init(cmd_ctx);
if (ERROR_OK != ret) if (ERROR_OK != ret)
@ -342,12 +353,18 @@ int openocd_main(int argc, char *argv[])
/* Start the executable meat that can evolve into thread in future. */ /* Start the executable meat that can evolve into thread in future. */
ret = openocd_thread(argc, argv, cmd_ctx); ret = openocd_thread(argc, argv, cmd_ctx);
flash_free_all_banks();
gdb_service_free();
server_free();
unregister_all_commands(cmd_ctx, NULL); unregister_all_commands(cmd_ctx, NULL);
adapter_quit();
/* Shutdown commandline interface */ /* Shutdown commandline interface */
command_exit(cmd_ctx); command_exit(cmd_ctx);
adapter_quit(); free_config();
if (ERROR_FAIL == ret) if (ERROR_FAIL == ret)
return EXIT_FAILURE; return EXIT_FAILURE;

View File

@ -247,7 +247,7 @@ static int ChibiOS_update_stacking(struct rtos *rtos)
/* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */ /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */
struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
if (is_armv7m(armv7m_target)) { if (is_armv7m(armv7m_target)) {
if (armv7m_target->fp_feature == FPv4_SP) { if (armv7m_target->fp_feature != FP_NONE) {
/* Found ARM v7m target which includes a FPU */ /* Found ARM v7m target which includes a FPU */
uint32_t cpacr; uint32_t cpacr;

View File

@ -248,7 +248,7 @@ static int riscv_gdb_v_packet(struct connection *connection, const char *packet,
if (strcmp(packet_stttrr, "vCont;c") == 0) { if (strcmp(packet_stttrr, "vCont;c") == 0) {
target_call_event_callbacks(target, TARGET_EVENT_GDB_START); target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
target_call_event_callbacks(target, TARGET_EVENT_RESUME_START); target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
riscv_resume_all_harts(target); riscv_openocd_resume(target, 1, 0, 0, 0);
target->state = TARGET_RUNNING; target->state = TARGET_RUNNING;
gdb_set_frontend_state_running(connection); gdb_set_frontend_state_running(connection);
target_call_event_callbacks(target, TARGET_EVENT_RESUMED); target_call_event_callbacks(target, TARGET_EVENT_RESUMED);

View File

@ -59,6 +59,15 @@ int rtos_smp_init(struct target *target)
return ERROR_TARGET_INIT_FAILED; return ERROR_TARGET_INIT_FAILED;
} }
static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t)
{
struct target *curr = get_target_from_connection(connection);
if (t)
*t = curr;
return ERROR_OK;
}
static int os_alloc(struct target *target, struct rtos_type *ostype) static int os_alloc(struct target *target, struct rtos_type *ostype)
{ {
struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos)); struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos));
@ -75,6 +84,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */ /* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet; os->gdb_thread_packet = rtos_thread_packet;
os->gdb_v_packet = NULL; os->gdb_v_packet = NULL;
os->gdb_target_for_threadid = rtos_target_for_threadid;
return JIM_OK; return JIM_OK;
} }
@ -339,8 +349,10 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa
return ERROR_OK; return ERROR_OK;
} else if (strncmp(packet, "qSymbol", 7) == 0) { } else if (strncmp(packet, "qSymbol", 7) == 0) {
if (rtos_qsymbol(connection, packet, packet_size) == 1) { if (rtos_qsymbol(connection, packet, packet_size) == 1) {
target->rtos_auto_detect = false; if (target->rtos_auto_detect == true) {
target->rtos->type->create(target); target->rtos_auto_detect = false;
target->rtos->type->create(target);
}
target->rtos->type->update_threads(target->rtos); target->rtos->type->update_threads(target->rtos);
} }
return ERROR_OK; return ERROR_OK;

View File

@ -55,6 +55,7 @@ struct rtos {
int thread_count; int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size); int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size); int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size);
int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target);
void *rtos_specific_params; void *rtos_specific_params;
}; };

View File

@ -41,6 +41,8 @@
#include <target/breakpoints.h> #include <target/breakpoints.h>
#include <target/target_request.h> #include <target/target_request.h>
#include <target/register.h> #include <target/register.h>
#include <target/target.h>
#include <target/target_type.h>
#include "server.h" #include "server.h"
#include <flash/nor/core.h> #include <flash/nor/core.h>
#include "gdb_server.h" #include "gdb_server.h"
@ -126,6 +128,9 @@ static int gdb_flash_program = 1;
* Disabled by default. * Disabled by default.
*/ */
static int gdb_report_data_abort; static int gdb_report_data_abort;
/* If set, errors when accessing registers are reported to gdb. Disabled by
* default. */
static int gdb_report_register_access_error;
/* set if we are sending target descriptions to gdb /* set if we are sending target descriptions to gdb
* via qXfer:features:read packet */ * via qXfer:features:read packet */
@ -728,7 +733,6 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
} else { } else {
if (gdb_connection->ctrl_c) { if (gdb_connection->ctrl_c) {
signal_var = 0x2; signal_var = 0x2;
gdb_connection->ctrl_c = 0;
} else } else
signal_var = gdb_last_signal(target); signal_var = gdb_last_signal(target);
@ -760,12 +764,19 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
current_thread[0] = '\0'; current_thread[0] = '\0';
if (target->rtos != NULL) { if (target->rtos != NULL) {
snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread); struct target *ct;
snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
target->rtos->current_thread);
target->rtos->current_threadid = target->rtos->current_thread; target->rtos->current_threadid = target->rtos->current_thread;
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
if (!gdb_connection->ctrl_c)
signal_var = gdb_last_signal(ct);
} }
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s", sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread); signal_var, stop_reason, current_thread);
gdb_connection->ctrl_c = 0;
} }
gdb_put_packet(connection, sig_reply, sig_reply_len); gdb_put_packet(connection, sig_reply, sig_reply_len);
@ -953,9 +964,14 @@ static int gdb_new_connection(struct connection *connection)
breakpoint_clear_target(target); breakpoint_clear_target(target);
watchpoint_clear_target(target); watchpoint_clear_target(target);
/* clean previous rtos session if supported*/ if (target->rtos) {
if ((target->rtos) && (target->rtos->type->clean)) /* clean previous rtos session if supported*/
target->rtos->type->clean(target); if (target->rtos->type->clean)
target->rtos->type->clean(target);
/* update threads */
rtos_update_threads(target);
}
/* remove the initial ACK from the incoming buffer */ /* remove the initial ACK from the incoming buffer */
retval = gdb_get_char(connection, &initial_ack); retval = gdb_get_char(connection, &initial_ack);
@ -1174,7 +1190,7 @@ static int gdb_get_registers_packet(struct connection *connection,
for (i = 0; i < reg_list_size; i++) { for (i = 0; i < reg_list_size; i++) {
if (!reg_list[i]->valid) { if (!reg_list[i]->valid) {
retval = reg_list[i]->type->get(reg_list[i]); retval = reg_list[i]->type->get(reg_list[i]);
if (retval != ERROR_OK) { if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name); LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name);
free(reg_packet); free(reg_packet);
free(reg_list); free(reg_list);
@ -1242,7 +1258,7 @@ static int gdb_set_registers_packet(struct connection *connection,
gdb_target_to_reg(target, packet_p, chars, bin_buf); gdb_target_to_reg(target, packet_p, chars, bin_buf);
retval = reg_list[i]->type->set(reg_list[i], bin_buf); retval = reg_list[i]->type->set(reg_list[i], bin_buf);
if (retval != ERROR_OK) { if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name); LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name);
free(reg_list); free(reg_list);
free(bin_buf); free(bin_buf);
@ -1289,7 +1305,7 @@ static int gdb_get_register_packet(struct connection *connection,
if (!reg_list[reg_num]->valid) { if (!reg_list[reg_num]->valid) {
retval = reg_list[reg_num]->type->get(reg_list[reg_num]); retval = reg_list[reg_num]->type->get(reg_list[reg_num]);
if (retval != ERROR_OK) { if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name); LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name);
free(reg_list); free(reg_list);
return gdb_error(connection, retval); return gdb_error(connection, retval);
@ -1349,7 +1365,7 @@ static int gdb_set_register_packet(struct connection *connection,
gdb_target_to_reg(target, separator + 1, chars, bin_buf); gdb_target_to_reg(target, separator + 1, chars, bin_buf);
retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf); retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
if (retval != ERROR_OK) { if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name); LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name);
free(bin_buf); free(bin_buf);
free(reg_list); free(reg_list);
@ -1919,6 +1935,8 @@ static int gdb_memory_map(struct connection *connection,
static const char *gdb_get_reg_type_name(enum reg_type type) static const char *gdb_get_reg_type_name(enum reg_type type)
{ {
switch (type) { switch (type) {
case REG_TYPE_BOOL:
return "bool";
case REG_TYPE_INT: case REG_TYPE_INT:
return "int"; return "int";
case REG_TYPE_INT8: case REG_TYPE_INT8:
@ -1931,6 +1949,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
return "int64"; return "int64";
case REG_TYPE_INT128: case REG_TYPE_INT128:
return "int128"; return "int128";
case REG_TYPE_UINT:
return "uint";
case REG_TYPE_UINT8: case REG_TYPE_UINT8:
return "uint8"; return "uint8";
case REG_TYPE_UINT16: case REG_TYPE_UINT16:
@ -1958,12 +1978,45 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
return "int"; /* "int" as default value */ return "int"; /* "int" as default value */
} }
static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], const char *type_id,
int *num_arch_defined_types)
{
int tbl_sz = *num_arch_defined_types;
if (type_id != NULL && (strcmp(type_id, ""))) {
for (int j = 0; j < (tbl_sz + 1); j++) {
if (!((*arch_defined_types_list)[j])) {
(*arch_defined_types_list)[tbl_sz++] = type_id;
*arch_defined_types_list = realloc(*arch_defined_types_list,
sizeof(char *) * (tbl_sz + 1));
(*arch_defined_types_list)[tbl_sz] = NULL;
*num_arch_defined_types = tbl_sz;
return 1;
} else {
if (!strcmp((*arch_defined_types_list)[j], type_id))
return 0;
}
}
}
return -1;
}
static int gdb_generate_reg_type_description(struct target *target, static int gdb_generate_reg_type_description(struct target *target,
char **tdesc, int *pos, int *size, struct reg_data_type *type) char **tdesc, int *pos, int *size, struct reg_data_type *type,
char const **arch_defined_types_list[], int * num_arch_defined_types)
{ {
int retval = ERROR_OK; int retval = ERROR_OK;
if (type->type_class == REG_TYPE_CLASS_VECTOR) { if (type->type_class == REG_TYPE_CLASS_VECTOR) {
struct reg_data_type *data_type = type->reg_type_vector->type;
if (data_type->type == REG_TYPE_ARCH_DEFINED) {
if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
num_arch_defined_types))
gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
arch_defined_types_list,
num_arch_defined_types);
}
/* <vector id="id" type="type" count="count"/> */ /* <vector id="id" type="type" count="count"/> */
xml_printf(&retval, tdesc, pos, size, xml_printf(&retval, tdesc, pos, size,
"<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n", "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
@ -1971,6 +2024,20 @@ static int gdb_generate_reg_type_description(struct target *target,
type->reg_type_vector->count); type->reg_type_vector->count);
} else if (type->type_class == REG_TYPE_CLASS_UNION) { } else if (type->type_class == REG_TYPE_CLASS_UNION) {
struct reg_data_type_union_field *field;
field = type->reg_type_union->fields;
while (field != NULL) {
struct reg_data_type *data_type = field->type;
if (data_type->type == REG_TYPE_ARCH_DEFINED) {
if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
num_arch_defined_types))
gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
arch_defined_types_list,
num_arch_defined_types);
}
field = field->next;
}
/* <union id="id"> /* <union id="id">
* <field name="name" type="type"/> ... * <field name="name" type="type"/> ...
* </union> */ * </union> */
@ -1978,7 +2045,6 @@ static int gdb_generate_reg_type_description(struct target *target,
"<union id=\"%s\">\n", "<union id=\"%s\">\n",
type->id); type->id);
struct reg_data_type_union_field *field;
field = type->reg_type_union->fields; field = type->reg_type_union->fields;
while (field != NULL) { while (field != NULL) {
xml_printf(&retval, tdesc, pos, size, xml_printf(&retval, tdesc, pos, size,
@ -2004,13 +2070,24 @@ static int gdb_generate_reg_type_description(struct target *target,
type->id, type->reg_type_struct->size); type->id, type->reg_type_struct->size);
while (field != NULL) { while (field != NULL) {
xml_printf(&retval, tdesc, pos, size, xml_printf(&retval, tdesc, pos, size,
"<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n", "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
field->name, field->bitfield->start, field->name, field->bitfield->start, field->bitfield->end,
field->bitfield->end); gdb_get_reg_type_name(field->bitfield->type));
field = field->next; field = field->next;
} }
} else { } else {
while (field != NULL) {
struct reg_data_type *data_type = field->type;
if (data_type->type == REG_TYPE_ARCH_DEFINED) {
if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
num_arch_defined_types))
gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
arch_defined_types_list,
num_arch_defined_types);
}
}
/* <struct id="id"> /* <struct id="id">
* <field name="name" type="type"/> ... * <field name="name" type="type"/> ...
* </struct> */ * </struct> */
@ -2041,8 +2118,9 @@ static int gdb_generate_reg_type_description(struct target *target,
field = type->reg_type_flags->fields; field = type->reg_type_flags->fields;
while (field != NULL) { while (field != NULL) {
xml_printf(&retval, tdesc, pos, size, xml_printf(&retval, tdesc, pos, size,
"<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n", "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
field->name, field->bitfield->start, field->bitfield->end); field->name, field->bitfield->start, field->bitfield->end,
gdb_get_reg_type_name(field->bitfield->type));
field = field->next; field = field->next;
} }
@ -2103,11 +2181,15 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
struct reg **reg_list = NULL; struct reg **reg_list = NULL;
int reg_list_size; int reg_list_size;
char const **features = NULL; char const **features = NULL;
char const **arch_defined_types = NULL;
int feature_list_size = 0; int feature_list_size = 0;
int num_arch_defined_types = 0;
char *tdesc = NULL; char *tdesc = NULL;
int pos = 0; int pos = 0;
int size = 0; int size = 0;
arch_defined_types = calloc(1, sizeof(char *));
retval = target_get_gdb_reg_list(target, &reg_list, retval = target_get_gdb_reg_list(target, &reg_list,
&reg_list_size, REG_CLASS_ALL); &reg_list_size, REG_CLASS_ALL);
@ -2160,8 +2242,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
if (reg_list[i]->reg_data_type != NULL) { if (reg_list[i]->reg_data_type != NULL) {
if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) { if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
/* generate <type... first, if there are architecture-defined types. */ /* generate <type... first, if there are architecture-defined types. */
gdb_generate_reg_type_description(target, &tdesc, &pos, &size, if (lookup_add_arch_defined_types(&arch_defined_types,
reg_list[i]->reg_data_type); reg_list[i]->reg_data_type->id,
&num_arch_defined_types))
gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
reg_list[i]->reg_data_type,
&arch_defined_types,
&num_arch_defined_types);
type_str = reg_list[i]->reg_data_type->id; type_str = reg_list[i]->reg_data_type->id;
} else { } else {
@ -2211,6 +2298,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
error: error:
free(features); free(features);
free(reg_list); free(reg_list);
free(arch_defined_types);
if (retval == ERROR_OK) if (retval == ERROR_OK)
*tdesc_out = tdesc; *tdesc_out = tdesc;
@ -2387,7 +2475,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
else else
transfer_type = 'l'; transfer_type = 'l';
*chunk = malloc(length + 2); *chunk = malloc(length + 2 + 3);
/* 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 */
if (*chunk == NULL) { if (*chunk == NULL) {
LOG_ERROR("Unable to allocate memory"); LOG_ERROR("Unable to allocate memory");
return ERROR_FAIL; return ERROR_FAIL;
@ -2495,7 +2587,7 @@ static int gdb_query_packet(struct connection *connection,
&buffer, &buffer,
&pos, &pos,
&size, &size,
"PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+", "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
(GDB_BUFFER_SIZE - 1), (GDB_BUFFER_SIZE - 1),
((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-', ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
(gdb_target_desc_supported == 1) ? '+' : '-'); (gdb_target_desc_supported == 1) ? '+' : '-');
@ -2584,6 +2676,185 @@ static int gdb_query_packet(struct connection *connection,
return ERROR_OK; return ERROR_OK;
} }
static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
{
struct gdb_connection *gdb_connection = connection->priv;
struct target *target = get_target_from_connection(connection);
const char *parse = packet;
int retval;
/* query for vCont supported */
if (parse[0] == '?') {
if (target->type->step != NULL) {
/* gdb doesn't accept c without C and s without S */
gdb_put_packet(connection, "vCont;c;C;s;S", 13);
return true;
}
return false;
}
if (parse[0] == ';') {
++parse;
--packet_size;
}
/* simple case, a continue packet */
if (parse[0] == 'c') {
LOG_DEBUG("target %s continue", target_name(target));
log_add_callback(gdb_log_callback, connection);
retval = target_resume(target, 1, 0, 0, 0);
if (retval == ERROR_TARGET_NOT_HALTED)
LOG_INFO("target %s was not halted when resume was requested", target_name(target));
/* poll target in an attempt to make its internal state consistent */
if (retval != ERROR_OK) {
retval = target_poll(target);
if (retval != ERROR_OK)
LOG_DEBUG("error polling target %s after failed resume", target_name(target));
}
/*
* We don't report errors to gdb here, move frontend_state to
* TARGET_RUNNING to stay in sync with gdb's expectation of the
* target state
*/
gdb_connection->frontend_state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
return true;
}
/* single-step or step-over-breakpoint */
if (parse[0] == 's') {
bool fake_step = false;
if (strncmp(parse, "s:", 2) == 0) {
struct target *ct = target;
int current_pc = 1;
int64_t thread_id;
char *endp;
parse += 2;
packet_size -= 2;
thread_id = strtoll(parse, &endp, 16);
if (endp != NULL) {
packet_size -= endp - parse;
parse = endp;
}
if (target->rtos != NULL) {
/* FIXME: why is this necessary? rtos state should be up-to-date here already! */
rtos_update_threads(target);
target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
/*
* check if the thread to be stepped is the current rtos thread
* if not, we must fake the step
*/
if (target->rtos->current_thread != thread_id)
fake_step = true;
}
if (parse[0] == ';') {
++parse;
--packet_size;
if (parse[0] == 'c') {
parse += 1;
packet_size -= 1;
/* check if thread-id follows */
if (parse[0] == ':') {
int64_t tid;
parse += 1;
packet_size -= 1;
tid = strtoll(parse, &endp, 16);
if (tid == thread_id) {
/*
* Special case: only step a single thread (core),
* keep the other threads halted. Currently, only
* aarch64 target understands it. Other target types don't
* care (nobody checks the actual value of 'current')
* and it doesn't really matter. This deserves
* a symbolic constant and a formal interface documentation
* at a later time.
*/
LOG_DEBUG("request to step current core only");
/* uncomment after checking that indeed other targets are safe */
/*current_pc = 2;*/
}
}
}
}
LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
log_add_callback(gdb_log_callback, connection);
target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
/*
* work around an annoying gdb behaviour: when the current thread
* is changed in gdb, it assumes that the target can follow and also
* make the thread current. This is an assumption that cannot hold
* for a real target running a multi-threading OS. We just fake
* the step to not trigger an internal error in gdb. See
* https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
*/
if (fake_step) {
int sig_reply_len;
char sig_reply[128];
LOG_DEBUG("fake step thread %"PRIx64, thread_id);
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
"T05thread:%016"PRIx64";", thread_id);
gdb_put_packet(connection, sig_reply, sig_reply_len);
log_remove_callback(gdb_log_callback, connection);
return true;
}
/* support for gdb_sync command */
if (gdb_connection->sync) {
gdb_connection->sync = false;
if (ct->state == TARGET_HALTED) {
LOG_WARNING("stepi ignored. GDB will now fetch the register state " \
"from the target.");
gdb_sig_halted(connection);
log_remove_callback(gdb_log_callback, connection);
} else
gdb_connection->frontend_state = TARGET_RUNNING;
return true;
}
retval = target_step(ct, current_pc, 0, 0);
if (retval == ERROR_TARGET_NOT_HALTED)
LOG_INFO("target %s was not halted when step was requested", target_name(ct));
/* if step was successful send a reply back to gdb */
if (retval == ERROR_OK) {
retval = target_poll(ct);
if (retval != ERROR_OK)
LOG_DEBUG("error polling target %s after successful step", target_name(ct));
/* send back signal information */
gdb_signal_reply(ct, connection);
/* stop forwarding log packets! */
log_remove_callback(gdb_log_callback, connection);
} else
gdb_connection->frontend_state = TARGET_RUNNING;
} else {
LOG_ERROR("Unknown vCont packet");
return false;
}
return true;
}
return false;
}
static int gdb_v_packet(struct connection *connection, static int gdb_v_packet(struct connection *connection,
char const *packet, int packet_size) char const *packet, int packet_size)
{ {
@ -2597,6 +2868,19 @@ static int gdb_v_packet(struct connection *connection,
return out; return out;
} }
if (strncmp(packet, "vCont", 5) == 0) {
bool handled;
packet += 5;
packet_size -= 5;
handled = gdb_handle_vcont_packet(connection, packet, packet_size);
if (!handled)
gdb_put_packet(connection, "", 0);
return ERROR_OK;
}
/* if flash programming disabled - send a empty reply */ /* if flash programming disabled - send a empty reply */
if (gdb_flash_program == 0) { if (gdb_flash_program == 0) {
@ -3036,7 +3320,12 @@ static int gdb_input_inner(struct connection *connection)
if (gdb_con->ctrl_c) { if (gdb_con->ctrl_c) {
if (target->state == TARGET_RUNNING) { if (target->state == TARGET_RUNNING) {
retval = target_halt(target); struct target *t = target;
if (target->rtos)
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
retval = target_halt(t);
if (retval == ERROR_OK)
retval = target_poll(t);
if (retval != ERROR_OK) if (retval != ERROR_OK)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT); target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = 0; gdb_con->ctrl_c = 0;
@ -3209,6 +3498,15 @@ COMMAND_HANDLER(handle_gdb_report_data_abort_command)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(handle_gdb_report_register_access_error)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_report_register_access_error);
return ERROR_OK;
}
/* gdb_breakpoint_override */ /* gdb_breakpoint_override */
COMMAND_HANDLER(handle_gdb_breakpoint_override_command) COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
{ {
@ -3330,6 +3628,13 @@ static const struct command_registration gdb_command_handlers[] = {
.help = "enable or disable reporting data aborts", .help = "enable or disable reporting data aborts",
.usage = "('enable'|'disable')" .usage = "('enable'|'disable')"
}, },
{
.name = "gdb_report_register_access_error",
.handler = handle_gdb_report_register_access_error,
.mode = COMMAND_CONFIG,
.help = "enable or disable reporting register access errors",
.usage = "('enable'|'disable')"
},
{ {
.name = "gdb_breakpoint_override", .name = "gdb_breakpoint_override",
.handler = handle_gdb_breakpoint_override_command, .handler = handle_gdb_breakpoint_override_command,
@ -3366,3 +3671,9 @@ void gdb_set_frontend_state_running(struct connection *connection)
struct gdb_connection *gdb_con = connection->priv; struct gdb_connection *gdb_con = connection->priv;
gdb_con->frontend_state = TARGET_RUNNING; gdb_con->frontend_state = TARGET_RUNNING;
} }
void gdb_service_free(void)
{
free(gdb_port);
free(gdb_port_next);
}

View File

@ -36,6 +36,7 @@ struct reg;
int gdb_target_add_all(struct target *target); int gdb_target_add_all(struct target *target);
int gdb_register_commands(struct command_context *command_context); int gdb_register_commands(struct command_context *command_context);
void gdb_service_free(void);
int gdb_put_packet(struct connection *connection, char *buffer, int len); int gdb_put_packet(struct connection *connection, char *buffer, int len);

View File

@ -259,7 +259,7 @@ int add_service(char *name,
c->sin.sin_family = AF_INET; c->sin.sin_family = AF_INET;
if (bindto_name == NULL) if (bindto_name == NULL)
c->sin.sin_addr.s_addr = INADDR_ANY; c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else { else {
hp = gethostbyname(bindto_name); hp = gethostbyname(bindto_name);
if (hp == NULL) { if (hp == NULL) {
@ -273,8 +273,7 @@ int add_service(char *name,
c->sin.sin_port = htons(c->portnumber); c->sin.sin_port = htons(c->portnumber);
if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) { if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) {
LOG_ERROR("couldn't bind %s to socket on port %d: %s", name, LOG_ERROR("couldn't bind %s to socket on port %d: %s", name, c->portnumber, strerror(errno));
c->portnumber, strerror(errno));
close_socket(c->fd); close_socket(c->fd);
free_service(c); free_service(c);
return ERROR_FAIL; return ERROR_FAIL;
@ -346,6 +345,21 @@ int add_service(char *name,
return ERROR_OK; return ERROR_OK;
} }
static void remove_connections(struct service *service)
{
struct connection *connection;
connection = service->connections;
while (connection) {
struct connection *tmp;
tmp = connection->next;
remove_connection(service, connection);
connection = tmp;
}
}
static int remove_services(void) static int remove_services(void)
{ {
struct service *c = services; struct service *c = services;
@ -354,6 +368,8 @@ static int remove_services(void)
while (c) { while (c) {
struct service *next = c->next; struct service *next = c->next;
remove_connections(c);
if (c->name) if (c->name)
free(c->name); free(c->name);
@ -625,6 +641,13 @@ int server_quit(void)
return last_signal; return last_signal;
} }
void server_free(void)
{
tcl_service_free();
telnet_service_free();
jsp_service_free();
}
void exit_on_signal(int sig) void exit_on_signal(int sig)
{ {
#ifndef _WIN32 #ifndef _WIN32

View File

@ -25,6 +25,10 @@
#ifndef OPENOCD_SERVER_SERVER_H #ifndef OPENOCD_SERVER_SERVER_H
#define OPENOCD_SERVER_SERVER_H #define OPENOCD_SERVER_SERVER_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <helper/log.h> #include <helper/log.h>
#ifdef HAVE_NETINET_IN_H #ifdef HAVE_NETINET_IN_H
@ -78,6 +82,7 @@ int add_service(char *name, const char *port,
int server_preinit(void); int server_preinit(void);
int server_init(struct command_context *cmd_ctx); int server_init(struct command_context *cmd_ctx);
int server_quit(void); int server_quit(void);
void server_free(void);
void exit_on_signal(int); void exit_on_signal(int);
int server_loop(struct command_context *command_context); int server_loop(struct command_context *command_context);

View File

@ -157,7 +157,7 @@ static int tcl_new_connection(struct connection *connection)
connection->priv = tclc; connection->priv = tclc;
struct target *target = get_target_by_num(connection->cmd_ctx->current_target); struct target *target = get_current_target(connection->cmd_ctx);
if (target != NULL) if (target != NULL)
tclc->tc_laststate = target->state; tclc->tc_laststate = target->state;
@ -359,3 +359,8 @@ int tcl_register_commands(struct command_context *cmd_ctx)
tcl_port = strdup("6666"); tcl_port = strdup("6666");
return register_commands(cmd_ctx, NULL, tcl_command_handlers); return register_commands(cmd_ctx, NULL, tcl_command_handlers);
} }
void tcl_service_free(void)
{
free(tcl_port);
}

View File

@ -22,5 +22,6 @@
int tcl_init(void); int tcl_init(void);
int tcl_register_commands(struct command_context *cmd_ctx); int tcl_register_commands(struct command_context *cmd_ctx);
void tcl_service_free(void);
#endif /* OPENOCD_SERVER_TCL_SERVER_H */ #endif /* OPENOCD_SERVER_TCL_SERVER_H */

View File

@ -719,3 +719,8 @@ int telnet_register_commands(struct command_context *cmd_ctx)
telnet_port = strdup("4444"); telnet_port = strdup("4444");
return register_commands(cmd_ctx, NULL, telnet_command_handlers); return register_commands(cmd_ctx, NULL, telnet_command_handlers);
} }
void telnet_service_free(void)
{
free(telnet_port);
}

View File

@ -64,5 +64,6 @@ struct telnet_service {
int telnet_init(char *banner); int telnet_init(char *banner);
int telnet_register_commands(struct command_context *command_context); int telnet_register_commands(struct command_context *command_context);
void telnet_service_free(void);
#endif /* OPENOCD_SERVER_TELNET_SERVER_H */ #endif /* OPENOCD_SERVER_TELNET_SERVER_H */

View File

@ -741,6 +741,9 @@ parse_char:
pos++; pos++;
} }
if (num == 0)
return ERROR_FAIL;
*num_of_argu = num; *num_of_argu = num;
return ERROR_OK; return ERROR_OK;
@ -1313,7 +1316,7 @@ XXR_common:
* SEC]] [ENDSTATE end_state] */ * SEC]] [ENDSTATE end_state] */
/* RUNTEST [run_state] min_time SEC [MAXIMUM max_time SEC] [ENDSTATE /* RUNTEST [run_state] min_time SEC [MAXIMUM max_time SEC] [ENDSTATE
* end_state] */ * end_state] */
if ((num_of_argu < 3) && (num_of_argu > 11)) { if ((num_of_argu < 3) || (num_of_argu > 11)) {
LOG_ERROR("invalid parameter of %s", argus[0]); LOG_ERROR("invalid parameter of %s", argus[0]);
return ERROR_FAIL; return ERROR_FAIL;
} }

View File

@ -89,6 +89,7 @@ ARM_DEBUG_SRC = \
%D%/arm_simulator.c \ %D%/arm_simulator.c \
%D%/arm_semihosting.c \ %D%/arm_semihosting.c \
%D%/arm_adi_v5.c \ %D%/arm_adi_v5.c \
%D%/arm_dap.c \
%D%/armv7a_cache.c \ %D%/armv7a_cache.c \
%D%/armv7a_cache_l2x.c \ %D%/armv7a_cache_l2x.c \
%D%/adi_v5_jtag.c \ %D%/adi_v5_jtag.c \

View File

@ -40,6 +40,11 @@ enum halt_mode {
HALT_SYNC, HALT_SYNC,
}; };
struct aarch64_private_config {
struct adiv5_private_config adiv5_config;
struct arm_cti *cti;
};
static int aarch64_poll(struct target *target); static int aarch64_poll(struct target *target);
static int aarch64_debug_entry(struct target *target); static int aarch64_debug_entry(struct target *target);
static int aarch64_restore_context(struct target *target, bool bpwp); static int aarch64_restore_context(struct target *target, bool bpwp);
@ -452,7 +457,7 @@ static int update_halt_gdb(struct target *target, enum target_debug_reason debug
struct target *curr; struct target *curr;
if (debug_reason == DBG_REASON_NOTHALTED) { if (debug_reason == DBG_REASON_NOTHALTED) {
LOG_INFO("Halting remaining targets in SMP group"); LOG_DEBUG("Halting remaining targets in SMP group");
aarch64_halt_smp(target, true); aarch64_halt_smp(target, true);
} }
@ -1086,7 +1091,7 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
if (target->smp && !handle_breakpoints) { if (target->smp && (current == 1)) {
/* /*
* isolate current target so that it doesn't get resumed * isolate current target so that it doesn't get resumed
* together with the others * together with the others
@ -1861,7 +1866,7 @@ static int aarch64_write_cpu_memory(struct target *target,
if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) { if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) {
/* Abort occurred - clear it and exit */ /* Abort occurred - clear it and exit */
LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr); LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr);
armv8_dpm_handle_exception(dpm); armv8_dpm_handle_exception(dpm, true);
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -2080,7 +2085,7 @@ static int aarch64_read_cpu_memory(struct target *target,
if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) { if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) {
/* Abort occurred - clear it and exit */ /* Abort occurred - clear it and exit */
LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr); LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr);
armv8_dpm_handle_exception(dpm); armv8_dpm_handle_exception(dpm, true);
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -2198,7 +2203,7 @@ static int aarch64_examine_first(struct target *target)
struct aarch64_common *aarch64 = target_to_aarch64(target); struct aarch64_common *aarch64 = target_to_aarch64(target);
struct armv8_common *armv8 = &aarch64->armv8_common; struct armv8_common *armv8 = &aarch64->armv8_common;
struct adiv5_dap *swjdp = armv8->arm.dap; struct adiv5_dap *swjdp = armv8->arm.dap;
uint32_t cti_base; struct aarch64_private_config *pc;
int i; int i;
int retval = ERROR_OK; int retval = ERROR_OK;
uint64_t debug, ttypr; uint64_t debug, ttypr;
@ -2206,10 +2211,6 @@ static int aarch64_examine_first(struct target *target)
uint32_t tmp0, tmp1, tmp2, tmp3; uint32_t tmp0, tmp1, tmp2, tmp3;
debug = ttypr = cpuid = 0; debug = ttypr = cpuid = 0;
retval = dap_dp_init(swjdp);
if (retval != ERROR_OK)
return retval;
/* Search for the APB-AB - it is needed for access to debug registers */ /* Search for the APB-AB - it is needed for access to debug registers */
retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv8->debug_ap); retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv8->debug_ap);
if (retval != ERROR_OK) { if (retval != ERROR_OK) {
@ -2289,17 +2290,15 @@ static int aarch64_examine_first(struct target *target)
LOG_DEBUG("ttypr = 0x%08" PRIx64, ttypr); LOG_DEBUG("ttypr = 0x%08" PRIx64, ttypr);
LOG_DEBUG("debug = 0x%08" PRIx64, debug); LOG_DEBUG("debug = 0x%08" PRIx64, debug);
if (target->ctibase == 0) { if (target->private_config == NULL)
/* assume a v8 rom table layout */
cti_base = armv8->debug_base + 0x10000;
LOG_INFO("Target ctibase is not set, assuming 0x%0" PRIx32, cti_base);
} else
cti_base = target->ctibase;
armv8->cti = arm_cti_create(armv8->debug_ap, cti_base);
if (armv8->cti == NULL)
return ERROR_FAIL; return ERROR_FAIL;
pc = (struct aarch64_private_config *)target->private_config;
if (pc->cti == NULL)
return ERROR_FAIL;
armv8->cti = pc->cti;
retval = aarch64_dpm_setup(aarch64, debug); retval = aarch64_dpm_setup(aarch64, debug);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
@ -2356,18 +2355,13 @@ static int aarch64_init_target(struct command_context *cmd_ctx,
} }
static int aarch64_init_arch_info(struct target *target, static int aarch64_init_arch_info(struct target *target,
struct aarch64_common *aarch64, struct jtag_tap *tap) struct aarch64_common *aarch64, struct adiv5_dap *dap)
{ {
struct armv8_common *armv8 = &aarch64->armv8_common; struct armv8_common *armv8 = &aarch64->armv8_common;
/* Setup struct aarch64_common */ /* Setup struct aarch64_common */
aarch64->common_magic = AARCH64_COMMON_MAGIC; aarch64->common_magic = AARCH64_COMMON_MAGIC;
/* tap has no dap initialized */ armv8->arm.dap = dap;
if (!tap->dap) {
tap->dap = dap_init();
tap->dap->tap = tap;
}
armv8->arm.dap = tap->dap;
/* register arch-specific functions */ /* register arch-specific functions */
armv8->examine_debug_reason = NULL; armv8->examine_debug_reason = NULL;
@ -2383,9 +2377,13 @@ static int aarch64_init_arch_info(struct target *target,
static int aarch64_target_create(struct target *target, Jim_Interp *interp) static int aarch64_target_create(struct target *target, Jim_Interp *interp)
{ {
struct aarch64_private_config *pc = target->private_config;
struct aarch64_common *aarch64 = calloc(1, sizeof(struct aarch64_common)); struct aarch64_common *aarch64 = calloc(1, sizeof(struct aarch64_common));
return aarch64_init_arch_info(target, aarch64, target->tap); if (adiv5_verify_config(&pc->adiv5_config) != ERROR_OK)
return ERROR_FAIL;
return aarch64_init_arch_info(target, aarch64, pc->adiv5_config.dap);
} }
static int aarch64_mmu(struct target *target, int *enabled) static int aarch64_mmu(struct target *target, int *enabled)
@ -2405,6 +2403,94 @@ static int aarch64_virt2phys(struct target *target, target_addr_t virt,
return armv8_mmu_translate_va_pa(target, virt, phys, 1); return armv8_mmu_translate_va_pa(target, virt, phys, 1);
} }
/*
* private target configuration items
*/
enum aarch64_cfg_param {
CFG_CTI,
};
static const Jim_Nvp nvp_config_opts[] = {
{ .name = "-cti", .value = CFG_CTI },
{ .name = NULL, .value = -1 }
};
static int aarch64_jim_configure(struct target *target, Jim_GetOptInfo *goi)
{
struct aarch64_private_config *pc;
Jim_Nvp *n;
int e;
pc = (struct aarch64_private_config *)target->private_config;
if (pc == NULL) {
pc = calloc(1, sizeof(struct aarch64_private_config));
target->private_config = pc;
}
/*
* Call adiv5_jim_configure() to parse the common DAP options
* It will return JIM_CONTINUE if it didn't find any known
* options, JIM_OK if it correctly parsed the topmost option
* and JIM_ERR if an error occured during parameter evaluation.
* For JIM_CONTINUE, we check our own params.
*/
e = adiv5_jim_configure(target, goi);
if (e != JIM_CONTINUE)
return e;
/* parse config or cget options ... */
if (goi->argc > 0) {
Jim_SetEmptyResult(goi->interp);
/* check first if topmost item is for us */
e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts,
goi->argv[0], &n);
if (e != JIM_OK)
return JIM_CONTINUE;
e = Jim_GetOpt_Obj(goi, NULL);
if (e != JIM_OK)
return e;
switch (n->value) {
case CFG_CTI: {
if (goi->isconfigure) {
Jim_Obj *o_cti;
struct arm_cti *cti;
e = Jim_GetOpt_Obj(goi, &o_cti);
if (e != JIM_OK)
return e;
cti = cti_instance_by_jim_obj(goi->interp, o_cti);
if (cti == NULL) {
Jim_SetResultString(goi->interp, "CTI name invalid!", -1);
return JIM_ERR;
}
pc->cti = cti;
} else {
if (goi->argc != 0) {
Jim_WrongNumArgs(goi->interp,
goi->argc, goi->argv,
"NO PARAMS");
return JIM_ERR;
}
if (pc == NULL || pc->cti == NULL) {
Jim_SetResultString(goi->interp, "CTI not configured", -1);
return JIM_ERR;
}
Jim_SetResultString(goi->interp, arm_cti_name(pc->cti), -1);
}
break;
}
default:
return JIM_CONTINUE;
}
}
return JIM_OK;
}
COMMAND_HANDLER(aarch64_handle_cache_info_command) COMMAND_HANDLER(aarch64_handle_cache_info_command)
{ {
struct target *target = get_current_target(CMD_CTX); struct target *target = get_current_target(CMD_CTX);
@ -2570,6 +2656,7 @@ struct target_type aarch64_target = {
.commands = aarch64_command_handlers, .commands = aarch64_command_handlers,
.target_create = aarch64_target_create, .target_create = aarch64_target_create,
.target_jim_configure = aarch64_jim_configure,
.init_target = aarch64_init_target, .init_target = aarch64_init_target,
.examine = aarch64_examine, .examine = aarch64_examine,

View File

@ -574,6 +574,7 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap)
if ((ctrlstat & (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) != if ((ctrlstat & (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) !=
(CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) { (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) {
LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened"); LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened");
dap->do_reconnect = true;
} }
if (ctrlstat & SSTICKYERR) if (ctrlstat & SSTICKYERR)
@ -598,6 +599,20 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int jtag_connect(struct adiv5_dap *dap)
{
dap->do_reconnect = false;
return dap_dp_init(dap);
}
static int jtag_check_reconnect(struct adiv5_dap *dap)
{
if (dap->do_reconnect)
return jtag_connect(dap);
return ERROR_OK;
}
static int jtag_dp_q_read(struct adiv5_dap *dap, unsigned reg, static int jtag_dp_q_read(struct adiv5_dap *dap, unsigned reg,
uint32_t *data) uint32_t *data)
{ {
@ -633,7 +648,11 @@ static int jtag_ap_q_bankselect(struct adiv5_ap *ap, unsigned reg)
static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg, static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg,
uint32_t *data) uint32_t *data)
{ {
int retval = jtag_ap_q_bankselect(ap, reg); int retval = jtag_check_reconnect(ap->dap);
if (retval != ERROR_OK)
return retval;
retval = jtag_ap_q_bankselect(ap, reg);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
@ -647,7 +666,11 @@ static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg,
static int jtag_ap_q_write(struct adiv5_ap *ap, unsigned reg, static int jtag_ap_q_write(struct adiv5_ap *ap, unsigned reg,
uint32_t data) uint32_t data)
{ {
int retval = jtag_ap_q_bankselect(ap, reg); int retval = jtag_check_reconnect(ap->dap);
if (retval != ERROR_OK)
return retval;
retval = jtag_ap_q_bankselect(ap, reg);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
@ -692,6 +715,7 @@ static int jtag_dp_sync(struct adiv5_dap *dap)
* part of DAP setup * part of DAP setup
*/ */
const struct dap_ops jtag_dp_ops = { const struct dap_ops jtag_dp_ops = {
.connect = jtag_connect,
.queue_dp_read = jtag_dp_q_read, .queue_dp_read = jtag_dp_q_read,
.queue_dp_write = jtag_dp_q_write, .queue_dp_write = jtag_dp_q_write,
.queue_ap_read = jtag_ap_q_read, .queue_ap_read = jtag_ap_q_read,

View File

@ -53,13 +53,11 @@
#include <jtag/swd.h> #include <jtag/swd.h>
/* YUK! - but this is currently a global.... */
extern struct jtag_interface *jtag_interface;
static bool do_sync; static bool do_sync;
static void swd_finish_read(struct adiv5_dap *dap) static void swd_finish_read(struct adiv5_dap *dap)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
if (dap->last_read != NULL) { if (dap->last_read != NULL) {
swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0); swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0);
dap->last_read = NULL; dap->last_read = NULL;
@ -73,7 +71,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
static void swd_clear_sticky_errors(struct adiv5_dap *dap) static void swd_clear_sticky_errors(struct adiv5_dap *dap)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd); assert(swd);
swd->write_reg(swd_cmd(false, false, DP_ABORT), swd->write_reg(swd_cmd(false, false, DP_ABORT),
@ -82,7 +80,7 @@ static void swd_clear_sticky_errors(struct adiv5_dap *dap)
static int swd_run_inner(struct adiv5_dap *dap) static int swd_run_inner(struct adiv5_dap *dap)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
int retval; int retval;
retval = swd->run(); retval = swd->run();
@ -97,6 +95,7 @@ static int swd_run_inner(struct adiv5_dap *dap)
static int swd_connect(struct adiv5_dap *dap) static int swd_connect(struct adiv5_dap *dap)
{ {
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
uint32_t dpidr; uint32_t dpidr;
int status; int status;
@ -120,7 +119,7 @@ static int swd_connect(struct adiv5_dap *dap)
} }
/* Note, debugport_init() does setup too */ /* Note, debugport_init() does setup too */
jtag_interface->swd->switch_seq(JTAG_TO_SWD); swd->switch_seq(JTAG_TO_SWD);
/* Clear link state, including the SELECT cache. */ /* Clear link state, including the SELECT cache. */
dap->do_reconnect = false; dap->do_reconnect = false;
@ -136,6 +135,7 @@ static int swd_connect(struct adiv5_dap *dap)
if (status == ERROR_OK) { if (status == ERROR_OK) {
LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr);
dap->do_reconnect = false; dap->do_reconnect = false;
status = dap_dp_init(dap);
} else } else
dap->do_reconnect = true; dap->do_reconnect = true;
@ -157,7 +157,7 @@ static int swd_check_reconnect(struct adiv5_dap *dap)
static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd); assert(swd);
swd->write_reg(swd_cmd(false, false, DP_ABORT), swd->write_reg(swd_cmd(false, false, DP_ABORT),
@ -187,7 +187,7 @@ static void swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg)
static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
uint32_t *data) uint32_t *data)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd); assert(swd);
int retval = swd_check_reconnect(dap); int retval = swd_check_reconnect(dap);
@ -203,7 +203,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
uint32_t data) uint32_t data)
{ {
const struct swd_driver *swd = jtag_interface->swd; const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd); assert(swd);
int retval = swd_check_reconnect(dap); int retval = swd_check_reconnect(dap);
@ -236,10 +236,9 @@ static void swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg)
static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
uint32_t *data) uint32_t *data)
{ {
const struct swd_driver *swd = jtag_interface->swd;
assert(swd);
struct adiv5_dap *dap = ap->dap; struct adiv5_dap *dap = ap->dap;
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
int retval = swd_check_reconnect(dap); int retval = swd_check_reconnect(dap);
if (retval != ERROR_OK) if (retval != ERROR_OK)
@ -255,10 +254,9 @@ static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg, static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
uint32_t data) uint32_t data)
{ {
const struct swd_driver *swd = jtag_interface->swd;
assert(swd);
struct adiv5_dap *dap = ap->dap; struct adiv5_dap *dap = ap->dap;
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
int retval = swd_check_reconnect(dap); int retval = swd_check_reconnect(dap);
if (retval != ERROR_OK) if (retval != ERROR_OK)
@ -279,6 +277,7 @@ static int swd_run(struct adiv5_dap *dap)
} }
const struct dap_ops swd_dap_ops = { const struct dap_ops swd_dap_ops = {
.connect = swd_connect,
.queue_dp_read = swd_queue_dp_read, .queue_dp_read = swd_queue_dp_read,
.queue_dp_write = swd_queue_dp_write, .queue_dp_write = swd_queue_dp_write,
.queue_ap_read = swd_queue_ap_read, .queue_ap_read = swd_queue_ap_read,
@ -381,15 +380,15 @@ static const struct command_registration swd_handlers[] = {
static int swd_select(struct command_context *ctx) static int swd_select(struct command_context *ctx)
{ {
/* FIXME: only place where global 'jtag_interface' is still needed */
extern struct jtag_interface *jtag_interface;
const struct swd_driver *swd = jtag_interface->swd;
int retval; int retval;
retval = register_commands(ctx, NULL, swd_handlers); retval = register_commands(ctx, NULL, swd_handlers);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
const struct swd_driver *swd = jtag_interface->swd;
/* be sure driver is in SWD mode; start /* be sure driver is in SWD mode; start
* with hardware default TRN (1), it can be changed later * with hardware default TRN (1), it can be changed later
*/ */
@ -404,33 +403,14 @@ static int swd_select(struct command_context *ctx)
return retval; return retval;
} }
/* force DAP into SWD mode (not JTAG) */
/*retval = dap_to_swd(target);*/
if (ctx->current_target) {
/* force DAP into SWD mode (not JTAG) */
struct target *target = get_current_target(ctx);
retval = dap_to_swd(target);
}
return retval; return retval;
} }
static int swd_init(struct command_context *ctx) static int swd_init(struct command_context *ctx)
{ {
struct target *target = get_current_target(ctx); /* nothing done here, SWD is initialized
struct arm *arm = target_to_arm(target); * together with the DAP */
struct adiv5_dap *dap = arm->dap; return ERROR_OK;
/* Force the DAP's ops vector for SWD mode.
* messy - is there a better way? */
arm->dap->ops = &swd_dap_ops;
/* First connect after init is not reconnecting. */
dap->do_reconnect = false;
int retval = swd_connect(dap);
if (retval != ERROR_OK)
LOG_ERROR("SWD connect failed");
return retval;
} }
static struct transport swd_transport = { static struct transport swd_transport = {

View File

@ -77,6 +77,43 @@ enum arm_mode {
ARM_MODE_ANY = -1 ARM_MODE_ANY = -1
}; };
/* VFPv3 internal register numbers mapping to d0:31 */
enum {
ARM_VFP_V3_D0 = 51,
ARM_VFP_V3_D1,
ARM_VFP_V3_D2,
ARM_VFP_V3_D3,
ARM_VFP_V3_D4,
ARM_VFP_V3_D5,
ARM_VFP_V3_D6,
ARM_VFP_V3_D7,
ARM_VFP_V3_D8,
ARM_VFP_V3_D9,
ARM_VFP_V3_D10,
ARM_VFP_V3_D11,
ARM_VFP_V3_D12,
ARM_VFP_V3_D13,
ARM_VFP_V3_D14,
ARM_VFP_V3_D15,
ARM_VFP_V3_D16,
ARM_VFP_V3_D17,
ARM_VFP_V3_D18,
ARM_VFP_V3_D19,
ARM_VFP_V3_D20,
ARM_VFP_V3_D21,
ARM_VFP_V3_D22,
ARM_VFP_V3_D23,
ARM_VFP_V3_D24,
ARM_VFP_V3_D25,
ARM_VFP_V3_D26,
ARM_VFP_V3_D27,
ARM_VFP_V3_D28,
ARM_VFP_V3_D29,
ARM_VFP_V3_D30,
ARM_VFP_V3_D31,
ARM_VFP_V3_FPSCR,
};
const char *arm_mode_name(unsigned psr_mode); const char *arm_mode_name(unsigned psr_mode);
bool is_arm_mode(unsigned psr_mode); bool is_arm_mode(unsigned psr_mode);
@ -89,6 +126,14 @@ enum arm_state {
ARM_STATE_AARCH64, ARM_STATE_AARCH64,
}; };
/** ARM vector floating point enabled, if yes which version. */
enum arm_vfp_version {
ARM_VFP_DISABLED,
ARM_VFP_V1,
ARM_VFP_V2,
ARM_VFP_V3,
};
#define ARM_COMMON_MAGIC 0x0A450A45 #define ARM_COMMON_MAGIC 0x0A450A45
/** /**
@ -145,6 +190,9 @@ struct arm {
/** Flag reporting whether semihosting fileio operation is active. */ /** Flag reporting whether semihosting fileio operation is active. */
bool semihosting_hit_fileio; bool semihosting_hit_fileio;
/** Floating point or VFP version, 0 if disabled. */
int arm_vfp_version;
/** Current semihosting operation. */ /** Current semihosting operation. */
int semihosting_op; int semihosting_op;
@ -225,7 +273,7 @@ struct arm_reg {
enum arm_mode mode; enum arm_mode mode;
struct target *target; struct target *target;
struct arm *arm; struct arm *arm;
uint8_t value[8]; uint8_t value[16];
}; };
struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm); struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm);

Some files were not shown because too many files have changed in this diff Show More