aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGareth McMullin2011-02-04 20:23:52 +1300
committerGareth McMullin2011-02-04 20:23:52 +1300
commit406617a2a470021d9412e9280feda0d28bdb653b (patch)
tree43b2cb9b562fde0bf5187c31dea77d72318cfc29
Import of working source tree.
-rw-r--r--src/Makefile41
-rw-r--r--src/adiv5.c268
-rw-r--r--src/adiv5_jtagdp.c123
-rw-r--r--src/adiv5_swdp.c171
-rw-r--r--src/command.c159
-rw-r--r--src/cortexm3.c394
-rw-r--r--src/gdb_main.c408
-rw-r--r--src/gdb_packet.c153
-rw-r--r--src/hex_utils.c62
-rw-r--r--src/include/adiv5.h119
-rw-r--r--src/include/command.h37
-rw-r--r--src/include/cortexm3.h29
-rw-r--r--src/include/gdb_if.h30
-rw-r--r--src/include/gdb_main.h27
-rw-r--r--src/include/gdb_packet.h36
-rw-r--r--src/include/general.h34
-rw-r--r--src/include/hex_utils.h29
-rw-r--r--src/include/jtag_scan.h56
-rw-r--r--src/include/jtagtap.h69
-rw-r--r--src/include/lmi.h29
-rw-r--r--src/include/stm32_tgt.h29
-rw-r--r--src/include/swdptap.h39
-rw-r--r--src/include/target.h173
-rw-r--r--src/jtag_scan.c232
-rw-r--r--src/jtagtap_generic.c72
-rw-r--r--src/lmi.c183
-rw-r--r--src/main.c53
-rw-r--r--src/stm32/Makefile.inc25
-rw-r--r--src/stm32/blackmagic.ld29
-rw-r--r--src/stm32/cdcacm.c304
-rw-r--r--src/stm32/gdb_if.c81
-rw-r--r--src/stm32/jtagtap.c90
-rw-r--r--src/stm32/platform.c179
-rw-r--r--src/stm32/platform.h121
-rw-r--r--src/stm32/swdptap.c156
-rw-r--r--src/stm32/usbdfu.c308
-rw-r--r--src/stm32_tgt.c229
37 files changed, 4577 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..d6f2972
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,41 @@
+ifndef HOST
+HOST = stm32
+endif
+
+VPATH += $(HOST)
+
+BUILDDATE := `date +"%Y%m%d"`
+
+CFLAGS = -Wall -Wextra -Wno-pointer-sign -Wno-char-subscripts\
+ -O0 -std=gnu99 -g3 -DBUILDDATE=\"$(BUILDDATE)\"\
+ -I. -Iinclude -I$(HOST)
+
+SRC = gdb_if.c \
+ gdb_packet.c \
+ gdb_main.c \
+ hex_utils.c \
+ jtagtap.c \
+ swdptap.c \
+ adiv5.c \
+ adiv5_swdp.c \
+ cortexm3.c \
+ stm32_tgt.c \
+ main.c \
+ platform.c \
+ command.c \
+ jtag_scan.c \
+ adiv5_jtagdp.c \
+ lmi.c \
+
+include $(HOST)/Makefile.inc
+
+OBJ = $(SRC:.c=.o)
+
+blackmagic: $(OBJ)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean host_clean
+
+clean: host_clean
+ $(RM) *.o *~ blackmagic $(HOSTFILES)
+
diff --git a/src/adiv5.c b/src/adiv5.c
new file mode 100644
index 0000000..f7278f8
--- /dev/null
+++ b/src/adiv5.c
@@ -0,0 +1,268 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the transport generic functions of the
+ * ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
+ *
+ * Issues:
+ * Currently doesn't use ROM table for introspection, just assumes
+ * the device is Cortex-M3.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "general.h"
+#include "jtag_scan.h"
+#include "gdb_packet.h"
+#include "adiv5.h"
+
+#include "target.h"
+
+#include "cortexm3.h"
+
+#ifndef DO_RESET_SEQ
+#define DO_RESET_SEQ 0
+#endif
+
+/* Bits in the DP_CTRLSTAT register */
+#define CSYSPWRUPACK 0x80000000L
+#define CSYSPWRUPREQ 0x40000000L
+#define CDBGPWRUPACK 0x20000000L
+#define CDBGPWRUPREQ 0x10000000L
+#define CDBGRSTACK 0x08000000L
+#define CDBGRSTREQ 0x04000000L
+
+/* This belongs elsewhere... */
+target *target_list = NULL;
+target *cur_target = NULL;
+target *last_target = NULL;
+
+static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP";
+
+ADIv5_DP_t *adiv5_dp_list;
+/*
+ADIv5_DP_t adiv5_dps[5];
+int adiv5_dp_count;
+*/
+
+ADIv5_AP_t adiv5_aps[5];
+int adiv5_ap_count;
+
+static int ap_check_error(struct target_s *target);
+
+static int ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len);
+static int ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len);
+static int ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len);
+static int ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len);
+
+void adiv5_free_all(void)
+{
+ ADIv5_DP_t *dp;
+
+ while(adiv5_dp_list) {
+ dp = adiv5_dp_list->next;
+ free(adiv5_dp_list);
+ adiv5_dp_list = dp;
+ }
+
+ adiv5_ap_count = 0;
+}
+
+
+void adiv5_dp_init(ADIv5_DP_t *dp)
+{
+ uint32_t ctrlstat;
+
+ dp->next = adiv5_dp_list;
+ adiv5_dp_list = dp;
+
+ ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT);
+
+ /* Write request for system and debug power up */
+ adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat |= CSYSPWRUPREQ | CDBGPWRUPREQ);
+ /* Wait for acknowledge */
+ while(((ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT)) &
+ (CSYSPWRUPACK | CDBGPWRUPACK)) != (CSYSPWRUPACK | 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.
+ */
+
+ /* Write request for debug reset */
+ adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat |= CDBGRSTREQ);
+ /* Wait for acknowledge */
+ while(!((ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT)) & CDBGRSTACK));
+
+ /* Write request for debug reset release */
+ adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat &= ~CDBGRSTREQ);
+ /* Wait for acknowledge */
+ while(adiv5_dp_read(dp, DP_CTRLSTAT) & CDBGRSTACK);
+ }
+
+ /* Probe for APs on this DP */
+ for(int i = 0; i < 256; i++) {
+ uint32_t idr;
+
+ adiv5_dp_write(dp, DP_SELECT, ((uint32_t)i << 24) | 0xF0);
+ idr = adiv5_dp_read_ap(dp, 0x0C); /* attempt to read IDR */
+
+ if(idr) { /* We have a valid AP, adding to list */
+ target *t;
+
+ adiv5_aps[adiv5_ap_count].dp = dp;
+ adiv5_aps[adiv5_ap_count].apsel = i;
+ adiv5_aps[adiv5_ap_count].idr = idr;
+ adiv5_aps[adiv5_ap_count].cfg = adiv5_dp_read_ap(dp, 0x04);
+ adiv5_aps[adiv5_ap_count].base = adiv5_dp_read_ap(dp, 0x08);
+ /* Should probe further here... */
+
+ /* Prepend to target list... */
+ t = target_list;
+ target_list = (void*)calloc(1, sizeof(struct target_ap_s));
+ target_list->next = t;
+ ((struct target_ap_s *)target_list)->ap = &adiv5_aps[adiv5_ap_count];
+
+ target_list->driver = adiv5_driver_str;
+ target_list->check_error = ap_check_error;
+
+ target_list->mem_read_words = ap_mem_read_words;
+ target_list->mem_write_words = ap_mem_write_words;
+ target_list->mem_read_bytes = ap_mem_read_bytes;
+ target_list->mem_write_bytes = ap_mem_write_bytes;
+
+ /* The rest sould only be added after checking ROM table */
+ cm3_probe((void*)target_list);
+
+ adiv5_ap_count++;
+ } else break;
+ }
+}
+
+
+static int
+ap_check_error(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+ return adiv5_dp_error(t->ap->dp);
+}
+
+static int
+ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len)
+{
+ struct target_ap_s *t = (void *)target;
+
+ len >>= 2;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, src);
+ adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
+ while(--len)
+ *dest++ = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
+
+ *dest++ = adiv5_dp_low_access(t->ap->dp, 0, 1, DP_RDBUFF, 0);
+
+ return 0;
+}
+
+static int
+ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t tmp = src;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000050);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, src);
+ adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
+ while(--len) {
+ tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
+ *dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
+ }
+ tmp = adiv5_dp_low_access(t->ap->dp, 0, 1, DP_RDBUFF, 0);
+ *dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
+
+ return 0;
+}
+
+
+static int
+ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len)
+{
+ struct target_ap_s *t = (void *)target;
+
+ len >>= 2;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, dest);
+ while(len--)
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x0C, *src++);
+
+ return 0;
+}
+
+static int
+ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t tmp;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000050);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, dest);
+ while(len--) {
+ tmp = (uint32_t)*src++ << ((dest++ & 3) << 3);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x0C, tmp);
+ }
+ return 0;
+}
+
+
+
+uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr)
+{
+ adiv5_ap_write(ap, 0x00, 0xA2000052);
+ adiv5_ap_write(ap, 0x04, addr);
+ return adiv5_ap_read(ap, 0x0C);
+}
+
+void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value)
+{
+ adiv5_ap_write(ap, 0x00, 0xA2000052);
+ adiv5_ap_write(ap, 0x04, addr);
+ adiv5_ap_write(ap, 0x0C, value);
+}
+
+void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value)
+{
+ adiv5_dp_write(ap->dp, DP_SELECT,
+ ((uint32_t)ap->apsel << 24)|(addr & 0xF0));
+ adiv5_dp_write_ap(ap->dp, addr, value);
+}
+
+uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr)
+{
+ uint32_t ret;
+ adiv5_dp_write(ap->dp, DP_SELECT,
+ ((uint32_t)ap->apsel << 24)|(addr & 0xF0));
+ ret = adiv5_dp_read_ap(ap->dp, addr);
+ return ret;
+}
+
diff --git a/src/adiv5_jtagdp.c b/src/adiv5_jtagdp.c
new file mode 100644
index 0000000..38955bb
--- /dev/null
+++ b/src/adiv5_jtagdp.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the JTAG-DP specific functions of the
+ * ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
+ */
+
+#include "general.h"
+#include "platform.h"
+#include "adiv5.h"
+#include "jtag_scan.h"
+#include "jtagtap.h"
+
+#include <stdlib.h>
+
+#define JTAGDP_ACK_OK 0x02
+#define JTAGDP_ACK_WAIT 0x01
+
+/* 35-bit registers that control the ADIv5 DP */
+#define IR_ABORT 0x8
+#define IR_DPACC 0xA
+#define IR_APACC 0xB
+
+static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
+static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr);
+
+static void adiv5_jtagdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
+static uint32_t adiv5_jtagdp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
+
+static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp);
+
+static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
+ uint8_t addr, uint32_t value);
+
+
+void adiv5_jtag_dp_handler(jtag_dev_t *dev)
+{
+ ADIv5_DP_t *dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
+
+ dp->dev = dev;
+ dp->idcode = dev->idcode;
+
+ dp->dp_write = adiv5_jtagdp_write;
+ dp->dp_read = adiv5_jtagdp_read;
+
+ dp->ap_write = adiv5_jtagdp_write_ap;
+ dp->ap_read = adiv5_jtagdp_read_ap;
+
+ dp->error = adiv5_jtagdp_error;
+
+ dp->low_access = adiv5_jtagdp_low_access;
+
+ adiv5_dp_init(dp);
+}
+
+static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ adiv5_jtagdp_low_access(dp, 0, 0, addr, value);
+}
+
+static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr)
+{
+ adiv5_jtagdp_low_access(dp, 0, 1, addr, 0);
+ return adiv5_jtagdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
+}
+
+static void adiv5_jtagdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ adiv5_jtagdp_low_access(dp, 1, 0, addr, value);
+}
+
+static uint32_t adiv5_jtagdp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
+{
+ adiv5_jtagdp_low_access(dp, 1, 1, addr, 0);
+ return adiv5_jtagdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
+}
+
+static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp)
+{
+ adiv5_jtagdp_low_access(dp, 0, 1, DP_CTRLSTAT, 0);
+ return adiv5_jtagdp_low_access(dp, 0, 0, DP_CTRLSTAT, 0xF0000032) & 0x32;
+}
+
+static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
+ uint8_t addr, uint32_t value)
+{
+ uint64_t request, response;
+ uint8_t ack;
+
+ request = ((uint64_t)value << 3) | ((addr >> 1) & 0x06) | (RnW?1:0);
+
+ jtag_dev_write_ir(dp->dev, APnDP?IR_APACC:IR_DPACC);
+
+ do {
+ jtag_dev_shift_dr(dp->dev, (uint8_t*)&response, (uint8_t*)&request, 35);
+ ack = response & 0x07;
+ } while(ack == JTAGDP_ACK_WAIT);
+
+ if((ack != JTAGDP_ACK_OK)) {
+ /* Fatal error if invalid ACK response */
+ PLATFORM_FATAL_ERROR(1);
+ }
+
+ return (uint32_t)(response >> 3);
+}
+
diff --git a/src/adiv5_swdp.c b/src/adiv5_swdp.c
new file mode 100644
index 0000000..b6e8d28
--- /dev/null
+++ b/src/adiv5_swdp.c
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the SW-DP specific functions of the
+ * ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
+ */
+
+#include "general.h"
+#include "platform.h"
+#include "adiv5.h"
+
+#include "swdptap.h"
+
+#include "command.h"
+
+#include <stdlib.h>
+
+#define SWDP_ACK_OK 0x01
+#define SWDP_ACK_WAIT 0x02
+#define SWDP_ACK_FAULT 0x04
+
+static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
+static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr);
+
+static void adiv5_swdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
+static uint32_t adiv5_swdp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
+
+static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp);
+
+static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
+ uint8_t addr, uint32_t value);
+
+
+int adiv5_swdp_scan(void)
+{
+ ADIv5_DP_t *dp;
+ uint8_t ack;
+
+ TARGET_LIST_FREE();
+ dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
+
+ swdptap_init();
+ /* Read the SW-DP IDCODE register to syncronise */
+ /* This could be done with adiv_swdp_low_access(), but this doesn't
+ * allow the ack to be checked here. */
+ swdptap_seq_out(0b10100101, 8);
+ ack = swdptap_seq_in(3);
+ if((ack != SWDP_ACK_OK) || swdptap_seq_in_parity(&dp->idcode, 32)) {
+ morse("NO TARGETS.", 1);
+ free(dp);
+ return -1;
+ }
+
+ dp->dp_write = adiv5_swdp_write;
+ dp->dp_read = adiv5_swdp_read;
+ dp->ap_write = adiv5_swdp_write_ap;
+ dp->ap_read = adiv5_swdp_read_ap;
+ dp->error = adiv5_swdp_error;
+ dp->low_access = adiv5_swdp_low_access;
+
+ adiv5_dp_init(dp);
+
+ if(!target_list) morse("NO TARGETS.", 1);
+ else morse(NULL, 0);
+
+ return target_list?1:0;
+}
+
+static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ adiv5_swdp_low_access(dp, 0, 0, addr, value);
+}
+
+static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr)
+{
+ return adiv5_swdp_low_access(dp, 0, 1, addr, 0);
+}
+
+static void adiv5_swdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ adiv5_swdp_low_access(dp, 1, 0, addr, value);
+}
+
+static uint32_t adiv5_swdp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
+{
+ uint32_t ret;
+
+ adiv5_swdp_low_access(dp, 1, 1, addr, 0);
+ ret = adiv5_swdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
+
+ return ret;
+}
+
+static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp)
+{
+ uint32_t err, clr = 0;
+
+ err = adiv5_swdp_read(dp, DP_CTRLSTAT) & 0x32;
+
+ if(err & 0x02) clr |= 0x10; /* STICKORUN */
+ if(err & 0x10) clr |= 0x02; /* STICKCMP */
+ if(err & 0x20) clr |= 0x04; /* STICKERR */
+
+ adiv5_swdp_write(dp, DP_ABORT, clr);
+ dp->fault = 0;
+
+ return err;
+}
+
+static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
+ uint8_t addr, uint32_t value)
+{
+ uint8_t request = 0b10000001;
+ uint32_t response;
+ uint8_t ack;
+
+ if(APnDP && dp->fault) return 0;
+
+ if(APnDP) request ^= 0b100010;
+ if(RnW) request ^= 0b100100;
+
+ addr &= 0xC;
+ request |= (addr << 1) & 0b11000;
+ if((addr == 4) || (addr == 8))
+ request ^= 0b100000;
+
+ do {
+ swdptap_seq_out(request, 8);
+ ack = swdptap_seq_in(3);
+ } while(ack == SWDP_ACK_WAIT);
+
+ if(ack == SWDP_ACK_FAULT) {
+ dp->fault = 1;
+ return 0;
+ }
+
+ if(ack != SWDP_ACK_OK) {
+ /* Fatal error if invalid ACK response */
+ PLATFORM_FATAL_ERROR(1);
+ }
+
+ if(RnW) {
+ if(swdptap_seq_in_parity(&response, 32)) /* Give up on parity error */
+ PLATFORM_FATAL_ERROR(1);
+ } else {
+ swdptap_seq_out_parity(value, 32);
+ }
+
+ /* REMOVE THIS */
+ swdptap_seq_out(0, 8);
+
+ return response;
+}
+
diff --git a/src/command.c b/src/command.c
new file mode 100644
index 0000000..66a017e
--- /dev/null
+++ b/src/command.c
@@ -0,0 +1,159 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements a basic command interpreter for GDB 'monitor'
+ * commands.
+ *
+ * TODO: Add a mechanism for target driver so register new commands.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+
+#include "general.h"
+
+#include "command.h"
+#include "gdb_packet.h"
+
+#include "jtag_scan.h"
+#include "target.h"
+
+#include "adiv5.h"
+
+void cmd_version(void);
+void cmd_help(void);
+
+void cmd_jtag_scan(void);
+void cmd_swdp_scan(void);
+void cmd_targets(void);
+void cmd_morse(void);
+
+const struct command_s cmd_list[] = {
+ {"version", (cmd_handler)cmd_version, "Display firmware version info"},
+ {"help", (cmd_handler)cmd_help, "Display help for monitor commands"},
+ {"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" },
+ {"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
+ {"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
+ {"morse", (cmd_handler)cmd_morse, "Display morse error message" },
+
+ {NULL, NULL, NULL}
+};
+
+
+int command_process(char *cmd)
+{
+ const struct command_s *c;
+ int argc = 0;
+ const char **argv;
+
+ /* Initial estimate for argc */
+ for(char *s = cmd; *s; s++)
+ if((*s == ' ') || (*s == '\t')) argc++;
+
+ 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"));
+
+ /* Look for match and call handler */
+ for(c = cmd_list; c->cmd; c++) {
+ if(!strcmp(argv[0], c->cmd)) {
+ c->handler(argc, argv);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void cmd_version(void)
+{
+ gdb_out("Black Magic Probe (Firmware 1.5, build " BUILDDATE ")\n");
+ gdb_out("Copyright (C) 2010 Black Sphere Technologies\n");
+ gdb_out("All rights reserved.\n\n");
+}
+
+void cmd_help(void)
+{
+ const struct command_s *c;
+
+ for(c = cmd_list; c->cmd; c++)
+ gdb_outf("%s -- %s\n", c->cmd, c->help);
+}
+
+void cmd_jtag_scan(void)
+{
+ int devs = jtag_scan();
+
+ if(devs < 0) {
+ gdb_out("JTAG device scan failed!\n");
+ return;
+ }
+ if(devs == 0) {
+ gdb_out("JTAG scan found no devices!\n");
+ return;
+ }
+ gdb_outf("Device IR Len IDCODE Description\n");
+ for(int i = 0; i < jtag_dev_count; i++)
+ gdb_outf("%d\t%d\t0x%08lX %s\n", i,
+ jtag_devs[i].ir_len, jtag_devs[i].idcode,
+ jtag_devs[i].descr);
+ gdb_out("\n");
+ cmd_targets();
+}
+
+void cmd_swdp_scan(void)
+{
+ if(adiv5_swdp_scan() < 0) {
+ gdb_out("SW-DP scan failed!\n");
+ return;
+ }
+
+ gdb_outf("SW-DP detected IDCODE: 0x%08X\n", adiv5_dp_list->idcode);
+
+ cmd_targets();
+}
+
+void cmd_targets(void)
+{
+ struct target_s *t;
+ int i;
+
+ if(!target_list) {
+ gdb_out("No usable targets found.\n");
+ return;
+ }
+
+ gdb_out("Available Targets:\n");
+ gdb_out("No. Att Driver\n");
+ for(t = target_list, i = 1; t; t = t->next, i++)
+ gdb_outf("%2d %c %s\n", i, t==cur_target?'*':' ',
+ t->driver);
+}
+
+void cmd_morse(void)
+{
+ if(morse_msg)
+ gdb_outf("%s\n", morse_msg);
+}
+
+
diff --git a/src/cortexm3.c b/src/cortexm3.c
new file mode 100644
index 0000000..4fd7fdb
--- /dev/null
+++ b/src/cortexm3.c
@@ -0,0 +1,394 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements debugging functionality specific to ARM
+ * the Cortex-M3 core. This should be generic to ARMv7-M as it is
+ * implemented according to the "ARMv7-M Architectue Reference Manual",
+ * ARM doc DDI0403C.
+ *
+ * Issues:
+ * There are way too many magic numbers used here.
+ */
+#include <stdio.h>
+
+#include "general.h"
+#include "jtagtap.h"
+#include "jtag_scan.h"
+#include "adiv5.h"
+#include "target.h"
+
+#include "cortexm3.h"
+#include "lmi.h"
+#include "stm32_tgt.h"
+
+static char cm3_driver_str[] = "ARM Cortex-M3";
+
+static void cm3_attach(struct target_s *target);
+static void cm3_detach(struct target_s *target);
+
+static int ap_regs_read(struct target_s *target, void *data);
+static int ap_regs_write(struct target_s *target, const void *data);
+static int ap_pc_write(struct target_s *target, const uint32_t val);
+
+static void cm3_reset(struct target_s *target);
+static void ap_halt_resume(struct target_s *target, uint8_t step);
+static int ap_halt_wait(struct target_s *target);
+static void ap_halt_request(struct target_s *target);
+
+static int cm3_set_hw_bp(struct target_s *target, uint32_t addr);
+static int cm3_clear_hw_bp(struct target_s *target, uint32_t addr);
+
+static int cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
+static int cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
+
+static int cm3_check_hw_wp(struct target_s *target, uint32_t *addr);
+
+/* Watchpoint unit status */
+static struct wp_unit_s {
+ uint32_t addr;
+ uint8_t type;
+ uint8_t size;
+} hw_watchpoint[4];
+
+/* Breakpoint unit status */
+static uint32_t hw_breakpoint[6];
+
+int
+cm3_probe(struct target_s *target)
+{
+ target->driver = cm3_driver_str;
+
+ target->attach = cm3_attach;
+ target->detach = cm3_detach;
+
+ /* Should probe here to make sure it's Cortex-M3 */
+ target->regs_read = ap_regs_read;
+ target->regs_write = ap_regs_write;
+// target->pc_read = ap_pc_read;
+ target->pc_write = ap_pc_write;
+
+ target->reset = cm3_reset;
+ target->halt_request = ap_halt_request;
+ target->halt_wait = ap_halt_wait;
+ target->halt_resume = ap_halt_resume;
+ target->regs_size = 16<<2;
+
+ /* if not STM32 try LMI */
+ if(stm32_probe(target) != 0)
+ lmi_probe(target);
+
+ return 0;
+}
+
+static void
+cm3_attach(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ target_halt_request(target);
+ while(!target_halt_wait(target));
+
+ /* Request halt on reset */
+ /* TRCENA | VC_CORERESET */
+ adiv5_ap_mem_write(t->ap, 0xE000EDFC, 0x01000401);
+
+ /* Clear any stale breakpoints */
+ for(i = 0; i < 6; i++) {
+ adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
+ hw_breakpoint[i] = 0;
+ }
+
+ /* Clear any stale watchpoints */
+ for(i = 0; i < 4; i++) {
+ adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
+ hw_watchpoint[i].type = 0;
+ }
+
+ /* Flash Patch Control Register: set ENABLE */
+ adiv5_ap_mem_write(t->ap, 0xE0002000, 3);
+ target->set_hw_bp = cm3_set_hw_bp;
+ target->clear_hw_bp = cm3_clear_hw_bp;
+
+ /* Data Watchpoint and Trace */
+ target->set_hw_wp = cm3_set_hw_wp;
+ target->clear_hw_wp = cm3_clear_hw_wp;
+ target->check_hw_wp = cm3_check_hw_wp;
+}
+
+static void
+cm3_detach(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ /* Clear any stale breakpoints */
+ for(i = 0; i < 6; i++)
+ adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
+
+ /* Clear any stale watchpoints */
+ for(i = 0; i < 4; i++)
+ adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
+
+ /* Disable debug */
+ adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, 0xA05F0000UL);
+}
+
+static int
+ap_regs_read(struct target_s *target, void *data)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t *regs = data;
+ int i;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
+ adiv5_ap_write(t->ap, 0x14, 0); /* Required to switch banks */
+ *regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
+ for(i = 1; i < 16; i++) {
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i);
+ *regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
+ }
+
+ return 0;
+}
+
+static int
+ap_regs_write(struct target_s *target, const void *data)
+{
+ struct target_ap_s *t = (void *)target;
+ const uint32_t *regs = data;
+ int i;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
+ adiv5_ap_write(t->ap, 0x18, *regs++); /* Required to switch banks */
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x10000);
+ for(i = 1; i < 16; i++) {
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x18, *regs++);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i | 0x10000);
+ }
+
+ return 0;
+}
+
+static int
+ap_pc_write(struct target_s *target, const uint32_t val)
+{
+ struct target_ap_s *t = (void *)target;
+
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
+
+ adiv5_ap_write(t->ap, 0x18, val); /* Required to switch banks */
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x1000F);
+
+ return 0;
+}
+
+/* The following three routines implement target halt/resume
+ * using the core debug registers in the NVIC. */
+static void
+cm3_reset(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+
+ jtagtap_srst();
+
+ /* Request system reset from NVIC: SRST doesn't work correctly */
+ /* This could be VECTRESET: 0x05FA0001 (reset only core)
+ * or SYSRESETREQ: 0x05FA0004 (system reset)
+ */
+ adiv5_ap_mem_write(t->ap, 0xE000ED0C, 0x05FA0004);
+ adiv5_ap_mem_write(t->ap, 0xE000ED0C, 0x05FA0001);
+}
+
+static void
+ap_halt_request(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+
+ adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, 0xA05F0003UL);
+}
+
+static int
+ap_halt_wait(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+
+ return adiv5_ap_mem_read(t->ap, 0xE000EDF0UL) & 0x20000;
+}
+
+static void
+ap_halt_resume(struct target_s *target, uint8_t step)
+{
+ struct target_ap_s *t = (void *)target;
+ static uint8_t old_step = 0;
+
+ /* Disable interrupts while single stepping... */
+ if(step != old_step) {
+ adiv5_ap_mem_write(t->ap, 0xE000EDF0UL,
+ step?0xA05F000FUL:0xA05F0003UL);
+ old_step = step;
+ }
+
+ adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, step?0xA05F000DUL:0xA05F0001UL);
+}
+
+
+/* The following routines implement hardware breakpoints.
+ * The Flash Patch and Breakpoint (FPB) system is used. */
+
+static int
+cm3_set_hw_bp(struct target_s *target, uint32_t addr)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t val = addr & 0x1FFFFFFC;
+ int i;
+
+ val |= (addr & 2)?0x80000000:0x40000000;
+ val |= 1;
+
+ for(i = 0; i < 6; i++)
+ if((hw_breakpoint[i] & 1) == 0) break;
+
+ if(i == 6) return -1;
+
+ hw_breakpoint[i] = addr | 1;
+
+ adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, val);
+
+ return 0;
+}
+
+static int
+cm3_clear_hw_bp(struct target_s *target, uint32_t addr)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ for(i = 0; i < 6; i++)
+ if((hw_breakpoint[i] & ~1) == addr) break;
+
+ if(i == 6) return -1;
+
+ hw_breakpoint[i] = 0;
+
+ adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
+
+ return 0;
+}
+
+
+/* The following routines implement hardware watchpoints.
+ * The Data Watch and Trace (DWT) system is used. */
+
+static int
+cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ switch(len) { /* Convert bytes size to mask size */
+ case 1: len = 0; break;
+ case 2: len = 1; break;
+ case 4: len = 2; break;
+ default:
+ return -1;
+ }
+
+ switch(type) { /* Convert gdb type to function type */
+ case 2: type = 6; break;
+ case 3: type = 5; break;
+ case 4: type = 7; break;
+ default:
+ return -1;
+ }
+
+ for(i = 0; i < 4; i++)
+ if((hw_watchpoint[i].type) == 0) break;
+
+ if(i == 4) return -2;
+
+ hw_watchpoint[i].type = type;
+ hw_watchpoint[i].addr = addr;
+ hw_watchpoint[i].size = len;
+
+ adiv5_ap_mem_write(t->ap, 0xE0001020 + i*0x10, addr);
+ adiv5_ap_mem_write(t->ap, 0xE0001024 + i*0x10, len);
+ adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0x800 | type);
+
+ return 0;
+}
+
+static int
+cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ switch(len) {
+ case 1: len = 0; break;
+ case 2: len = 1; break;
+ case 4: len = 2; break;
+ default:
+ return -1;
+ }
+
+ switch(type) {
+ case 2: type = 6; break;
+ case 3: type = 5; break;
+ case 4: type = 7; break;
+ default:
+ return -1;
+ }
+
+ for(i = 0; i < 4; i++)
+ if((hw_watchpoint[i].addr == addr) &&
+ (hw_watchpoint[i].type == type) &&
+ (hw_watchpoint[i].size == len)) break;
+
+ if(i == 4) return -2;
+
+ hw_watchpoint[i].type = 0;
+
+ adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
+
+ return 0;
+}
+
+static int
+cm3_check_hw_wp(struct target_s *target, uint32_t *addr)
+{
+ struct target_ap_s *t = (void *)target;
+ int i;
+
+ for(i = 0; i < 4; i++)
+ /* if SET and MATCHED then break */
+ if(hw_watchpoint[i].type &&
+ (adiv5_ap_mem_read(t->ap, 0xE0001028 + i*0x10) & 0x01000000))
+ break;
+
+ if(i == 4) return 0;
+
+ *addr = hw_watchpoint[i].addr;
+ return 1;
+}
+
diff --git a/src/gdb_main.c b/src/gdb_main.c
new file mode 100644
index 0000000..3c36347
--- /dev/null
+++ b/src/gdb_main.c
@@ -0,0 +1,408 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the GDB Remote Serial Debugging protocol as
+ * described in "Debugging with GDB" build from GDB source.
+ *
+ * Originally written for GDB 6.8, updated and tested with GDB 7.2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <alloca.h>
+#include <assert.h>
+
+#include "platform.h"
+
+#include "general.h"
+#include "hex_utils.h"
+#include "gdb_if.h"
+#include "gdb_packet.h"
+#include "gdb_main.h"
+
+#include "jtagtap.h"
+#include "jtag_scan.h"
+#include "adiv5.h"
+
+#include "target.h"
+
+#include "command.h"
+
+#define BUF_SIZE 1024
+
+#define ERROR_IF_NO_TARGET() \
+ if(!cur_target) { gdb_putpacketz("EFF"); break; }
+
+static unsigned char pbuf[BUF_SIZE];
+
+static void handle_q_packet(char *packet, int len);
+static void handle_v_packet(char *packet, int len);
+
+uint32_t arm_regs[16];
+
+void
+gdb_main(void)
+{
+ int size;
+ static uint8_t single_step = 0;
+
+ DEBUG("Entring GDB protocol main loop\n");
+ /* GDB protocol main loop */
+ while(1) {
+ SET_IDLE_STATE(1);
+ size = gdb_getpacket(pbuf, BUF_SIZE);
+ SET_IDLE_STATE(0);
+ DEBUG("%s\n", pbuf);
+ switch(pbuf[0]) {
+ /* Implementation of these is mandatory! */
+ case 'g': { /* 'g': Read general registers */
+ ERROR_IF_NO_TARGET();
+ target_regs_read(cur_target, (void*)arm_regs);
+ gdb_putpacket(hexify(pbuf, (void*)arm_regs, sizeof(arm_regs)), sizeof(arm_regs)*2);
+ break;
+ }
+ case 'm': { /* 'm addr,len': Read len bytes from addr */
+ uint32_t addr, len;
+ char *mem;
+ ERROR_IF_NO_TARGET();
+ siscanf(pbuf, "m%08lX,%08lX", &addr, &len);
+ DEBUG("m packet: addr = %08lX, len = %08lX\n", addr, len);
+ mem = malloc(len);
+ if(!mem) break;
+ if(((addr & 3) == 0) && ((len & 3) == 0))
+ target_mem_read_words(cur_target, (void*)mem, addr, len);
+ else
+ target_mem_read_bytes(cur_target, (void*)mem, addr, len);
+ if(target_check_error(cur_target))
+ gdb_putpacket("E01", 3);
+ else
+ gdb_putpacket(hexify(pbuf, mem, len), len*2);
+ free(mem);
+ break;
+ }
+ case 'G': /* 'G XX': Write general registers */
+ ERROR_IF_NO_TARGET();
+ unhexify((void*)arm_regs, &pbuf[1], cur_target->regs_size);
+ target_regs_write(cur_target, arm_regs);
+ gdb_putpacket("OK", 2);
+ break;
+
+ case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
+ uint32_t addr, len;
+ int hex;
+ char *mem;
+ ERROR_IF_NO_TARGET();
+ siscanf(pbuf, "M%08lX,%08lX:%n", &addr, &len, &hex);
+ DEBUG("M packet: addr = %08lX, len = %08lX\n", addr, len);
+ mem = malloc(len);
+ unhexify(mem, pbuf + hex, len);
+ if(((addr & 3) == 0) && ((len & 3) == 0))
+ target_mem_write_words(cur_target, addr, (void*)mem, len);
+ else
+ target_mem_write_bytes(cur_target, addr, (void*)mem, len);
+ if(target_check_error(cur_target))
+ gdb_putpacket("E01", 3);
+ else
+ gdb_putpacket("OK", 2);
+ free(mem);
+ break;
+ }
+ case 's': /* 's [addr]': Single step [start at addr] */
+ single_step = 1;
+ // Fall through to resume target
+ case 'c': /* 'c [addr]': Continue [at addr] */
+ if(!cur_target) {
+ gdb_putpacketz("X1D");
+ break;
+ }
+
+ target_halt_resume(cur_target, single_step);
+ SET_RUN_STATE(1);
+ single_step = 0;
+ // Fall through to wait for target halt
+ case '?': { /* '?': Request reason for target halt */
+ /* This packet isn't documented as being mandatory,
+ * but GDB doesn't work without it. */
+ int sent_int = 0;
+ uint32_t watch_addr;
+
+ if(!cur_target) {
+ /* Report "target exited" if no target */
+ gdb_putpacketz("W00");
+ break;
+ }
+
+ /* Wait for target halt */
+ while(!target_halt_wait(cur_target))
+ if(gdb_if_getchar_to(0) == '\x03') {
+ target_halt_request(cur_target);
+ sent_int = 1;
+ }
+
+ SET_RUN_STATE(0);
+ /* Report reason for halt */
+ if(target_check_hw_wp(cur_target, &watch_addr))
+ /* Watchpoint hit */
+ gdb_putpacket_f("T05watch:%08X;", watch_addr);
+ else if(sent_int)
+ /* Target interrupted */
+ gdb_putpacketz("T02");
+ else
+ gdb_putpacketz("T05");
+ break;
+ }
+
+ /* Optional GDB packet support */
+ case '!': /* Enable Extended GDB Protocol. */
+ /* This doesn't do anything, we support the extended
+ * protocol anyway, but GDB will never send us a 'R'
+ * packet unless we answer 'OK' here.
+ */
+ gdb_putpacket("OK", 2);
+ break;
+
+ case 0x04:
+ case 'D': /* GDB 'detach' command. */
+ if(cur_target) target_detach(cur_target);
+ last_target = cur_target;
+ cur_target = NULL;
+ gdb_putpacket("OK", 2);
+ break;
+
+ case 'k': /* Kill the target */
+ if(cur_target) {
+ target_reset(cur_target);
+ target_detach(cur_target);
+ last_target = cur_target;
+ cur_target = NULL;
+ }
+ break;
+
+ case 'r': /* Reset the target system */
+ case 'R': /* Restart the target program */
+ if(cur_target)
+ target_reset(cur_target);
+ else if(last_target) {
+ cur_target = last_target;
+ target_attach(cur_target);
+ target_reset(cur_target);
+ }
+ break;
+
+ case 'X': { /* 'X addr,len:XX': Write binary data to addr */
+ uint32_t addr, len;
+ int bin;
+ ERROR_IF_NO_TARGET();
+ siscanf(pbuf, "X%08lX,%08lX:%n", &addr, &len, &bin);
+ DEBUG("X packet: addr = %08lX, len = %08lX\n", addr, len);
+ if(((addr & 3) == 0) && ((len & 3) == 0))
+ target_mem_write_words(cur_target, addr, (void*)pbuf+bin, len);
+ else
+ target_mem_write_bytes(cur_target, addr, (void*)pbuf+bin, len);
+ if(target_check_error(cur_target))
+ gdb_putpacket("E01", 3);
+ else
+ gdb_putpacket("OK", 2);
+ break;
+ }
+
+ case 'q': /* General query packet */
+ handle_q_packet(pbuf, size);
+ break;
+
+ case 'v': /* General query packet */
+ handle_v_packet(pbuf, size);
+ break;
+
+ /* These packet implement hardware break-/watchpoints */
+ case 'Z': /* Z type,addr,len: Set breakpoint packet */
+ case 'z': { /* z type,addr,len: Clear breakpoint packet */
+ uint8_t set = (pbuf[0]=='Z')?1:0;
+ int type, len;
+ uint32_t addr;
+ int ret;
+ ERROR_IF_NO_TARGET();
+ /* I have no idea why this doesn't work. Seems to work
+ * with real sscanf() though... */
+ //siscanf(pbuf, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
+ type = pbuf[1] - '0';
+ siscanf(pbuf + 2, ",%08lX,%d", &addr, &len);
+ switch(type) {
+ case 1: /* Hardware breakpoint */
+ if(!cur_target->set_hw_bp) { /* Not supported */
+ gdb_putpacket("", 0);
+ break;
+ }
+ if(set) ret = target_set_hw_bp(cur_target, addr);
+ else ret = target_clear_hw_bp(cur_target, addr);
+
+ if(!ret) gdb_putpacket("OK", 2);
+ else gdb_putpacket("E01", 3);
+
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ if(!cur_target->set_hw_wp) { /* Not supported */
+ gdb_putpacket("", 0);
+ break;
+ }
+ if(set) ret = target_set_hw_wp(cur_target, type, addr, len);
+ else ret = target_clear_hw_wp(cur_target, type, addr, len);
+
+ if(!ret) gdb_putpacket("OK", 2);
+ else gdb_putpacket("E01", 3);
+
+ break;
+ default:
+ gdb_putpacket("", 0);
+ }
+ break;
+ }
+
+ default: /* Packet not implemented */
+ DEBUG("Unsupported packet: %s\n", pbuf);
+ gdb_putpacket("", 0);
+ }
+ }
+}
+
+
+static void
+handle_q_packet(char *packet, int len)
+{
+ /* These 'monitor' commands only available on the real deal */
+ if(!strncmp(packet, "qRcmd,", 6)) {
+ unsigned char *data;
+ int datalen;
+
+ /* calculate size and allocate buffer for command */
+ datalen = (len - 6) / 2;
+ data = alloca(datalen+1);
+ /* dehexify command */
+ unhexify(data, packet+6, datalen);
+ data[datalen] = 0; /* add terminating null */
+
+ if(command_process(data) < 0)
+ gdb_putpacket("", 0);
+ else gdb_putpacket("OK", 2);
+
+ } else if (!strncmp (packet, "qSupported", 10)) {
+ /* Query supported protocol features */
+ gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+", BUF_SIZE);
+
+ } else if (strncmp (packet, "qXfer:memory-map:read::", 23) == 0) {
+ /* Read target XML memory map */
+ uint32_t addr, len;
+ siscanf(packet+23, "%08lX,%08lX", &addr, &len);
+ if((!cur_target) && last_target) {
+ /* Attach to last target if detached. */
+ cur_target = last_target;
+ target_attach(cur_target);
+ }
+ if((!cur_target) || (!cur_target->xml_mem_map)) {
+ gdb_putpacketz("E01");
+ return;
+ }
+ if (addr < strlen (cur_target->xml_mem_map)) {
+ uint8_t reply[len+2];
+ reply[0] = 'm';
+ strncpy (reply + 1, &cur_target->xml_mem_map[addr], len);
+ if(len > strlen(&cur_target->xml_mem_map[addr]))
+ len = strlen(&cur_target->xml_mem_map[addr]);
+ gdb_putpacket(reply, len + 1);
+ } else if (addr == strlen (cur_target->xml_mem_map)) {
+ gdb_putpacketz("l");
+ } else
+ gdb_putpacketz("E01");
+
+ } else gdb_putpacket("", 0);
+}
+
+static void
+handle_v_packet(char *packet, int plen)
+{
+ uint32_t addr, len;
+ int bin;
+ static uint8_t flash_mode = 0;
+
+ if (siscanf(packet, "vAttach;%08lX", &addr) == 1) {
+ /* Attach to remote target processor */
+ target *t;
+ uint32_t i;
+ for(t = target_list, i = 1; t; t = t->next, i++)
+ if(i == addr) {
+ cur_target = t;
+ target_attach(t);
+ gdb_putpacketz("T05");
+ break;
+ }
+ if(!cur_target) /* Failed to attach */
+ gdb_putpacketz("E01");
+
+ } else if (!strcmp(packet, "vRun;")) {
+ /* Run target program. For us (embedded) this means reset. */
+ if(cur_target) {
+ target_reset(cur_target);
+ gdb_putpacketz("T05");
+ } else if(last_target) {
+ cur_target = last_target;
+ target_attach(cur_target);
+ target_reset(cur_target);
+ gdb_putpacketz("T05");
+ } else gdb_putpacketz("E01");
+
+ } else if (siscanf(packet, "vFlashErase:%08lX,%08lX", &addr, &len) == 2) {
+ /* Erase Flash Memory */
+ DEBUG("Flash Erase %08lX %08lX\n", addr, len);
+ if(!cur_target) { gdb_putpacketz("EFF"); return; }
+
+ if(!flash_mode) {
+ /* Reset target if first flash command! */
+ /* This saves us if we're interrupted in IRQ context */
+ target_reset(cur_target);
+ flash_mode = 1;
+ }
+ if(target_flash_erase(cur_target, addr, len) == 0)
+ gdb_putpacketz("OK");
+ else
+ gdb_putpacketz("EFF");
+
+ } else if (siscanf(packet, "vFlashWrite:%08lX:%n", &addr, &bin) == 1) {
+ /* Write Flash Memory */
+ len = plen - bin;
+ DEBUG("Flash Write %08lX %08lX\n", addr, len);
+ if(cur_target && target_flash_write_words(cur_target, addr, (void*)packet + bin, len) == 0)
+ gdb_putpacketz("OK");
+ else
+ gdb_putpacketz("EFF");
+
+ } else if (!strcmp(packet, "vFlashDone")) {
+ /* Commit flash operations. */
+ gdb_putpacketz("OK");
+ flash_mode = 0;
+
+ } else
+ gdb_putpacket("", 0);
+}
+
diff --git a/src/gdb_packet.c b/src/gdb_packet.c
new file mode 100644
index 0000000..0c690f8
--- /dev/null
+++ b/src/gdb_packet.c
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the GDB Remote Serial Debugging protocol packet
+ * reception and transmission as well as some convenience functions.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdarg.h>
+
+#include <alloca.h>
+
+#include "general.h"
+#include "gdb_if.h"
+#include "gdb_packet.h"
+#include "hex_utils.h"
+
+int
+gdb_getpacket(unsigned char *packet, int size)
+{
+ unsigned char c;
+ unsigned char csum;
+ char recv_csum[3];
+ int i;
+
+ while(1) {
+ /* Wait for packet start */
+ while((packet[0] = gdb_if_getchar()) != '$')
+ if(packet[0] == 0x04) return 1;
+
+ i = 0; csum = 0;
+ /* Capture packet data into buffer */
+ while((c = gdb_if_getchar()) != '#') {
+
+ if(i == size) break; /* Oh shit */
+
+ if(c == '$') { /* Restart capture */
+ i = 0;
+ csum = 0;
+ continue;
+ }
+ if(c == '}') { /* escaped char */
+ c = gdb_if_getchar();
+ csum += c + '}';
+ packet[i++] = c ^ 0x20;
+ continue;
+ }
+ csum += c;
+ packet[i++] = c;
+ }
+ recv_csum[0] = gdb_if_getchar();
+ recv_csum[1] = gdb_if_getchar();
+ recv_csum[2] = 0;
+
+ /* return packet if checksum matches */
+ if(csum == strtol(recv_csum, NULL, 16)) break;
+
+ /* get here if checksum fails */
+ gdb_if_putchar('-', 1); /* send nack */
+ }
+ gdb_if_putchar('+', 1); /* send ack */
+ packet[i] = 0;
+ return i;
+}
+
+void gdb_putpacket(unsigned char *packet, int size)
+{
+ int i;
+ unsigned char csum;
+ unsigned char c;
+ char xmit_csum[3];
+ int tries = 0;
+
+ do {
+ csum = 0;
+ gdb_if_putchar('$', 0);
+ for(i = 0; i < size; i++) {
+ c = packet[i];
+ if((c == '$') || (c == '#') || (c == '}')) {
+ gdb_if_putchar('}', 0);
+ gdb_if_putchar(c ^ 0x20, 0);
+ csum += '}' + (c ^ 0x20);
+ } else {
+ gdb_if_putchar(c, 0);
+ csum += c;
+ }
+ }
+ gdb_if_putchar('#', 0);
+ siprintf(xmit_csum, "%02X", csum);
+ gdb_if_putchar(xmit_csum[0], 0);
+ gdb_if_putchar(xmit_csum[1], 1);
+
+ } while((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
+
+}
+
+void gdb_putpacket_f(const unsigned char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int size;
+
+ va_start(ap, fmt);
+ size = vasiprintf(&buf, fmt, ap);
+ gdb_putpacket(buf, size);
+ free(buf);
+ va_end(ap);
+}
+
+void gdb_out(const char *buf)
+{
+ char *hexdata;
+ int i;
+
+ hexdata = alloca((i = strlen(buf)*2 + 1) + 1);
+ hexdata[0] = 'O';
+ hexify(hexdata+1, buf, strlen(buf));
+ gdb_putpacket(hexdata, i);
+}
+
+void gdb_outf(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, fmt);
+ vasiprintf(&buf, fmt, ap);
+ gdb_out(buf);
+ free(buf);
+ va_end(ap);
+}
+
diff --git a/src/hex_utils.c b/src/hex_utils.c
new file mode 100644
index 0000000..6629f10
--- /dev/null
+++ b/src/hex_utils.c
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Convenience function to convert to/from ascii strings of hex digits.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "hex_utils.h"
+
+static char hexdigits[] = "0123456789abcdef";
+
+char * hexify(char *hex, const unsigned char *buf, int size)
+{
+ char *tmp = hex;
+
+ while(size--) {
+ *tmp++ = hexdigits[*buf >> 4];
+ *tmp++ = hexdigits[*buf++ & 0xF];
+ }
+ *tmp++ = 0;
+
+ return hex;
+}
+
+static uint8_t unhex_digit(char hex)
+{
+ uint8_t tmp = hex - '0';
+ if(tmp > 9)
+ tmp -= 'A' - '0' - 10;
+ if(tmp > 16)
+ tmp -= 'a' - 'A';
+ return tmp;
+}
+
+char * unhexify(unsigned char *buf, const char *hex, int size)
+{
+ while(size--) {
+ *buf = unhex_digit(*hex++) << 4;
+ *buf++ |= unhex_digit(*hex++);
+ }
+ return buf;
+}
+
diff --git a/src/include/adiv5.h b/src/include/adiv5.h
new file mode 100644
index 0000000..a9cacf1
--- /dev/null
+++ b/src/include/adiv5.h
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ADIV5_H
+#define __ADIV5_H
+
+#include "general.h"
+#include "jtag_scan.h"
+#include "target.h"
+
+#define DP_ABORT 0x0
+#define DP_CTRLSTAT 0x4
+#define DP_SELECT 0x8
+#define DP_RDBUFF 0xC
+
+
+/* Try to keep this somewhat absract for later adding SW-DP */
+typedef struct ADIv5_DP_s {
+ struct ADIv5_DP_s *next;
+ uint32_t idcode;
+
+ void (*dp_write)(struct ADIv5_DP_s *dp, uint8_t addr, uint32_t value);
+ uint32_t (*dp_read)(struct ADIv5_DP_s *dp, uint8_t addr);
+
+ void (*ap_write)(struct ADIv5_DP_s *dp, uint8_t addr, uint32_t value);
+ uint32_t (*ap_read)(struct ADIv5_DP_s *dp, uint8_t addr);
+
+ uint32_t (*error)(struct ADIv5_DP_s *dp);
+
+ uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t APnDP, uint8_t RnW,
+ uint8_t addr, uint32_t value);
+
+ union {
+ jtag_dev_t *dev;
+ uint8_t fault;
+ };
+} ADIv5_DP_t;
+
+static inline void adiv5_dp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ dp->dp_write(dp, addr, value);
+}
+
+static inline uint32_t adiv5_dp_read(ADIv5_DP_t *dp, uint8_t addr)
+{
+ return dp->dp_read(dp, addr);
+}
+
+static inline void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+{
+ dp->ap_write(dp, addr, value);
+}
+
+static inline uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
+{
+ return dp->ap_read(dp, addr);
+}
+
+static inline uint32_t adiv5_dp_error(ADIv5_DP_t *dp)
+{
+ return dp->error(dp);
+}
+
+static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t APnDP,
+ uint8_t RnW, uint8_t addr, uint32_t value)
+{
+ return dp->low_access(dp, APnDP, RnW, addr, value);
+}
+
+extern ADIv5_DP_t *adiv5_dp_list;
+
+typedef struct ADIv5_AP_s {
+ ADIv5_DP_t *dp;
+ uint8_t apsel;
+
+ uint32_t idr;
+ uint32_t cfg;
+ uint32_t base;
+} ADIv5_AP_t;
+
+struct target_ap_s {
+ target t;
+ ADIv5_AP_t *ap;
+};
+
+extern ADIv5_AP_t adiv5_aps[5];
+extern int adiv5_ap_count;
+
+void adiv5_free_all(void);
+void adiv5_dp_init(ADIv5_DP_t *dp);
+
+uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr);
+void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value);
+
+void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value);
+uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr);
+
+void adiv5_jtag_dp_handler(jtag_dev_t *dev);
+int adiv5_swdp_scan(void);
+
+#endif
+
diff --git a/src/include/command.h b/src/include/command.h
new file mode 100644
index 0000000..34db13d
--- /dev/null
+++ b/src/include/command.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COMMAND_H
+#define __COMMAND_H
+
+#include "general.h"
+
+int command_process(char *cmd);
+typedef void (*cmd_handler)(int argc, const char **argv);
+
+struct command_s {
+ const char *cmd;
+ cmd_handler handler;
+
+ const char *help;
+};
+
+#endif
+
diff --git a/src/include/cortexm3.h b/src/include/cortexm3.h
new file mode 100644
index 0000000..7a3d449
--- /dev/null
+++ b/src/include/cortexm3.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __CORTEXM3_H
+#define __CORTEXM3_H
+
+#include "target.h"
+
+int cm3_probe(struct target_s *target);
+
+#endif
+
diff --git a/src/include/gdb_if.h b/src/include/gdb_if.h
new file mode 100644
index 0000000..930a3d6
--- /dev/null
+++ b/src/include/gdb_if.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDB_IF_H
+#define __GDB_IF_H
+
+int gdb_if_init(void);
+unsigned char gdb_if_getchar(void);
+unsigned char gdb_if_getchar_to(int timeout);
+void gdb_if_putchar(unsigned char c, int flush);
+
+#endif
+
diff --git a/src/include/gdb_main.h b/src/include/gdb_main.h
new file mode 100644
index 0000000..f57e5e3
--- /dev/null
+++ b/src/include/gdb_main.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDB_MAIN_H
+#define __GDB_MAIN_H
+
+void gdb_main(void);
+
+#endif
+
diff --git a/src/include/gdb_packet.h b/src/include/gdb_packet.h
new file mode 100644
index 0000000..9f5430f
--- /dev/null
+++ b/src/include/gdb_packet.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDB_PACKET_H
+#define __GDB_PACKET_H
+
+#include <string.h>
+
+int gdb_getpacket(unsigned char *packet, int size);
+void gdb_putpacket(unsigned char *packet, int size);
+#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
+void gdb_putpacket_f(const unsigned char *packet, ...);
+
+void gdb_out(const char *buf);
+void gdb_outf(const char *fmt, ...);
+
+#endif
+
+
diff --git a/src/include/general.h b/src/include/general.h
new file mode 100644
index 0000000..65ec2af
--- /dev/null
+++ b/src/include/general.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GENERAL_H
+#define __GENERAL_H
+
+#include "platform.h"
+
+#ifndef DEBUG
+#include <stdio.h>
+#define DEBUG printf
+#endif
+
+#include <stdint.h>
+
+#endif
+
diff --git a/src/include/hex_utils.h b/src/include/hex_utils.h
new file mode 100644
index 0000000..3aa210b
--- /dev/null
+++ b/src/include/hex_utils.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __HEX_UTILS_H
+#define __HEX_UTILS_H
+
+char * hexify(char *hex, const unsigned char *buf, int size);
+
+char * unhexify(unsigned char *buf, const char *hex, int size);
+
+#endif
+
diff --git a/src/include/jtag_scan.h b/src/include/jtag_scan.h
new file mode 100644
index 0000000..083aea0
--- /dev/null
+++ b/src/include/jtag_scan.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JTAG_SCAN_H
+#define __JTAG_SCAN_H
+
+#include "general.h"
+
+#define JTAG_MAX_DEVS 5
+#define JTAG_MAX_IR_LEN 16
+
+typedef struct jtag_dev_s {
+ union {
+ uint8_t dev;
+ uint8_t dr_prescan;
+ };
+ uint8_t dr_postscan;
+
+ uint8_t ir_len;
+ uint8_t ir_prescan;
+ uint8_t ir_postscan;
+
+ uint32_t idcode;
+ char *descr;
+
+ uint32_t current_ir;
+
+} jtag_dev_t;
+
+extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
+extern int jtag_dev_count;
+
+int jtag_scan(void);
+
+void jtag_dev_write_ir(jtag_dev_t *dev, uint32_t ir);
+void jtag_dev_shift_dr(jtag_dev_t *dev, uint8_t *dout, const uint8_t *din, int ticks);
+
+#endif
+
diff --git a/src/include/jtagtap.h b/src/include/jtagtap.h
new file mode 100644
index 0000000..9fe3e0d
--- /dev/null
+++ b/src/include/jtagtap.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JTAGTAP_H
+#define __JTAGTAP_H
+
+#include "general.h"
+
+/* Note: Signal names are as for the device under test. */
+
+int jtagtap_init(void);
+
+void jtagtap_reset(void);
+
+void jtagtap_srst(void);
+
+uint8_t jtagtap_next(const uint8_t TMS, const uint8_t TDI);
+/* tap_next executes one state transision in the JTAG TAP state machine:
+ * - Ensure TCK is low
+ * - Assert the values of TMS and TDI
+ * - Assert TCK (TMS and TDO are latched on rising edge
+ * - Caputure the value on TDO
+ * - Release TCK.
+ */
+
+void jtagtap_tms_seq(uint32_t MS, int ticks);
+void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks);
+void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks);
+/* Shift out a sequence on MS and DI, capture data to DO.
+ * - This is not endian safe: First byte will always be first shifted out.
+ * - DO may be NULL to ignore captured data.
+ * - DO may be point to the same address as DI.
+ */
+
+/* generic soft reset: 1, 1, 1, 1, 1, 0 */
+#define jtagtap_soft_reset() \
+ jtagtap_tms_seq(0x1F, 6)
+
+/* Goto Shift-IR: 1, 1, 0, 0 */
+#define jtagtap_shift_ir() \
+ jtagtap_tms_seq(0x03, 4)
+
+/* Goto Shift-DR: 1, 0, 0 */
+#define jtagtap_shift_dr() \
+ jtagtap_tms_seq(0x01, 3)
+
+/* Goto Run-test/Idle: 1, 1, 0 */
+#define jtagtap_return_idle() \
+ jtagtap_tms_seq(0x01, 2)
+
+#endif
+
diff --git a/src/include/lmi.h b/src/include/lmi.h
new file mode 100644
index 0000000..ac21a33
--- /dev/null
+++ b/src/include/lmi.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LMI_H
+#define __LMI_H
+
+#include "target.h"
+
+int lmi_probe(struct target_s *target);
+
+#endif
+
diff --git a/src/include/stm32_tgt.h b/src/include/stm32_tgt.h
new file mode 100644
index 0000000..e63310f
--- /dev/null
+++ b/src/include/stm32_tgt.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_TGT_H
+#define __STM32_TGT_H
+
+#include "target.h"
+
+int stm32_probe(struct target_s *target);
+
+#endif
+
diff --git a/src/include/swdptap.h b/src/include/swdptap.h
new file mode 100644
index 0000000..a6252dc
--- /dev/null
+++ b/src/include/swdptap.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SWDPTAP_H
+#define __SWDPTAP_H
+
+#include "general.h"
+
+int swdptap_init(void);
+void swdptap_reset(void);
+
+void swdptap_turnaround(uint8_t dir);
+uint8_t swdptap_bit_in(void);
+void swdptap_bit_out(uint8_t val);
+
+uint32_t swdptap_seq_in(int ticks);
+uint8_t swdptap_seq_in_parity(uint32_t *data, int ticks);
+void swdptap_seq_out(uint32_t MS, int ticks);
+void swdptap_seq_out_parity(uint32_t MS, int ticks);
+
+#endif
+
diff --git a/src/include/target.h b/src/include/target.h
new file mode 100644
index 0000000..bd32c0f
--- /dev/null
+++ b/src/include/target.h
@@ -0,0 +1,173 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Provides an abstract 'target object', the 'methods' of which must be
+ * implemented by a target driver when a supported device is detected.
+ */
+
+#ifndef __TARGET_H
+#define __TARGET_H
+
+/* Halt/resume functions */
+#define target_attach(target) \
+ (target)->attach(target)
+
+#define target_detach(target) \
+ (target)->detach(target)
+
+#define target_check_error(target) \
+ (target)->check_error(target)
+
+
+/* Memory access functions */
+#define target_mem_read_words(target, dest, src, len) \
+ (target)->mem_read_words((target), (dest), (src), (len))
+
+#define target_mem_write_words(target, dest, src, len) \
+ (target)->mem_write_words((target), (dest), (src), (len))
+
+#define target_mem_read_bytes(target, dest, src, len) \
+ (target)->mem_read_bytes((target), (dest), (src), (len))
+
+#define target_mem_write_bytes(target, dest, src, len) \
+ (target)->mem_write_bytes((target), (dest), (src), (len))
+
+
+/* Register access functions */
+#define target_regs_read(target, data) \
+ (target)->regs_read((target), (data))
+
+#define target_regs_write(target, data) \
+ (target)->regs_write((target), (data))
+
+#define target_pc_read(target) \
+ (target)->pc_read((target))
+
+#define target_pc_write(target, val) \
+ (target)->pc_write((target), (val))
+
+
+/* Halt/resume functions */
+#define target_reset(target) \
+ (target)->reset(target)
+
+#define target_halt_request(target) \
+ (target)->halt_request(target)
+
+#define target_halt_wait(target) \
+ (target)->halt_wait(target)
+
+#define target_halt_resume(target, step) \
+ (target)->halt_resume((target), (step))
+
+
+/* Break-/watchpoint functions */
+#define target_set_hw_bp(target, addr) \
+ (target)->set_hw_bp((target), (addr))
+
+#define target_clear_hw_bp(target, addr) \
+ (target)->clear_hw_bp((target), (addr))
+
+
+#define target_set_hw_wp(target, type, addr, len) \
+ (target)->set_hw_wp((target), (type), (addr), (len))
+
+#define target_clear_hw_wp(target, type, addr, len) \
+ (target)->clear_hw_wp((target), (type), (addr), (len))
+
+
+#define target_check_hw_wp(target, addr) \
+ (target)->check_hw_wp((target), (addr))
+
+
+/* Flash memory access functions */
+#define target_flash_erase(target, addr, len) \
+ (target)->flash_erase((target), (addr), (len))
+
+#define target_flash_write_words(target, dest, src, len) \
+ (target)->flash_write_words((target), (dest), (src), (len))
+
+
+#define TARGET_LIST_FREE() { \
+ while(target_list) { \
+ target *t = target_list->next; \
+ free(target_list); \
+ target_list = t; \
+ } \
+ last_target = cur_target = NULL; \
+}
+
+
+typedef struct target_s {
+ /* Attach/Detach funcitons */
+ void (*attach)(struct target_s *target);
+ void (*detach)(struct target_s *target);
+ int (*check_error)(struct target_s *target);
+
+ /* Memory access functions */
+ int (*mem_read_words)(struct target_s *target, uint32_t *dest, uint32_t src,
+ int len);
+ int (*mem_write_words)(struct target_s *target, uint32_t dest,
+ const uint32_t *src, int len);
+
+ int (*mem_read_bytes)(struct target_s *target, uint8_t *dest, uint32_t src,
+ int len);
+ int (*mem_write_bytes)(struct target_s *target, uint32_t dest,
+ const uint8_t *src, int len);
+
+ /* Register access functions */
+ int regs_size;
+ int (*regs_read)(struct target_s *target, void *data);
+ int (*regs_write)(struct target_s *target, const void *data);
+
+ uint32_t (*pc_read)(struct target_s *target);
+ int (*pc_write)(struct target_s *target, const uint32_t val);
+
+ /* Halt/resume functions */
+ void (*reset)(struct target_s *target);
+ void (*halt_request)(struct target_s *target);
+ int (*halt_wait)(struct target_s *target);
+ void (*halt_resume)(struct target_s *target, uint8_t step);
+
+ /* Break-/watchpoint functions */
+ int (*set_hw_bp)(struct target_s *target, uint32_t addr);
+ int (*clear_hw_bp)(struct target_s *target, uint32_t addr);
+
+ int (*set_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
+ int (*clear_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
+
+ int (*check_hw_wp)(struct target_s *target, uint32_t *addr);
+
+ /* Flash memory access functions */
+ const char *xml_mem_map;
+ int (*flash_erase)(struct target_s *target, uint32_t addr, int len);
+ int (*flash_write_words)(struct target_s *target, uint32_t dest,
+ const uint32_t *src, int len);
+
+ const char *driver;
+
+ int size;
+ struct target_s *next;
+} target;
+
+extern target *target_list, *cur_target, *last_target;
+
+#endif
+
diff --git a/src/jtag_scan.c b/src/jtag_scan.c
new file mode 100644
index 0000000..ae48826
--- /dev/null
+++ b/src/jtag_scan.c
@@ -0,0 +1,232 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements JTAG protocol support. Provides functionality
+ * to detect devices on the scan chain and read their IDCODEs.
+ * It depends on the low-level function provided by the platform's jtagtap.c.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+
+#include <alloca.h>
+
+#include "general.h"
+#include "jtagtap.h"
+#include "jtag_scan.h"
+
+#include "gdb_packet.h"
+
+#include "adiv5.h"
+
+struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
+int jtag_dev_count;
+
+static struct jtag_dev_descr_s {
+ uint32_t idcode;
+ uint32_t idmask;
+ char *descr;
+ void (*handler)(jtag_dev_t *dev);
+} dev_descr[] = {
+ {.idcode = 0x0BA00477, .idmask = 0x0FFFFFFF,
+ .descr = "ARM Limited: ADIv5 JTAG-DP port.",
+ .handler = adiv5_jtag_dp_handler},
+ {.idcode = 0x06410041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, Medium density."},
+ {.idcode = 0x06412041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, Low density."},
+ {.idcode = 0x06414041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, High density."},
+ {.idcode = 0x06418041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, Connectivity Line."},
+ {.idcode = 0x06420041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, Value Line."},
+ {.idcode = 0x06428041, .idmask = 0x0FFFFFFF,
+ .descr = "ST Microelectronics: STM32, Value Line, High density."},
+/* Just for fun, unsupported */
+ {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."},
+ {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."},
+ {.idcode = 0x20270013, .idmask = 0xFFFFFFFF, .descr = "Intel: i80386ex."},
+ {.idcode = 0, .idmask = 0, .descr = "Unknown"},
+};
+
+/* bucket of ones for don't care TDI */
+static const char ones[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
+
+/* Scan JTAG chain for devices, store IR length and IDCODE (if present).
+ * Reset TAP state machine.
+ * Select Shift-IR state.
+ * Each device is assumed to shift out IR at 0x01. (this may not always be true)
+ * Shift in ones until we read two consecutive ones, then we have shifted out the
+ * IRs of all devices.
+ *
+ * After this process all the IRs are loaded with the BYPASS command.
+ * Select Shift-DR state.
+ * Shift in ones and count zeros shifted out. Should be one for each device.
+ * Check this against device count obtained by IR scan above.
+ *
+ * Reset the TAP state machine again. This should load all IRs with IDCODE.
+ * For each device, shift out one bit. If this is zero IDCODE isn't present,
+ * continue to next device. If this is one shift out the remaining 31 bits
+ * of the IDCODE register.
+ */
+int jtag_scan(void)
+{
+ int i;
+ uint32_t j;
+
+ TARGET_LIST_FREE();
+
+ jtag_dev_count = 0;
+ memset(&jtag_devs, 0, sizeof(jtag_devs));
+
+#warning "These should be elsewhere!"
+ adiv5_free_all();
+
+ /* Run throught the SWD to JTAG sequence for the case where an attached SWJ-DP is
+ * in SW-DP mode.
+ */
+ DEBUG("Resetting TAP\n");
+ jtagtap_init();
+ jtagtap_reset();
+
+ DEBUG("Change state to Shift-IR\n");
+ jtagtap_shift_ir();
+
+ DEBUG("Scanning out IRs\n");
+ if(!jtagtap_next(0, 1)) {
+ DEBUG("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n");
+ jtag_dev_count = -1;
+ return -1; /* must be 1 */
+ }
+ jtag_devs[0].ir_len = 1; j = 1;
+ while((jtag_dev_count <= JTAG_MAX_DEVS) &&
+ (jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) {
+ if(jtagtap_next(0, 1)) {
+ if(jtag_devs[jtag_dev_count].ir_len == 1) break;
+ jtag_devs[++jtag_dev_count].ir_len = 1;
+ jtag_devs[jtag_dev_count].ir_prescan = j;
+ jtag_devs[jtag_dev_count].dev = jtag_dev_count;
+ } else jtag_devs[jtag_dev_count].ir_len++;
+ j++;
+ }
+ if(jtag_dev_count > JTAG_MAX_DEVS) {
+ DEBUG("jtag_scan: Maximum device count exceeded\n");
+ jtag_dev_count = -1;
+ return -1;
+ }
+ if(jtag_devs[jtag_dev_count].ir_len > JTAG_MAX_IR_LEN) {
+ DEBUG("jtag_scan: Maximum IR length exceeded\n");
+ jtag_dev_count = -1;
+ return -1;
+ }
+
+ DEBUG("Return to Run-Test/Idle\n");
+ jtagtap_next(1, 1);
+ jtagtap_return_idle();
+
+ /* All devices should be in BYPASS now */
+
+ /* Count device on chain */
+ DEBUG("Change state to Shift-DR\n");
+ jtagtap_shift_dr();
+ for(i = 0; (jtagtap_next(0, 1) == 0) && (i <= jtag_dev_count); i++)
+ jtag_devs[i].dr_postscan = jtag_dev_count - i - 1;
+
+ if(i != jtag_dev_count) {
+ DEBUG("jtag_scan: Sanity check failed: "
+ "BYPASS dev count doesn't match IR scan\n");
+ jtag_dev_count = -1;
+ return -1;
+ }
+
+ DEBUG("Return to Run-Test/Idle\n");
+ jtagtap_next(1, 1);
+ jtagtap_return_idle();
+ if(!jtag_dev_count) {
+ morse("NO TARGETS.", 1);
+ return 0;
+ }
+
+ /* Fill in the ir_postscan fields */
+ for(i = jtag_dev_count - 1; i; i--)
+ jtag_devs[i-1].ir_postscan = jtag_devs[i].ir_postscan +
+ jtag_devs[i].ir_len;
+
+ /* Reset jtagtap: should take all devs to IDCODE */
+ jtagtap_reset();
+ jtagtap_shift_dr();
+ for(i = 0; i < jtag_dev_count; i++) {
+ if(!jtagtap_next(0, 1)) continue;
+ jtag_devs[i].idcode = 1;
+ for(j = 2; j; j <<= 1)
+ if(jtagtap_next(0, 1)) jtag_devs[i].idcode |= j;
+
+ }
+ DEBUG("Return to Run-Test/Idle\n");
+ jtagtap_next(1, 1);
+ jtagtap_return_idle();
+
+ /* Check for known devices and handle accordingly */
+ for(i = 0; i < jtag_dev_count; i++)
+ for(j = 0; dev_descr[j].idcode; j++)
+ if((jtag_devs[i].idcode & dev_descr[j].idmask) ==
+ dev_descr[j].idcode) {
+ jtag_devs[i].current_ir = -1;
+ /* Save description in table */
+ jtag_devs[i].descr = dev_descr[j].descr;
+ /* Call handler to initialise/probe device further */
+ if(dev_descr[j].handler)
+ dev_descr[j].handler(&jtag_devs[i]);
+ break;
+ }
+
+ if(!target_list) morse("NO TARGETS.", 1);
+ else morse(NULL, 0);
+
+ return jtag_dev_count;
+}
+
+void jtag_dev_write_ir(jtag_dev_t *d, uint32_t ir)
+{
+ if(ir == d->current_ir) return;
+ d->current_ir = ir;
+
+ jtagtap_shift_ir();
+ jtagtap_tdi_seq(0, ones, d->ir_prescan);
+ jtagtap_tdi_seq(d->ir_postscan?0:1, (void*)&ir, d->ir_len);
+ jtagtap_tdi_seq(1, ones, d->ir_postscan);
+ jtagtap_return_idle();
+}
+
+void jtag_dev_shift_dr(jtag_dev_t *d, uint8_t *dout, const uint8_t *din, int ticks)
+{
+ jtagtap_shift_dr();
+ jtagtap_tdi_seq(0, ones, d->dr_prescan);
+ if(dout)
+ jtagtap_tdi_tdo_seq((void*)dout, d->dr_postscan?0:1, (void*)din, ticks);
+ else
+ jtagtap_tdi_seq(d->dr_postscan?0:1, (void*)din, ticks);
+ jtagtap_tdi_seq(1, ones, d->dr_postscan);
+ jtagtap_return_idle();
+}
+
diff --git a/src/jtagtap_generic.c b/src/jtagtap_generic.c
new file mode 100644
index 0000000..a2e723b
--- /dev/null
+++ b/src/jtagtap_generic.c
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file provides generic forms of the low-level jtagtap functions
+ * for platforms that don't require optimised forms.
+ */
+
+#ifdef PROVIDE_GENERIC_JTAGTAP_TMS_SEQ
+void
+jtagtap_tms_seq(uint32_t MS, int ticks)
+{
+ while(ticks--) {
+ jtagtap_next(MS & 1, 1);
+ MS >>= 1;
+ }
+}
+#endif
+
+
+#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
+void
+jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
+{
+ uint8_t index = 1;
+ while(ticks--) {
+ if(jtagtap_next(ticks?0:final_tms, *DI & index)) {
+ *DO |= index;
+ } else {
+ *DO &= ~index;
+ }
+ if(!(index <<= 1)) {
+ index = 1;
+ DI++; DO++;
+ }
+ }
+}
+#endif
+
+
+#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
+void
+jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
+{
+ uint8_t index = 1;
+ while(ticks--) {
+ jtagtap_next(ticks?0:final_tms, *DI & index);
+ if(!(index <<= 1)) {
+ index = 1;
+ DI++;
+ }
+ }
+}
+#endif
+
+
diff --git a/src/lmi.c b/src/lmi.c
new file mode 100644
index 0000000..51ea0e8
--- /dev/null
+++ b/src/lmi.c
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements TI/LMI LM3S target specific functions providing
+ * the XML memory map and Flash memory programming.
+ *
+ * Issues:
+ * No detection of the target device.
+ * Add reference to documentation.
+ * Flash erase is very slow.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "adiv5.h"
+#include "target.h"
+
+static int lmi_flash_erase(struct target_s *target, uint32_t addr, int len);
+static int lmi_flash_write_words(struct target_s *target, uint32_t dest,
+ const uint32_t *src, int len);
+
+static const char lmi_driver_str[] = "LuminaryMicro Stellaris";
+
+static const char lmi_xml_memory_map[] = "<?xml version=\"1.0\"?>"
+/* "<!DOCTYPE memory-map "
+ " PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
+ " \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
+ "<memory-map>"
+ " <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
+ "</memory-map>";
+
+
+uint16_t lmi_flash_write_stub[] = {
+// _start:
+ 0x4809, // ldr r0, [pc, #36] // _flashbase
+ 0x490b, // ldr r1, [pc, #44] // _addr
+ 0x467a, // mov r2, pc
+ 0x3230, // adds r2, #48
+ 0x4b0a, // ldr r3, [pc, #40] // _size
+ 0x4d08, // ldr r5, [pc, #32] // _flash_write_cmd
+// _next:
+ 0xb15b, // cbz r3, _done
+ 0x6001, // str r1, [r0, #0]
+ 0x6814, // ldr r4, [r2]
+ 0x6044, // str r4, [r0, #4]
+ 0x6085, // str r5, [r0, #8]
+// _wait:
+ 0x6884, // ldr r4, [r0, #8]
+ 0x2601, // movs r6, #1
+ 0x4234, // tst r4, r6
+ 0xd1fb, // bne _wait
+
+ 0x3b01, // subs r3, #1
+ 0x3104, // adds r1, #4
+ 0x3204, // adds r2, #4
+ 0xe7f2, // b _next
+// _done:
+ 0xbe00, // bkpt
+// _flashbase:
+ 0xd000, 0x400f, // .word 0x400fd000
+// _flash_write_cmd:
+ 0x0001, 0xa442, // .word 0xa4420001
+// _addr:
+// 0x0000, 0x0000,
+// _size:
+// 0x0000, 0x0000,
+// _data:
+// ...
+};
+
+int lmi_probe(struct target_s *target)
+{
+ /* How do we really probe the LMI device??? */
+ target->driver = lmi_driver_str;
+ target->xml_mem_map = lmi_xml_memory_map;
+ target->flash_erase = lmi_flash_erase;
+ target->flash_write_words = lmi_flash_write_words;
+ return 0;
+}
+
+int lmi_flash_erase(struct target_s *target, uint32_t addr, int len)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t tmp;
+
+ addr &= 0xFFFFFC00;
+ len &= 0xFFFFFC00;
+
+ /* setup word access */
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+
+ /* select Flash Control */
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0x400FD000);
+
+ while(len) {
+ /* write address to FMA */
+ adiv5_ap_write(t->ap, 0x10, addr); /* Required to switch banks */
+ /* set ERASE bit in FMC */
+ adiv5_dp_low_access(t->ap->dp, 1, 0, 0x08, 0xA4420002);
+ /* Read FMC to poll for ERASE bit */
+ adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
+ do {
+ tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
+ } while (tmp & 2);
+
+ len -= 0x400;
+ addr += 0x400;
+ }
+ return 0;
+}
+
+int lmi_flash_write_words(struct target_s *target, uint32_t dest,
+ const uint32_t *src, int len)
+{
+#if 0
+ struct target_ap_s *t = (void *)target;
+ uint32_t tmp;
+
+ dest &= 0xFFFFFFFC;
+ len &= 0xFFFFFFFC;
+
+ /* setup word access */
+ adiv5_ap_write(t->ap, 0x00, 0xA2000052);
+
+ /* select Flash Control */
+ while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x04, 0x400FD000) != 2);
+
+ while(len) {
+ /* write address to FMA */
+ adiv5_ap_write(t->ap, 0x10, dest); /* Required to switch banks */
+ /* Write data in FMD */
+ while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x04, *src++) != 2);
+ /* set ERASE bit in FMC */
+ while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x08, 0xA4420001) != 2);
+ /* Read FMC to poll for ERASE bit */
+ while(adiv5_dp_access(t->ap->dp, NULL, 1, 0x08, 0) != 2);
+ do {
+ while(adiv5_dp_access(t->ap->dp, &tmp, 1, 0x08, 0) != 2);
+ } while (tmp & 1);
+
+ len -= 0x4;
+ dest += 0x4;
+ }
+#else
+ uint32_t data[(len>>2)+2];
+ data[0] = dest;
+ data[1] = len >> 2;
+ memcpy(&data[2], src, len);
+ DEBUG("Sending stub\n");
+ target_mem_write_words(target, 0x20000000, (void*)lmi_flash_write_stub, 0x30);
+ DEBUG("Sending data\n");
+ target_mem_write_words(target, 0x20000030, data, len + 8);
+ DEBUG("Running stub\n");
+ target_pc_write(target, 0x20000000);
+ target_halt_resume(target, 0);
+ DEBUG("Waiting for halt\n");
+ while(!target_halt_wait(target));
+#endif
+ return 0;
+}
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..78a863a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Provides main entry point. Initialise subsystems and enter GDB
+ * protocol loop.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gdb_if.h"
+#include "gdb_main.h"
+#include "jtagtap.h"
+#include "jtag_scan.h"
+
+#include "target.h"
+
+int
+main(void)
+{
+ assert(platform_init() == 0);
+ assert(gdb_if_init() == 0);
+ assert(jtagtap_init() == 0);
+
+ jtag_scan();
+// adiv5_swdp_scan();
+
+ PLATFORM_SET_FATAL_ERROR_RECOVERY();
+
+ gdb_main();
+
+ /* Should never get here */
+ return 0;
+}
+
diff --git a/src/stm32/Makefile.inc b/src/stm32/Makefile.inc
new file mode 100644
index 0000000..d9bc876
--- /dev/null
+++ b/src/stm32/Makefile.inc
@@ -0,0 +1,25 @@
+CC = arm-cortexm3-eabi-gcc
+OBJCOPY = arm-cortexm3-eabi-objcopy
+
+CFLAGS += -Istm32/include
+LDFLAGS_BOOT = -lopencm3_stm32 -Wl,--defsym,_stack=0x20005000 \
+ -Wl,-T,stm32/blackmagic.ld -nostartfiles -lc -lnosys -Wl,-Map=mapfile
+LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
+
+SRC += cdcacm.c \
+ platform.c \
+
+all: blackmagic.bin blackmagic_dfu.bin
+
+blackmagic.bin: blackmagic
+ $(OBJCOPY) -O binary $^ $@
+
+blackmagic_dfu: usbdfu.c
+ $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS_BOOT)
+
+blackmagic_dfu.bin: blackmagic_dfu
+ $(OBJCOPY) -O binary $^ $@
+
+host_clean:
+ -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin
+
diff --git a/src/stm32/blackmagic.ld b/src/stm32/blackmagic.ld
new file mode 100644
index 0000000..52314c5
--- /dev/null
+++ b/src/stm32/blackmagic.ld
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Define memory regions. */
+MEMORY
+{
+ rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
+}
+
+/* Include the common ld script from libopenstm32. */
+INCLUDE libopencm3_stm32.ld
+
diff --git a/src/stm32/cdcacm.c b/src/stm32/cdcacm.c
new file mode 100644
index 0000000..b2fe1d1
--- /dev/null
+++ b/src/stm32/cdcacm.c
@@ -0,0 +1,304 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements a the USB Communications Device Class - Abstract
+ * Control Model (CDC-ACM) as defined in CDC PSTN subclass 1.2.
+ * A Device Firmware Upgrade (DFU 1.1) class interface is provided for
+ * field firmware upgrade.
+ *
+ * The device's unique id is used as the USB serial number string.
+ */
+
+#include <libopencm3/stm32/nvic.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/cdc.h>
+#include <libopencm3/stm32/scb.h>
+#include <libopencm3/usb/dfu.h>
+#include <stdlib.h>
+
+#include "platform.h"
+
+static char *get_dev_unique_id(char *serial_no);
+
+static int configured;
+
+static const struct usb_device_descriptor dev = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = USB_CLASS_CDC,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = 0x0483,
+ .idProduct = 0x5740,
+ .bcdDevice = 0x0200,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 1,
+};
+
+/* This notification endpoint isn't implemented. According to CDC spec its
+ * optional, but its absence causes a NULL pointer dereference in Linux cdc_acm
+ * driver. */
+static const struct usb_endpoint_descriptor comm_endp[] = {{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x83,
+ .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
+ .wMaxPacketSize = 16,
+ .bInterval = 255,
+}};
+
+static const struct usb_endpoint_descriptor data_endp[] = {{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x01,
+ .bmAttributes = USB_ENDPOINT_ATTR_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+}, {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x82,
+ .bmAttributes = USB_ENDPOINT_ATTR_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+}};
+
+static const struct {
+ struct usb_cdc_header_descriptor header;
+ struct usb_cdc_call_management_descriptor call_mgmt;
+ struct usb_cdc_acm_descriptor acm;
+ struct usb_cdc_union_descriptor cdc_union;
+} __attribute__((packed)) cdcacm_functional_descriptors = {
+ .header = {
+ .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
+ .bcdCDC = 0x0110,
+ },
+ .call_mgmt = {
+ .bFunctionLength =
+ sizeof(struct usb_cdc_call_management_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
+ .bmCapabilities = 0,
+ .bDataInterface = 1,
+ },
+ .acm = {
+ .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_CDC_TYPE_ACM,
+ .bmCapabilities = 0,
+ },
+ .cdc_union = {
+ .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_CDC_TYPE_UNION,
+ .bControlInterface = 0,
+ .bSubordinateInterface0 = 1,
+ }
+};
+
+static const struct usb_interface_descriptor comm_iface[] = {{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_CDC,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
+ .iInterface = 0,
+
+ .endpoint = comm_endp,
+
+ .extra = &cdcacm_functional_descriptors,
+ .extralen = sizeof(cdcacm_functional_descriptors)
+}};
+
+static const struct usb_interface_descriptor data_iface[] = {{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+
+ .endpoint = data_endp,
+}};
+
+const struct usb_dfu_descriptor dfu_function = {
+ .bLength = sizeof(struct usb_dfu_descriptor),
+ .bDescriptorType = DFU_FUNCTIONAL,
+ .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
+ .wDetachTimeout = 255,
+ .wTransferSize = 1024,
+ .bcdDFUVersion = 0x011A,
+};
+
+const struct usb_interface_descriptor dfu_iface = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 2,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0xFE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+ .iInterface = 0,
+
+ .extra = &dfu_function,
+ .extralen = sizeof(dfu_function),
+};
+
+static const struct usb_interface ifaces[] = {{
+ .num_altsetting = 1,
+ .altsetting = comm_iface,
+}, {
+ .num_altsetting = 1,
+ .altsetting = data_iface,
+}, {
+ .num_altsetting = 1,
+ .altsetting = &dfu_iface,
+}};
+
+static const struct usb_config_descriptor config = {
+ .bLength = USB_DT_CONFIGURATION_SIZE,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+ .wTotalLength = 0,
+ .bNumInterfaces = 3,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = 0x80,
+ .bMaxPower = 0x32,
+
+ .interface = ifaces,
+};
+
+static char serial_no[25];
+
+static const char *usb_strings[] = {
+ "x",
+ "Black Sphere Technologies",
+ "Black Magic Probe",
+ serial_no,
+};
+
+static void dfu_detach_complete(struct usb_setup_data *req)
+{
+ (void)req;
+
+ /* Disconnect USB cable */
+ gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, 0, USB_PU_PIN);
+
+ /* Assert boot-request pin */
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
+ gpio_clear(GPIOB, GPIO12);
+
+ /* Reset core to enter bootloader */
+ scb_reset_core();
+}
+
+static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len, void (**complete)(struct usb_setup_data *req))
+{
+ (void)complete;
+ (void)buf;
+ (void)len;
+
+ switch(req->bRequest) {
+ case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ /* This Linux cdc_acm driver requires this to be implemented
+ * even though it's optional in the CDC spec, and we don't
+ * advertise it in the ACM functional descriptor. */
+ return 1;
+ case DFU_DETACH:
+ if(req->wIndex == 2) {
+ *complete = dfu_detach_complete;
+ return 1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+int cdcacm_get_config(void)
+{
+ return configured;
+}
+
+static void cdcacm_set_config(u16 wValue)
+{
+ configured = wValue;
+
+ usbd_ep_setup(0x01, USB_ENDPOINT_ATTR_BULK, 64, NULL);
+ usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
+ usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
+
+ usbd_register_control_callback(
+ USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+ USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+ cdcacm_control_request);
+}
+
+void cdcacm_init(void)
+{
+ get_dev_unique_id(serial_no);
+
+ usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings);
+ usbd_register_set_config_callback(cdcacm_set_config);
+
+ nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
+
+ gpio_set(USB_PU_PORT, USB_PU_PIN);
+ gpio_set_mode(USB_PU_PORT, GPIO_MODE_OUTPUT_10_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, USB_PU_PIN);
+}
+
+void usb_lp_can_rx0_isr(void)
+{
+ usbd_poll();
+}
+
+static char *get_dev_unique_id(char *s)
+{
+ volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFFF7E8;
+ int i;
+
+ /* Fetch serial number from chip's unique ID */
+ for(i = 0; i < 24; i+=2) {
+ s[i] = ((*unique_id >> 4) & 0xF) + '0';
+ s[i+1] = (*unique_id++ & 0xF) + '0';
+ }
+ for(i = 0; i < 24; i++)
+ if(s[i] > '9')
+ s[i] += 'A' - '9' - 1;
+
+ return s;
+}
+
diff --git a/src/stm32/gdb_if.c b/src/stm32/gdb_if.c
new file mode 100644
index 0000000..ca0b520
--- /dev/null
+++ b/src/stm32/gdb_if.c
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements a transparent channel over which the GDB Remote
+ * Serial Debugging protocol is implemented. This implementation for STM32
+ * uses the USB CDC-ACM device bulk endpoints to implement the channel.
+ */
+#include "platform.h"
+#include <usbd.h>
+
+#include "gdb_if.h"
+
+#define VIRTUAL_COM_PORT_DATA_SIZE 64
+
+static uint32_t count_out;
+static uint32_t count_in;
+static uint32_t out_ptr;
+static uint8_t buffer_out[VIRTUAL_COM_PORT_DATA_SIZE];
+static uint8_t buffer_in[VIRTUAL_COM_PORT_DATA_SIZE];
+
+int gdb_if_init(void)
+{
+ cdcacm_init();
+
+ return 0;
+}
+
+void gdb_if_putchar(unsigned char c, int flush)
+{
+ buffer_in[count_in++] = c;
+ if(flush || (count_in == VIRTUAL_COM_PORT_DATA_SIZE)) {
+ while(usbd_ep_write_packet(2, buffer_in, count_in) <= 0);
+ count_in = 0;
+ }
+}
+
+unsigned char gdb_if_getchar(void)
+{
+ while(!(out_ptr < count_out)) {
+ while(cdcacm_get_config() != 1);
+ count_out = usbd_ep_read_packet(1, buffer_out,
+ VIRTUAL_COM_PORT_DATA_SIZE);
+ out_ptr = 0;
+ }
+
+ return buffer_out[out_ptr++];
+}
+
+unsigned char gdb_if_getchar_to(int timeout)
+{
+ timeout_counter = timeout/100;
+
+ if(!(out_ptr < count_out)) do {
+ count_out = usbd_ep_read_packet(1, buffer_out,
+ VIRTUAL_COM_PORT_DATA_SIZE);
+ out_ptr = 0;
+ } while(timeout_counter && !(out_ptr < count_out));
+
+ if(out_ptr < count_out)
+ return gdb_if_getchar();
+
+ return -1;
+}
+
diff --git a/src/stm32/jtagtap.c b/src/stm32/jtagtap.c
new file mode 100644
index 0000000..ca5576d
--- /dev/null
+++ b/src/stm32/jtagtap.c
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the low-level JTAG TAP interface. */
+
+#include <libopencm3/stm32/gpio.h>
+#include <stdio.h>
+
+#include "general.h"
+
+#include "jtagtap.h"
+
+int jtagtap_init(void)
+{
+ /* This needs some fixing... */
+ /* Toggle required to sort out line drivers... */
+ gpio_port_write(GPIOA, 0x8100);
+ gpio_port_write(GPIOB, 0x0000);
+
+ gpio_port_write(GPIOA, 0x8180);
+ gpio_port_write(GPIOB, 0x0002);
+
+ gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
+
+ /* Go to JTAG mode for SWJ-DP */
+ for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-DP */
+ jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */
+ jtagtap_soft_reset();
+
+ return 0;
+}
+
+void jtagtap_reset(void)
+{
+ volatile int i;
+ gpio_clear(GPIOB, GPIO1);
+ for(i = 0; i < 10000; i++) asm("nop");
+ gpio_set(GPIOB, GPIO1);
+ jtagtap_soft_reset();
+}
+
+void jtagtap_srst(void)
+{
+ volatile int i;
+ gpio_set(GPIOA, GPIO2);
+ for(i = 0; i < 10000; i++) asm("nop");
+ gpio_clear(GPIOA, GPIO2);
+}
+
+inline uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO)
+{
+ uint8_t ret;
+
+ gpio_set_val(JTAG_PORT, TMS_PIN, dTMS);
+ gpio_set_val(JTAG_PORT, TDI_PIN, dTDO);
+ gpio_set(JTAG_PORT, TCK_PIN);
+ ret = gpio_get(JTAG_PORT, TDO_PIN);
+ gpio_clear(JTAG_PORT, TCK_PIN);
+
+ DEBUG("jtagtap_next(TMS = %d, TDO = %d) = %d\n", dTMS, dTDO, ret);
+
+ return ret;
+}
+
+
+
+#define PROVIDE_GENERIC_JTAGTAP_TMS_SEQ
+#define PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
+#define PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
+
+#include "jtagtap_generic.c"
+
diff --git a/src/stm32/platform.c b/src/stm32/platform.c
new file mode 100644
index 0000000..bdf2b33
--- /dev/null
+++ b/src/stm32/platform.c
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the platform specific functions for the STM32
+ * implementation.
+ */
+
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/systick.h>
+#include <libopencm3/stm32/scb.h>
+#include <libopencm3/stm32/nvic.h>
+
+#include "platform.h"
+
+#include <ctype.h>
+
+uint8_t running_status;
+volatile uint32_t timeout_counter;
+
+jmp_buf fatal_error_jmpbuf;
+
+void morse(const char *msg, char repeat);
+static void morse_update(void);
+
+int
+platform_init(void)
+{
+#ifndef LIGHT
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+#else
+ rcc_clock_setup_in_hsi_out_48mhz();
+#endif
+
+ /* Enable peripherals */
+ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
+ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN);
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPDEN);
+
+ /* Setup GPIO ports */
+#ifdef LIGHT
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
+ AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;
+#endif
+ gpio_clear(USB_PU_PORT, USB_PU_PIN);
+ gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT,
+ USB_PU_PIN);
+
+ gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_10_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL,
+ TMS_PIN | TCK_PIN | TDO_PIN);
+
+ gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL,
+ LED_RUN | LED_IDLE | LED_ERROR);
+
+ /* Setup heartbeat timer */
+ systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(900000); /* Interrupt us at 10 Hz */
+ systick_interrupt_enable();
+ systick_counter_enable();
+
+#ifndef LIGHT
+ SCB_VTOR = 0x2000; // Relocate interrupt vector table here
+#endif
+ /* Enable IRQs */
+ nvic_enable_irq(NVIC_TIM2_IRQ);
+
+ return 0;
+}
+
+void sys_tick_handler(void)
+{
+ if(running_status)
+ gpio_toggle(LED_PORT, LED_RUN);
+ else
+ gpio_clear(LED_PORT, LED_RUN);
+
+ if(timeout_counter)
+ timeout_counter--;
+
+ morse_update();
+}
+
+
+/* Morse code patterns and lengths */
+static const struct {
+ uint16_t code;
+ uint8_t bits;
+} morse_letter[] = {
+ { 0b00011101, 8}, // 'A' .-
+ { 0b000101010111, 12}, // 'B' -...
+ { 0b00010111010111, 14}, // 'C' -.-.
+ { 0b0001010111, 10}, // 'D' -..
+ { 0b0001, 4}, // 'E' .
+ { 0b000101110101, 12}, // 'F' ..-.
+ { 0b000101110111, 12}, // 'G' --.
+ { 0b0001010101, 10}, // 'H' ....
+ { 0b000101, 6}, // 'I' ..
+ {0b0001110111011101, 16}, // 'J' .---
+ { 0b000111010111, 12}, // 'K' -.-
+ { 0b000101011101, 12}, // 'L' .-..
+ { 0b0001110111, 10}, // 'M' --
+ { 0b00010111, 8}, // 'N' -.
+ { 0b00011101110111, 14}, // 'O' ---
+ { 0b00010111011101, 14}, // 'P' .--.
+ {0b0001110101110111, 16}, // 'Q' --.-
+ { 0b0001011101, 10}, // 'R' .-.
+ { 0b00010101, 8}, // 'S' ...
+ { 0b000111, 6}, // 'T' -
+ { 0b0001110101, 10}, // 'U' ..-
+ { 0b000111010101, 12}, // 'V' ...-
+ { 0b000111011101, 12}, // 'W' .--
+ { 0b00011101010111, 14}, // 'X' -..-
+ {0b0001110111010111, 16}, // 'Y' -.--
+ { 0b00010101110111, 14}, // 'Z' --..
+};
+
+
+const char *morse_msg;
+static const char * volatile morse_ptr;
+static char morse_repeat;
+
+void morse(const char *msg, char repeat)
+{
+ morse_msg = morse_ptr = msg;
+ morse_repeat = repeat;
+ SET_ERROR_STATE(0);
+}
+
+static void morse_update(void)
+{
+ static uint16_t code;
+ static uint8_t bits;
+
+ if(!morse_ptr) return;
+
+ if(!bits) {
+ char c = *morse_ptr++;
+ if(!c) {
+ if(morse_repeat) {
+ morse_ptr = morse_msg;
+ c = *morse_ptr++;
+ } else {
+ morse_ptr = 0;
+ return;
+ }
+ }
+ if((c >= 'A') && (c <= 'Z')) {
+ c -= 'A';
+ code = morse_letter[c].code;
+ bits = morse_letter[c].bits;
+ } else {
+ code = 0; bits = 4;
+ }
+ }
+ SET_ERROR_STATE(code & 1);
+ code >>= 1; bits--;
+}
+
diff --git a/src/stm32/platform.h b/src/stm32/platform.h
new file mode 100644
index 0000000..daeafa8
--- /dev/null
+++ b/src/stm32/platform.h
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the platform specific functions for the STM32
+ * implementation.
+ */
+#ifndef __PLATFORM_H
+#define __PLATFORM_H
+
+#include <libopencm3/stm32/gpio.h>
+
+#include <setjmp.h>
+
+#include "gdb_packet.h"
+
+/* Important pin mappings for STM32 implementation:
+ *
+ * LED0 = PB2 (Yellow LED : Running)
+ * LED1 = PB10 (Yellow LED : Idle)
+ * LED2 = PB11 (Red LED : Error)
+ *
+ * TPWR = RB0 (input)
+ * nTRST = PB1
+ * SRST_OUT = PA2
+ * TDI = PA3
+ * TMS = PA4 (input for SWDP)
+ * TCK = PA5
+ * TDO = PA6 (input)
+ * nSRST = PA7 (input)
+ *
+ * USB cable pull-up: PA8 // was PA10 on prototype
+ * Force DFU mode button: PB12
+ */
+
+/* Hardware definitions... */
+#ifndef LIGHT
+# define JTAG_PORT GPIOA
+# define TDI_PIN GPIO3
+# define TMS_PIN GPIO4
+# define TCK_PIN GPIO5
+# define TDO_PIN GPIO6
+
+# define SWDP_PORT JTAG_PORT
+# define SWDIO_PIN TMS_PIN
+# define SWCLK_PIN TCK_PIN
+
+# define USB_PU_PORT GPIOA
+# define USB_PU_PIN GPIO8
+
+# define LED_PORT GPIOB
+# define LED_RUN GPIO2
+# define LED_IDLE GPIO10
+# define LED_ERROR GPIO11
+#else
+# define JTAG_PORT GPIOA
+# define TDI_PIN GPIO3
+# define TMS_PIN GPIO2
+# define TCK_PIN GPIO7
+# define TDO_PIN GPIO6
+
+# define SWDP_PORT JTAG_PORT
+# define SWDIO_PIN TMS_PIN
+# define SWCLK_PIN TCK_PIN
+
+# define USB_PU_PORT GPIOA
+# define USB_PU_PIN GPIO15
+#endif
+
+#define DEBUG(...)
+
+extern uint8_t running_status;
+extern volatile uint32_t timeout_counter;
+
+extern jmp_buf fatal_error_jmpbuf;
+
+extern const char *morse_msg;
+
+#define gpio_set_val(port, pin, val) do { \
+ if(val) \
+ gpio_set((port), (pin)); \
+ else \
+ gpio_clear((port), (pin)); \
+} while(0)
+
+#define SET_RUN_STATE(state) {running_status = (state);}
+#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE, state);}
+#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);}
+
+#define PLATFORM_SET_FATAL_ERROR_RECOVERY() {setjmp(fatal_error_jmpbuf);}
+#define PLATFORM_FATAL_ERROR(error) { \
+ if(running_status) gdb_putpacketz("X1D"); \
+ else gdb_putpacketz("EFF"); \
+ running_status = 0; \
+ TARGET_LIST_FREE(); \
+ cur_target = last_target = NULL; \
+ morse("TARGET LOST.", 1); \
+ longjmp(fatal_error_jmpbuf, (error)); \
+}
+
+int platform_init(void);
+void morse(const char *msg, char repeat);
+
+#endif
+
diff --git a/src/stm32/swdptap.c b/src/stm32/swdptap.c
new file mode 100644
index 0000000..e6799eb
--- /dev/null
+++ b/src/stm32/swdptap.c
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the low-level SW-DP interface. */
+
+#include <libopencm3/stm32/gpio.h>
+#include <stdio.h>
+
+#include "general.h"
+
+#include "swdptap.h"
+
+#include "gdb_packet.h"
+
+void swdptap_turnaround(uint8_t dir)
+{
+ static uint8_t olddir = 0;
+
+ DEBUG("%s", dir ? "\n-> ":"\n<- ");
+
+ /* Don't turnaround if direction not changing */
+ if(dir == olddir) return;
+ olddir = dir;
+
+ if(dir)
+ gpio_set_mode(SWDP_PORT, GPIO_MODE_INPUT,
+ GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
+ gpio_set(SWDP_PORT, SWCLK_PIN);
+ gpio_clear(SWDP_PORT, SWCLK_PIN);
+ if(!dir)
+ gpio_set_mode(SWDP_PORT, GPIO_MODE_OUTPUT_10_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
+}
+
+uint8_t swdptap_bit_in(void)
+{
+ uint8_t ret;
+
+ ret = gpio_get(SWDP_PORT, SWDIO_PIN);
+ gpio_set(SWDP_PORT, SWCLK_PIN);
+ gpio_clear(SWDP_PORT, SWCLK_PIN);
+
+ DEBUG("%d", ret?1:0);
+
+ return ret;
+}
+
+void swdptap_bit_out(uint8_t val)
+{
+ DEBUG("%d", val);
+
+ gpio_set_val(SWDP_PORT, SWDIO_PIN, val);
+ gpio_set(SWDP_PORT, SWCLK_PIN);
+ gpio_clear(SWDP_PORT, SWCLK_PIN);
+}
+
+int swdptap_init(void)
+{
+ /* This must be investigated in more detail.
+ * As described in STM32 Reference Manual... */
+ swdptap_reset();
+ swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */
+ swdptap_reset();
+ swdptap_seq_out(0, 16);
+
+ return 0;
+}
+
+
+void swdptap_reset(void)
+{
+ swdptap_turnaround(0);
+ /* 50 clocks with TMS high */
+ for(int i = 0; i < 50; i++) swdptap_bit_out(1);
+}
+
+
+uint32_t swdptap_seq_in(int ticks)
+{
+ uint32_t index = 1;
+ uint32_t ret = 0;
+
+ swdptap_turnaround(1);
+
+ while(ticks--) {
+ if(swdptap_bit_in()) ret |= index;
+ index <<= 1;
+ }
+
+ return ret;
+}
+
+
+uint8_t swdptap_seq_in_parity(uint32_t *ret, int ticks)
+{
+ uint32_t index = 1;
+ uint8_t parity = 0;
+ *ret = 0;
+
+ swdptap_turnaround(1);
+
+ while(ticks--) {
+ if(swdptap_bit_in()) {
+ *ret |= index;
+ parity ^= 1;
+ }
+ index <<= 1;
+ }
+ if(swdptap_bit_in()) parity ^= 1;
+
+ return parity;
+}
+
+
+void swdptap_seq_out(uint32_t MS, int ticks)
+{
+ swdptap_turnaround(0);
+
+ while(ticks--) {
+ swdptap_bit_out(MS & 1);
+ MS >>= 1;
+ }
+}
+
+
+void swdptap_seq_out_parity(uint32_t MS, int ticks)
+{
+ uint8_t parity = 0;
+
+ swdptap_turnaround(0);
+
+ while(ticks--) {
+ swdptap_bit_out(MS & 1);
+ parity ^= MS;
+ MS >>= 1;
+ }
+ swdptap_bit_out(parity & 1);
+}
+
diff --git a/src/stm32/usbdfu.c b/src/stm32/usbdfu.c
new file mode 100644
index 0000000..a6194ee
--- /dev/null
+++ b/src/stm32/usbdfu.c
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libopencm3/stm32/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/flash.h>
+#include <libopencm3/stm32/scb.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/dfu.h>
+
+#define APP_ADDRESS 0x08002000
+
+/* Commands sent with wBlockNum == 0 as per ST implementation. */
+#define CMD_SETADDR 0x21
+#define CMD_ERASE 0x41
+
+/* We need a special large control buffer for this device: */
+u8 usbd_control_buffer[1024];
+
+static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
+
+static char *get_dev_unique_id(char *serial_no);
+
+static struct {
+ u8 buf[sizeof(usbd_control_buffer)];
+ u16 len;
+ u32 addr;
+ u16 blocknum;
+} prog;
+
+const struct usb_device_descriptor dev = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = 0x0483,
+ .idProduct = 0xDF11,
+ .bcdDevice = 0x0200,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 1,
+};
+
+const struct usb_dfu_descriptor dfu_function = {
+ .bLength = sizeof(struct usb_dfu_descriptor),
+ .bDescriptorType = DFU_FUNCTIONAL,
+ .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
+ .wDetachTimeout = 255,
+ .wTransferSize = 1024,
+ .bcdDFUVersion = 0x011A,
+};
+
+const struct usb_interface_descriptor iface = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 2,
+
+ /* The ST Microelectronics DfuSe application needs this string.
+ * The format isn't documented... */
+ .iInterface = 4,
+
+ .extra = &dfu_function,
+ .extralen = sizeof(dfu_function),
+};
+
+const struct usb_interface ifaces[] = {{
+ .num_altsetting = 1,
+ .altsetting = &iface,
+}};
+
+const struct usb_config_descriptor config = {
+ .bLength = USB_DT_CONFIGURATION_SIZE,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+ .wTotalLength = 0,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = 0xC0,
+ .bMaxPower = 0x32,
+
+ .interface = ifaces,
+};
+
+static char serial_no[25];
+
+static const char *usb_strings[] = {
+ "x",
+ "Black Sphere Technologies",
+ "Black Magic Probe (Upgrade)",
+ serial_no,
+ /* This string is used by ST Microelectronics' DfuSe utility */
+ "@Internal Flash /0x08000000/8*001Ka,120*001Kg"
+};
+
+static u8 usbdfu_getstatus(u32 *bwPollTimeout)
+{
+ switch(usbdfu_state) {
+ case STATE_DFU_DNLOAD_SYNC:
+ usbdfu_state = STATE_DFU_DNBUSY;
+ *bwPollTimeout = 100;
+ return DFU_STATUS_OK;
+
+ case STATE_DFU_MANIFEST_SYNC:
+ /* Device will reset when read is complete */
+ usbdfu_state = STATE_DFU_MANIFEST;
+ return DFU_STATUS_OK;
+
+ default:
+ return DFU_STATUS_OK;
+ }
+}
+
+static void usbdfu_getstatus_complete(struct usb_setup_data *req)
+{
+ int i;
+ (void)req;
+
+ switch(usbdfu_state) {
+ case STATE_DFU_DNBUSY:
+
+ flash_unlock();
+ if(prog.blocknum == 0) {
+ switch(prog.buf[0]) {
+ case CMD_ERASE:
+ flash_erase_page(*(u32*)(prog.buf+1));
+ case CMD_SETADDR:
+ prog.addr = *(u32*)(prog.buf+1);
+ }
+ } else {
+ u32 baseaddr = prog.addr +
+ ((prog.blocknum - 2) *
+ dfu_function.wTransferSize);
+ for(i = 0; i < prog.len; i += 2)
+ flash_program_half_word(baseaddr + i,
+ *(u16*)(prog.buf+i));
+ }
+ flash_lock();
+
+ /* We jump straight to dfuDNLOAD-IDLE,
+ * skipping dfuDNLOAD-SYNC
+ */
+ usbdfu_state = STATE_DFU_DNLOAD_IDLE;
+ return;
+
+ case STATE_DFU_MANIFEST:
+ /* USB device must detach, we just reset... */
+ scb_reset_system();
+ return; /* Will never return */
+ default:
+ return;
+ }
+}
+
+static int usbdfu_control_request(struct usb_setup_data *req, u8 **buf,
+ u16 *len, void (**complete)(struct usb_setup_data *req))
+{
+
+ if((req->bmRequestType & 0x7F) != 0x21)
+ return 0; /* Only accept class request */
+
+ switch(req->bRequest) {
+ case DFU_DNLOAD:
+ if((len == NULL) || (*len == 0)) {
+ usbdfu_state = STATE_DFU_MANIFEST_SYNC;
+ return 1;
+ } else {
+ /* Copy download data for use on GET_STATUS */
+ prog.blocknum = req->wValue;
+ prog.len = *len;
+ memcpy(prog.buf, *buf, *len);
+ usbdfu_state = STATE_DFU_DNLOAD_SYNC;
+ return 1;
+ }
+ case DFU_CLRSTATUS:
+ /* Clear error and return to dfuIDLE */
+ if(usbdfu_state == STATE_DFU_ERROR)
+ usbdfu_state = STATE_DFU_IDLE;
+ return 1;
+ case DFU_ABORT:
+ /* Abort returns to dfuIDLE state */
+ usbdfu_state = STATE_DFU_IDLE;
+ return 1;
+ case DFU_UPLOAD:
+ /* Upload not supported for now */
+ return 0;
+ case DFU_GETSTATUS: {
+ u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
+
+ (*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
+ (*buf)[1] = bwPollTimeout & 0xFF;
+ (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
+ (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
+ (*buf)[4] = usbdfu_state;
+ (*buf)[5] = 0; /* iString not used here */
+ *len = 6;
+
+ *complete = usbdfu_getstatus_complete;
+
+ return 1;
+ }
+ case DFU_GETSTATE:
+ /* Return state with no state transision */
+ *buf[0] = usbdfu_state;
+ *len = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(void)
+{
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
+ if(gpio_get(GPIOB, GPIO12)) {
+ /* Boot the application if it's valid */
+ if((*(volatile u32*)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
+ /* Set vector table base address */
+ SCB_VTOR = APP_ADDRESS & 0xFFFF;
+ /* Initialise master stack pointer */
+ asm volatile ("msr msp, %0"::"g"
+ (*(volatile u32*)APP_ADDRESS));
+ /* Jump to application */
+ (*(void(**)())(APP_ADDRESS + 4))();
+ }
+ }
+
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+
+ gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO8);
+
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, GPIO11);
+ systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(900000);
+ systick_interrupt_enable();
+ systick_counter_enable();
+ gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
+ GPIO_CNF_INPUT_FLOAT, GPIO2 | GPIO10);
+
+ get_dev_unique_id(serial_no);
+
+ usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings);
+ usbd_set_control_buffer_size(sizeof(usbd_control_buffer));
+ usbd_register_control_callback(
+ USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+ USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+ usbdfu_control_request);
+
+ gpio_set(GPIOA, GPIO8);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
+
+ while (1)
+ usbd_poll();
+}
+
+static char *get_dev_unique_id(char *s)
+{
+ volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFFF7E8;
+ int i;
+
+ /* Fetch serial number from chip's unique ID */
+ for(i = 0; i < 24; i+=2) {
+ s[i] = ((*unique_id >> 4) & 0xF) + '0';
+ s[i+1] = (*unique_id++ & 0xF) + '0';
+ }
+ for(i = 0; i < 24; i++)
+ if(s[i] > '9')
+ s[i] += 'A' - '9' - 1;
+
+ return s;
+}
+
+void sys_tick_handler()
+{
+ gpio_toggle(GPIOB, GPIO11); /* LED2 on/off */
+}
+
diff --git a/src/stm32_tgt.c b/src/stm32_tgt.c
new file mode 100644
index 0000000..f422606
--- /dev/null
+++ b/src/stm32_tgt.c
@@ -0,0 +1,229 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements STM32 target specific functions for detecting
+ * the device, providing the XML memory map and Flash memory programming.
+ *
+ * Refereces:
+ * ST doc - RM0008
+ * Reference manual - STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx
+ * and STM32F107xx advanced ARM-based 32-bit MCUs
+ * ST doc - PM0075
+ * Programming manual - STM32F10xxx Flash memory microcontrollers
+ *
+ * Issues:
+ * On some devices this fails occasionally. A retry suceeds. Maybe
+ * a timing issue.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "adiv5.h"
+#include "target.h"
+#include "stm32_tgt.h"
+
+static int stm32md_flash_erase(struct target_s *target, uint32_t addr, int len);
+static int stm32hd_flash_erase(struct target_s *target, uint32_t addr, int len);
+static int stm32_flash_erase(struct target_s *target, uint32_t addr, int len,
+ uint32_t pagesize);
+static int stm32_flash_write_words(struct target_s *target, uint32_t dest, const uint32_t *src,
+ int len);
+
+static const char stm32_driver_str[] = "STM32, Medium density.";
+static const char stm32hd_driver_str[] = "STM32, High density.";
+
+static const char stm32_xml_memory_map[] = "<?xml version=\"1.0\"?>"
+/* "<!DOCTYPE memory-map "
+ " PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
+ " \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
+ "<memory-map>"
+ " <memory type=\"flash\" start=\"0x8000000\" length=\"0x20000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x5000\"/>"
+ "</memory-map>";
+
+static const char stm32hd_xml_memory_map[] = "<?xml version=\"1.0\"?>"
+/* "<!DOCTYPE memory-map "
+ " PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
+ " \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
+ "<memory-map>"
+ " <memory type=\"flash\" start=\"0x8000000\" length=\"0x80000\">"
+ " <property name=\"blocksize\">0x800</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
+ "</memory-map>";
+
+/* Flash Program ad Erase Controller Register Map */
+#define FPEC_BASE 0x40022000
+#define FLASH_ACR (FPEC_BASE+0x00)
+#define FLASH_KEYR (FPEC_BASE+0x04)
+#define FLASH_OPTKEYR (FPEC_BASE+0x08)
+#define FLASH_SR (FPEC_BASE+0x0C)
+#define FLASH_CR (FPEC_BASE+0x10)
+#define FLASH_AR (FPEC_BASE+0x14)
+#define FLASH_OBR (FPEC_BASE+0x1C)
+#define FLASH_WRPR (FPEC_BASE+0x20)
+
+#define KEY1 0x45670123
+#define KEY2 0xCDEF89AB
+
+#define SR_ERROR_MASK 0x14
+#define SR_EOP 0x20
+
+uint16_t stm32_flash_write_stub[] = {
+// _start:
+ 0x4809, // ldr r0, [pc, #36] // _flashbase
+ 0x490a, // ldr r1, [pc, #40] // _addr
+ 0x467a, // mov r2, pc
+ 0x322c, // adds r2, #44
+ 0x4b09, // ldr r3, [pc, #36] // _size
+ 0x2501, // movs r5, #1
+// _next:
+ 0xb153, // cbz r3, _done
+ 0x6105, // str r5, [r0, #16]
+ 0x8814, // ldrh r4, [r2]
+ 0x800c, // strh r4, [r1]
+// _wait:
+ 0x68c4, // ldr r4, [r0, #12]
+ 0x2601, // movs r6, #1
+ 0x4234, // tst r4, r6
+ 0xd1fb, // bne _wait
+
+ 0x3b02, // subs r3, #2
+ 0x3102, // adds r1, #2
+ 0x3202, // adds r2, #2
+ 0xe7f3, // b _next
+// _done:
+ 0xbe00, // bkpt
+ 0x0000,
+// .org 0x28
+// _flashbase:
+ 0x2000, 0x4002, // .word 0x40022000 (FPEC_BASE)
+// _addr:
+// 0x0000, 0x0000,
+// _size:
+// 0x0000, 0x0000,
+// _data:
+// ...
+};
+
+int stm32_probe(struct target_s *target)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t idcode;
+
+ idcode = adiv5_ap_mem_read(t->ap, 0xE0042000);
+ switch(idcode & 0xFFF) {
+ case 0x410: /* Medium density */
+ case 0x412: /* Low denisty */
+ case 0x420: /* Value Line, Low-/Medium density */
+ target->driver = stm32_driver_str;
+ target->xml_mem_map = stm32_xml_memory_map;
+ target->flash_erase = stm32md_flash_erase;
+ target->flash_write_words = stm32_flash_write_words;
+ return 0;
+ case 0x414: /* High density */
+ case 0x418: /* Connectivity Line */
+ case 0x428: /* Value Line, High Density */
+ target->driver = stm32hd_driver_str;
+ target->xml_mem_map = stm32hd_xml_memory_map;
+ target->flash_erase = stm32hd_flash_erase;
+ target->flash_write_words = stm32_flash_write_words;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+
+static int stm32_flash_erase(struct target_s *target, uint32_t addr, int len, uint32_t pagesize)
+{
+ struct target_ap_s *t = (void *)target;
+ uint16_t sr;
+
+ addr &= ~(pagesize - 1);
+ len &= ~(pagesize - 1);
+
+ /* Enable FPEC controller access */
+ adiv5_ap_mem_write(t->ap, FLASH_KEYR, KEY1);
+ adiv5_ap_mem_write(t->ap, FLASH_KEYR, KEY2);
+ while(len) {
+ /* Flash page erase instruction */
+ adiv5_ap_mem_write(t->ap, FLASH_CR, 2);
+ /* write address to FMA */
+ adiv5_ap_mem_write(t->ap, FLASH_AR, addr);
+ /* Flash page erase start instruction */
+ adiv5_ap_mem_write(t->ap, FLASH_CR, 0x42);
+
+ /* Read FLASH_SR to poll for BSY bit */
+ while(adiv5_ap_mem_read(t->ap, FLASH_SR) & 1)
+ if(target_check_error(target)) return -1;
+
+ len -= pagesize;
+ addr += pagesize;
+ }
+
+ /* Check for error */
+ sr = adiv5_ap_mem_read(t->ap, FLASH_SR);
+ if((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) return -1;
+
+ return 0;
+}
+
+static int stm32hd_flash_erase(struct target_s *target, uint32_t addr, int len)
+{
+ return stm32_flash_erase(target, addr, len, 0x800);
+}
+
+static int stm32md_flash_erase(struct target_s *target, uint32_t addr, int len)
+{
+ return stm32_flash_erase(target, addr, len, 0x400);
+}
+
+static int stm32_flash_write_words(struct target_s *target, uint32_t dest,
+ const uint32_t *src, int len)
+{
+ struct target_ap_s *t = (void *)target;
+ uint32_t data[(len>>2)+2];
+
+ /* Construct data buffer used by stub */
+ data[0] = dest & 0xFFFFFFFE;
+ data[1] = len & 0xFFFFFFFE;
+ memcpy(&data[2], src, len);
+
+ /* Write stub and data to target ram and set PC */
+ target_mem_write_words(target, 0x20000000, (void*)stm32_flash_write_stub, 0x2C);
+ target_mem_write_words(target, 0x2000002C, data, len + 8);
+ target_pc_write(target, 0x20000000);
+ if(target_check_error(target)) return -1;
+
+ /* Execute the stub */
+ target_halt_resume(target, 0);
+ while(!target_halt_wait(target));
+
+ /* Check for error */
+ if(adiv5_ap_mem_read(t->ap, FLASH_SR) & SR_ERROR_MASK) return -1;
+
+ return 0;
+}
+