From d3979a57b6f9da24abc31548a3e217a8c265486f Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 12 Apr 2017 11:15:50 -0400 Subject: [PATCH 01/26] Add LPC command to read out unique ID from target. This commit modifies lpc_iap_call() to work with IAP commands that return additional data. If the "result" argument is non-null, 16 bytes of data (the maximum returned by any IAP command) are copied to the specified address. --- src/target/lpc11xx.c | 23 +++++++++++++++++++++++ src/target/lpc15xx.c | 21 +++++++++++++++++++++ src/target/lpc43xx.c | 8 ++++---- src/target/lpc_common.c | 24 +++++++++++++++--------- src/target/lpc_common.h | 3 ++- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index 30143bac..47226ca0 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: @@ -109,6 +130,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 +139,7 @@ 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 0x0003D440: /* LPC11U34/311 */ case 0x0001cc40: /* LPC11U34/421 */ 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..c7331382 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 = { @@ -79,7 +80,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 +92,12 @@ 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; + + /* 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 +111,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 +131,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 +139,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); From 880613d6414b73fb3f096f540ec7f23cca42c4e3 Mon Sep 17 00:00:00 2001 From: Alexander Zhang Date: Fri, 24 May 2019 10:02:29 -0400 Subject: [PATCH 02/26] lpc_common: restore RAM and registers after IAP call Restore the RAM and registers which are clobbered by an LPC IAP call. This does not restore any additional RAM which might be clobbered by a *particular* IAP call. (For example, flash programming always clobbers the last page of RAM.) --- src/target/lpc_common.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/target/lpc_common.c b/src/target/lpc_common.c index c7331382..2a35bb54 100644 --- a/src/target/lpc_common.c +++ b/src/target/lpc_common.c @@ -66,6 +66,14 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, void *result, 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); @@ -93,6 +101,10 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, void *result, enum iap_cmd cmd /* copy back just the parameters structure */ target_mem_read(t, ¶m, f->iap_ram, sizeof(param)); + /* 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)); From 0ae7cea1aecfc2d0a9efa5eb133f7cb3cbbdcf5f Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 30 Sep 2019 14:58:51 +0200 Subject: [PATCH 03/26] Add LPC84 from UM11029, Rev. 1.4, tested on LPC845 Breakout board. --- src/target/lpc11xx.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index 30143bac..f97b374e 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -100,6 +100,7 @@ lpc11xx_probe(target *t) } idcode = target_mem_read32(t, LPC8XX_DEVICE_ID); + DEBUG("LPC11/84: IDCODE 0x%08" PRIx32 "\n", idcode); switch (idcode) { case 0x00008100: /* LPC810M021FN8 */ case 0x00008110: /* LPC811M001JDH16 */ @@ -118,6 +119,22 @@ lpc11xx_probe(target *t) target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400); 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 */ case 0x0001BC40: /* LPC11U35/401 */ From 8a1d8bfba3c24d5570b8f18be968bb0be8b5e57a Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 30 Sep 2019 19:50:21 +0200 Subject: [PATCH 04/26] Stlink: Variant for Stlink V2 that works with the original ST bootloader. Use https://github.com/jeanthom/stlink-tool.git for encrypted upload and switch to BMP. --- src/platforms/stlink/Makefile.inc | 6 ++++ src/platforms/stlink/README.md | 50 +++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) 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. + From 90df8172caa70161e0a133367dc4d4ab11490e37 Mon Sep 17 00:00:00 2001 From: Dave Marples Date: Tue, 1 Oct 2019 14:06:30 +0200 Subject: [PATCH 05/26] Addition of pc-hosted variant and extensions to other targets to support it. --- Makefile | 4 + src/Makefile | 5 +- src/gdb_packet.c | 50 +++- src/platforms/pc-hosted/Makefile.inc | 12 + src/platforms/pc-hosted/README.md | 40 +++ src/platforms/pc-hosted/jtagtap.c | 144 +++++++++++ src/platforms/pc-hosted/platform.c | 352 +++++++++++++++++++++++++++ src/platforms/pc-hosted/platform.h | 53 ++++ src/platforms/pc-hosted/swdptap.c | 123 ++++++++++ src/remote.c | 276 +++++++++++++++++++++ src/remote.h | 131 ++++++++++ 11 files changed, 1186 insertions(+), 4 deletions(-) create mode 100644 src/platforms/pc-hosted/Makefile.inc create mode 100644 src/platforms/pc-hosted/README.md create mode 100644 src/platforms/pc-hosted/jtagtap.c create mode 100644 src/platforms/pc-hosted/platform.c create mode 100644 src/platforms/pc-hosted/platform.h create mode 100644 src/platforms/pc-hosted/swdptap.c create mode 100644 src/remote.c create mode 100644 src/remote.h 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 7a76aca3..502d30d2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,7 +46,7 @@ SRC = \ nxpke04.c \ platform.c \ sam3x.c \ - sam4l.c \ + sam4l.c \ samd.c \ stm32f1.c \ stm32f4.c \ @@ -71,6 +71,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))) 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 + * 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; + uint64_t *DOl=(uint64_t *)DO; + + if(!ticks) return; + if (!DI && !DO) 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) + *DOl=remotehston(-1,(char *)&construct[1]); +} + +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/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 From 5c805c7d351dcc10f53d671b9a0ba455dcd508f5 Mon Sep 17 00:00:00 2001 From: Ken Healy Date: Sat, 12 Oct 2019 03:34:21 -0400 Subject: [PATCH 06/26] Fix buffer overflow in adiv5_component_probe() --- src/target/adiv5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index f56ce0cf..230fc445 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -263,7 +263,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; From 4a8cba0e9cb2121b3e8b991e4829e0906adc6233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20B=C3=A9n=C3=A9teau?= Date: Sun, 13 Oct 2019 00:11:55 +1300 Subject: [PATCH 07/26] Add support for LPC1114/333 (LPC1100XL series) This is not given in the user manual but the register immediately following DEVICE_ID does apparently contain the correct part ID. --- src/target/lpc11xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index f97b374e..f454c603 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -147,6 +147,7 @@ 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); From 0aedff662392cddb35d29654013f154d38af6c52 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 14 Oct 2019 10:14:25 +1030 Subject: [PATCH 08/26] Set the accepted to socket to be blocking as that is what the code expects. This is necessary on OSX (and probably BSDs) where they default to non blocking which causes recv() to return -1 with errno set to EAGAIN. --- src/platforms/pc/gdb_if.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) 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 '+'; } From 2eef202ee1476b613944c4709f366edcf40f0716 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 14 Oct 2019 09:46:39 +1030 Subject: [PATCH 09/26] Use pkg-config to get CFLAGS & LDFLAGS for libftdi1 Pull request #535 --- src/platforms/libftdi/Makefile.inc | 3 ++- src/platforms/pc-hosted/Makefile.inc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 index 14106e91..7af77436 100644 --- a/src/platforms/pc-hosted/Makefile.inc +++ b/src/platforms/pc-hosted/Makefile.inc @@ -1,7 +1,8 @@ TARGET=blackmagic_hosted 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 CFLAGS += -Wno-cast-function-type From 58febdff79f9f00cb51a245238d28e11cf8c8fc1 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Fri, 18 Oct 2019 22:59:02 +0200 Subject: [PATCH 10/26] Fixed tokenization of gdb monitor commands. --- src/command.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/command.c b/src/command.c index 29d2e0fc..2e674bc4 100644 --- a/src/command.c +++ b/src/command.c @@ -94,8 +94,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++) @@ -104,8 +105,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++) { From c6f43cf23f6915a29d2d79fd3cc2946cb4d4042f Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sat, 19 Oct 2019 16:25:30 +0200 Subject: [PATCH 11/26] Stlinkv2: Always call stlink_leave_state() first. V2 devices after power on are in DFU mode. It is needed to leave DFU before doing anything else. --- src/platforms/pc-stlinkv2/stlinkv2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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: From b9249fe10490b38fa6c8e990add6b1d05537d939 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sun, 20 Oct 2019 15:55:21 +0200 Subject: [PATCH 12/26] adiv5: Activate DP reset sequence, guarded with timeouts. While not working on most STM32, it succeeds on STM32G474. Thanks to Dave Marples --- src/target/adiv5.c | 47 ++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 230fc445..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. */ @@ -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) { From 76f9a6ad42aa109f21652121c2d070d9b8d4c71a Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Fri, 11 Oct 2019 13:40:13 +0200 Subject: [PATCH 13/26] stm32: Portability changes. --- src/crc32.c | 5 ++++- src/platforms/stm32/gpio.h | 9 ++------- src/platforms/stm32/usbuart.c | 5 ++++- 3 files changed, 10 insertions(+), 9 deletions(-) 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/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/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 */ From 04d9749f258161db09596477304a5ed7d9643927 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 28 Oct 2019 15:58:48 +0100 Subject: [PATCH 14/26] Makefile: Publish DFU binaries with the daily build. (#549) Fixes #548. --- src/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile b/src/Makefile index 502d30d2..9d5060be 100644 --- a/src/Makefile +++ b/src/Makefile @@ -121,6 +121,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 --always) From 67f9d26644e8cf121018135280cb6042aaee9054 Mon Sep 17 00:00:00 2001 From: dpslwk Date: Sun, 6 May 2018 15:10:16 +0100 Subject: [PATCH 15/26] samd: Add support for L21 & L22 (PR #345) --- src/target/samd.c | 139 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 126 insertions(+), 13 deletions(-) diff --git a/src/target/samd.c b/src/target/samd.c index f5d4282d..df2b3718 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: @@ -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 */ @@ -293,17 +369,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 +393,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 +418,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 +425,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 +448,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 +498,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 */ From 28f0ced97e391f60d282edbee58bf9b714c3b155 Mon Sep 17 00:00:00 2001 From: Kirill Zhumarin Date: Sat, 9 Nov 2019 18:21:37 +0200 Subject: [PATCH 16/26] Support NXP LPC1343 --- src/target/lpc11xx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index f454c603..483f7065 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -97,6 +97,12 @@ 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; } idcode = target_mem_read32(t, LPC8XX_DEVICE_ID); From 5a05cedd7d8ea12d41d90daa4cb28f4409678314 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sun, 10 Nov 2019 12:24:44 +0100 Subject: [PATCH 17/26] pc-hosted/jtagtap.c: Handle DO as bytes (PR #552) Fixes #540/#542 --- src/platforms/pc-hosted/jtagtap.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/platforms/pc-hosted/jtagtap.c b/src/platforms/pc-hosted/jtagtap.c index d6f49445..0bac5765 100644 --- a/src/platforms/pc-hosted/jtagtap.c +++ b/src/platforms/pc-hosted/jtagtap.c @@ -94,7 +94,6 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI int s; uint64_t DIl=*(uint64_t *)DI; - uint64_t *DOl=(uint64_t *)DO; if(!ticks) return; if (!DI && !DO) return; @@ -112,8 +111,14 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI exit(-1); } - if (DO) - *DOl=remotehston(-1,(char *)&construct[1]); + if (DO) { + int i = 1; + while (ticks) { + *DO = (uint8_t)remotehston(2,(char *)&construct[i]); + DO++; + i += 2; + } + } } void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks) From 26216beaab9eb310a376f159160c2f2f58735366 Mon Sep 17 00:00:00 2001 From: Ken Healy Date: Thu, 10 Oct 2019 00:38:36 -0400 Subject: [PATCH 18/26] Microchip SAM D51 / E5x support Adds a target driver for Microchip SAM D51 / E5x family. Tested on SAMD51G19A and SAMD51J19A. According to the datasheet, the D51 / E5x family share the same core feature set, differing only in the addition of CAN (E51) or ethernet controllers (E53/54). All members of the family should be equivalent from a debug and programming perspective. --- src/Makefile | 1 + src/target/cortexm.c | 1 + src/target/samx5x.c | 1198 ++++++++++++++++++++++++++++++++++ src/target/target_internal.h | 1 + 4 files changed, 1201 insertions(+) create mode 100644 src/target/samx5x.c diff --git a/src/Makefile b/src/Makefile index 9d5060be..00727157 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,6 +48,7 @@ SRC = \ sam3x.c \ sam4l.c \ samd.c \ + samx5x.c \ stm32f1.c \ stm32f4.c \ stm32h7.c \ 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/samx5x.c b/src/target/samx5x.c new file mode 100644 index 00000000..30315cc7 --- /dev/null +++ b/src/target/samx5x.c @@ -0,0 +1,1198 @@ +/* + * 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_erase_all(target *t, int argc, const char **argv); +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_mbist(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); +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); + +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"}, + {"mbist", (cmd_handler)samx5x_cmd_mbist, + "Runs the built-in memory test"}, + {"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: "}, + {"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 "}, + {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 + */ +uint64_t samx5x_read_pid(target *t) +{ + uint64_t pid = 0; + uint8_t i, j; + + /* Five PID registers to read LSB first */ + for (i = 0, j = 0; i < 5; i++, j += 8) + pid |= (target_mem_read32(t, SAMX5X_DSU_PID(i)) & 0xFF) << j; + + return pid; +} +/** + * Reads the SAM D5x/E5x Component ID + */ +uint32_t samx5x_read_cid(target *t) +{ + uint64_t cid = 0; + uint8_t i, j; + + /* Four CID registers to read LSB first */ + for (i = 0, j = 0; i < 4; i++, j += 8) + cid |= (target_mem_read32(t, SAMX5X_DSU_CID(i)) & 0xFF) << j; + + return cid; +} + +/** + * Overloads the default cortexm reset function with a version that + * removes the target from extended reset where required. + */ +static void +samx5x_reset(target *t) +{ + /** + * SRST is not asserted here as it appears to reset the adiv5 + * logic, meaning that subsequent adiv5_* calls PLATFORM_FATAL_ERROR. + * + * This is ok as normally you can just connect the debugger and go, + * but if that's not possible (protection or SWCLK being used for + * something else) then having SWCLK low on reset should get you + * debug access (cold-plugging). TODO: Confirm this + * + * See the SAM D5x/E5x datasheet §12.6 Debug Operation for more + * details. + * + * jtagtap_srst(true); + * jtagtap_srst(false); + */ + + /* Read DHCSR here to clear S_RESET_ST bit before reset */ + target_mem_read32(t, CORTEXM_DHCSR); + + /* Request system reset from NVIC: SRST doesn't work correctly */ + /* This could be VECTRESET: 0x05FA0001 (reset only core) + * or SYSRESETREQ: 0x05FA0004 (system reset) + */ + target_mem_write32(t, CORTEXM_AIRCR, + CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ); + + /* Exit extended reset */ + 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); + } + + /* Poll for release from reset */ + while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST); + + /* Reset DFSR flags */ + target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL); + + /* Clear any target errors */ + target_check_error(t); +} + +/** + * 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. + */ +static bool samx5x_protected_attach(target *t) +{ + /** + * TODO: Notify the user that we're not really attached and + * they should issue the 'monitor erase_mass' command to + * regain access to the chip. + */ + + /* Patch back in the normal cortexm attach for next time */ + t->attach = cortexm_attach; + + /* Allow attach this time */ + return true; +} + +/** + * 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%x).\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%08x)\n", + dest, len); + return -1; + } + + /* Lock */ + samx5x_lock_current_address(t); + + return 0; +} + +/** + * Uses the Device Service Unit to erase the entire flash + */ +static bool samx5x_cmd_erase_all(target *t, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* Clear the DSU status bits */ + target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, + SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR | + SAMX5X_STATUSA_FAIL); + + /* Erase all */ + target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_CHIP_ERASE); + + /* 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, "Erase failed due to a protection error.\n"); + return false; + } + + /* Test the fail bit in Status A */ + if (status & SAMX5X_STATUSA_FAIL) { + tc_printf(t, "Erase failed.\n"); + return false; + } + + tc_printf(t, "Erase successful!\n"); + + return true; +} + +/** + * 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, 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; +} + +/** + * 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; +} + +/** + * 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; +} + +/** + * 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; +} 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); From d3c330ea1a40b320d9cb20df765ee77f878359b1 Mon Sep 17 00:00:00 2001 From: Ken Healy Date: Sat, 12 Oct 2019 22:19:28 -0400 Subject: [PATCH 19/26] Fix issues with Travis CI build It appears the Travis version of gcc-arm-none-eabi doesn't allow the %x printf format specifier for size_t arguments, in contrast with the version I'm running on Ubuntu 18.04 (15:6.3.1+svn253039-1build1). --- src/target/samx5x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/target/samx5x.c b/src/target/samx5x.c index 30315cc7..660922d5 100644 --- a/src/target/samx5x.c +++ b/src/target/samx5x.c @@ -581,7 +581,7 @@ static int samx5x_check_nvm_error(target *t) #define NVM_ERROR_BITS_MSG \ "Warning: Found NVM error bits set while preparing to %s\n" \ - " flash block at 0x%08"PRIx32" (length 0x%x).\n" \ + " flash block at 0x%08"PRIx32" (length 0x%zx).\n" \ " Clearing these before proceeding:\n" \ " " @@ -680,7 +680,7 @@ static int samx5x_flash_write(struct target_flash *f, if (error || target_check_error(t) || samx5x_check_nvm_error(t)) { DEBUG("Error writing flash page at 0x%08"PRIx32 - " (len 0x%08x)\n", + " (len 0x%08zx)\n", dest, len); return -1; } @@ -739,7 +739,8 @@ 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, SAMX5X_PAGE_SIZE); + (uint32_t)SAMX5X_NVM_USER_PAGE, + (size_t)SAMX5X_PAGE_SIZE); samx5x_print_nvm_error(errs); samx5x_clear_nvm_error(t); } From 9198c951bbcdc488b3719efa4db53aadfc982219 Mon Sep 17 00:00:00 2001 From: Ken Healy Date: Tue, 15 Oct 2019 19:10:45 -0400 Subject: [PATCH 20/26] Reduce flash space required for SAM D51/E5x driver * Reuse functions that are common with the SAM D1x/D2x driver * Only include the mbist and write8/16/32 user commands if SAMX5X_EXTRA_CMDS is defined --- src/target/samd.c | 9 +- src/target/samx5x.c | 298 +++++++++++++++----------------------------- 2 files changed, 107 insertions(+), 200 deletions(-) diff --git a/src/target/samd.c b/src/target/samd.c index df2b3718..c3da3693 100644 --- a/src/target/samd.c +++ b/src/target/samd.c @@ -42,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); @@ -254,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 @@ -349,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 @@ -637,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 index 660922d5..66f1fda9 100644 --- a/src/target/samx5x.c +++ b/src/target/samx5x.c @@ -42,20 +42,26 @@ 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_erase_all(target *t, int argc, const char **argv); 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_mbist(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, @@ -72,18 +78,20 @@ const struct command_s samx5x_cmd_list[] = { "Prints user page from flash"}, {"serial", (cmd_handler)samx5x_cmd_serial, "Prints serial number"}, - {"mbist", (cmd_handler)samx5x_cmd_mbist, - "Runs the built-in memory test"}, {"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} }; @@ -213,83 +221,29 @@ const struct command_s samx5x_protected_cmd_list[] = { /** * Reads the SAM D5x/E5x Peripheral ID + * + * (Reuses the SAM D1x/2x implementation as it is identical) */ -uint64_t samx5x_read_pid(target *t) -{ - uint64_t pid = 0; - uint8_t i, j; +extern uint64_t samd_read_pid(target *t); +#define samx5x_read_pid samd_read_pid - /* Five PID registers to read LSB first */ - for (i = 0, j = 0; i < 5; i++, j += 8) - pid |= (target_mem_read32(t, SAMX5X_DSU_PID(i)) & 0xFF) << j; - - return pid; -} /** * Reads the SAM D5x/E5x Component ID + * + * (Reuses the SAM D1x/2x implementation as it is identical) */ -uint32_t samx5x_read_cid(target *t) -{ - uint64_t cid = 0; - uint8_t i, j; +extern uint32_t samd_read_cid(target *t); +#define samx5x_read_cid samd_read_cid - /* Four CID registers to read LSB first */ - for (i = 0, j = 0; i < 4; i++, j += 8) - cid |= (target_mem_read32(t, SAMX5X_DSU_CID(i)) & 0xFF) << j; - - return 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) */ -static void -samx5x_reset(target *t) -{ - /** - * SRST is not asserted here as it appears to reset the adiv5 - * logic, meaning that subsequent adiv5_* calls PLATFORM_FATAL_ERROR. - * - * This is ok as normally you can just connect the debugger and go, - * but if that's not possible (protection or SWCLK being used for - * something else) then having SWCLK low on reset should get you - * debug access (cold-plugging). TODO: Confirm this - * - * See the SAM D5x/E5x datasheet §12.6 Debug Operation for more - * details. - * - * jtagtap_srst(true); - * jtagtap_srst(false); - */ - - /* Read DHCSR here to clear S_RESET_ST bit before reset */ - target_mem_read32(t, CORTEXM_DHCSR); - - /* Request system reset from NVIC: SRST doesn't work correctly */ - /* This could be VECTRESET: 0x05FA0001 (reset only core) - * or SYSRESETREQ: 0x05FA0004 (system reset) - */ - target_mem_write32(t, CORTEXM_AIRCR, - CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ); - - /* Exit extended reset */ - 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); - } - - /* Poll for release from reset */ - while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST); - - /* Reset DFSR flags */ - target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL); - - /* Clear any target errors */ - target_check_error(t); -} +extern void samd_reset(target *t); +#define samx5x_reset samd_reset /** * Overload the default cortexm attach for when the samd is protected. @@ -298,21 +252,11 @@ samx5x_reset(target *t) * 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) */ -static bool samx5x_protected_attach(target *t) -{ - /** - * TODO: Notify the user that we're not really attached and - * they should issue the 'monitor erase_mass' command to - * regain access to the chip. - */ - - /* Patch back in the normal cortexm attach for next time */ - t->attach = cortexm_attach; - - /* Allow attach this time */ - return true; -} +extern bool samd_protected_attach(target *t); +#define samx5x_protected_attach samd_protected_attach /** * Use the DSU Device Indentification Register to populate a struct @@ -691,46 +635,6 @@ static int samx5x_flash_write(struct target_flash *f, return 0; } -/** - * Uses the Device Service Unit to erase the entire flash - */ -static bool samx5x_cmd_erase_all(target *t, int argc, const char **argv) -{ - (void)argc; - (void)argv; - /* Clear the DSU status bits */ - target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, - SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR | - SAMX5X_STATUSA_FAIL); - - /* Erase all */ - target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_CHIP_ERASE); - - /* 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, "Erase failed due to a protection error.\n"); - return false; - } - - /* Test the fail bit in Status A */ - if (status & SAMX5X_STATUSA_FAIL) { - tc_printf(t, "Erase failed.\n"); - return false; - } - - tc_printf(t, "Erase successful!\n"); - - return true; -} - /** * Erase and write the NVM user page */ @@ -958,76 +862,6 @@ static bool samx5x_cmd_serial(target *t, int argc, const char **argv) return true; } -/** - * 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; -} - /** * Sets the security bit */ @@ -1107,6 +941,78 @@ static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv) 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 */ @@ -1197,3 +1103,5 @@ static bool samx5x_cmd_write32(target *t, int argc, const char **argv) return true; } + +#endif From e7e34600a4fe4717d8f6b90d95c1501320e559f7 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sun, 17 Nov 2019 12:56:09 +0100 Subject: [PATCH 21/26] lpc11: Only print Idcode if not zero and not yet handled. Otherwise for all Cortex-M not yet handled this LPC messages appears. --- src/target/lpc11xx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index 483f7065..3a07e610 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -104,9 +104,10 @@ lpc11xx_probe(target *t) 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); - DEBUG("LPC11/84: IDCODE 0x%08" PRIx32 "\n", idcode); switch (idcode) { case 0x00008100: /* LPC810M021FN8 */ case 0x00008110: /* LPC811M001JDH16 */ @@ -160,6 +161,9 @@ lpc11xx_probe(target *t) lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000); return true; } + if (idcode) { + DEBUG("LPC8xx: Unknown IDCODE 0x%08" PRIx32 "\n", idcode); + } return false; } From 1924aa4999b20d49fcfe2b0f8d259e1e21f53945 Mon Sep 17 00:00:00 2001 From: Thiadmer Riemersma Date: Tue, 19 Nov 2019 15:02:14 +0100 Subject: [PATCH 22/26] Make baudrate parameter of traceswo command mandatory for stlink/swlink, and superfluous on other platforms; change help message accordingly. --- src/command.c | 26 +++++++++++++++++++------- src/platforms/common/traceswo.h | 5 +++++ src/platforms/stlink/platform.h | 1 + src/platforms/stm32/traceswo.c | 1 + src/platforms/stm32/traceswoasync.c | 2 +- src/platforms/swlink/platform.h | 1 + 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/command.c b/src/command.c index 2e674bc4..0d4d0cb2 100644 --- a/src/command.c +++ b/src/command.c @@ -77,7 +77,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)"}, @@ -356,13 +360,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/platforms/common/traceswo.h b/src/platforms/common/traceswo.h index fb57a892..50c95781 100644 --- a/src/platforms/common/traceswo.h +++ b/src/platforms/common/traceswo.h @@ -22,7 +22,12 @@ #include +#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/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/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/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)) From 8a07f444351298f93b271ad6240ea9b3f8da4fe0 Mon Sep 17 00:00:00 2001 From: Artur Maciuszonek Date: Thu, 21 Nov 2019 10:52:25 -0600 Subject: [PATCH 23/26] Add support for the kinetis KL16Zxx devices. Tested on KL16Z128VFM4 custom hardware --- src/target/kinetis.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) 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); From f10ccfd83e0235b2e9159af2755efbc3d1872ae5 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 5 Dec 2019 12:21:02 +0100 Subject: [PATCH 24/26] f4discovery: Fix error with GCC9 being more picky with sizes. --- src/platforms/f4discovery/platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) || From d42477ebc5d7599fd0684470f5436577342fe3f2 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Mon, 25 Nov 2019 23:03:20 +0100 Subject: [PATCH 25/26] Fix infinite loop in jtagtap_tdi_tdo_seq --- src/platforms/pc-hosted/jtagtap.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/platforms/pc-hosted/jtagtap.c b/src/platforms/pc-hosted/jtagtap.c index 0bac5765..fee3f13a 100644 --- a/src/platforms/pc-hosted/jtagtap.c +++ b/src/platforms/pc-hosted/jtagtap.c @@ -112,12 +112,14 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI } if (DO) { - int i = 1; - while (ticks) { - *DO = (uint8_t)remotehston(2,(char *)&construct[i]); - DO++; - i += 2; - } + uint64_t Dol; + int i = 8; + while (ticks > 0) { + Dol = remotehston(8 , (char *)&construct[s - i]); + memcpy (DO, &Dol, (ticks + 7) / 8); + ticks -= 64; + i += 8; + } } } From 5ec12e41039900e26a716ca6024ad863789737a6 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 26 Nov 2019 19:14:24 +0100 Subject: [PATCH 26/26] Support arbitrary bit counts jtagtap_tdi_tdo_seq pc-hosted --- src/platforms/pc-hosted/jtagtap.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/platforms/pc-hosted/jtagtap.c b/src/platforms/pc-hosted/jtagtap.c index fee3f13a..cce1c71f 100644 --- a/src/platforms/pc-hosted/jtagtap.c +++ b/src/platforms/pc-hosted/jtagtap.c @@ -95,8 +95,7 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI uint64_t DIl=*(uint64_t *)DI; - if(!ticks) return; - if (!DI && !DO) return; + if(!ticks || !DI) return; /* Reduce the length of DI according to the bits we're transmitting */ DIl&=(1L<<(ticks+1))-1; @@ -112,14 +111,8 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI } if (DO) { - uint64_t Dol; - int i = 8; - while (ticks > 0) { - Dol = remotehston(8 , (char *)&construct[s - i]); - memcpy (DO, &Dol, (ticks + 7) / 8); - ticks -= 64; - i += 8; - } + for (unsigned int i = 1; i*8 <= (unsigned int)ticks; i++) + DO[i - 1] = remotehston(2 , (char *)&construct[s - (i * 2)]); } }