Merge branch 'master' into from_upstream
Conflicts: src/rtos/rtos.c src/rtos/rtos.h src/server/gdb_server.c Change-Id: Icd5a8165fe111f699542530c9cb034faf30e09b2
This commit is contained in:
commit
c73e06809d
13
HACKING
13
HACKING
|
@ -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
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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
|
|
@ -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");
|
||||||
|
}
|
|
@ -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,
|
450
doc/openocd.texi
450
doc/openocd.texi
|
@ -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
|
||||||
@}
|
@}
|
||||||
|
@ -2563,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
|
||||||
|
@ -3976,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
|
||||||
|
@ -4142,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
|
||||||
|
@ -4195,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
|
||||||
|
|
||||||
|
@ -4214,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
|
||||||
|
|
||||||
|
@ -4224,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.}
|
||||||
|
@ -4261,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
|
||||||
|
|
||||||
|
@ -4434,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
|
||||||
|
|
||||||
|
@ -4453,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}
|
||||||
|
@ -4461,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}
|
||||||
|
@ -4480,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
|
||||||
|
@ -4520,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,
|
||||||
|
@ -5200,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}
|
||||||
|
@ -5348,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
|
||||||
|
@ -5898,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
|
||||||
|
@ -7019,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}
|
||||||
|
@ -7666,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
|
||||||
|
@ -8145,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
|
||||||
|
|
||||||
|
@ -9102,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
|
||||||
|
@ -9154,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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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, ®_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(®_params[0], "r0", 32, PARAM_IN_OUT);
|
||||||
|
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||||
|
init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
|
||||||
|
init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
|
||||||
|
init_reg_param(®_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(®_params[0]);
|
||||||
|
destroy_reg_param(®_params[1]);
|
||||||
|
destroy_reg_param(®_params[2]);
|
||||||
|
destroy_reg_param(®_params[3]);
|
||||||
|
destroy_reg_param(®_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,
|
||||||
|
};
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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) \
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(®_params[0]);
|
destroy_reg_param(®_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,
|
||||||
¶m, sizeof(param));
|
¶m, 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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(®_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, ®_params, g_stack_area->address,
|
||||||
|
0, SROMAPI_CALL_TIMEOUT_MS, &armv7m_info);
|
||||||
|
|
||||||
|
destroy_reg_param(®_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), ®_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), ®_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,
|
||||||
|
};
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -1308,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);
|
||||||
|
@ -1472,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <helper/time_support.h>
|
#include <helper/time_support.h>
|
||||||
|
#include "transport/transport.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -110,6 +112,8 @@ static char *gdb_port_next;
|
||||||
static void gdb_log_callback(void *priv, const char *file, unsigned line,
|
static void gdb_log_callback(void *priv, const char *file, unsigned line,
|
||||||
const char *function, const char *string);
|
const char *function, const char *string);
|
||||||
|
|
||||||
|
static void gdb_sig_halted(struct connection *connection);
|
||||||
|
|
||||||
/* number of gdb connections, mainly to suppress gdb related debugging spam
|
/* number of gdb connections, mainly to suppress gdb related debugging spam
|
||||||
* in helper/log.c when no gdb connections are actually active */
|
* in helper/log.c when no gdb connections are actually active */
|
||||||
int gdb_actual_connections;
|
int gdb_actual_connections;
|
||||||
|
@ -731,7 +735,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);
|
||||||
|
|
||||||
|
@ -763,12 +766,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);
|
||||||
|
@ -956,9 +966,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);
|
||||||
|
@ -1922,6 +1937,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:
|
||||||
|
@ -1934,6 +1951,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:
|
||||||
|
@ -1961,12 +1980,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",
|
||||||
|
@ -1974,6 +2026,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> */
|
||||||
|
@ -1981,7 +2047,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,
|
||||||
|
@ -2007,13 +2072,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> */
|
||||||
|
@ -2044,8 +2120,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;
|
||||||
}
|
}
|
||||||
|
@ -2106,11 +2183,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, ®_list,
|
retval = target_get_gdb_reg_list(target, ®_list,
|
||||||
®_list_size, REG_CLASS_ALL);
|
®_list_size, REG_CLASS_ALL);
|
||||||
|
|
||||||
|
@ -2163,8 +2244,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 {
|
||||||
|
@ -2214,6 +2300,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;
|
||||||
|
@ -2390,7 +2477,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;
|
||||||
|
@ -2498,7 +2589,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) ? '+' : '-');
|
||||||
|
@ -2587,6 +2678,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)
|
||||||
{
|
{
|
||||||
|
@ -2600,6 +2870,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) {
|
||||||
|
@ -3039,7 +3322,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;
|
||||||
|
@ -3385,3 +3673,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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -345,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;
|
||||||
|
@ -353,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);
|
||||||
|
|
||||||
|
@ -624,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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
#include <helper/jep106.h>
|
#include <helper/jep106.h>
|
||||||
#include <helper/time_support.h>
|
#include <helper/time_support.h>
|
||||||
#include <helper/list.h>
|
#include <helper/list.h>
|
||||||
|
#include <helper/jim-nvp.h>
|
||||||
|
|
||||||
/* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */
|
/* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */
|
||||||
|
|
||||||
|
@ -614,33 +615,8 @@ int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
|
||||||
|
|
||||||
#define DAP_POWER_DOMAIN_TIMEOUT (10)
|
#define DAP_POWER_DOMAIN_TIMEOUT (10)
|
||||||
|
|
||||||
/* FIXME don't import ... just initialize as
|
|
||||||
* part of DAP transport setup
|
|
||||||
*/
|
|
||||||
extern const struct dap_ops jtag_dp_ops;
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new DAP
|
|
||||||
*/
|
|
||||||
struct adiv5_dap *dap_init(void)
|
|
||||||
{
|
|
||||||
struct adiv5_dap *dap = calloc(1, sizeof(struct adiv5_dap));
|
|
||||||
int i;
|
|
||||||
/* Set up with safe defaults */
|
|
||||||
for (i = 0; i <= 255; i++) {
|
|
||||||
dap->ap[i].dap = dap;
|
|
||||||
dap->ap[i].ap_num = i;
|
|
||||||
/* memaccess_tck max is 255 */
|
|
||||||
dap->ap[i].memaccess_tck = 255;
|
|
||||||
/* Number of bits for tar autoincrement, impl. dep. at least 10 */
|
|
||||||
dap->ap[i].tar_autoincr_block = (1<<10);
|
|
||||||
}
|
|
||||||
INIT_LIST_HEAD(&dap->cmd_journal);
|
|
||||||
return dap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate cached DP select and cached TAR and CSW of all APs
|
* Invalidate cached DP select and cached TAR and CSW of all APs
|
||||||
*/
|
*/
|
||||||
|
@ -667,14 +643,7 @@ int dap_dp_init(struct adiv5_dap *dap)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
LOG_DEBUG(" ");
|
LOG_DEBUG("%s", adiv5_dap_name(dap));
|
||||||
/* JTAG-DP or SWJ-DP, in JTAG mode
|
|
||||||
* ... for SWD mode this is patched as part
|
|
||||||
* of link switchover
|
|
||||||
* FIXME: This should already be setup by the respective transport specific DAP creation.
|
|
||||||
*/
|
|
||||||
if (!dap->ops)
|
|
||||||
dap->ops = &jtag_dp_ops;
|
|
||||||
|
|
||||||
dap_invalidate_cache(dap);
|
dap_invalidate_cache(dap);
|
||||||
|
|
||||||
|
@ -1376,7 +1345,7 @@ static int dap_rom_display(struct command_context *cmd_ctx,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dap_info_command(struct command_context *cmd_ctx,
|
int dap_info_command(struct command_context *cmd_ctx,
|
||||||
struct adiv5_ap *ap)
|
struct adiv5_ap *ap)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -1434,46 +1403,131 @@ static int dap_info_command(struct command_context *cmd_ctx,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum adiv5_cfg_param {
|
||||||
|
CFG_DAP,
|
||||||
|
CFG_AP_NUM
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Jim_Nvp nvp_config_opts[] = {
|
||||||
|
{ .name = "-dap", .value = CFG_DAP },
|
||||||
|
{ .name = "-ap-num", .value = CFG_AP_NUM },
|
||||||
|
{ .name = NULL, .value = -1 }
|
||||||
|
};
|
||||||
|
|
||||||
int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi)
|
int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi)
|
||||||
{
|
{
|
||||||
struct adiv5_private_config *pc;
|
struct adiv5_private_config *pc;
|
||||||
const char *arg;
|
|
||||||
jim_wide ap_num;
|
|
||||||
int e;
|
int e;
|
||||||
|
|
||||||
/* check if argv[0] is for us */
|
pc = (struct adiv5_private_config *)target->private_config;
|
||||||
arg = Jim_GetString(goi->argv[0], NULL);
|
if (pc == NULL) {
|
||||||
if (strcmp(arg, "-ap-num"))
|
|
||||||
return JIM_CONTINUE;
|
|
||||||
|
|
||||||
e = Jim_GetOpt_String(goi, &arg, NULL);
|
|
||||||
if (e != JIM_OK)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
if (goi->argc == 0) {
|
|
||||||
Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-ap-num ?ap-number? ...");
|
|
||||||
return JIM_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
e = Jim_GetOpt_Wide(goi, &ap_num);
|
|
||||||
if (e != JIM_OK)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
if (target->private_config == NULL) {
|
|
||||||
pc = calloc(1, sizeof(struct adiv5_private_config));
|
pc = calloc(1, sizeof(struct adiv5_private_config));
|
||||||
|
pc->ap_num = -1;
|
||||||
target->private_config = pc;
|
target->private_config = pc;
|
||||||
pc->ap_num = ap_num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target->has_dap = true;
|
||||||
|
|
||||||
|
if (goi->argc > 0) {
|
||||||
|
Jim_Nvp *n;
|
||||||
|
|
||||||
|
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_DAP:
|
||||||
|
if (goi->isconfigure) {
|
||||||
|
Jim_Obj *o_t;
|
||||||
|
struct adiv5_dap *dap;
|
||||||
|
e = Jim_GetOpt_Obj(goi, &o_t);
|
||||||
|
if (e != JIM_OK)
|
||||||
|
return e;
|
||||||
|
dap = dap_instance_by_jim_obj(goi->interp, o_t);
|
||||||
|
if (dap == NULL) {
|
||||||
|
Jim_SetResultString(goi->interp, "DAP name invalid!", -1);
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
if (pc->dap != NULL && pc->dap != dap) {
|
||||||
|
Jim_SetResultString(goi->interp,
|
||||||
|
"DAP assignment cannot be changed after target was created!", -1);
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
if (target->tap_configured) {
|
||||||
|
Jim_SetResultString(goi->interp,
|
||||||
|
"-chain-position and -dap configparams are mutually exclusive!", -1);
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
pc->dap = dap;
|
||||||
|
target->tap = dap->tap;
|
||||||
|
target->dap_configured = true;
|
||||||
|
} else {
|
||||||
|
if (goi->argc != 0) {
|
||||||
|
Jim_WrongNumArgs(goi->interp,
|
||||||
|
goi->argc, goi->argv,
|
||||||
|
"NO PARAMS");
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->dap == NULL) {
|
||||||
|
Jim_SetResultString(goi->interp, "DAP not configured", -1);
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
Jim_SetResultString(goi->interp, adiv5_dap_name(pc->dap), -1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_AP_NUM:
|
||||||
|
if (goi->isconfigure) {
|
||||||
|
jim_wide ap_num;
|
||||||
|
e = Jim_GetOpt_Wide(goi, &ap_num);
|
||||||
|
if (e != JIM_OK)
|
||||||
|
return e;
|
||||||
|
pc->ap_num = ap_num;
|
||||||
|
} else {
|
||||||
|
if (goi->argc != 0) {
|
||||||
|
Jim_WrongNumArgs(goi->interp,
|
||||||
|
goi->argc, goi->argv,
|
||||||
|
"NO PARAMS");
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->ap_num < 0) {
|
||||||
|
Jim_SetResultString(goi->interp, "AP number not configured", -1);
|
||||||
|
return JIM_ERR;
|
||||||
|
}
|
||||||
|
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, (int)pc->ap_num));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return JIM_OK;
|
return JIM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int adiv5_verify_config(struct adiv5_private_config *pc)
|
||||||
|
{
|
||||||
|
if (pc == NULL)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (pc->dap == NULL)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
COMMAND_HANDLER(handle_dap_info_command)
|
COMMAND_HANDLER(handle_dap_info_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
uint32_t apsel;
|
uint32_t apsel;
|
||||||
|
|
||||||
switch (CMD_ARGC) {
|
switch (CMD_ARGC) {
|
||||||
|
@ -1494,10 +1548,7 @@ COMMAND_HANDLER(handle_dap_info_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_baseaddr_command)
|
COMMAND_HANDLER(dap_baseaddr_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t apsel, baseaddr;
|
uint32_t apsel, baseaddr;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -1534,10 +1585,7 @@ COMMAND_HANDLER(dap_baseaddr_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_memaccess_command)
|
COMMAND_HANDLER(dap_memaccess_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t memaccess_tck;
|
uint32_t memaccess_tck;
|
||||||
|
|
||||||
switch (CMD_ARGC) {
|
switch (CMD_ARGC) {
|
||||||
|
@ -1560,10 +1608,7 @@ COMMAND_HANDLER(dap_memaccess_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_apsel_command)
|
COMMAND_HANDLER(dap_apsel_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t apsel, apid;
|
uint32_t apsel, apid;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -1598,11 +1643,9 @@ COMMAND_HANDLER(dap_apsel_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_apcsw_command)
|
COMMAND_HANDLER(dap_apcsw_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
uint32_t apcsw = dap->ap[dap->apsel].csw_default;
|
||||||
struct adiv5_dap *dap = arm->dap;
|
uint32_t sprot = 0;
|
||||||
|
|
||||||
uint32_t apcsw = dap->ap[dap->apsel].csw_default, sprot = 0;
|
|
||||||
|
|
||||||
switch (CMD_ARGC) {
|
switch (CMD_ARGC) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -1631,10 +1674,7 @@ COMMAND_HANDLER(dap_apcsw_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_apid_command)
|
COMMAND_HANDLER(dap_apid_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t apsel, apid;
|
uint32_t apsel, apid;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -1666,10 +1706,7 @@ COMMAND_HANDLER(dap_apid_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_apreg_command)
|
COMMAND_HANDLER(dap_apreg_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t apsel, reg, value;
|
uint32_t apsel, reg, value;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -1705,10 +1742,7 @@ COMMAND_HANDLER(dap_apreg_command)
|
||||||
|
|
||||||
COMMAND_HANDLER(dap_ti_be_32_quirks_command)
|
COMMAND_HANDLER(dap_ti_be_32_quirks_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||||
struct arm *arm = target_to_arm(target);
|
|
||||||
struct adiv5_dap *dap = arm->dap;
|
|
||||||
|
|
||||||
uint32_t enable = dap->ti_be_32_quirks;
|
uint32_t enable = dap->ti_be_32_quirks;
|
||||||
|
|
||||||
switch (CMD_ARGC) {
|
switch (CMD_ARGC) {
|
||||||
|
@ -1729,7 +1763,7 @@ COMMAND_HANDLER(dap_ti_be_32_quirks_command)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct command_registration dap_commands[] = {
|
const struct command_registration dap_instance_commands[] = {
|
||||||
{
|
{
|
||||||
.name = "info",
|
.name = "info",
|
||||||
.handler = handle_dap_info_command,
|
.handler = handle_dap_info_command,
|
||||||
|
@ -1795,14 +1829,3 @@ static const struct command_registration dap_commands[] = {
|
||||||
},
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct command_registration dap_command_handlers[] = {
|
|
||||||
{
|
|
||||||
.name = "dap",
|
|
||||||
.mode = COMMAND_EXEC,
|
|
||||||
.help = "DAP command group",
|
|
||||||
.usage = "",
|
|
||||||
.chain = dap_commands,
|
|
||||||
},
|
|
||||||
COMMAND_REGISTRATION_DONE
|
|
||||||
};
|
|
||||||
|
|
|
@ -254,6 +254,8 @@ struct adiv5_dap {
|
||||||
* available until run().
|
* available until run().
|
||||||
*/
|
*/
|
||||||
struct dap_ops {
|
struct dap_ops {
|
||||||
|
/** connect operation for SWD */
|
||||||
|
int (*connect)(struct adiv5_dap *dap);
|
||||||
/** DP register read. */
|
/** DP register read. */
|
||||||
int (*queue_dp_read)(struct adiv5_dap *dap, unsigned reg,
|
int (*queue_dp_read)(struct adiv5_dap *dap, unsigned reg,
|
||||||
uint32_t *data);
|
uint32_t *data);
|
||||||
|
@ -473,9 +475,6 @@ int mem_ap_read_buf_noincr(struct adiv5_ap *ap,
|
||||||
int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
|
int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
|
||||||
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address);
|
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address);
|
||||||
|
|
||||||
/* Create DAP struct */
|
|
||||||
struct adiv5_dap *dap_init(void);
|
|
||||||
|
|
||||||
/* Initialisation of the debug system, power domains and registers */
|
/* Initialisation of the debug system, power domains and registers */
|
||||||
int dap_dp_init(struct adiv5_dap *dap);
|
int dap_dp_init(struct adiv5_dap *dap);
|
||||||
int mem_ap_init(struct adiv5_ap *ap);
|
int mem_ap_init(struct adiv5_ap *ap);
|
||||||
|
@ -509,12 +508,24 @@ int dap_to_swd(struct target *target);
|
||||||
/* Put debug link into JTAG mode */
|
/* Put debug link into JTAG mode */
|
||||||
int dap_to_jtag(struct target *target);
|
int dap_to_jtag(struct target *target);
|
||||||
|
|
||||||
extern const struct command_registration dap_command_handlers[];
|
extern const struct command_registration dap_instance_commands[];
|
||||||
|
|
||||||
|
struct arm_dap_object;
|
||||||
|
extern struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o);
|
||||||
|
extern struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj);
|
||||||
|
extern int dap_info_command(struct command_context *cmd_ctx,
|
||||||
|
struct adiv5_ap *ap);
|
||||||
|
extern int dap_register_commands(struct command_context *cmd_ctx);
|
||||||
|
extern const char *adiv5_dap_name(struct adiv5_dap *self);
|
||||||
|
extern const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self);
|
||||||
|
extern int dap_cleanup_all(void);
|
||||||
|
|
||||||
struct adiv5_private_config {
|
struct adiv5_private_config {
|
||||||
int ap_num;
|
int ap_num;
|
||||||
|
struct adiv5_dap *dap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern int adiv5_verify_config(struct adiv5_private_config *pc);
|
||||||
extern int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi);
|
extern int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi);
|
||||||
|
|
||||||
#endif /* OPENOCD_TARGET_ARM_ADI_V5_H */
|
#endif /* OPENOCD_TARGET_ARM_ADI_V5_H */
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue