diff --git a/Makefile b/Makefile index db9178f4..15ce20aa 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ ifeq ($(PROBE_HOST), pc-stlinkv2) PC_HOSTED = true NO_LIBOPENCM3 = true endif +ifeq ($(PROBE_HOST), pc-hosted) + PC_HOSTED = true + NO_LIBOPENCM3 = true +endif all: ifndef NO_LIBOPENCM3 diff --git a/src/Makefile b/src/Makefile index dd245704..2a030499 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,8 +46,9 @@ SRC = \ nxpke04.c \ platform.c \ sam3x.c \ - sam4l.c \ + sam4l.c \ samd.c \ + samx5x.c \ stm32f1.c \ stm32f4.c \ stm32h7.c \ @@ -71,6 +72,9 @@ endif ifndef OWN_HL SRC += jtag_scan.c jtagtap.c swdptap.c +SRC += remote.c +else +CFLAGS += -DOWN_HL endif OBJ = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SRC))) @@ -118,6 +122,11 @@ all_platforms: echo "
  • $$PROBE_HOST
  • "\ >> artifacts/index.html ;\ fi ;\ + if [ -f blackmagic_dfu.bin ]; then \ + mv blackmagic_dfu.bin artifacts/blackmagic_dfu-$$PROBE_HOST.bin ;\ + echo "
  • $$PROBE_HOST DFU
  • "\ + >> artifacts/index.html ;\ + fi ;\ done ;\ echo "" >> artifacts/index.html ;\ cp artifacts/*.bin artifacts/$(shell git describe --tags --always) diff --git a/src/command.c b/src/command.c index 15860636..bd6701d7 100644 --- a/src/command.c +++ b/src/command.c @@ -88,7 +88,11 @@ const struct command_s cmd_list[] = { {"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"}, #endif #ifdef PLATFORM_HAS_TRACESWO - {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture [(baudrate) for async swo]" }, +#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 + {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate)" }, +#else + {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, Manchester mode" }, +#endif #endif #if defined(PLATFORM_HAS_DEBUG) && !defined(PC_HOSTED) {"debug_bmp", (cmd_handler)cmd_debug_bmp, "Output BMP \"debug\" strings to the second vcom: (enable|disable)"}, @@ -112,8 +116,9 @@ long cortexm_wait_timeout = 2000; /* Timeout to wait for Cortex to react on halt int command_process(target *t, char *cmd) { const struct command_s *c; - int argc = 0; + int argc = 1; const char **argv; + const char *part; /* Initial estimate for argc */ for(char *s = cmd; *s; s++) @@ -122,8 +127,9 @@ int command_process(target *t, char *cmd) argv = alloca(sizeof(const char *) * argc); /* Tokenize cmd to find argv */ - for(argc = 0, argv[argc] = strtok(cmd, " \t"); - argv[argc]; argv[++argc] = strtok(NULL, " \t")); + argc = 0; + for (part = strtok(cmd, " \t"); part; part = strtok(NULL, " \t")) + argv[argc++] = part; /* Look for match and call handler */ for(c = cmd_list; c->cmd; c++) { @@ -372,13 +378,21 @@ static bool cmd_traceswo(target *t, int argc, const char **argv) #else extern char serial_no[9]; #endif - uint32_t baudrate = 0; (void)t; - - if (argc > 1) - baudrate = atoi(argv[1]); - - traceswo_init(baudrate); +#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 + if (argc > 1) { + uint32_t baudrate = atoi(argv[1]); + traceswo_init(baudrate); + } else { + gdb_outf("Missing baudrate parameter in command\n"); + } +#else + (void)argv; + traceswo_init(); + if (argc > 1) { + gdb_outf("Superfluous parameter(s) ignored\n"); + } +#endif gdb_outf("%s:%02X:%02X\n", serial_no, 5, 0x85); return true; } diff --git a/src/crc32.c b/src/crc32.c index 881f12d1..63e37293 100644 --- a/src/crc32.c +++ b/src/crc32.c @@ -21,7 +21,10 @@ #include "general.h" #include "target.h" -#if !defined(STM32F1) && !defined(STM32F4) +#if !defined(STM32F0) && !defined(STM32F1) && !defined(STM32F2) && \ + !defined(STM32F3) && !defined(STM32F4) && !defined(STM32F7) && \ + !defined(STM32L0) && !defined(STM32L1) && !defined(STM32F4) && \ + !defined(STM32G0) && !defined(STM32G4) static const uint32_t crc32_table[] = { 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 0396e0d3..f0f1f1d2 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -26,6 +26,7 @@ #include "gdb_if.h" #include "gdb_packet.h" #include "hex_utils.h" +#include "remote.h" #include @@ -37,9 +38,52 @@ int gdb_getpacket(char *packet, int size) int i; while(1) { - /* Wait for packet start */ - while((packet[0] = gdb_if_getchar()) != '$') - if(packet[0] == 0x04) return 1; + /* Wait for packet start */ + do { + /* Spin waiting for a start of packet character - either a gdb + * start ('$') or a BMP remote packet start ('!'). + */ + do { + packet[0] = gdb_if_getchar(); + if (packet[0]==0x04) return 1; + } while ((packet[0] != '$') && (packet[0] != REMOTE_SOM)); +#ifndef OWN_HL + if (packet[0]==REMOTE_SOM) { + /* This is probably a remote control packet + * - get and handle it */ + i=0; + bool gettingRemotePacket=true; + while (gettingRemotePacket) { + c=gdb_if_getchar(); + switch (c) { + case REMOTE_SOM: /* Oh dear, packet restarts */ + i=0; + break; + + case REMOTE_EOM: /* Complete packet for processing */ + packet[i]=0; + remotePacketProcess(i,packet); + gettingRemotePacket=false; + break; + + case '$': /* A 'real' gdb packet, best stop squatting now */ + packet[0]='$'; + gettingRemotePacket=false; + break; + + default: + if (i +#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 void traceswo_init(uint32_t baudrate); +#else +void traceswo_init(void); +#endif + void trace_buf_drain(usbd_device *dev, uint8_t ep); #endif diff --git a/src/platforms/f4discovery/platform.c b/src/platforms/f4discovery/platform.c index 4703317d..875c1571 100644 --- a/src/platforms/f4discovery/platform.c +++ b/src/platforms/f4discovery/platform.c @@ -38,11 +38,11 @@ #include jmp_buf fatal_error_jmpbuf; -extern uint32_t _ebss; +extern char _ebss[]; void platform_init(void) { - volatile uint32_t *magic = (uint32_t *) &_ebss; + volatile uint32_t *magic = (uint32_t *)_ebss; /* Check the USER button*/ rcc_periph_clock_enable(RCC_GPIOA); if (gpio_get(GPIOA, GPIO0) || diff --git a/src/platforms/libftdi/Makefile.inc b/src/platforms/libftdi/Makefile.inc index b890e860..d48a5a70 100644 --- a/src/platforms/libftdi/Makefile.inc +++ b/src/platforms/libftdi/Makefile.inc @@ -1,6 +1,7 @@ SYS = $(shell $(CC) -dumpmachine) CFLAGS += -DPC_HOSTED -DNO_LIBOPENCM3 -DENABLE_DEBUG -LDFLAGS += -lftdi1 +CFLAGS += $(shell pkg-config --cflags libftdi1) +LDFLAGS += $(shell pkg-config --libs libftdi1) ifneq (, $(findstring mingw, $(SYS))) LDFLAGS += -lusb-1.0 -lws2_32 else ifneq (, $(findstring cygwin, $(SYS))) diff --git a/src/platforms/pc-hosted/Makefile.inc b/src/platforms/pc-hosted/Makefile.inc new file mode 100644 index 00000000..7af77436 --- /dev/null +++ b/src/platforms/pc-hosted/Makefile.inc @@ -0,0 +1,13 @@ +TARGET=blackmagic_hosted +SYS = $(shell $(CC) -dumpmachine) +CFLAGS += -DPC_HOSTED -DNO_LIBOPENCM3 -DENABLE_DEBUG +CFLAGS += $(shell pkg-config --cflags libftdi1) +LDFLAGS += $(shell pkg-config --libs libftdi1) +ifneq (, $(findstring mingw, $(SYS))) +LDFLAGS += -lusb-1.0 -lws2_32 +CFLAGS += -Wno-cast-function-type +else ifneq (, $(findstring cygwin, $(SYS))) +LDFLAGS += -lusb-1.0 -lws2_32 +endif +VPATH += platforms/pc +SRC += timing.c \ diff --git a/src/platforms/pc-hosted/README.md b/src/platforms/pc-hosted/README.md new file mode 100644 index 00000000..cca59d11 --- /dev/null +++ b/src/platforms/pc-hosted/README.md @@ -0,0 +1,40 @@ +PC Hosted variant + +THIS IS INCOMPLETE - ONLY SUPPORTS SWD AT THE MOMENT + +This variant will use any BMP probe with recent firmware as a remote +actuator, with the actual probe code running on the PC. The BMP itself +is 'dumb' and doesn't do anything (although any secondary serial port +remains available). + +To use it, compile for the pc-hosted target and then connect to your normal +BMP GDB port; + +src/blackmagic -s /dev/ttyACM0 + +...you can then connect your gdb session to localhost:2000 for all your +debugging goodness; + +$arm-eabi-none-gdb +(gdb) monitor swdp_scan +Target voltage: not supported +Available Targets: +No. Att Driver + 1 STM32F1 medium density M3/M4 +(gdb) attach 1 +Attaching to program: Builds/blackmagic/src/blackmagic, Remote target +0x08001978 in ?? () +(gdb) file src/blackmagic +A program is being debugged already. +Are you sure you want to change the file? (y or n) y +Load new symbol table from "src/blackmagic"? (y or n) y +Reading symbols from src/blackmagic... +(gdb) load +Loading section .text, size 0x1201c lma 0x8002000 +Loading section .data, size 0xd8 lma 0x801401c +Start address 0x800d9fc, load size 73972 +Transfer rate: 2 KB/sec, 960 bytes/write. +(gdb) + +...note that the speed of the probe in this way is about 10 times less than +running native. This build is intended for debug and development only. \ No newline at end of file diff --git a/src/platforms/pc-hosted/jtagtap.c b/src/platforms/pc-hosted/jtagtap.c new file mode 100644 index 00000000..cce1c71f --- /dev/null +++ b/src/platforms/pc-hosted/jtagtap.c @@ -0,0 +1,144 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2008 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * Modified by Dave Marples + * + * 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 3 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 . + */ + +/* Low level JTAG implementation using FT2232 with libftdi. + * + * Issues: + * Should share interface with swdptap.c or at least clean up... + */ + +#include +#include +#include + +#include + +#include "general.h" +#include "remote.h" +#include "jtagtap.h" + +/* See remote.c/.h for protocol information */ + +int jtagtap_init(void) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_JTAG_INIT_STR); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"jtagtap_init failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + return 0; +} + +void jtagtap_reset(void) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_JTAG_RESET_STR); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"jtagtap_reset failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } +} + +void jtagtap_tms_seq(uint32_t MS, int ticks) + +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,REMOTE_JTAG_TMS_STR,ticks,MS); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"jtagtap_tms_seq failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } +} + +void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + uint64_t DIl=*(uint64_t *)DI; + + if(!ticks || !DI) return; + + /* Reduce the length of DI according to the bits we're transmitting */ + DIl&=(1L<<(ticks+1))-1; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,REMOTE_JTAG_TDIDO_STR,final_tms?REMOTE_TDITDO_TMS:REMOTE_TDITDO_NOTMS,ticks,DIl); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"jtagtap_tms_seq failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + if (DO) { + for (unsigned int i = 1; i*8 <= (unsigned int)ticks; i++) + DO[i - 1] = remotehston(2 , (char *)&construct[s - (i * 2)]); + } +} + +void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks) + +{ + return jtagtap_tdi_tdo_seq(NULL, final_tms, DI, ticks); +} + + +uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI) + +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,REMOTE_JTAG_NEXT,dTMS?'1':'0',dTDI?'1':'0'); + + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"jtagtap_next failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + return remotehston(-1,(char *)&construct[1]); +} diff --git a/src/platforms/pc-hosted/platform.c b/src/platforms/pc-hosted/platform.c new file mode 100644 index 00000000..ca88d31f --- /dev/null +++ b/src/platforms/pc-hosted/platform.c @@ -0,0 +1,352 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * Additions by Dave Marples + * + * 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 3 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 . + */ +#include "general.h" +#include "gdb_if.h" +#include "version.h" +#include "platform.h" +#include "remote.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Allow 100mS for responses to reach us */ +#define RESP_TIMEOUT (100) + +/* Define this to see the transactions across the link */ +//#define DUMP_TRANSACTIONS + +static int f; /* File descriptor for connection to GDB remote */ + +int set_interface_attribs (int fd, int speed, int parity) + +/* A nice routine grabbed from + * https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c + */ + +{ + struct termios tty; + memset (&tty, 0, sizeof tty); + if (tcgetattr (fd, &tty) != 0) + { + fprintf(stderr,"error %d from tcgetattr", errno); + return -1; + } + + cfsetospeed (&tty, speed); + cfsetispeed (&tty, speed); + + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars + // disable IGNBRK for mismatched speed tests; otherwise receive break + // as \000 chars + tty.c_iflag &= ~IGNBRK; // disable break processing + tty.c_lflag = 0; // no signaling chars, no echo, + // no canonical processing + tty.c_oflag = 0; // no remapping, no delays + tty.c_cc[VMIN] = 0; // read doesn't block + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl + + tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, + // enable reading + tty.c_cflag &= ~(PARENB | PARODD); // shut off parity + tty.c_cflag |= parity; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CRTSCTS; + + if (tcsetattr (fd, TCSANOW, &tty) != 0) + { + fprintf(stderr,"error %d from tcsetattr", errno); + return -1; + } + return 0; +} + + +void platform_init(int argc, char **argv) +{ + int c; + char construct[PLATFORM_MAX_MSG_SIZE]; + char *serial = NULL; + while((c = getopt(argc, argv, "s:")) != -1) { + switch(c) + { + case 's': + serial = optarg; + break; + } + } + + printf("\nBlack Magic Probe (" FIRMWARE_VERSION ")\n"); + printf("Copyright (C) 2019 Black Sphere Technologies Ltd.\n"); + printf("License GPLv3+: GNU GPL version 3 or later " + "\n\n"); + + assert(gdb_if_init() == 0); + + f=open(serial,O_RDWR|O_SYNC|O_NOCTTY); + if (f<0) + { + fprintf(stderr,"Couldn't open serial port %s\n",serial); + exit(-1); + } + + if (set_interface_attribs (f, 115000, 0)<0) + { + exit(-1); + } + + c=snprintf(construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_START_STR); + platform_buffer_write((uint8_t *)construct,c); + c=platform_buffer_read((uint8_t *)construct, PLATFORM_MAX_MSG_SIZE); + + if ((!c) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"Remote Start failed, error %s\n",c?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + printf("Remote is %s\n",&construct[1]); +} + +bool platform_target_get_power(void) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_PWR_GET_STR); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"platform_target_get_power failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + return (construct[1]=='1'); +} + +void platform_target_set_power(bool power) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,REMOTE_PWR_SET_STR,power?'1':'0'); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"platform_target_set_power failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } +} + +void platform_srst_set_val(bool assert) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,REMOTE_SRST_SET_STR,assert?'1':'0'); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"platform_srst_set_val failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } +} + +bool platform_srst_get_val(void) + +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=snprintf((char *)construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_SRST_GET_STR); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"platform_srst_set_val failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + return (construct[1]=='1'); +} + +void platform_buffer_flush(void) +{ + +} + +int platform_buffer_write(const uint8_t *data, int size) +{ + int s; + +#ifdef DUMP_TRANSACTIONS + printf("%s\n",data); +#endif + s=write(f,data,size); + if (s<0) + { + fprintf(stderr,"Failed to write\n"); + exit(-2); + } + + return size; +} + +int platform_buffer_read(uint8_t *data, int maxsize) + +{ + uint8_t *c; + int s; + int ret; + uint32_t endTime; + fd_set rset; + struct timeval tv; + + c=data; + tv.tv_sec=0; + + endTime=platform_time_ms()+RESP_TIMEOUT; + tv.tv_usec=1000*(endTime-platform_time_ms()); + + /* Look for start of response */ + do + { + FD_ZERO(&rset); + FD_SET(f, &rset); + + ret = select(f + 1, &rset, NULL, NULL, &tv); + if (ret < 0) + { + fprintf(stderr,"Failed on select\n"); + exit(-4); + } + if(ret == 0) + { + fprintf(stderr,"Timeout on read\n"); + exit(-3); + } + + s=read(f,c,1); + } + while ((s>0) && (*c!=REMOTE_RESP)); + + /* Now collect the response */ + do + { + FD_ZERO(&rset); + FD_SET(f, &rset); + ret = select(f + 1, &rset, NULL, NULL, &tv); + if (ret < 0) + { + fprintf(stderr,"Failed on select\n"); + exit(-4); + } + if(ret == 0) + { + fprintf(stderr,"Timeout on read\n"); + exit(-3); + } + s=read(f,c,1); + if (*c==REMOTE_EOM) + { + *c=0; +#ifdef DUMP_TRANSACTIONS + printf(" %s\n",data); +#endif + return (c-data); + } + else + c++; + } + while ((s>=0) && (c-data + * Additions by Dave Marples + * + * 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 3 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 . + */ + +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include "timing.h" + +#ifndef _WIN32 +# include +#else +# ifndef alloca +# define alloca __builtin_alloca +# endif +#endif + +#define PLATFORM_HAS_DEBUG +#define PLATFORM_HAS_POWER_SWITCH +#define PLATFORM_MAX_MSG_SIZE (256) +#define PLATFORM_IDENT "PC-HOSTED" +#define BOARD_IDENT PLATFORM_IDENT +#define SET_RUN_STATE(state) +#define SET_IDLE_STATE(state) +#define SET_ERROR_STATE(state) + +void platform_buffer_flush(void); +int platform_buffer_write(const uint8_t *data, int size); +int platform_buffer_read(uint8_t *data, int size); + +static inline int platform_hwversion(void) +{ + return 0; +} + +#endif diff --git a/src/platforms/pc-hosted/swdptap.c b/src/platforms/pc-hosted/swdptap.c new file mode 100644 index 00000000..e63ed521 --- /dev/null +++ b/src/platforms/pc-hosted/swdptap.c @@ -0,0 +1,123 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2018 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) + * Written by Gareth McMullin + * Modified by Dave Marples + * + * 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 3 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 . + */ + +/* MPSSE bit-banging SW-DP interface over FTDI with loop unrolled. + * Speed is sensible. + */ + +#include +#include + +#include "general.h" +#include "swdptap.h" +#include "remote.h" + +int swdptap_init(void) + +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=sprintf((char *)construct,"%s",REMOTE_SWDP_INIT_STR); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((!s) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"swdptap_init failed, error %s\n",s?(char *)&(construct[1]):"unknown"); + exit(-1); + } + + return 0; +} + + +bool swdptap_seq_in_parity(uint32_t *res, int ticks) + +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=sprintf((char *)construct,REMOTE_SWDP_IN_PAR_STR,ticks); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((s<2) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"swdptap_seq_in_parity failed, error %s\n",s?(char *)&(construct[1]):"short response"); + exit(-1); + } + + *res=remotehston(-1,(char *)&construct[1]); + return (construct[0]!=REMOTE_RESP_OK); +} + + +uint32_t swdptap_seq_in(int ticks) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=sprintf((char *)construct,REMOTE_SWDP_IN_STR,ticks); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((s<2) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"swdptap_seq_in failed, error %s\n",s?(char *)&(construct[1]):"short response"); + exit(-1); + } + + return remotehston(-1,(char *)&construct[1]); +} + +void swdptap_seq_out(uint32_t MS, int ticks) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=sprintf((char *)construct,REMOTE_SWDP_OUT_STR,ticks,MS); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((s<1) || (construct[0]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"swdptap_seq_out failed, error %s\n",s?(char *)&(construct[1]):"short response"); + exit(-1); + } +} + + +void swdptap_seq_out_parity(uint32_t MS, int ticks) +{ + uint8_t construct[PLATFORM_MAX_MSG_SIZE]; + int s; + + s=sprintf((char *)construct,REMOTE_SWDP_OUT_PAR_STR,ticks,MS); + platform_buffer_write(construct,s); + + s=platform_buffer_read(construct, PLATFORM_MAX_MSG_SIZE); + if ((s<1) || (construct[1]==REMOTE_RESP_ERR)) + { + fprintf(stderr,"swdptap_seq_out_parity failed, error %s\n",s?(char *)&(construct[2]):"short response"); + exit(-1); + } +} diff --git a/src/platforms/pc-stlinkv2/stlinkv2.c b/src/platforms/pc-stlinkv2/stlinkv2.c index 0e1723bd..09d1c892 100644 --- a/src/platforms/pc-stlinkv2/stlinkv2.c +++ b/src/platforms/pc-stlinkv2/stlinkv2.c @@ -654,9 +654,9 @@ void stlink_leave_state(void) DEBUG("Leaving DEBUG Mode\n"); send_recv(dbg_cmd, 16, NULL, 0); } else if (data[0] == STLINK_DEV_BOOTLOADER_MODE) { - DEBUG("BOOTLOADER Mode\n"); + DEBUG("Leaving BOOTLOADER Mode\n"); } else if (data[0] == STLINK_DEV_MASS_MODE) { - DEBUG("MASS Mode\n"); + DEBUG("Leaving MASS Mode\n"); } else { DEBUG("Unknown Mode %02x\n", data[0]); } @@ -921,8 +921,8 @@ void stlink_init(int argc, char **argv) DEBUG("Please update Firmware\n"); goto error_1; } - stlink_resetsys(); stlink_leave_state(); + stlink_resetsys(); assert(gdb_if_init() == 0); return; error_1: diff --git a/src/platforms/pc/gdb_if.c b/src/platforms/pc/gdb_if.c index b5583b6f..6a81a0df 100644 --- a/src/platforms/pc/gdb_if.c +++ b/src/platforms/pc/gdb_if.c @@ -98,14 +98,18 @@ unsigned char gdb_if_getchar(void) { unsigned char ret; int i = 0; - +#if defined(_WIN32) || defined(__CYGWIN__) + unsigned long opt; +#else + int flags; +#endif while(i <= 0) { if(gdb_if_conn <= 0) { #if defined(_WIN32) || defined(__CYGWIN__) - unsigned long opt = 1; + opt = 1; ioctlsocket(gdb_if_serv, FIONBIO, &opt); #else - int flags = fcntl(gdb_if_serv, F_GETFL); + flags = fcntl(gdb_if_serv, F_GETFL); fcntl(gdb_if_serv, F_SETFL, flags | O_NONBLOCK); #endif while(1) { @@ -115,12 +119,12 @@ unsigned char gdb_if_getchar(void) SET_IDLE_STATE(1); platform_delay(100); } else { - DEBUG("error when accepting connection"); + DEBUG("error when accepting connection: %s", strerror(errno)); exit(1); } } else { #if defined(_WIN32) || defined(__CYGWIN__) - unsigned long opt = 0; + opt = 0; ioctlsocket(gdb_if_serv, FIONBIO, &opt); #else fcntl(gdb_if_serv, F_SETFL, flags); @@ -129,11 +133,18 @@ unsigned char gdb_if_getchar(void) } } DEBUG("Got connection\n"); +#if defined(_WIN32) || defined(__CYGWIN__) + opt = 0; + ioctlsocket(gdb_if_conn, FIONBIO, &opt); +#else + flags = fcntl(gdb_if_conn, F_GETFL); + fcntl(gdb_if_conn, F_SETFL, flags & ~O_NONBLOCK); +#endif } i = recv(gdb_if_conn, (void*)&ret, 1, 0); if(i <= 0) { gdb_if_conn = -1; - DEBUG("Dropped broken connection\n"); + DEBUG("Dropped broken connection: %s\n", strerror(errno)); /* Return '+' in case we were waiting for an ACK */ return '+'; } diff --git a/src/platforms/stlink/Makefile.inc b/src/platforms/stlink/Makefile.inc index 9dc971a6..fe50dc14 100644 --- a/src/platforms/stlink/Makefile.inc +++ b/src/platforms/stlink/Makefile.inc @@ -1,4 +1,5 @@ CROSS_COMPILE ?= arm-none-eabi- +ST_BOOTLOADER ?= CC = $(CROSS_COMPILE)gcc OBJCOPY = $(CROSS_COMPILE)objcopy @@ -10,7 +11,12 @@ LDFLAGS_BOOT := $(LDFLAGS) --specs=nano.specs -lopencm3_stm32f1 \ -Wl,-T,platforms/stm32/stlink.ld -nostartfiles -lc \ -Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections \ -L../libopencm3/lib +ifeq ($(ST_BOOTLOADER), 1) +$(info Load address 0x08004000 for original ST-LinkV2 Bootloader) +LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8004000 +else LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 +endif ifeq ($(ENABLE_DEBUG), 1) LDFLAGS += --specs=rdimon.specs diff --git a/src/platforms/stlink/README.md b/src/platforms/stlink/README.md index a22cf088..05ee5c53 100644 --- a/src/platforms/stlink/README.md +++ b/src/platforms/stlink/README.md @@ -8,10 +8,29 @@ Only if you have a Stlinkv2 with STM32F103C8 versus the STM32F103CB on V2/1 and you want to rewire and use the UART, consider reflashing the the Stlink firmware. +On StlinkV2, the original ST Bootloader can also be used with + +- Compile firmware with "make PROBE_HOST=stlink ST_BOOTLOADER=1" + +- Upload firmware with stlink-tool from [stlink-tool](https://github.com/jeanthom/stlink-tool.git). + Before upload, replug the stlink to enter the bootloader. + +- After each stlink replug, use call "stlink-tool" without arguments + to enter BMP + +Drawback: After each USB replug, DFU needs to be left explicit! +On Linux, add someting like : + +`> cat /etc/udev/rules.d/98-stlink.rules` + + `SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", ACTION=="add", RUN+="/stlink-tool"` + +for automatic switch to BMP on replug. However this defeats reflashing further +BMP reflash as long as this rule is active. + ## Versions -### [Standalone ST-LINKV2 -](https://www.st.com/content/st_com/en/products/development-tools/hardware-development-tools/development-tool-hardware-for-mcus/debug-hardware-for-mcus/debug-hardware-for-stm32-mcus/st-link-v2.html) +### [Standalone ST-LINKV2](https://www.st.com/content/st_com/en/products/development-tools/hardware-development-tools/development-tool-hardware-for-mcus/debug-hardware-for-mcus/debug-hardware-for-stm32-mcus/st-link-v2.html) Accessible connectors for JTAG/SWD (20-pin) and SWIM. ST-LINKV2/ISOL). ### ST-LINKV2 clones aka "baite" @@ -21,8 +40,7 @@ board. JTAG and target SWIM pins are accessible on connector (footprints). They are handled in the swlink branch. ### SWIM-only ST-LINK adapters on STM8 Nucleo-Stm8 boards As only a SWIM connector is accessible, they are not usefull as BMP target. -### [SWD only ST-LINK adapter -](https://www.st.com/content/ccc/resource/technical/document/technical_note/group0/30/c8/1d/0f/15/62/46/ef/DM00290229/files/DM00290229.pdf/jcr:content/translations/en.DM00290229.pdf) +### [SWD only ST-LINK adapter](https://www.st.com/content/ccc/resource/technical/document/technical_note/group0/30/c8/1d/0f/15/62/46/ef/DM00290229/files/DM00290229.pdf/jcr:content/translations/en.DM00290229.pdf) (Stm32 Nucleo Boards, recent Discovery boards) SWD, SWO and Reset are accessible on a 6-pin connector row. Jumper allow to route SWD to on-board target or off-board. Newer variants have UART TX/RX accessible on a connector @@ -35,8 +53,26 @@ CDCACM USART pins are not accessible. MCO output is used for LED. #### ST-Link/V2 and ST-Link/V2-A CDCACM USART pins are not accessible. MCO is connected to on board target. #### ST-Link/V2-1 and ST-Link/V2-B -### [STLINK-V3SET -](https://www.st.com/content/st_com/en/products/development-tools/hardware-development-tools/development-tool-hardware-for-mcus/debug-hardware-for-mcus/debug-hardware-for-stm32-mcus/stlink-v3set.html) +### [STLINK-V3SET](https://www.st.com/content/st_com/en/products/development-tools/hardware-development-tools/development-tool-hardware-for-mcus/debug-hardware-for-mcus/debug-hardware-for-stm32-mcus/stlink-v3set.html) + +## Wiring on Discovery and Nucleo Boards + +If there is a 6-pin connector, connect an external target after removing +the 2 jumper shortening the 4-pin connector like this: + +1: VCC sense, used only for measurement + +2: SWCLK + +3: GND + +4: SWDIO + +5: nSRST (pulled high by on board target. Will reset with on board target + unpowered. + +6: SWO + ## BMP version detection and handling All stlink variants @@ -45,3 +81,5 @@ UART RX/TX PC13 low -> SWIM internal connection +PC13/PC14 both low -> ST-LinkV2 on some F4_Diso boards. + diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h index 548a4c1d..0ac91bb3 100644 --- a/src/platforms/stlink/platform.h +++ b/src/platforms/stlink/platform.h @@ -70,6 +70,7 @@ #define PLATFORM_HAS_TRACESWO 1 #define NUM_TRACE_PACKETS (128) /* This is an 8K buffer */ +#define TRACESWO_PROTOCOL 2 /* 1 = Manchester, 2 = NRZ / async */ # define SWD_CR GPIO_CRH(SWDIO_PORT) # define SWD_CR_MULT (1 << ((14 - 8) << 2)) diff --git a/src/platforms/stm32/gpio.h b/src/platforms/stm32/gpio.h index d39fd4fe..dad20bb2 100644 --- a/src/platforms/stm32/gpio.h +++ b/src/platforms/stm32/gpio.h @@ -22,13 +22,8 @@ #include -#ifndef STM32F4 -# include -# include -#else -# include -# include -#endif +#include +#include #define INLINE_GPIO diff --git a/src/platforms/stm32/traceswo.c b/src/platforms/stm32/traceswo.c index 25ae60a0..392de224 100644 --- a/src/platforms/stm32/traceswo.c +++ b/src/platforms/stm32/traceswo.c @@ -33,6 +33,7 @@ */ #include "general.h" #include "cdcacm.h" +#include "traceswo.h" #include #include diff --git a/src/platforms/stm32/traceswoasync.c b/src/platforms/stm32/traceswoasync.c index dfa6baee..98cf45db 100644 --- a/src/platforms/stm32/traceswoasync.c +++ b/src/platforms/stm32/traceswoasync.c @@ -30,7 +30,7 @@ #include "general.h" #include "cdcacm.h" -#include "platform.h" +#include "traceswo.h" #include #include diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index 83f1d38a..f13ff104 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -216,7 +216,10 @@ void USBUSART_ISR(void) { uint32_t err = USART_SR(USBUSART); char c = usart_recv(USBUSART); - if (err & (USART_SR_ORE | USART_SR_FE | USART_SR_NE)) +#if !defined(USART_SR_NE) && defined(USART_ISR_NF) +# define USART_SR_NE USART_ISR_NF +#endif + if (err & (USART_FLAG_ORE | USART_FLAG_FE | USART_SR_NE)) return; /* Turn on LED */ diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h index bbccac9f..78bc4498 100644 --- a/src/platforms/swlink/platform.h +++ b/src/platforms/swlink/platform.h @@ -64,6 +64,7 @@ #define PLATFORM_HAS_TRACESWO 1 #define NUM_TRACE_PACKETS (128) /* This is an 8K buffer */ +#define TRACESWO_PROTOCOL 2 /* 1 = Manchester, 2 = NRZ / async */ # define SWD_CR GPIO_CRH(SWDIO_PORT) # define SWD_CR_MULT (1 << ((13 - 8) << 2)) diff --git a/src/remote.c b/src/remote.c new file mode 100644 index 00000000..d54f0e22 --- /dev/null +++ b/src/remote.c @@ -0,0 +1,276 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2019 Black Sphere Technologies Ltd. + * Written by Dave Marples + * + * 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 3 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 . + */ + +#include "general.h" +#include "remote.h" +#include "gdb_packet.h" +#include "swdptap.h" +#include "jtagtap.h" +#include "gdb_if.h" +#include "version.h" +#include + + +#define NTOH(x) ((x<=9)?x+'0':'a'+x-10) +#define HTON(x) ((x<='9')?x-'0':((TOUPPER(x))-'A'+10)) +#define TOUPPER(x) ((((x)>='a') && ((x)<='z'))?((x)-('a'-'A')):(x)) +#define ISHEX(x) ( \ + (((x)>='0') && ((x)<='9')) || \ + (((x)>='A') && ((x)<='F')) || \ + (((x)>='a') && ((x)<='f')) \ + ) + + +uint64_t remotehston(uint32_t limit, char *s) + +/* Return numeric version of string, until illegal hex digit, or limit */ + +{ + uint64_t ret=0L; + char c; + + while (limit--) { + c=*s++; + if (!ISHEX(c)) + return ret; + ret=(ret<<4)|HTON(c); + } + + return ret; +} + +static void _respond(char respCode, uint64_t param) + +/* Send response to far end */ + +{ + char buf[34]; + char *p=buf; + + gdb_if_putchar(REMOTE_RESP,0); + gdb_if_putchar(respCode,0); + + do { + *p++=NTOH((param&0x0f)); + param>>=4; + } + while (param); + + /* At this point the number to print is the buf, but backwards, so spool it out */ + do { + gdb_if_putchar(*--p,0); + } while (p>buf); + gdb_if_putchar(REMOTE_EOM,1); +} + +static void _respondS(char respCode, const char *s) +/* Send response to far end */ +{ + gdb_if_putchar(REMOTE_RESP,0); + gdb_if_putchar(respCode,0); + while (*s) { + /* Just clobber illegal characters so they don't disturb the protocol */ + if ((*s=='$') || (*s==REMOTE_SOM) || (*s==REMOTE_EOM)) + gdb_if_putchar(' ', 0); + else + gdb_if_putchar(*s, 0); + s++; + } + gdb_if_putchar(REMOTE_EOM,1); +} + +void remotePacketProcessSWD(uint8_t i, char *packet) +{ + uint8_t ticks; + uint32_t param; + bool badParity; + + switch (packet[1]) { + case REMOTE_INIT: /* SS = initialise ================================= */ + if (i==2) { + swdptap_init(); + _respond(REMOTE_RESP_OK, 0); + } else { + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN); + } + break; + + case REMOTE_IN_PAR: /* = In parity ================================== */ + ticks=remotehston(2,&packet[2]); + badParity=swdptap_seq_in_parity(¶m, ticks); + _respond(badParity?REMOTE_RESP_PARERR:REMOTE_RESP_OK,param); + break; + + case REMOTE_IN: /* = In ========================================= */ + ticks=remotehston(2,&packet[2]); + param=swdptap_seq_in(ticks); + _respond(REMOTE_RESP_OK,param); + break; + + case REMOTE_OUT: /* = Out ======================================== */ + ticks=remotehston(2,&packet[2]); + param=remotehston(-1, &packet[4]); + swdptap_seq_out(param, ticks); + _respond(REMOTE_RESP_OK, 0); + break; + + case REMOTE_OUT_PAR: /* = Out parity ================================= */ + ticks=remotehston(2,&packet[2]); + param=remotehston(-1, &packet[4]); + swdptap_seq_out_parity(param, ticks); + _respond(REMOTE_RESP_OK, 0); + break; + + default: + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED); + break; + } +} + +void remotePacketProcessJTAG(uint8_t i, char *packet) +{ + uint32_t MS; + uint64_t DO; + uint8_t ticks; + uint64_t DI; + + switch (packet[1]) { + case REMOTE_INIT: /* = initialise ================================= */ + jtagtap_init(); + _respond(REMOTE_RESP_OK, 0); + break; + + case REMOTE_RESET: /* = reset ================================= */ + jtagtap_reset(); + _respond(REMOTE_RESP_OK, 0); + break; + + case REMOTE_TMS: /* = TMS Sequence ================================== */ + ticks=remotehston(2,&packet[2]); + MS=remotehston(2,&packet[4]); + + if (i<4) { + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN); + } else { + jtagtap_tms_seq( MS, ticks); + _respond(REMOTE_RESP_OK, 0); + } + break; + + case REMOTE_TDITDO_TMS: /* = TDI/TDO ========================================= */ + case REMOTE_TDITDO_NOTMS: + + if (i<5) { + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN); + } else { + ticks=remotehston(2,&packet[2]); + DI=remotehston(-1,&packet[4]); + jtagtap_tdi_tdo_seq((void *)&DO, (packet[1]==REMOTE_TDITDO_TMS), (void *)&DI, ticks); + + /* Mask extra bits on return value... */ + DO&=(1<<(ticks+1))-1; + + _respond(REMOTE_RESP_OK, DO); + } + break; + + case REMOTE_NEXT: /* = NEXT ======================================== */ + if (i!=4) { + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN); + } else { + uint32_t dat=jtagtap_next( (packet[2]=='1'), (packet[3]=='1')); + _respond(REMOTE_RESP_OK,dat); + } + break; + + default: + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED); + break; + } +} + +void remotePacketProcessGEN(uint8_t i, char *packet) + +{ + (void)i; + switch (packet[1]) { + case REMOTE_VOLTAGE: + _respondS(REMOTE_RESP_OK,platform_target_voltage()); + break; + + case REMOTE_SRST_SET: + platform_srst_set_val(packet[2]=='1'); + _respond(REMOTE_RESP_OK,0); + break; + + case REMOTE_SRST_GET: + _respond(REMOTE_RESP_OK,platform_srst_get_val()); + break; + + case REMOTE_PWR_SET: +#ifdef PLATFORM_HAS_POWER_SWITCH + platform_target_set_power(packet[2]=='1'); + _respond(REMOTE_RESP_OK,0); +#else + _respond(REMOTE_RESP_NOTSUP,0); +#endif + break; + + case REMOTE_PWR_GET: +#ifdef PLATFORM_HAS_POWER_SWITCH + _respond(REMOTE_RESP_OK,platform_target_get_power()); +#else + _respond(REMOTE_RESP_NOTSUP,0); +#endif + break; + +#if !defined(BOARD_IDENT) && defined(PLATFORM_IDENT) +# define BOARD_IDENT PLATFORM_IDENT +#endif + case REMOTE_START: + _respondS(REMOTE_RESP_OK, BOARD_IDENT " " FIRMWARE_VERSION); + break; + + default: + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED); + break; + } +} + +void remotePacketProcess(uint8_t i, char *packet) +{ + switch (packet[0]) { + case REMOTE_SWDP_PACKET: + remotePacketProcessSWD(i,packet); + break; + + case REMOTE_JTAG_PACKET: + remotePacketProcessJTAG(i,packet); + break; + + case REMOTE_GEN_PACKET: + remotePacketProcessGEN(i,packet); + break; + + default: /* Oh dear, unrecognised, return an error */ + _respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED); + break; + } +} diff --git a/src/remote.h b/src/remote.h new file mode 100644 index 00000000..bddab64e --- /dev/null +++ b/src/remote.h @@ -0,0 +1,131 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2019 Black Sphere Technologies Ltd. + * Written by Dave Marples + * + * 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 3 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 . + */ + +#ifndef _REMOTE_ +#define _REMOTE_ + +#include +#include "general.h" + +/* + * Commands to remote end, and responses + * ===================================== + * + * All commands as sent as ASCII and begin with !, ending with #. + * Parameters are hex digits and format is per command. + * + * !# + * - 2 digit ASCII value + * - x digits (according to command) ASCII value + * + * So, for example; + * + * SI - swdptap_seq_in_parity + * tt - Ticks + * e.g. SI21 : Request input with parity, 33 ticks + * resp: K - hex value returned. + * resp: F - hex value returned, bad parity. + * X - error occured + * + * The whole protocol is defined in this header file. Parameters have + * to be marshalled in remote.c, swdptap.c and jtagtap.c, so be + * careful to ensure the parameter handling matches the protocol + * definition when anything is changed. + */ + +/* Protocol error messages */ +#define REMOTE_ERROR_UNRECOGNISED 1 +#define REMOTE_ERROR_WRONGLEN 2 + +/* Start and end of message identifiers */ +#define REMOTE_SOM '!' +#define REMOTE_EOM '#' +#define REMOTE_RESP '&' + +/* Generic protocol elements */ +#define REMOTE_START 'A' +#define REMOTE_TDITDO_TMS 'D' +#define REMOTE_TDITDO_NOTMS 'd' +#define REMOTE_IN_PAR 'I' +#define REMOTE_IN 'i' +#define REMOTE_NEXT 'N' +#define REMOTE_OUT_PAR 'O' +#define REMOTE_OUT 'o' +#define REMOTE_PWR_SET 'P' +#define REMOTE_PWR_GET 'p' +#define REMOTE_RESET 'R' +#define REMOTE_INIT 'S' +#define REMOTE_TMS 'T' +#define REMOTE_VOLTAGE 'V' +#define REMOTE_SRST_SET 'Z' +#define REMOTE_SRST_GET 'z' + +/* Protocol response options */ +#define REMOTE_RESP_OK 'K' +#define REMOTE_RESP_PARERR 'P' +#define REMOTE_RESP_ERR 'E' +#define REMOTE_RESP_NOTSUP 'N' + +/* Generic protocol elements */ +#define REMOTE_GEN_PACKET 'G' + +#define REMOTE_START_STR (char []){ '+', REMOTE_EOM, REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_START, REMOTE_EOM, 0 } +#define REMOTE_VOLTAGE_STR (char []){ REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_VOLTAGE, REMOTE_EOM, 0 } +#define REMOTE_SRST_SET_STR (char []){ REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_SRST_SET, '%', 'c', REMOTE_EOM, 0 } +#define REMOTE_SRST_GET_STR (char []){ REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_SRST_GET, REMOTE_EOM, 0 } +#define REMOTE_PWR_SET_STR (char []){ REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_PWR_SET, '%', 'c', REMOTE_EOM, 0 } +#define REMOTE_PWR_GET_STR (char []){ REMOTE_SOM, REMOTE_GEN_PACKET, REMOTE_PWR_GET, REMOTE_EOM, 0 } + +/* SWDP protocol elements */ +#define REMOTE_SWDP_PACKET 'S' +#define REMOTE_SWDP_INIT_STR (char []){ REMOTE_SOM, REMOTE_SWDP_PACKET, REMOTE_INIT, REMOTE_EOM, 0 } + +#define REMOTE_SWDP_IN_PAR_STR (char []){ REMOTE_SOM, REMOTE_SWDP_PACKET, REMOTE_IN_PAR, \ + '%','0','2','x',REMOTE_EOM, 0 } + +#define REMOTE_SWDP_IN_STR (char []){ REMOTE_SOM, REMOTE_SWDP_PACKET, REMOTE_IN, \ + '%','0','2','x',REMOTE_EOM, 0 } + +#define REMOTE_SWDP_OUT_STR (char []){ REMOTE_SOM, REMOTE_SWDP_PACKET, REMOTE_OUT, \ + '%','0','2','x','%','x',REMOTE_EOM, 0 } + +#define REMOTE_SWDP_OUT_PAR_STR (char []){ REMOTE_SOM, REMOTE_SWDP_PACKET, REMOTE_OUT_PAR, \ + '%','0','2','x','%','x',REMOTE_EOM, 0 } + +/* JTAG protocol elements */ +#define REMOTE_JTAG_PACKET 'J' + +#define REMOTE_JTAG_INIT_STR (char []){ '+',REMOTE_EOM, REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_INIT, REMOTE_EOM, 0 } + +#define REMOTE_JTAG_RESET_STR (char []){ '+',REMOTE_EOM, REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_RESET, REMOTE_EOM, 0 } + +#define REMOTE_JTAG_TMS_STR (char []){ REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_TMS, \ + '%','0','2','x','%','x',REMOTE_EOM, 0 } + +#define REMOTE_JTAG_TDIDO_STR (char []){ REMOTE_SOM, REMOTE_JTAG_PACKET, '%', 'c', \ + '%','0','2','x','%','l', 'x', REMOTE_EOM, 0 } + +#define REMOTE_JTAG_NEXT (char []){ REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_NEXT, \ + '%','c','%','c',REMOTE_EOM, 0 } + +uint64_t remotehston(uint32_t limit, char *s); +void remotePacketProcess(uint8_t i, char *packet); + +#endif diff --git a/src/target/adiv5.c b/src/target/adiv5.c index f56ce0cf..599aa78d 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -30,10 +30,6 @@ #include "cortexm.h" #include "exception.h" -#ifndef DO_RESET_SEQ -#define DO_RESET_SEQ 0 -#endif - /* All this should probably be defined in a dedicated ADIV5 header, so that they * are consistently named and accessible when needed in the codebase. */ @@ -263,7 +259,7 @@ static bool adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr, int recursion, uint32_t cidr = 0; bool res = false; #if defined(ENABLE_DEBUG) && defined(PLATFORM_HAS_DEBUG) - char indent[recursion]; + char indent[recursion + 1]; for(int i = 0; i < recursion; i++) indent[i] = ' '; indent[recursion] = 0; @@ -455,26 +451,33 @@ void adiv5_dp_init(ADIv5_DP_t *dp) (ADIV5_DP_CTRLSTAT_CSYSPWRUPACK | ADIV5_DP_CTRLSTAT_CDBGPWRUPACK)) != (ADIV5_DP_CTRLSTAT_CSYSPWRUPACK | ADIV5_DP_CTRLSTAT_CDBGPWRUPACK)); - if(DO_RESET_SEQ) { - /* This AP reset logic is described in ADIv5, but fails to work - * correctly on STM32. CDBGRSTACK is never asserted, and we - * just wait forever. - */ + /* This AP reset logic is described in ADIv5, but fails to work + * correctly on STM32. CDBGRSTACK is never asserted, and we + * just wait forever. This scenario is described in B2.4.1 + * so we have a timeout mechanism in addition to the sensing one. + */ - /* Write request for debug reset */ - adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT, - ctrlstat |= ADIV5_DP_CTRLSTAT_CDBGRSTREQ); - /* Wait for acknowledge */ - while(!((ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT)) & - ADIV5_DP_CTRLSTAT_CDBGRSTACK)); + /* Write request for debug reset */ + adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT, + ctrlstat |= ADIV5_DP_CTRLSTAT_CDBGRSTREQ); - /* Write request for debug reset release */ - adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT, - ctrlstat &= ~ADIV5_DP_CTRLSTAT_CDBGRSTREQ); - /* Wait for acknowledge */ - while(adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT) & - ADIV5_DP_CTRLSTAT_CDBGRSTACK); - } + platform_timeout timeout; + platform_timeout_set(&timeout,200); + /* Wait for acknowledge */ + while ((!platform_timeout_is_expired(&timeout)) && + (!((ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT)) & ADIV5_DP_CTRLSTAT_CDBGRSTACK)) + ); + + /* Write request for debug reset release */ + adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT, + ctrlstat &= ~ADIV5_DP_CTRLSTAT_CDBGRSTREQ); + + platform_timeout_set(&timeout,200); + /* Wait for acknowledge */ + while ((!platform_timeout_is_expired(&timeout)) && + (adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT) & ADIV5_DP_CTRLSTAT_CDBGRSTACK) + ); + DEBUG("RESET_SEQ %s\n", (platform_timeout_is_expired(&timeout)) ? "failed": "succeeded"); dp->dp_idcode = adiv5_dp_read(dp, ADIV5_DP_IDCODE); if ((dp->dp_idcode & ADIV5_DP_VERSION_MASK) == ADIV5_DPv2) { diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 17017aa3..95473ef5 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -370,6 +370,7 @@ bool cortexm_probe(ADIv5_AP_t *ap, bool forced) PROBE(sam4l_probe); PROBE(nrf51_probe); PROBE(samd_probe); + PROBE(samx5x_probe); PROBE(lmi_probe); PROBE(kinetis_probe); PROBE(efm32_probe); diff --git a/src/target/kinetis.c b/src/target/kinetis.c index e0b1530b..3cbe18e2 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -39,6 +39,7 @@ #include "target_internal.h" #define SIM_SDID 0x40048024 +#define SIM_FCFG1 0x4004804C #define FTFA_BASE 0x40020000 #define FTFA_FSTAT (FTFA_BASE + 0x00) @@ -127,7 +128,61 @@ static void kl_gen_add_flash(target *t, uint32_t addr, size_t length, bool kinetis_probe(target *t) { uint32_t sdid = target_mem_read32(t, SIM_SDID); + uint32_t fcfg1 = target_mem_read32(t, SIM_FCFG1); + switch (sdid >> 20) { + case 0x161: + /* sram memory size */ + switch((sdid >> 16) & 0x0f) { + case 0x03:/* 4 KB */ + target_add_ram(t, 0x1ffffc00, 0x0400); + target_add_ram(t, 0x20000000, 0x0C00); + break; + case 0x04:/* 8 KB */ + target_add_ram(t, 0x1ffff800, 0x0800); + target_add_ram(t, 0x20000000, 0x1800); + break; + case 0x05:/* 16 KB */ + target_add_ram(t, 0x1ffff000, 0x1000); + target_add_ram(t, 0x20000000, 0x3000); + break; + case 0x06:/* 32 KB */ + target_add_ram(t, 0x1fffe000, 0x2000); + target_add_ram(t, 0x20000000, 0x6000); + break; + default: + return false; + break; + } + + /* flash memory size */ + switch((fcfg1 >> 24) & 0x0f) { + case 0x03: /* 32 KB */ + t->driver = "KL16Z32Vxxx"; + kl_gen_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN); + break; + + case 0x05: /* 64 KB */ + t->driver = "KL16Z64Vxxx"; + kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN); + break; + + case 0x07: /* 128 KB */ + t->driver = "KL16Z128Vxxx"; + kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN); + break; + + case 0x09: /* 256 KB */ + t->driver = "KL16Z256Vxxx"; + kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN); + break; + default: + return false; + break; + } + + break; + case 0x251: t->driver = "KL25"; target_add_ram(t, 0x1ffff000, 0x1000); diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index 30143bac..43e6cd24 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -35,6 +35,26 @@ #define LPC11XX_DEVICE_ID 0x400483F4 #define LPC8XX_DEVICE_ID 0x400483F8 +static bool lpc11xx_read_uid(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct lpc_flash *f = (struct lpc_flash *)t->flash; + uint8_t uid[16]; + if (lpc_iap_call(f, uid, IAP_CMD_READUID)) + return false; + tc_printf(t, "UID: 0x"); + for (uint32_t i = 0; i < sizeof(uid); ++i) + tc_printf(t, "%02x", uid[i]); + tc_printf(t, "\n"); + return true; +} + +const struct command_s lpc11xx_cmd_list[] = { + {"readuid", lpc11xx_read_uid, "Read out the 16-byte UID."}, + {NULL, NULL, NULL} +}; + void lpc11xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize) { struct lpc_flash *lf = lpc_add_flash(t, addr, len); @@ -89,6 +109,7 @@ lpc11xx_probe(target *t) t->driver = "LPC11xx"; target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); + target_add_commands(t, lpc11xx_cmd_list, "LPC11xx"); return true; case 0x0A24902B: @@ -97,8 +118,16 @@ lpc11xx_probe(target *t) target_add_ram(t, 0x10000000, 0x1000); lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x1000); return true; + case 0x3000002B: + case 0x3D00002B: + t->driver = "LPC1343"; + target_add_ram(t, 0x10000000, 0x2000); + lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x1000); + return true; + } + if (idcode) { + DEBUG("LPC11xx: Unknown IDCODE 0x%08" PRIx32 "\n", idcode); } - idcode = target_mem_read32(t, LPC8XX_DEVICE_ID); switch (idcode) { case 0x00008100: /* LPC810M021FN8 */ @@ -109,6 +138,7 @@ lpc11xx_probe(target *t) t->driver = "LPC81x"; target_add_ram(t, 0x10000000, 0x1000); lpc11xx_add_flash(t, 0x00000000, 0x4000, 0x400); + target_add_commands(t, lpc11xx_cmd_list, "LPC81x"); return true; case 0x00008221: /* LPC822M101JHI33 */ case 0x00008222: /* LPC822M101JDH20 */ @@ -117,6 +147,23 @@ lpc11xx_probe(target *t) t->driver = "LPC82x"; target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400); + target_add_commands(t, lpc11xx_cmd_list, "LPC82x"); + return true; + case 0x00008441: + case 0x00008442: + case 0x00008443: /* UM11029 Rev.1.4 list 8442 */ + case 0x00008444: + t->driver = "LPC844"; + target_add_ram(t, 0x10000000, 0x2000); + lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400); + return true; + case 0x00008451: + case 0x00008452: + case 0x00008453: + case 0x00008454: + t->driver = "LPC845"; + target_add_ram(t, 0x10000000, 0x4000); + lpc11xx_add_flash(t, 0x00000000, 0x10000, 0x400); return true; case 0x0003D440: /* LPC11U34/311 */ case 0x0001cc40: /* LPC11U34/421 */ @@ -130,12 +177,16 @@ lpc11xx_probe(target *t) target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); return true; + case 0x00040070: /* LPC1114/333 */ case 0x00050080: /* lpc1115XL */ t->driver = "LPC1100XL"; target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); return true; } + if (idcode) { + DEBUG("LPC8xx: Unknown IDCODE 0x%08" PRIx32 "\n", idcode); + } return false; } diff --git a/src/target/lpc15xx.c b/src/target/lpc15xx.c index 7b86d42f..66074091 100644 --- a/src/target/lpc15xx.c +++ b/src/target/lpc15xx.c @@ -35,6 +35,26 @@ #define LPC15XX_DEVICE_ID 0x400743F8 +static bool lpc15xx_read_uid(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct lpc_flash *f = (struct lpc_flash *)t->flash; + uint8_t uid[16]; + if (lpc_iap_call(f, uid, IAP_CMD_READUID)) + return false; + tc_printf(t, "UID: 0x"); + for (uint32_t i = 0; i < sizeof(uid); ++i) + tc_printf(t, "%02x", uid[i]); + tc_printf(t, "\n"); + return true; +} + +const struct command_s lpc15xx_cmd_list[] = { + {"readuid", lpc15xx_read_uid, "Read out the 16-byte UID."}, + {NULL, NULL, NULL} +}; + void lpc15xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize) { struct lpc_flash *lf = lpc_add_flash(t, addr, len); @@ -72,6 +92,7 @@ lpc15xx_probe(target *t) t->driver = "LPC15xx"; target_add_ram(t, 0x02000000, ram_size); lpc15xx_add_flash(t, 0x00000000, 0x40000, 0x1000); + target_add_commands(t, lpc15xx_cmd_list, "LPC15xx"); return true; } diff --git a/src/target/lpc43xx.c b/src/target/lpc43xx.c index f08c4f8f..a1fb2145 100644 --- a/src/target/lpc43xx.c +++ b/src/target/lpc43xx.c @@ -164,11 +164,11 @@ static bool lpc43xx_cmd_erase(target *t, int argc, const char *argv[]) for (int bank = 0; bank < FLASH_NUM_BANK; bank++) { struct lpc_flash *f = (struct lpc_flash *)t->flash; - if (lpc_iap_call(f, IAP_CMD_PREPARE, + if (lpc_iap_call(f, NULL, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR-1, bank)) return false; - if (lpc_iap_call(f, IAP_CMD_ERASE, + if (lpc_iap_call(f, NULL, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR-1, CPU_CLK_KHZ, bank)) return false; } @@ -188,7 +188,7 @@ static int lpc43xx_flash_init(target *t) /* Initialize flash IAP */ struct lpc_flash *f = (struct lpc_flash *)t->flash; - if (lpc_iap_call(f, IAP_CMD_INIT)) + if (lpc_iap_call(f, NULL, IAP_CMD_INIT)) return -1; return 0; @@ -234,7 +234,7 @@ static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[]) /* special command to compute/write magic vector for signature */ struct lpc_flash *f = (struct lpc_flash *)t->flash; - if (lpc_iap_call(f, IAP_CMD_SET_ACTIVE_BANK, bank, CPU_CLK_KHZ)) { + if (lpc_iap_call(f, NULL, IAP_CMD_SET_ACTIVE_BANK, bank, CPU_CLK_KHZ)) { tc_printf(t, "Set bootable failed.\n"); return false; } diff --git a/src/target/lpc_common.c b/src/target/lpc_common.c index fa486ef4..2a35bb54 100644 --- a/src/target/lpc_common.c +++ b/src/target/lpc_common.c @@ -29,7 +29,8 @@ struct flash_param { uint16_t pad0; uint32_t command; uint32_t words[4]; - uint32_t result; + uint32_t status; + uint32_t result[4]; } __attribute__((aligned(4))); @@ -53,7 +54,7 @@ struct lpc_flash *lpc_add_flash(target *t, target_addr addr, size_t length) return lf; } -enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...) +enum iap_status lpc_iap_call(struct lpc_flash *f, void *result, enum iap_cmd cmd, ...) { target *t = f->f.t; struct flash_param param = { @@ -65,6 +66,14 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...) if (f->wdt_kick) f->wdt_kick(t); + /* save IAP RAM to restore after IAP call */ + struct flash_param backup_param; + target_mem_read(t, &backup_param, f->iap_ram, sizeof(backup_param)); + + /* save registers to restore after IAP call */ + uint32_t backup_regs[t->regs_size / sizeof(uint32_t)]; + target_regs_read(t, backup_regs); + /* fill out the remainder of the parameters */ va_list ap; va_start(ap, cmd); @@ -79,7 +88,7 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...) uint32_t regs[t->regs_size / sizeof(uint32_t)]; target_regs_read(t, regs); regs[0] = f->iap_ram + offsetof(struct flash_param, command); - regs[1] = f->iap_ram + offsetof(struct flash_param, result); + regs[1] = f->iap_ram + offsetof(struct flash_param, status); regs[REG_MSP] = f->iap_msp; regs[REG_LR] = f->iap_ram | 1; regs[REG_PC] = f->iap_entry; @@ -91,7 +100,16 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...) /* copy back just the parameters structure */ target_mem_read(t, ¶m, f->iap_ram, sizeof(param)); - return param.result; + + /* restore the original data in RAM and registers */ + target_mem_write(t, f->iap_ram, &backup_param, sizeof(param)); + target_regs_write(t, backup_regs); + + /* if the user expected a result, set the result (16 bytes). */ + if (result != NULL) + memcpy(result, param.result, sizeof(param.result)); + + return param.status; } static uint8_t lpc_sector_for_addr(struct lpc_flash *f, uint32_t addr) @@ -105,15 +123,15 @@ int lpc_flash_erase(struct target_flash *tf, target_addr addr, size_t len) uint32_t start = lpc_sector_for_addr(f, addr); uint32_t end = lpc_sector_for_addr(f, addr + len - 1); - if (lpc_iap_call(f, IAP_CMD_PREPARE, start, end, f->bank)) + if (lpc_iap_call(f, NULL, IAP_CMD_PREPARE, start, end, f->bank)) return -1; /* and now erase them */ - if (lpc_iap_call(f, IAP_CMD_ERASE, start, end, CPU_CLK_KHZ, f->bank)) + if (lpc_iap_call(f, NULL, IAP_CMD_ERASE, start, end, CPU_CLK_KHZ, f->bank)) return -2; /* check erase ok */ - if (lpc_iap_call(f, IAP_CMD_BLANKCHECK, start, end, f->bank)) + if (lpc_iap_call(f, NULL, IAP_CMD_BLANKCHECK, start, end, f->bank)) return -3; return 0; @@ -125,7 +143,7 @@ int lpc_flash_write(struct target_flash *tf, struct lpc_flash *f = (struct lpc_flash *)tf; /* prepare... */ uint32_t sector = lpc_sector_for_addr(f, dest); - if (lpc_iap_call(f, IAP_CMD_PREPARE, sector, sector, f->bank)) + if (lpc_iap_call(f, NULL, IAP_CMD_PREPARE, sector, sector, f->bank)) return -1; /* Write payload to target ram */ @@ -133,7 +151,7 @@ int lpc_flash_write(struct target_flash *tf, target_mem_write(f->f.t, bufaddr, src, len); /* set the destination address and program */ - if (lpc_iap_call(f, IAP_CMD_PROGRAM, dest, bufaddr, len, CPU_CLK_KHZ)) + if (lpc_iap_call(f, NULL, IAP_CMD_PROGRAM, dest, bufaddr, len, CPU_CLK_KHZ)) return -2; return 0; diff --git a/src/target/lpc_common.h b/src/target/lpc_common.h index 5b132263..3130e96b 100644 --- a/src/target/lpc_common.h +++ b/src/target/lpc_common.h @@ -27,6 +27,7 @@ enum iap_cmd { IAP_CMD_ERASE = 52, IAP_CMD_BLANKCHECK = 53, IAP_CMD_PARTID = 54, + IAP_CMD_READUID = 58, IAP_CMD_SET_ACTIVE_BANK = 60, }; @@ -60,7 +61,7 @@ struct lpc_flash { }; struct lpc_flash *lpc_add_flash(target *t, target_addr addr, size_t length); -enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...); +enum iap_status lpc_iap_call(struct lpc_flash *f, void *result, enum iap_cmd cmd, ...); int lpc_flash_erase(struct target_flash *f, target_addr addr, size_t len); int lpc_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); diff --git a/src/target/samd.c b/src/target/samd.c index f5d4282d..c3da3693 100644 --- a/src/target/samd.c +++ b/src/target/samd.c @@ -25,6 +25,7 @@ * * SAMD20E17A (rev C) * * SAMD20J18A (rev B) * * SAMD21J18A (rev B) + * * SAML21J17B (rev B) * * */ /* Refer to the SAM D20 Datasheet: @@ -41,7 +42,7 @@ static int samd_flash_erase(struct target_flash *t, target_addr addr, size_t len static int samd_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); -static bool samd_cmd_erase_all(target *t, int argc, const char **argv); +bool samd_cmd_erase_all(target *t, int argc, const char **argv); static bool samd_cmd_lock_flash(target *t, int argc, const char **argv); static bool samd_cmd_unlock_flash(target *t, int argc, const char **argv); static bool samd_cmd_unlock_bootprot(target *t, int argc, const char **argv); @@ -129,14 +130,16 @@ const struct command_s samd_cmd_list[] = { #define SAMD_STATUSB_PROT (1 << 16) /* Device Identification Register (DID) */ -#define SAMD_DID_MASK 0xFFBC0000 +#define SAMD_DID_MASK 0xFF3C0000 #define SAMD_DID_CONST_VALUE 0x10000000 -#define SAMD_DID_DEVSEL_MASK 0x0F +#define SAMD_DID_DEVSEL_MASK 0xFF #define SAMD_DID_DEVSEL_POS 0 #define SAMD_DID_REVISION_MASK 0x0F #define SAMD_DID_REVISION_POS 8 -#define SAMD_DID_SERIES_MASK 0x03 +#define SAMD_DID_SERIES_MASK 0x1F #define SAMD_DID_SERIES_POS 16 +#define SAMD_DID_FAMILY_MASK 0x1F +#define SAMD_DID_FAMILY_POS 23 /* Peripheral ID */ #define SAMD_PID_MASK 0x00F7FFFF @@ -145,6 +148,79 @@ const struct command_s samd_cmd_list[] = { /* Component ID */ #define SAMD_CID_VALUE 0xB105100D +/* Family parts */ +struct samd_part { + uint8_t devsel; + char pin; + uint8_t mem; + uint8_t variant; +}; + +static const struct samd_part samd_d21_parts[] = { + {0x00, 'J', 18, 'A'}, /* SAMD21J18A */ + {0x01, 'J', 17, 'A'}, /* SAMD21J17A */ + {0x02, 'J', 16, 'A'}, /* SAMD21J16A */ + {0x03, 'J', 15, 'A'}, /* SAMD21J15A */ + {0x05, 'G', 18, 'A'}, /* SAMD21G18A */ + {0x06, 'G', 17, 'A'}, /* SAMD21G17A */ + {0x07, 'G', 16, 'A'}, /* SAMD21G16A */ + {0x08, 'G', 15, 'A'}, /* SAMD21G15A */ + {0x0A, 'E', 18, 'A'}, /* SAMD21E18A */ + {0x0B, 'E', 17, 'A'}, /* SAMD21E17A */ + {0x0C, 'E', 16, 'A'}, /* SAMD21E16A */ + {0x0D, 'E', 15, 'A'}, /* SAMD21E15A */ + {0x0F, 'G', 18, 'A'}, /* SAMD21G18A (WLCSP) */ + {0x10, 'G', 17, 'A'}, /* SAMD21G17A (WLCSP) */ + {0x20, 'J', 16, 'B'}, /* SAMD21J16B */ + {0x21, 'J', 15, 'B'}, /* SAMD21J15B */ + {0x23, 'G', 16, 'B'}, /* SAMD21G16B */ + {0x24, 'G', 15, 'B'}, /* SAMD21G15B */ + {0x26, 'E', 16, 'B'}, /* SAMD21E16B */ + {0x27, 'E', 15, 'B'}, /* SAMD21E15B */ + {0x55, 'E', 16, 'B'}, /* SAMD21E16B (WLCSP) */ + {0x56, 'E', 15, 'B'}, /* SAMD21E15B (WLCSP) */ + {0x62, 'E', 16, 'C'}, /* SAMD21E16C (WLCSP) */ + {0x63, 'E', 15, 'C'}, /* SAMD21E15C (WLCSP) */ + {0xFF, 0, 0, 0} +}; + +static const struct samd_part samd_l21_parts[] = { + {0x00, 'J', 18, 'A'}, /* SAML21J18A */ + {0x01, 'J', 17, 'A'}, /* SAML21J17A */ + {0x02, 'J', 16, 'A'}, /* SAML21J16A */ + {0x05, 'G', 18, 'A'}, /* SAML21G18A */ + {0x06, 'G', 17, 'A'}, /* SAML21G17A */ + {0x07, 'G', 16, 'A'}, /* SAML21G16A */ + {0x0A, 'E', 18, 'A'}, /* SAML21E18A */ + {0x0B, 'E', 17, 'A'}, /* SAML21E17A */ + {0x0C, 'E', 16, 'A'}, /* SAML21E16A */ + {0x0D, 'E', 15, 'A'}, /* SAML21E15A */ + {0x0F, 'J', 18, 'B'}, /* SAML21J18B */ + {0x10, 'J', 17, 'B'}, /* SAML21J17B */ + {0x11, 'J', 16, 'B'}, /* SAML21J16B */ + {0x14, 'G', 18, 'B'}, /* SAML21G18B */ + {0x15, 'G', 17, 'B'}, /* SAML21G17B */ + {0x16, 'G', 16, 'B'}, /* SAML21G16B */ + {0x19, 'E', 18, 'B'}, /* SAML21E18B */ + {0x1A, 'E', 17, 'B'}, /* SAML21E17B */ + {0x1B, 'E', 16, 'B'}, /* SAML21E16B */ + {0x1C, 'E', 15, 'B'}, /* SAML21E15B */ + {0xFF, 0, 0, 0} +}; + +static const struct samd_part samd_l22_parts[] = { + {0x00, 'N', 18, 'A'}, /* SAML22N18 */ + {0x01, 'N', 17, 'A'}, /* SAML22N17 */ + {0x02, 'N', 16, 'A'}, /* SAML22N16 */ + {0x05, 'J', 18, 'A'}, /* SAML22J18 */ + {0x06, 'J', 17, 'A'}, /* SAML22J17 */ + {0x07, 'J', 16, 'A'}, /* SAML22J16 */ + {0x0A, 'G', 18, 'A'}, /* SAML22G18 */ + {0x0B, 'G', 17, 'A'}, /* SAML22G17 */ + {0x0C, 'G', 16, 'A'}, /* SAML22G16 */ + {0xFF, 0, 0, 0} +}; + /** * Reads the SAM D20 Peripheral ID */ @@ -178,8 +254,7 @@ uint32_t samd_read_cid(target *t) * Overloads the default cortexm reset function with a version that * removes the target from extended reset where required. */ -static void -samd_reset(target *t) +void samd_reset(target *t) { /** * SRST is not asserted here as it appears to reset the adiv5 @@ -273,7 +348,7 @@ samd20_revB_halt_resume(target *t, bool step) * function allows users to attach on a temporary basis so they can * rescue the device. */ -static bool samd_protected_attach(target *t) +bool samd_protected_attach(target *t) { /** * TODO: Notify the user that we're not really attached and @@ -293,17 +368,23 @@ static bool samd_protected_attach(target *t) * describing the SAM D device. */ struct samd_descr { + char family; uint8_t series; char revision; char pin; uint8_t mem; + char variant; char package[3]; }; struct samd_descr samd_parse_device_id(uint32_t did) { struct samd_descr samd; + uint8_t i = 0; + const struct samd_part *parts = samd_d21_parts; memset(samd.package, 0, 3); + uint8_t family = (did >> SAMD_DID_FAMILY_POS) + & SAMD_DID_FAMILY_MASK; uint8_t series = (did >> SAMD_DID_SERIES_POS) & SAMD_DID_SERIES_MASK; uint8_t revision = (did >> SAMD_DID_REVISION_POS) @@ -311,11 +392,24 @@ struct samd_descr samd_parse_device_id(uint32_t did) uint8_t devsel = (did >> SAMD_DID_DEVSEL_POS) & SAMD_DID_DEVSEL_MASK; + /* Family */ + switch (family) { + case 0: samd.family = 'D'; break; + case 1: samd.family = 'L'; parts = samd_l21_parts; break; + case 2: samd.family = 'C'; break; + } /* Series */ switch (series) { case 0: samd.series = 20; break; case 1: samd.series = 21; break; - case 2: samd.series = 10; break; + case 2: + if (family == 1) { + samd.series = 22; + parts = samd_l22_parts; + } else { + samd.series = 10; + } + break; case 3: samd.series = 11; break; } /* Revision */ @@ -323,7 +417,6 @@ struct samd_descr samd_parse_device_id(uint32_t did) switch (samd.series) { case 20: /* SAM D20 */ - case 21: /* SAM D21 */ switch (devsel / 5) { case 0: samd.pin = 'J'; break; case 1: samd.pin = 'G'; break; @@ -331,6 +424,20 @@ struct samd_descr samd_parse_device_id(uint32_t did) default: samd.pin = 'u'; break; } samd.mem = 18 - (devsel % 5); + samd.variant = 'A'; + break; + case 21: /* SAM D21/L21 */ + case 22: /* SAM L22 */ + i = 0; + while (parts[i].devsel != 0xFF) { + if (parts[i].devsel == devsel) { + samd.pin = parts[i].pin; + samd.mem = parts[i].mem; + samd.variant = parts[i].variant; + break; + } + i++; + } break; case 10: /* SAM D10 */ case 11: /* SAM D11 */ @@ -340,6 +447,7 @@ struct samd_descr samd_parse_device_id(uint32_t did) } samd.pin = 'D'; samd.mem = 14 - (devsel % 3); + samd.variant = 'A'; break; } @@ -389,15 +497,19 @@ bool samd_probe(target *t) /* Part String */ if (protected) { - snprintf(variant_string, sizeof(variant_string), - "Atmel SAMD%d%c%dA%s (rev %c) (PROT=1)", - samd.series, samd.pin, samd.mem, - samd.package, samd.revision); + sprintf(variant_string, + "Atmel SAM%c%d%c%d%c%s (rev %c) (PROT=1)", + samd.family, + samd.series, samd.pin, samd.mem, + samd.variant, + samd.package, samd.revision); } else { - snprintf(variant_string, sizeof(variant_string), - "Atmel SAMD%d%c%dA%s (rev %c)", - samd.series, samd.pin, samd.mem, - samd.package, samd.revision); + sprintf(variant_string, + "Atmel SAM%c%d%c%d%c%s (rev %c)", + samd.family, + samd.series, samd.pin, samd.mem, + samd.variant, + samd.package, samd.revision); } /* Setup Target */ @@ -524,7 +636,7 @@ static int samd_flash_write(struct target_flash *f, /** * Uses the Device Service Unit to erase the entire flash */ -static bool samd_cmd_erase_all(target *t, int argc, const char **argv) +bool samd_cmd_erase_all(target *t, int argc, const char **argv) { (void)argc; (void)argv; diff --git a/src/target/samx5x.c b/src/target/samx5x.c new file mode 100644 index 00000000..66f1fda9 --- /dev/null +++ b/src/target/samx5x.c @@ -0,0 +1,1107 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2019 Ken Healy + * + * 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 3 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 . + */ + +/* This file implements Microchip SAM D5x/E5x target specific functions + * for detecting the device, providing the XML memory map and Flash + * memory programming. + * + * Tested with + * * SAMD51G19A (rev A) + * * SAMD51J19A (rev A) + * * + */ +/* Refer to the SAM D5x/E5x Datasheet: + * http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf + * particularly Sections 12. DSU and 25. NVMCTRL + */ + +#include + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +static int samx5x_flash_erase(struct target_flash *t, target_addr addr, + size_t len); +static int samx5x_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len); +static bool samx5x_cmd_lock_flash(target *t, int argc, const char **argv); +static bool samx5x_cmd_unlock_flash(target *t, int argc, const char **argv); +static bool samx5x_cmd_unlock_bootprot(target *t, int argc, const char **argv); +static bool samx5x_cmd_lock_bootprot(target *t, int argc, const char **argv); +static bool samx5x_cmd_read_userpage(target *t, int argc, const char **argv); +static bool samx5x_cmd_serial(target *t, int argc, const char **argv); +static bool samx5x_cmd_ssb(target *t, int argc, const char **argv); +static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv); + +/* (The SAM D1x/2x implementation of erase_all is reused as it's identical)*/ +extern bool samd_cmd_erase_all(target *t, int argc, const char **argv); +#define samx5x_cmd_erase_all samd_cmd_erase_all + + +#ifdef SAMX5X_EXTRA_CMDS +static bool samx5x_cmd_mbist(target *t, int argc, const char **argv); +static bool samx5x_cmd_write8(target *t, int argc, const char **argv); +static bool samx5x_cmd_write16(target *t, int argc, const char **argv); +static bool samx5x_cmd_write32(target *t, int argc, const char **argv); +#endif + +const struct command_s samx5x_cmd_list[] = { + {"erase_mass", (cmd_handler)samx5x_cmd_erase_all, + "Erase entire flash memory"}, + {"lock_flash", (cmd_handler)samx5x_cmd_lock_flash, + "Locks flash against spurious commands"}, + {"unlock_flash", (cmd_handler)samx5x_cmd_unlock_flash, + "Unlocks flash"}, + {"lock_bootprot", (cmd_handler)samx5x_cmd_lock_bootprot, + "Lock the boot protections to maximum"}, + {"unlock_bootprot", (cmd_handler)samx5x_cmd_unlock_bootprot, + "Unlock the boot protections to minimum"}, + {"user_page", (cmd_handler)samx5x_cmd_read_userpage, + "Prints user page from flash"}, + {"serial", (cmd_handler)samx5x_cmd_serial, + "Prints serial number"}, + {"set_security_bit", (cmd_handler)samx5x_cmd_ssb, + "Sets the security bit"}, + {"update_user_word", (cmd_handler)samx5x_cmd_update_user_word, + "Sets 32-bits in the user page: "}, +#ifdef SAMX5X_EXTRA_CMDS + {"mbist", (cmd_handler)samx5x_cmd_mbist, + "Runs the built-in memory test"}, + {"write8", (cmd_handler)samx5x_cmd_write8, + "Writes an 8-bit word: write8 "}, + {"write16", (cmd_handler)samx5x_cmd_write16, + "Writes a 16-bit word: write16 "}, + {"write32", (cmd_handler)samx5x_cmd_write32, + "Writes a 32-bit word: write32 "}, +#endif + {NULL, NULL, NULL} +}; + +const struct command_s samx5x_protected_cmd_list[] = { + {"erase_mass", (cmd_handler)samx5x_cmd_erase_all, + "Erase entire flash memory"}, + {NULL, NULL, NULL} +}; + +/* RAM Parameters */ +#define SAMX5X_RAM_START 0x20000000 + +/* Non-Volatile Memory Controller (NVMC) Parameters */ +#define SAMX5X_PAGE_SIZE 512 +#define SAMX5X_BLOCK_SIZE (SAMX5X_PAGE_SIZE * 16) + +/* -------------------------------------------------------------------------- */ +/* Non-Volatile Memory Controller (NVMC) Registers */ +/* -------------------------------------------------------------------------- */ + +#define SAMX5X_NVMC 0x41004000 +#define SAMX5X_NVMC_CTRLA (SAMX5X_NVMC + 0x0) +#define SAMX5X_NVMC_CTRLB (SAMX5X_NVMC + 0x04) +#define SAMX5X_NVMC_PARAM (SAMX5X_NVMC + 0x08) +#define SAMX5X_NVMC_INTFLAG (SAMX5X_NVMC + 0x10) +#define SAMX5X_NVMC_STATUS (SAMX5X_NVMC + 0x12) +#define SAMX5X_NVMC_ADDRESS (SAMX5X_NVMC + 0x14) +#define SAMX5X_NVMC_RUNLOCK (SAMX5X_NVMC + 0x18) + +/* Control B Register (CTRLB) */ +#define SAMX5X_CTRLB_CMD_KEY 0xA500 +#define SAMX5X_CTRLB_CMD_ERASEPAGE 0x0000 +#define SAMX5X_CTRLB_CMD_ERASEBLOCK 0x0001 +#define SAMX5X_CTRLB_CMD_WRITEPAGE 0x0003 +#define SAMX5X_CTRLB_CMD_WRITEQUADWORD 0x0004 +#define SAMX5X_CTRLB_CMD_LOCK 0x0011 +#define SAMX5X_CTRLB_CMD_UNLOCK 0x0012 +#define SAMX5X_CTRLB_CMD_PAGEBUFFERCLEAR 0x0015 +#define SAMX5X_CTRLB_CMD_SSB 0x0016 + +/* Interrupt Flag Register (INTFLAG) */ +#define SAMX5X_INTFLAG_DONE (1 << 0) +#define SAMX5X_INTFLAG_ADDRE (1 << 1) +#define SAMX5X_INTFLAG_PROGE (1 << 2) +#define SAMX5X_INTFLAG_LOCKE (1 << 3) +#define SAMX5X_INTFLAG_ECCSE (1 << 4) +#define SAMX5X_INTFLAG_ECCDE (1 << 5) +#define SAMX5X_INTFLAG_NVME (1 << 6) +#define SAMX5X_INTFLAG_SUSP (1 << 7) +#define SAMX5X_INTFLAG_SEESFULL (1 << 8) +#define SAMX5X_INTFLAG_SEESOVF (1 << 9) + +/* Status Register (STATUS) */ +#define SAMX5X_STATUS_READY (1 << 0) + +/* Non-Volatile Memory Calibration and Auxiliary Registers */ +#define SAMX5X_NVM_USER_PAGE 0x00804000 +#define SAMX5X_NVM_CALIBRATION 0x00800000 +#define SAMX5X_NVM_SERIAL(n) (0x0080600C + \ + (n == 0 ? 0x1F0 : n * 4)) + +#define SAMX5X_USER_PAGE_OFFSET_LOCK 0x08 +#define SAMX5X_USER_PAGE_OFFSET_BOOTPROT 0x03 +#define SAMX5X_USER_PAGE_MASK_BOOTPROT 0x3C +#define SAMX5X_USER_PAGE_SHIFT_BOOTPROT 2 + +/* -------------------------------------------------------------------------- */ +/* Device Service Unit (DSU) Registers */ +/* -------------------------------------------------------------------------- */ + +#define SAMX5X_DSU 0x41002000 +#define SAMX5X_DSU_EXT_ACCESS (SAMX5X_DSU + 0x100) +#define SAMX5X_DSU_CTRLSTAT (SAMX5X_DSU_EXT_ACCESS + 0x00) +#define SAMX5X_DSU_ADDRESS (SAMX5X_DSU_EXT_ACCESS + 0x04) +#define SAMX5X_DSU_LENGTH (SAMX5X_DSU_EXT_ACCESS + 0x08) +#define SAMX5X_DSU_DATA (SAMX5X_DSU_EXT_ACCESS + 0x0C) +#define SAMX5X_DSU_DID (SAMX5X_DSU_EXT_ACCESS + 0x18) +#define SAMX5X_DSU_PID(n) (SAMX5X_DSU + 0x1FE0 + \ + (0x4 * (n % 4)) - \ + (0x10 * (n / 4))) +#define SAMX5X_DSU_CID(n) (SAMX5X_DSU + 0x1FF0 + \ + (0x4 * (n % 4))) + +/* Control and Status Register (CTRLSTAT) */ +#define SAMX5X_CTRL_CHIP_ERASE (1 << 4) +#define SAMX5X_CTRL_MBIST (1 << 3) +#define SAMX5X_CTRL_CRC (1 << 2) +#define SAMX5X_STATUSA_PERR (1 << 12) +#define SAMX5X_STATUSA_FAIL (1 << 11) +#define SAMX5X_STATUSA_BERR (1 << 10) +#define SAMX5X_STATUSA_CRSTEXT (1 << 9) +#define SAMX5X_STATUSA_DONE (1 << 8) +#define SAMX5X_STATUSB_PROT (1 << 16) + + +/* Device Identification Register (DID) + + Bits 31-17 + + SAME54 0110 0001 1000 0100 + SAME53 0110 0001 1000 0011 + SAME51 0110 0001 1000 0001 + SAMD51 0110 0000 0000 0110 + + Common + mask 1111 1110 0111 1000 + + Masked common + value 0110 0000 0000 0000 == 0x6000 +*/ + +#define SAMX5X_DID_MASK 0xFE780000 +#define SAMX5X_DID_CONST_VALUE 0x60000000 +#define SAMX5X_DID_DEVSEL_MASK 0xFF +#define SAMX5X_DID_DEVSEL_POS 0 +#define SAMX5X_DID_REVISION_MASK 0x0F +#define SAMX5X_DID_REVISION_POS 8 +#define SAMX5X_DID_SERIES_MASK 0x3F +#define SAMX5X_DID_SERIES_POS 16 + +/* Peripheral ID */ +#define SAMX5X_PID_MASK 0x00F7FFFF +#define SAMX5X_PID_CONST_VALUE 0x0001FCD0 + +/* Component ID */ +#define SAMX5X_CID_VALUE 0xB105100D + +/** + * Reads the SAM D5x/E5x Peripheral ID + * + * (Reuses the SAM D1x/2x implementation as it is identical) + */ +extern uint64_t samd_read_pid(target *t); +#define samx5x_read_pid samd_read_pid + +/** + * Reads the SAM D5x/E5x Component ID + * + * (Reuses the SAM D1x/2x implementation as it is identical) + */ +extern uint32_t samd_read_cid(target *t); +#define samx5x_read_cid samd_read_cid + + +/** + * Overloads the default cortexm reset function with a version that + * removes the target from extended reset where required. + * + * (Reuses the SAM D1x/2x implementation as it is identical) + */ +extern void samd_reset(target *t); +#define samx5x_reset samd_reset + +/** + * Overload the default cortexm attach for when the samd is protected. + * + * If the samd is protected then the default cortexm attach will + * fail as the S_HALT bit in the DHCSR will never go high. This + * function allows users to attach on a temporary basis so they can + * rescue the device. + * + * (Reuses the SAM D1x/2x implementation as it is identical) + */ +extern bool samd_protected_attach(target *t); +#define samx5x_protected_attach samd_protected_attach + +/** + * Use the DSU Device Indentification Register to populate a struct + * describing the SAM D device. + */ +struct samx5x_descr { + char series_letter; + uint8_t series_number; + char revision; + char pin; + uint8_t mem; + char package[3]; +}; +struct samx5x_descr samx5x_parse_device_id(uint32_t did) +{ + struct samx5x_descr samd; + memset(samd.package, 0, 3); + + uint8_t series = (did >> SAMX5X_DID_SERIES_POS) + & SAMX5X_DID_SERIES_MASK; + uint8_t revision = (did >> SAMX5X_DID_REVISION_POS) + & SAMX5X_DID_REVISION_MASK; + uint8_t devsel = (did >> SAMX5X_DID_DEVSEL_POS) + & SAMX5X_DID_DEVSEL_MASK; + + /* Series */ + switch (series) { + case 1: + samd.series_letter = 'E'; + samd.series_number = 51; + break; + case 6: + samd.series_letter = 'D'; + samd.series_number = 51; + break; + case 3: + samd.series_letter = 'E'; + samd.series_number = 53; + break; + case 4: + samd.series_letter = 'E'; + samd.series_number = 54; + break; + } + /* Revision */ + samd.revision = 'A' + revision; + + switch(devsel) { + case 0: + samd.pin = 'P'; + samd.mem = 20; + break; + case 1: + samd.pin = 'P'; + samd.mem = 19; + break; + case 2: + samd.pin = 'N'; + samd.mem = 20; + break; + case 3: + samd.pin = 'N'; + samd.mem = 19; + break; + case 4: + samd.pin = 'J'; + samd.mem = 20; + break; + case 5: + samd.pin = 'J'; + samd.mem = 19; + break; + case 6: + samd.pin = 'J'; + samd.mem = 18; + break; + case 7: + samd.pin = 'G'; + samd.mem = 19; + break; + case 8: + samd.pin = 'G'; + samd.mem = 18; + break; + } + + return samd; +} + +static void samx5x_add_flash(target *t, uint32_t addr, size_t length, + size_t erase_block_size, size_t write_page_size) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + if (!f) { /* calloc failed: heap exhaustion */ + DEBUG("calloc: failed in %s\n", __func__); + return; + } + + f->start = addr; + f->length = length; + f->blocksize = erase_block_size; + f->erase = samx5x_flash_erase; + f->write = samx5x_flash_write; + f->buf_size = write_page_size; + target_add_flash(t, f); +} + +char variant_string[60]; +bool samx5x_probe(target *t) +{ + uint32_t cid = samx5x_read_cid(t); + uint32_t pid = samx5x_read_pid(t); + + /* Check the ARM Coresight Component and Perhiperal IDs */ + if ((cid != SAMX5X_CID_VALUE) || + ((pid & SAMX5X_PID_MASK) != SAMX5X_PID_CONST_VALUE)) + return false; + + /* Read the Device ID */ + uint32_t did = target_mem_read32(t, SAMX5X_DSU_DID); + + /* If the Device ID matches */ + if ((did & SAMX5X_DID_MASK) != SAMX5X_DID_CONST_VALUE) + return false; + + uint32_t ctrlstat = target_mem_read32(t, SAMX5X_DSU_CTRLSTAT); + struct samx5x_descr samx5x = samx5x_parse_device_id(did); + + /* Protected? */ + bool protected = (ctrlstat & SAMX5X_STATUSB_PROT); + + /* Part String */ + if (protected) { + snprintf(variant_string, sizeof(variant_string), + "Microchip SAM%c%d%c%dA (rev %c) (PROT=1)", + samx5x.series_letter, samx5x.series_number, + samx5x.pin, samx5x.mem, samx5x.revision); + } else { + snprintf(variant_string, sizeof(variant_string), + "Microchip SAM%c%d%c%dA (rev %c)", + samx5x.series_letter, samx5x.series_number, + samx5x.pin, samx5x.mem, samx5x.revision); + } + + /* Setup Target */ + t->driver = variant_string; + t->reset = samx5x_reset; + + if (protected) { + /** + * Overload the default cortexm attach + * for when the samx5x is protected. + * This function allows users to + * attach on a temporary basis so they + * can rescue the device. + */ + t->attach = samx5x_protected_attach; + } + switch(samx5x.mem) { + default: + case 18: + target_add_ram(t, 0x20000000, 0x20000); + samx5x_add_flash(t, 0x00000000, 0x40000, + SAMX5X_BLOCK_SIZE, + SAMX5X_PAGE_SIZE); + break; + case 19: + target_add_ram(t, 0x20000000, 0x30000); + samx5x_add_flash(t, 0x00000000, 0x80000, + SAMX5X_BLOCK_SIZE, + SAMX5X_PAGE_SIZE); + break; + case 20: + target_add_ram(t, 0x20000000, 0x40000); + samx5x_add_flash(t, 0x00000000, 0x100000, + SAMX5X_BLOCK_SIZE, + SAMX5X_PAGE_SIZE); + break; + } + + if (protected) + target_add_commands(t, samx5x_protected_cmd_list, + "SAMD5x/E5x (protected)"); + else + target_add_commands(t, samx5x_cmd_list, "SAMD5x/E5x"); + + /* If we're not in reset here */ + if (!platform_srst_get_val()) { + /* We'll have to release the target from + * extended reset to make attach possible */ + if (target_mem_read32(t, SAMX5X_DSU_CTRLSTAT) & + SAMX5X_STATUSA_CRSTEXT) { + + /* Write bit to clear from extended reset */ + target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, + SAMX5X_STATUSA_CRSTEXT); + } + } + + return true; +} + +/** + * Temporary (until next reset) flash memory locking / unlocking + */ +static void samx5x_lock_current_address(target *t) +{ + /* Issue the unlock command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | SAMX5X_CTRLB_CMD_LOCK); +} +static void samx5x_unlock_current_address(target *t) +{ + /* Issue the unlock command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | SAMX5X_CTRLB_CMD_UNLOCK); +} + +/** + * Check for NVM errors and print debug messages + */ +static void samx5x_print_nvm_error(uint16_t errs) +{ + if (errs & SAMX5X_INTFLAG_ADDRE) { + DEBUG(" ADDRE"); + } + if (errs & SAMX5X_INTFLAG_PROGE) { + DEBUG(" PROGE"); + } + if (errs & SAMX5X_INTFLAG_LOCKE) { + DEBUG(" LOCKE"); + } + if (errs & SAMX5X_INTFLAG_NVME) { + DEBUG(" NVME"); + } + + DEBUG("\n"); +} + +static int samx5x_read_nvm_error(target *t) +{ + uint16_t intflag = target_mem_read16(t, SAMX5X_NVMC_INTFLAG); + + return intflag & (SAMX5X_INTFLAG_ADDRE | SAMX5X_INTFLAG_PROGE | + SAMX5X_INTFLAG_LOCKE | SAMX5X_INTFLAG_NVME); +} + +static void samx5x_clear_nvm_error(target *t) +{ + target_mem_write16(t, SAMX5X_NVMC_INTFLAG, + SAMX5X_INTFLAG_ADDRE | SAMX5X_INTFLAG_PROGE | + SAMX5X_INTFLAG_LOCKE | SAMX5X_INTFLAG_NVME); +} + +static int samx5x_check_nvm_error(target *t) +{ + uint16_t errs = samx5x_read_nvm_error(t); + + if (!errs) + return 0; + + DEBUG("NVM error(s) detected:"); + samx5x_print_nvm_error(errs); + return -1; +} + +#define NVM_ERROR_BITS_MSG \ + "Warning: Found NVM error bits set while preparing to %s\n" \ + " flash block at 0x%08"PRIx32" (length 0x%zx).\n" \ + " Clearing these before proceeding:\n" \ + " " + +/** + * Erase flash block by block + */ +static int samx5x_flash_erase(struct target_flash *f, target_addr addr, + size_t len) +{ + target *t = f->t; + uint16_t errs = samx5x_read_nvm_error(t); + if (errs) { + DEBUG(NVM_ERROR_BITS_MSG, "erase", addr, len); + samx5x_print_nvm_error(errs); + samx5x_clear_nvm_error(t); + } + + /* Check if the bootprot or region lock settings + * are going to prevent erasing flash. */ + uint32_t bootprot, runlock, flash_size, lock_region_size; + bootprot = (target_mem_read16(t, SAMX5X_NVMC_STATUS) >> 8) & 0xf; + runlock = target_mem_read32(t, SAMX5X_NVMC_RUNLOCK); + flash_size = (target_mem_read32(t, SAMX5X_NVMC_PARAM) & 0xffff) * + SAMX5X_PAGE_SIZE; + lock_region_size = flash_size >> 5; + + if (addr < (15 - bootprot) * 8192) + return -1; + + if (~runlock & (1 << addr / lock_region_size)) + return -1; + + while (len) { + target_mem_write32(t, SAMX5X_NVMC_ADDRESS, addr); + + /* Unlock */ + samx5x_unlock_current_address(t); + + /* Issue the erase command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | + SAMX5X_CTRLB_CMD_ERASEBLOCK); + + /* Poll for NVM Ready */ + while ((target_mem_read32(t, SAMX5X_NVMC_STATUS) & + SAMX5X_STATUS_READY) == 0) + if (target_check_error(t) || samx5x_check_nvm_error(t)) + return -1; + + if (target_check_error(t) || samx5x_check_nvm_error(t)) + return -1; + + /* Lock */ + samx5x_lock_current_address(t); + + addr += f->blocksize; + len -= f->blocksize; + } + + return 0; +} + +/** + * Write flash page by page + */ +static int samx5x_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len) +{ + target *t = f->t; + bool error = false; + uint16_t errs = samx5x_read_nvm_error(t); + if (errs) { + DEBUG(NVM_ERROR_BITS_MSG, "write", dest, len); + samx5x_print_nvm_error(errs); + samx5x_clear_nvm_error(t); + } + + /* Unlock */ + target_mem_write32(t, SAMX5X_NVMC_ADDRESS, dest); + samx5x_unlock_current_address(t); + + /* Write within a single page. This may be part or all of the page */ + target_mem_write(t, dest, src, len); + + /* Issue the write page command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | SAMX5X_CTRLB_CMD_WRITEPAGE); + + /* Poll for NVM Ready */ + while ((target_mem_read32(t, SAMX5X_NVMC_STATUS) & + SAMX5X_STATUS_READY) == 0) + if (target_check_error(t) || samx5x_check_nvm_error(t)) { + error = true; + break; + } + + if (error || target_check_error(t) || samx5x_check_nvm_error(t)) { + DEBUG("Error writing flash page at 0x%08"PRIx32 + " (len 0x%08zx)\n", + dest, len); + return -1; + } + + /* Lock */ + samx5x_lock_current_address(t); + + return 0; +} + +/** + * Erase and write the NVM user page + */ +static int samx5x_write_user_page(target *t, uint8_t *buffer) +{ + uint16_t errs = samx5x_read_nvm_error(t); + if (errs) { + DEBUG(NVM_ERROR_BITS_MSG, "erase and write", + (uint32_t)SAMX5X_NVM_USER_PAGE, + (size_t)SAMX5X_PAGE_SIZE); + samx5x_print_nvm_error(errs); + samx5x_clear_nvm_error(t); + } + + /* Erase the user page */ + target_mem_write32(t, SAMX5X_NVMC_ADDRESS, SAMX5X_NVM_USER_PAGE); + /* Issue the erase command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | SAMX5X_CTRLB_CMD_ERASEPAGE); + + /* Poll for NVM Ready */ + while ((target_mem_read32(t, SAMX5X_NVMC_STATUS) & + SAMX5X_STATUS_READY) == 0) + if (target_check_error(t) || samx5x_check_nvm_error(t)) + return -1; + + /* Write back */ + for (uint32_t offset = 0; offset < SAMX5X_PAGE_SIZE; offset += 16) { + target_mem_write(t, SAMX5X_NVM_USER_PAGE + offset, + buffer + offset, 16); + + /* Issue the write page command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | + SAMX5X_CTRLB_CMD_WRITEQUADWORD); + + /* Poll for NVM Ready */ + while ((target_mem_read32(t, SAMX5X_NVMC_STATUS) & + SAMX5X_STATUS_READY) == 0) + if (target_check_error(t) || + samx5x_check_nvm_error(t)) + return -2; + } + return 0; +} + +static int samx5x_update_user_word(target *t, uint32_t addr, uint32_t value, + uint32_t *value_written, bool force) +{ + uint8_t factory_bits[] = { + /* 0 8 16 24 32 40 48 56 */ + 0x00, 0x80, 0xFF, 0xC3, 0x00, 0xFF, 0x00, 0x80, + + /*64 72 80 88 96 104 112 120 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /*128 136 144 152 */ + 0xFF, 0xFF, 0xFF, 0xFF }; + + uint8_t buffer[SAMX5X_PAGE_SIZE]; + uint32_t current_word, new_word; + + target_mem_read(t, buffer, SAMX5X_NVM_USER_PAGE, SAMX5X_PAGE_SIZE); + memcpy(¤t_word, buffer + addr, 4); + + uint32_t factory_word = 0; + + for (int i = 0; !force && i < 4 && addr + i < 20; i++) + factory_word |= (uint32_t)factory_bits[addr + i] << (i * 8); + + new_word = current_word & factory_word; + new_word |= value & ~factory_word; + if (value_written != NULL) + *value_written = new_word; + + if (new_word != current_word) { + DEBUG("Writing user page word 0x%08"PRIx32 + " at offset 0x%03"PRIx32"\n", new_word, addr); + memcpy(buffer + addr, &new_word, 4); + return samx5x_write_user_page(t, buffer); + } + else { + DEBUG("Skipping user page write as no change would be made"); + } + + return 0; +} + +/** + * Sets the NVM region lock bits in the User Page. This value is read + * at startup as the default value for the lock bits, and hence does + * not take effect until a reset. + * + * 0x00000000 = Lock, 0xFFFFFFFF = Unlock (default) + */ +static int samx5x_set_flashlock(target *t, uint32_t value) +{ + uint8_t buffer[SAMX5X_PAGE_SIZE]; + target_mem_read(t, buffer, SAMX5X_NVM_USER_PAGE, SAMX5X_PAGE_SIZE); + + uint32_t current_value; + memcpy(¤t_value, buffer + SAMX5X_USER_PAGE_OFFSET_LOCK, 4); + + if (value != current_value) + return samx5x_update_user_word(t, SAMX5X_USER_PAGE_OFFSET_LOCK, + value, NULL, false); + + return 0; +} + +static bool samx5x_cmd_lock_flash(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (samx5x_set_flashlock(t, 0x00000000)) { + tc_printf(t, "Error writing NVM page\n"); + return false; + } + tc_printf(t, "Flash locked. The target must be reset for " + "this to take effect.\n"); + return true; +} + +static bool samx5x_cmd_unlock_flash(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (samx5x_set_flashlock(t, 0xFFFFFFFF)) { + tc_printf(t, "Error writing NVM page\n"); + return false; + } + tc_printf(t, "Flash unlocked. The target must be reset for " + "this to take effect.\n"); + return true; +} + +/** + * Sets the BOOTPROT bits in the User Page. This value is read at + * startup as the default value for BOOTPROT, and hence does not + * take effect until a reset. + * + * Size of protected region at beginning of flash: + * (15 - BOOTPROT) * 8192 + */ +static int samx5x_set_bootprot(target *t, uint8_t value) +{ + uint8_t buffer[SAMX5X_PAGE_SIZE]; + target_mem_read(t, buffer, SAMX5X_NVM_USER_PAGE, SAMX5X_PAGE_SIZE); + + uint32_t current_value, new_value; + memcpy(¤t_value, buffer + SAMX5X_USER_PAGE_OFFSET_BOOTPROT, 4); + + new_value = current_value & ~SAMX5X_USER_PAGE_MASK_BOOTPROT; + new_value |= (value << SAMX5X_USER_PAGE_SHIFT_BOOTPROT) & + SAMX5X_USER_PAGE_MASK_BOOTPROT; + + if (new_value != current_value) + return samx5x_update_user_word(t, + SAMX5X_USER_PAGE_OFFSET_BOOTPROT, + new_value, NULL, false); + + return 0; +} + +static bool samx5x_cmd_lock_bootprot(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (samx5x_set_bootprot(t, 0)) { + tc_printf(t, "Error writing NVM page\n"); + return false; + } + tc_printf(t, "Bootprot locked. The target must be reset for " + "this to take effect.\n"); + return true; +} + +static bool samx5x_cmd_unlock_bootprot(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (samx5x_set_bootprot(t, 0xf)) { + tc_printf(t, "Error writing NVM page\n"); + return false; + } + tc_printf(t, "Bootprot unlocked. The target must be reset for " + "this to take effect.\n"); + return true; +} + +static bool samx5x_cmd_read_userpage(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + uint8_t buffer[SAMX5X_PAGE_SIZE]; + int i = 0; + + target_mem_read(t, buffer, SAMX5X_NVM_USER_PAGE, SAMX5X_PAGE_SIZE); + + tc_printf(t, "User Page:\n"); + while (i < SAMX5X_PAGE_SIZE) { + tc_printf(t, "%02x%c", buffer[i], + (i + 1) % 16 == 0 ? '\n' : ' '); + i++; + } + return true; +} + +/** + * Reads the 128-bit serial number from the NVM + */ +static bool samx5x_cmd_serial(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + tc_printf(t, "Serial Number: 0x"); + + for (uint32_t i = 0; i < 4; i++) { + tc_printf(t, "%08x", + target_mem_read32(t, SAMX5X_NVM_SERIAL(i))); + } + + tc_printf(t, "\n"); + + return true; +} + +/** + * Sets the security bit + */ +static bool samx5x_cmd_ssb(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* Issue the ssb command */ + target_mem_write32(t, SAMX5X_NVMC_CTRLB, + SAMX5X_CTRLB_CMD_KEY | SAMX5X_CTRLB_CMD_SSB); + + /* Poll for NVM Ready */ + while ((target_mem_read32(t, SAMX5X_NVMC_STATUS) & + SAMX5X_STATUS_READY) == 0) + if (target_check_error(t)) + return -1; + + tc_printf(t, "Set the security bit! " + "You will need to issue 'monitor erase_mass' " + "to clear this.\n"); + + return true; +} + +#define FACTORY_BITS_MSG \ + "Warning: the value provided would have modified factory\n" \ + " setting bits that should not be changed. The\n" \ + " actual value written was: 0x%08"PRIx32"\n" \ + "To override this protection to write the factory setting\n" \ + "bits, use: update_user_word force\n" + +/** + * Updates a 32-bit word in the NVM user page. Factory setting bits are + * not modified unless the "force" argument is provided. + */ +static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv) +{ + if (argc < 3 || argc > 4) { + tc_printf(t, "Error: incorrect number of arguments\n"); + return false; + } + + char *addr_end, *value_end; + uint32_t addr = strtoul(argv[1], &addr_end, 0); + uint32_t value = strtoul(argv[2], &value_end, 0); + uint32_t actual_value; + + if (addr_end == argv[1] || (!addr && *(addr_end - 1) != '0') || + value_end == argv[2] || (!value && *(value_end - 1) != '0') || + (argc == 4 && strcmp(argv[3], "force"))) { + tc_printf(t, "Error: unrecognized arguments\n"); + return false; + } + + if (addr > 0x1fc) { + tc_printf(t, "Error: address out of range. " + "User page is 512 bytes.\n"); + return false; + } + + if (samx5x_update_user_word(t, addr, value, &actual_value, argc == 4)) { + tc_printf(t, "Error updating NVM page\n"); + return false; + } + + if (argc != 4 && value != actual_value) + tc_printf(t, FACTORY_BITS_MSG, actual_value); + + tc_printf(t, "User page updated."); + if (addr < 12) + tc_printf(t, + " The target must be reset for the new config " + "settings\n" + "(bootprot, wdt, etc.) to take effect."); + tc_printf(t, "\n"); + + return true; +} + +#ifdef SAMX5X_EXTRA_CMDS + +/** + * Returns the size (in bytes) of the RAM. + */ +static uint32_t samx5x_ram_size(target *t) +{ + /* Read the Device ID */ + uint32_t did = target_mem_read32(t, SAMX5X_DSU_DID); + + /* Mask off the device select bits */ + struct samx5x_descr samx5x = samx5x_parse_device_id(did); + + /* Adjust the maximum ram size (256KB) down as appropriate */ + return (0x40000 - 0x10000 * (20 - samx5x.mem)); +} + +/** + * Runs the Memory Built In Self Test (MBIST) + */ +static bool samx5x_cmd_mbist(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + DEBUG("Running MBIST for memory range 0x%08x-%08"PRIx32"\n", + SAMX5X_RAM_START, samx5x_ram_size(t)); + + /* Write the memory parameters to the DSU + * Note that the two least significant bits of the address are + * the access mode, so the actual starting address should be + * left shifted by 2 + * + * Similarly, the length must also be left shifted by 2 as the + * two least significant bits of that register are unused */ + target_mem_write32(t, SAMX5X_DSU_ADDRESS, SAMX5X_RAM_START); + target_mem_write32(t, SAMX5X_DSU_LENGTH, samx5x_ram_size(t) << 2); + + /* Clear the fail and protection error bits */ + target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_STATUSA_FAIL | + SAMX5X_STATUSA_PERR); + + /* Write the MBIST command */ + target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_MBIST); + + /* Poll for DSU Ready */ + uint32_t status; + while (((status = target_mem_read32(t, SAMX5X_DSU_CTRLSTAT)) & + (SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR | + SAMX5X_STATUSA_FAIL)) == 0) + if (target_check_error(t)) + return false; + + /* Test the protection error bit in Status A */ + if (status & SAMX5X_STATUSA_PERR) { + tc_printf(t, "MBIST not run due to protection error.\n"); + return true; + } + + /* Test the fail bit in Status A */ + if (status & SAMX5X_STATUSA_FAIL) { + uint32_t data = target_mem_read32(t, SAMX5X_DSU_DATA); + tc_printf(t, "MBIST Fail @ 0x%08x (bit %d in phase %d)\n", + target_mem_read32(t, SAMX5X_DSU_ADDRESS), + data & 0x1f, data >> 8); + } else { + tc_printf(t, "MBIST Passed!\n"); + } + + return true; +} + +/** + * Writes an 8-bit word to the specified address + */ +static bool samx5x_cmd_write8(target *t, int argc, const char **argv) +{ + if (argc != 3) { + tc_printf(t, "Error: incorrect number of arguments\n"); + return false; + } + + char *addr_end, *value_end; + uint32_t addr = strtoul(argv[1], &addr_end, 0); + uint32_t value = strtoul(argv[2], &value_end, 0); + + if (addr_end == argv[1] || (!addr && *(addr_end - 1) != '0') || + value_end == argv[2] || (!value && *(value_end - 1) != '0')) { + tc_printf(t, "Error: unrecognized arguments\n"); + return false; + } + + if (value > 0xff) { + tc_printf(t, "Error: value out of range\n"); + return false; + } + + DEBUG("Writing 8-bit value 0x%02"PRIx32" at address 0x%08"PRIx32"\n", + value, addr); + target_mem_write8(t, addr, (uint8_t)value); + + return true; +} + +/** + * Writes a 16-bit word to the specified address + */ +static bool samx5x_cmd_write16(target *t, int argc, const char **argv) +{ + if (argc != 3) { + tc_printf(t, "Error: incorrect number of arguments\n"); + return false; + } + + char *addr_end, *value_end; + uint32_t addr = strtoul(argv[1], &addr_end, 0); + uint32_t value = strtoul(argv[2], &value_end, 0); + + if (addr_end == argv[1] || (!addr && *(addr_end - 1) != '0') || + value_end == argv[2] || (!value && *(value_end - 1) != '0')) { + tc_printf(t, "Error: unrecognized arguments\n"); + return false; + } + + if (value > 0xffff) { + tc_printf(t, "Error: value out of range\n"); + return false; + } + + DEBUG("Writing 16-bit value 0x%04"PRIx32" at address 0x%08"PRIx32"\n", + value, addr); + target_mem_write16(t, addr, (uint16_t)value); + + return true; +} + +/** + * Writes a 32-bit word to the specified address + */ +static bool samx5x_cmd_write32(target *t, int argc, const char **argv) +{ + if (argc != 3) { + tc_printf(t, "Error: incorrect number of arguments\n"); + return false; + } + + char *addr_end, *value_end; + uint32_t addr = strtoul(argv[1], &addr_end, 0); + uint32_t value = strtoul(argv[2], &value_end, 0); + + if (addr_end == argv[1] || (!addr && *(addr_end - 1) != '0') || + value_end == argv[2] || (!value && *(value_end - 1) != '0')) { + tc_printf(t, "Error: unrecognized arguments\n"); + return false; + } + + DEBUG("Writing 32-bit value 0x%08"PRIx32" at address 0x%08"PRIx32"\n", + value, addr); + target_mem_write32(t, addr, value); + + return true; +} + +#endif diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 69981f3f..15a47293 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -178,6 +178,7 @@ bool sam3x_probe(target *t); bool sam4l_probe(target *t); bool nrf51_probe(target *t); bool samd_probe(target *t); +bool samx5x_probe(target *t); bool kinetis_probe(target *t); bool efm32_probe(target *t); bool msp432_probe(target *t);