aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile69
-rw-r--r--src/adiv5.c264
-rw-r--r--src/adiv5_jtagdp.c51
-rw-r--r--src/adiv5_swdp.c70
-rw-r--r--src/arm7tdmi.c297
-rw-r--r--src/command.c114
-rw-r--r--src/cortexm.c748
-rw-r--r--src/crc32.c42
-rw-r--r--src/exception.c39
-rw-r--r--src/gdb_main.c133
-rw-r--r--src/gdb_packet.c33
-rw-r--r--src/hex_utils.c30
-rw-r--r--src/include/adiv5.h68
-rw-r--r--src/include/arm7tdmi.h1
-rw-r--r--src/include/command.h1
-rw-r--r--src/include/cortexm.h153
-rw-r--r--src/include/crc32.h4
-rw-r--r--src/include/exception.h74
-rw-r--r--src/include/gdb_packet.h8
-rw-r--r--src/include/general.h15
-rw-r--r--src/include/hex_utils.h5
-rw-r--r--src/include/jtag_scan.h2
-rw-r--r--src/include/jtagtap.h10
-rw-r--r--src/include/morse.h30
-rw-r--r--src/include/platform_support.h44
-rw-r--r--src/include/stm32lx-nvm.h190
-rw-r--r--src/include/swdptap.h2
-rw-r--r--src/include/target.h153
-rw-r--r--src/jtag_scan.c15
-rw-r--r--src/jtagtap_generic.c8
-rw-r--r--src/kinetis.c150
-rw-r--r--src/lmi.c158
-rw-r--r--src/lpc11xx.c321
-rw-r--r--src/lpc43xx.c530
-rw-r--r--src/main.c28
-rw-r--r--src/morse.c100
-rw-r--r--src/nrf51.c294
-rw-r--r--src/nxp_tgt.c252
-rw-r--r--src/platforms/common/cdcacm.c (renamed from src/platforms/stm32/cdcacm.c)131
-rw-r--r--src/platforms/common/cdcacm.h45
-rw-r--r--src/platforms/common/serialno.h26
-rw-r--r--src/platforms/common/traceswo.h (renamed from src/include/traceswo.h)0
-rw-r--r--src/platforms/common/usbuart.h (renamed from src/include/usbuart.h)1
-rw-r--r--src/platforms/dev2/jtagtap.c6
-rw-r--r--src/platforms/dev2/platform.c37
-rw-r--r--src/platforms/dev2/platform.h13
-rw-r--r--src/platforms/dev2/swdptap.c6
-rw-r--r--src/platforms/f4discovery/Makefile.inc22
-rw-r--r--src/platforms/f4discovery/Readme35
-rw-r--r--src/platforms/f4discovery/platform.c176
-rw-r--r--src/platforms/f4discovery/platform.h156
-rw-r--r--src/platforms/f4discovery/usbdfu.c14
-rw-r--r--src/platforms/launchpad-icdi/Makefile.inc21
-rw-r--r--src/platforms/launchpad-icdi/platform.c130
-rw-r--r--src/platforms/launchpad-icdi/platform.h118
-rw-r--r--src/platforms/libftdi/gdb_if.c4
-rw-r--r--src/platforms/libftdi/jtagtap.c22
-rw-r--r--src/platforms/libftdi/platform.c71
-rw-r--r--src/platforms/libftdi/platform.h11
-rw-r--r--src/platforms/libftdi/swdptap.c32
-rw-r--r--src/platforms/native/Makefile.inc19
-rw-r--r--src/platforms/native/platform.c191
-rw-r--r--src/platforms/native/platform.h150
-rw-r--r--src/platforms/native/usbdfu.c16
-rw-r--r--src/platforms/stlink/Makefile.inc27
-rw-r--r--src/platforms/stlink/dfu_upgrade.c125
-rw-r--r--src/platforms/stlink/platform.c164
-rw-r--r--src/platforms/stlink/platform.h134
-rw-r--r--src/platforms/stlink/usbdfu.c55
-rw-r--r--src/platforms/stm32/dfu_f1.c25
-rw-r--r--src/platforms/stm32/dfu_f4.c39
-rw-r--r--src/platforms/stm32/dfucore.c121
-rw-r--r--src/platforms/stm32/gdb_if.c79
-rw-r--r--src/platforms/stm32/gpio.h71
-rw-r--r--src/platforms/stm32/jtagtap.c27
-rw-r--r--src/platforms/stm32/serialno.c45
-rw-r--r--src/platforms/stm32/swdptap.c8
-rw-r--r--src/platforms/stm32/timing.c66
-rw-r--r--src/platforms/stm32/timing.h27
-rw-r--r--src/platforms/stm32/traceswo.c6
-rw-r--r--src/platforms/stm32/usbdfu.h16
-rw-r--r--src/platforms/stm32/usbuart.c147
-rw-r--r--src/platforms/swlink/Makefile.inc23
-rw-r--r--src/platforms/swlink/platform.c111
-rw-r--r--src/platforms/swlink/platform.h135
-rw-r--r--src/platforms/swlink/usbdfu.c46
-rw-r--r--src/platforms/tm4c/gdb_if.c102
-rw-r--r--src/platforms/tm4c/jtagtap.c59
-rw-r--r--src/platforms/tm4c/swdptap.c124
-rw-r--r--src/platforms/tm4c/tm4c.ld29
-rw-r--r--src/platforms/tm4c/traceswo.c161
-rw-r--r--src/platforms/tm4c/usbuart.c182
-rw-r--r--src/sam3x.c353
-rw-r--r--src/samd.c737
-rw-r--r--src/stm32f1.c289
-rw-r--r--src/stm32f4.c202
-rw-r--r--src/stm32l0.c1019
-rw-r--r--src/stm32l1.c229
-rw-r--r--src/target.c3
99 files changed, 7592 insertions, 3353 deletions
diff --git a/src/Makefile b/src/Makefile
index d9f3e9e..3d879ef 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,23 +1,26 @@
PROBE_HOST ?= native
PLATFORM_DIR = platforms/$(PROBE_HOST)
-VPATH += $(PLATFORM_DIR)
+VPATH += platforms/common $(PLATFORM_DIR)
-BUILDDATE := `date +"%Y%m%d"`
+ifneq ($(V), 1)
+MAKEFLAGS += --no-print-dir
+Q := @
+endif
-CFLAGS += -Wall -Wextra -Wno-pointer-sign -Wno-char-subscripts\
- -Wno-sign-compare \
- -O2 -std=gnu99 -g3 -DBUILDDATE=\"$(BUILDDATE)\"\
- -I. -Iinclude -I$(PLATFORM_DIR) \
- -DVERSION_SUFFIX=\"`../scripts/setlocalversion`\" -MD
+OPT_FLAGS ?= -O2
+
+CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts\
+ $(OPT_FLAGS) -std=gnu99 -g3 -MD \
+ -I. -Iinclude -Iplatforms/common -I$(PLATFORM_DIR) \
SRC = \
adiv5.c \
adiv5_jtagdp.c \
adiv5_swdp.c \
- arm7tdmi.c \
command.c \
cortexm.c \
crc32.c \
+ exception.c \
gdb_if.c \
gdb_main.c \
gdb_packet.c \
@@ -25,14 +28,18 @@ SRC = \
jtag_scan.c \
jtagtap.c \
lmi.c \
+ lpc11xx.c \
lpc43xx.c \
+ kinetis.c \
main.c \
- nxp_tgt.c \
+ morse.c \
+ nrf51.c \
platform.c \
sam3x.c \
+ samd.c \
stm32f1.c \
stm32f4.c \
- stm32l1.c \
+ stm32l0.c \
swdptap.c \
target.c \
@@ -40,14 +47,46 @@ include $(PLATFORM_DIR)/Makefile.inc
OBJ = $(SRC:.c=.o)
-blackmagic: $(OBJ)
- $(CC) -o $@ $^ $(LDFLAGS)
+blackmagic: include/version.h $(OBJ)
+ @echo " LD $@"
+ $(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+%.o: %.c
+ @echo " CC $<"
+ $(Q)$(CC) $(CFLAGS) -c $< -o $@
+
+%.bin: %
+ @echo " OBJCOPY $@"
+ $(Q)$(OBJCOPY) -O binary $^ $@
-.PHONY: clean host_clean
+%.hex: %
+ @echo " OBJCOPY $@"
+ $(Q)$(OBJCOPY) -O ihex $^ $@
+
+.PHONY: clean host_clean all_platforms FORCE
clean: host_clean
- $(RM) *.o *.d *~ blackmagic $(HOSTFILES)
- $(RM) platforms/*/*.o platforms/*/*.d mapfile
+ $(Q)echo " CLEAN"
+ -$(Q)$(RM) -f *.o *.d *~ blackmagic $(HOSTFILES)
+ -$(Q)$(RM) -f platforms/*/*.o platforms/*/*.d mapfile
+
+all_platforms:
+ $(Q)set -e ;\
+ for i in platforms/*/Makefile.inc ; do \
+ export DIRNAME=`dirname $$i` ;\
+ export PROBE_HOST=`basename $$DIRNAME` ;\
+ export CFLAGS=-Werror ;\
+ echo "Building for hardware platform: $$PROBE_HOST" ;\
+ $(MAKE) $(MAKEFLAGS) clean ;\
+ $(MAKE) $(MAKEFLAGS);\
+ if [ -f blackmagic.bin ]; then \
+ mv blackmagic.bin blackmagic-$$PROBE_HOST.bin ;\
+ fi ;\
+ done
+
+include/version.h: FORCE
+ $(Q)echo " GIT include/version.h"
+ $(Q)echo "#define FIRMWARE_VERSION \"`git describe --dirty`\"" > $@
-include *.d
diff --git a/src/adiv5.c b/src/adiv5.c
index 45bf94f..4bc64bb 100644
--- a/src/adiv5.c
+++ b/src/adiv5.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Black Magic Debug project.
*
- * Copyright (C) 2011 Black Sphere Technologies Ltd.
+ * Copyright (C) 2015 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
@@ -25,15 +25,10 @@
* 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"
#ifndef DO_RESET_SEQ
@@ -42,12 +37,10 @@
static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP";
-static int ap_check_error(struct target_s *target);
+static bool ap_check_error(target *t);
-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);
+static void ap_mem_read(target *t, void *dest, uint32_t src, size_t len);
+static void ap_mem_write(target *t, uint32_t dest, const void *src, size_t len);
void adiv5_dp_ref(ADIv5_DP_t *dp)
{
@@ -75,6 +68,11 @@ void adiv5_ap_unref(ADIv5_AP_t *ap)
}
}
+void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value)
+{
+ dp->low_access(dp, ADIV5_LOW_WRITE, addr, value);
+}
+
void adiv5_dp_init(ADIv5_DP_t *dp)
{
uint32_t ctrlstat;
@@ -150,10 +148,8 @@ void adiv5_dp_init(ADIv5_DP_t *dp)
t->driver = adiv5_driver_str;
t->check_error = ap_check_error;
- t->mem_read_words = ap_mem_read_words;
- t->mem_write_words = ap_mem_write_words;
- t->mem_read_bytes = ap_mem_read_bytes;
- t->mem_write_bytes = ap_mem_write_bytes;
+ t->mem_read = ap_mem_read;
+ t->mem_write = ap_mem_write;
/* The rest sould only be added after checking ROM table */
cortexm_probe(t);
@@ -161,205 +157,135 @@ void adiv5_dp_init(ADIv5_DP_t *dp)
adiv5_dp_unref(dp);
}
-void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
+static bool ap_check_error(target *t)
{
- adiv5_dp_low_access(dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE, addr, value);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
+ return adiv5_dp_error(ap->dp) != 0;
}
-uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
-{
- uint32_t ret;
+enum align {
+ ALIGN_BYTE = 0,
+ ALIGN_HALFWORD = 1,
+ ALIGN_WORD = 2
+};
+#define ALIGNOF(x) (((x) & 3) == 0 ? ALIGN_WORD : \
+ (((x) & 1) == 0 ? ALIGN_HALFWORD : ALIGN_BYTE))
- adiv5_dp_low_access(dp, ADIV5_LOW_AP, ADIV5_LOW_READ, addr, 0);
- ret = adiv5_dp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
- ADIV5_DP_RDBUFF, 0);
-
- return ret;
-}
-
-
-static int
-ap_check_error(struct target_s *target)
+/* Program the CSW and TAR for sequencial access at a given width */
+static void ap_mem_access_setup(ADIv5_AP_t *ap, uint32_t addr, enum align align)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- return adiv5_dp_error(ap->dp);
+ uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE;
+
+ switch (align) {
+ case ALIGN_BYTE:
+ csw |= ADIV5_AP_CSW_SIZE_BYTE;
+ break;
+ case ALIGN_HALFWORD:
+ csw |= ADIV5_AP_CSW_SIZE_HALFWORD;
+ break;
+ case ALIGN_WORD:
+ csw |= ADIV5_AP_CSW_SIZE_WORD;
+ break;
+ }
+ adiv5_ap_write(ap, ADIV5_AP_CSW, csw);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_TAR, addr);
}
-static int
-ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len)
+/* Extract read data from data lane based on align and src address */
+static void * extract(void *dest, uint32_t src, uint32_t val, enum align align)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint32_t osrc = src;
-
- len >>= 2;
-
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_TAR, src);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_READ,
- ADIV5_AP_DRW, 0);
- while(--len) {
- *dest++ = adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
- ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
- src += 4;
- /* Check for 10 bit address overflow */
- if ((src ^ osrc) & 0xfffffc00) {
- osrc = src;
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
- ADIV5_LOW_WRITE, ADIV5_AP_TAR, src);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
- ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
- }
-
+ switch (align) {
+ case ALIGN_BYTE:
+ *(uint8_t *)dest = (val >> ((src & 0x3) << 3) & 0xFF);
+ break;
+ case ALIGN_HALFWORD:
+ *(uint16_t *)dest = (val >> ((src & 0x2) << 3) & 0xFFFF);
+ break;
+ case ALIGN_WORD:
+ *(uint32_t *)dest = val;
+ break;
}
- *dest++ = adiv5_dp_low_access(ap->dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
- ADIV5_DP_RDBUFF, 0);
-
- return 0;
+ return (uint8_t *)dest + (1 << align);
}
-static int
-ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len)
+static void
+ap_mem_read(target *t, void *dest, uint32_t src, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
uint32_t tmp;
uint32_t osrc = src;
+ enum align align = MIN(ALIGNOF(src), ALIGNOF(len));
+
+ len >>= align;
+ ap_mem_access_setup(ap, src, align);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
+ while (--len) {
+ tmp = adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
+ dest = extract(dest, src, tmp, align);
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_BYTE | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_TAR, src);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_READ,
- ADIV5_AP_DRW, 0);
- while(--len) {
- tmp = adiv5_dp_low_access(ap->dp, 1, 1, ADIV5_AP_DRW, 0);
- *dest++ = (tmp >> ((src & 0x3) << 3) & 0xFF);
-
- src++;
+ src += (1 << align);
/* Check for 10 bit address overflow */
if ((src ^ osrc) & 0xfffffc00) {
osrc = src;
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
+ adiv5_dp_low_access(ap->dp,
ADIV5_LOW_WRITE, ADIV5_AP_TAR, src);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
+ adiv5_dp_low_access(ap->dp,
ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
}
}
- tmp = adiv5_dp_low_access(ap->dp, 0, 1, ADIV5_DP_RDBUFF, 0);
- *dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
-
- return 0;
+ tmp = adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_DP_RDBUFF, 0);
+ extract(dest, src, tmp, align);
}
-
-static int
-ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len)
+static void
+ap_mem_write(target *t, uint32_t dest, const void *src, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
uint32_t odest = dest;
-
- len >>= 2;
-
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_TAR, dest);
- while(len--) {
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_DRW, *src++);
- dest += 4;
- /* Check for 10 bit address overflow */
- if ((dest ^ odest) & 0xfffffc00) {
- odest = dest;
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
- ADIV5_LOW_WRITE, ADIV5_AP_TAR, dest);
+ enum align align = MIN(ALIGNOF(dest), ALIGNOF(len));
+
+ len >>= align;
+ ap_mem_access_setup(ap, dest, align);
+ while (len--) {
+ uint32_t tmp = 0;
+ /* Pack data into correct data lane */
+ switch (align) {
+ case ALIGN_BYTE:
+ tmp = ((uint32_t)*(uint8_t *)src) << ((dest & 3) << 3);
+ break;
+ case ALIGN_HALFWORD:
+ tmp = ((uint32_t)*(uint16_t *)src) << ((dest & 2) << 3);
+ break;
+ case ALIGN_WORD:
+ tmp = *(uint32_t *)src;
+ break;
}
- }
-
- return 0;
-}
-
-static int
-ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
-{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint32_t odest = dest;
-
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_BYTE | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_TAR, dest);
- while(len--) {
- uint32_t tmp = (uint32_t)*src++ << ((dest++ & 3) << 3);
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
- ADIV5_AP_DRW, tmp);
+ src = (uint8_t *)src + (1 << align);
+ dest += (1 << align);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DRW, tmp);
/* Check for 10 bit address overflow */
if ((dest ^ odest) & 0xfffffc00) {
odest = dest;
- adiv5_dp_low_access(ap->dp, ADIV5_LOW_AP,
+ adiv5_dp_low_access(ap->dp,
ADIV5_LOW_WRITE, ADIV5_AP_TAR, dest);
}
}
- return 0;
-}
-
-
-
-uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr)
-{
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
- return adiv5_ap_read(ap, ADIV5_AP_DRW);
-}
-
-void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value)
-{
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
- adiv5_ap_write(ap, ADIV5_AP_DRW, value);
-}
-
-uint16_t adiv5_ap_mem_read_halfword(ADIv5_AP_t *ap, uint32_t addr)
-{
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_HALFWORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
- uint32_t v = adiv5_ap_read(ap, ADIV5_AP_DRW);
- if (addr & 2)
- return v >> 16;
- else
- return v & 0xFFFF;
-}
-
-void adiv5_ap_mem_write_halfword(ADIv5_AP_t *ap, uint32_t addr, uint16_t value)
-{
- uint32_t v = value;
- if (addr & 2)
- v <<= 16;
-
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_HALFWORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
- adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
- adiv5_ap_write(ap, ADIV5_AP_DRW, v);
}
-void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value)
+void adiv5_ap_write(ADIv5_AP_t *ap, uint16_t addr, uint32_t value)
{
adiv5_dp_write(ap->dp, ADIV5_DP_SELECT,
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
- adiv5_dp_write_ap(ap->dp, addr, value);
+ adiv5_dp_write(ap->dp, addr, value);
}
-uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr)
+uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint16_t addr)
{
uint32_t ret;
adiv5_dp_write(ap->dp, ADIV5_DP_SELECT,
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
- ret = adiv5_dp_read_ap(ap->dp, addr);
+ ret = adiv5_dp_read(ap->dp, addr);
return ret;
}
diff --git a/src/adiv5_jtagdp.c b/src/adiv5_jtagdp.c
index 68bcddd..d97bc99 100644
--- a/src/adiv5_jtagdp.c
+++ b/src/adiv5_jtagdp.c
@@ -18,17 +18,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* This file implements the JTAG-DP specific functions of the
+/* 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 "exception.h"
#include "adiv5.h"
#include "jtag_scan.h"
#include "jtagtap.h"
-
-#include <stdlib.h>
+#include "morse.h"
#define JTAGDP_ACK_OK 0x02
#define JTAGDP_ACK_WAIT 0x01
@@ -38,13 +37,12 @@
#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 uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint16_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);
+static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value);
void adiv5_jtag_dp_handler(jtag_dev_t *dev)
@@ -54,7 +52,6 @@ void adiv5_jtag_dp_handler(jtag_dev_t *dev)
dp->dev = dev;
dp->idcode = dev->idcode;
- dp->dp_write = adiv5_jtagdp_write;
dp->dp_read = adiv5_jtagdp_read;
dp->error = adiv5_jtagdp_error;
dp->low_access = adiv5_jtagdp_low_access;
@@ -62,45 +59,43 @@ void adiv5_jtag_dp_handler(jtag_dev_t *dev)
adiv5_dp_init(dp);
}
-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, uint16_t addr)
{
- adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_WRITE, addr, value);
-}
-
-static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr)
-{
- adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ, addr, 0);
- return adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
+ adiv5_jtagdp_low_access(dp, ADIV5_LOW_READ, addr, 0);
+ return adiv5_jtagdp_low_access(dp, ADIV5_LOW_READ,
ADIV5_DP_RDBUFF, 0);
}
static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp)
{
- adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
- ADIV5_DP_CTRLSTAT, 0);
- return adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_WRITE,
+ adiv5_jtagdp_low_access(dp, ADIV5_LOW_READ, ADIV5_DP_CTRLSTAT, 0);
+ return adiv5_jtagdp_low_access(dp, ADIV5_LOW_WRITE,
ADIV5_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)
+static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value)
{
+ bool APnDP = addr & ADIV5_APnDP;
+ addr &= 0xff;
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);
+ jtag_dev_write_ir(dp->dev, APnDP ? IR_APACC : IR_DPACC);
+ platform_timeout_set(2000);
do {
jtag_dev_shift_dr(dp->dev, (uint8_t*)&response, (uint8_t*)&request, 35);
ack = response & 0x07;
- } while(ack == JTAGDP_ACK_WAIT);
+ } while(!platform_timeout_is_expired() && (ack == JTAGDP_ACK_WAIT));
+
+ if (ack == JTAGDP_ACK_WAIT)
+ raise_exception(EXCEPTION_TIMEOUT, "JTAG-DP ACK timeout");
- if((ack != JTAGDP_ACK_OK)) {
- /* Fatal error if invalid ACK response */
- PLATFORM_FATAL_ERROR(1);
- }
+ if((ack != JTAGDP_ACK_OK))
+ raise_exception(EXCEPTION_ERROR, "JTAG-DP invalid ACK");
return (uint32_t)(response >> 3);
}
diff --git a/src/adiv5_swdp.c b/src/adiv5_swdp.c
index 06d5446..b53fcb5 100644
--- a/src/adiv5_swdp.c
+++ b/src/adiv5_swdp.c
@@ -18,31 +18,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* This file implements the SW-DP specific functions of the
+/* 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 "exception.h"
#include "adiv5.h"
-
#include "swdptap.h"
-
+#include "jtagtap.h"
#include "command.h"
-
-#include <stdlib.h>
+#include "morse.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 uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint16_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);
+static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value);
int adiv5_swdp_scan(void)
@@ -53,6 +50,8 @@ int adiv5_swdp_scan(void)
ADIv5_DP_t *dp = (void*)calloc(1, sizeof(*dp));
swdptap_init();
+ if(connect_assert_srst)
+ jtagtap_srst(true); /* will be deasserted after attach */
/* 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. */
@@ -65,11 +64,11 @@ int adiv5_swdp_scan(void)
return -1;
}
- dp->dp_write = adiv5_swdp_write;
dp->dp_read = adiv5_swdp_read;
dp->error = adiv5_swdp_error;
dp->low_access = adiv5_swdp_low_access;
+ adiv5_swdp_error(dp);
adiv5_dp_init(dp);
if(!target_list) morse("NO TARGETS.", 1);
@@ -78,40 +77,45 @@ int adiv5_swdp_scan(void)
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, ADIV5_LOW_DP, ADIV5_LOW_WRITE, addr, value);
-}
-
-static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr)
+static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint16_t addr)
{
- return adiv5_swdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ, addr, 0);
+ if (addr & ADIV5_APnDP) {
+ adiv5_dp_low_access(dp, ADIV5_LOW_READ, addr, 0);
+ return adiv5_dp_low_access(dp, ADIV5_LOW_READ,
+ ADIV5_DP_RDBUFF, 0);
+ } else {
+ return adiv5_swdp_low_access(dp, ADIV5_LOW_READ, addr, 0);
+ }
}
static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp)
{
uint32_t err, clr = 0;
- err = adiv5_swdp_read(dp, ADIV5_DP_CTRLSTAT) &
+ err = adiv5_swdp_read(dp, ADIV5_DP_CTRLSTAT) &
(ADIV5_DP_CTRLSTAT_STICKYORUN | ADIV5_DP_CTRLSTAT_STICKYCMP |
- ADIV5_DP_CTRLSTAT_STICKYERR);
+ ADIV5_DP_CTRLSTAT_STICKYERR | ADIV5_DP_CTRLSTAT_WDATAERR);
- if(err & ADIV5_DP_CTRLSTAT_STICKYORUN)
+ if(err & ADIV5_DP_CTRLSTAT_STICKYORUN)
clr |= ADIV5_DP_ABORT_ORUNERRCLR;
- if(err & ADIV5_DP_CTRLSTAT_STICKYCMP)
+ if(err & ADIV5_DP_CTRLSTAT_STICKYCMP)
clr |= ADIV5_DP_ABORT_STKCMPCLR;
- if(err & ADIV5_DP_CTRLSTAT_STICKYERR)
+ if(err & ADIV5_DP_CTRLSTAT_STICKYERR)
clr |= ADIV5_DP_ABORT_STKERRCLR;
+ if(err & ADIV5_DP_CTRLSTAT_WDATAERR)
+ clr |= ADIV5_DP_ABORT_WDERRCLR;
- adiv5_swdp_write(dp, ADIV5_DP_ABORT, clr);
+ adiv5_dp_write(dp, ADIV5_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)
+static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value)
{
+ bool APnDP = addr & ADIV5_APnDP;
+ addr &= 0xff;
uint8_t request = 0x81;
uint32_t response;
uint8_t ack;
@@ -126,24 +130,26 @@ static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW
if((addr == 4) || (addr == 8))
request ^= 0x20;
+ platform_timeout_set(2000);
do {
swdptap_seq_out(request, 8);
ack = swdptap_seq_in(3);
- } while(ack == SWDP_ACK_WAIT);
+ } while (!platform_timeout_is_expired() && ack == SWDP_ACK_WAIT);
+
+ if (ack == SWDP_ACK_WAIT)
+ raise_exception(EXCEPTION_TIMEOUT, "SWDP ACK timeout");
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(ack != SWDP_ACK_OK)
+ raise_exception(EXCEPTION_ERROR, "SWDP invalid ACK");
if(RnW) {
if(swdptap_seq_in_parity(&response, 32)) /* Give up on parity error */
- PLATFORM_FATAL_ERROR(1);
+ raise_exception(EXCEPTION_ERROR, "SWDP Parity error");
} else {
swdptap_seq_out_parity(value, 32);
}
diff --git a/src/arm7tdmi.c b/src/arm7tdmi.c
deleted file mode 100644
index 805f995..0000000
--- a/src/arm7tdmi.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * 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 ARM7TDMI target support using the JTAG
- * interface as described in ARM7TDMI Technical Reference Manual,
- * ARM Document DDI 0210C
- */
-
-#include "general.h"
-#include "platform.h"
-#include "target.h"
-#include "jtag_scan.h"
-#include "jtagtap.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/* TODO:
- * Skeleton target.
- * EmbeddedICE registers, halt/resume target.
- * Check target mode on halt, switch to ARM if needed.
- * Read registers on halt, restore on resume. Give GDB cached copy.
- * System speed access, read/write memory.
- * Misaligned/byte memory access.
- * Breakpoint support.
- * Watchpoint support.
- * Funnies: abort on breakpointed instruction, etc.
- * Flash programming for STR73x and LPC2xxx.
- */
-static const char arm7_driver_str[] = "ARM7TDMI";
-
-/* ARM7 JTAG IR values */
-#define ARM7_IR_EXTEST 0x0
-#define ARM7_IR_SCAN_N 0x2
-#define ARM7_IR_SAMPLE_PRELOAD 0x3
-#define ARM7_IR_RESTART 0x4
-#define ARM7_IR_CLAMP 0x5
-#define ARM7_IR_HIGHZ 0x7
-#define ARM7_IR_CLAMPZ 0x9
-#define ARM7_IR_INTEST 0xC
-#define ARM7_IR_IDCODE 0xE
-#define ARM7_IR_BYPASS 0xF
-
-/* ARM7 SCAN_N scan chain values */
-#define ARM7_SCANN_BOUNDARY 0
-#define ARM7_SCANN_DBUS 1
-#define ARM7_SCANN_EICE 2
-
-/* EmbeddedICE-RT Register addresses */
-#define ARM7_EICE_DEBUG_CTRL 0x00
-#define ARM7_EICE_DEBUG_STAT 0x01
-#define ARM7_EICE_ABORT_STAT 0x02
-#define ARM7_EICE_COMMS_CTRL 0x04
-#define ARM7_EICE_COMMS_DATA 0x05
-#define ARM7_EICE_WATCH_ADDR(x) (0x08 + (8 * (x))
-#define ARM7_EICE_WATCH_ADDR_MASK(x) (0x09 + (8 * (x))
-#define ARM7_EICE_WATCH_DATA(x) (0x0A + (8 * (x))
-#define ARM7_EICE_WATCH_DATA_MASK(x) (0x0B + (8 * (x))
-#define ARM7_EICE_WATCH_CTRL(x) (0x0C + (8 * (x))
-#define ARM7_EICE_WATCH_CTRL_MASK(x) (0x0D + (8 * (x))
-
-/* Read/write bit in EmbeddedICE-RT scan chain */
-#define ARM7_EICE_READ (0uLL << 37)
-#define ARM7_EICE_WRITE (1uLL << 37)
-
-/* Debug Control Register bits */
-#define ARM7_EICE_DEBUG_CTRL_EICE_DISABLE (1 << 5)
-#define ARM7_EICE_DEBUG_CTRL_MONITOR (1 << 4)
-/* Bit 3 - Reserved */
-#define ARM7_EICE_DEBUG_CTRL_INTDIS (1 << 2)
-#define ARM7_EICE_DEBUG_CTRL_DBGRQ (1 << 1)
-#define ARM7_EICE_DEBUG_CTRL_DBGACK (1 << 0)
-
-/* Debug Status Register bits */
-#define ARM7_EICE_DEBUG_STAT_TBIT (1 << 4)
-#define ARM7_EICE_DEBUG_STAT_NMREQ (1 << 3)
-#define ARM7_EICE_DEBUG_STAT_INTDIS (1 << 2)
-#define ARM7_EICE_DEBUG_STAT_DBGRQ (1 << 1)
-#define ARM7_EICE_DEBUG_STAT_DBGACK (1 << 0)
-
-#define ARM7_OP_NOP 0xE1A00000
-
-struct target_arm7_s {
- target t;
- jtag_dev_t *jtag;
- uint32_t reg_cache[16];
-};
-
-/* FIXME: Remove: */
-static void do_nothing(void)
-{
-}
-
-static bool arm7_attach(struct target_s *target);
-static int arm7_regs_read(struct target_s *target, void *data);
-static int arm7_regs_write(struct target_s *target, const void *data);
-static void arm7_halt_request(struct target_s *target);
-static int arm7_halt_wait(struct target_s *target);
-static void arm7_halt_resume(struct target_s *target, bool step);
-
-void arm7tdmi_jtag_handler(jtag_dev_t *dev)
-{
- struct target_arm7_s *tj = (void*)target_new(sizeof(*tj));
- target *t = (target *)tj;
-
- t->driver = arm7_driver_str;
- tj->jtag = dev;
-
- /* Setup mandatory virtual methods */
- t->attach = arm7_attach;
- t->detach = (void *)do_nothing;
- t->check_error = (void *)do_nothing;
- t->mem_read_words = (void *)do_nothing;
- t->mem_write_words = (void *)do_nothing;
- t->mem_read_bytes = (void *)do_nothing;
- t->mem_write_bytes = (void *)do_nothing;
- t->regs_size = 16 * 4;
- t->regs_read = (void *)arm7_regs_read;
- t->regs_write = (void *)arm7_regs_write;
- t->pc_write = (void *)do_nothing;
- t->reset = (void *)do_nothing;
- t->halt_request = arm7_halt_request;
- t->halt_wait = arm7_halt_wait;
- t->halt_resume = arm7_halt_resume;
-
- /* TODO: Breakpoint and watchpoint functions. */
- /* TODO: Fault unwinder. */
- /* TODO: Memory map / Flash programming. */
-}
-
-static void arm7_select_scanchain(struct target_arm7_s *target, uint8_t chain)
-{
- jtag_dev_write_ir(target->jtag, ARM7_IR_SCAN_N);
- jtag_dev_shift_dr(target->jtag, NULL, &chain, 4);
- jtag_dev_write_ir(target->jtag, ARM7_IR_INTEST);
-}
-
-static void arm7_eice_write(struct target_arm7_s *target,
- uint8_t addr, uint32_t value)
-{
- uint64_t val = ((uint64_t)addr << 32) | value | ARM7_EICE_WRITE;
-
- arm7_select_scanchain(target, ARM7_SCANN_EICE);
- jtag_dev_shift_dr(target->jtag, NULL, (uint8_t *)&val, 38);
- DEBUG("eice_write(%d, 0x%08X)\n", addr, value);
-}
-
-static uint32_t arm7_eice_read(struct target_arm7_s *target, uint8_t addr)
-{
- uint64_t val = ((uint64_t)addr << 32) | ARM7_EICE_READ;
-
- arm7_select_scanchain(target, ARM7_SCANN_EICE);
- jtag_dev_shift_dr(target->jtag, NULL, (uint8_t *)&val, 38);
- jtag_dev_shift_dr(target->jtag, (uint8_t *)&val, (uint8_t *)&val, 38);
- DEBUG("eice_read(%d, 0x%08X)\n", addr, (uint32_t)val);
-
- return (uint32_t)val;
-}
-
-/* Execute a single instruction at debug speed.
- * Performs datalen data bus accesses after the op to capture data.
- */
-static void arm7_op_debug(struct target_arm7_s *t, uint32_t op, uint32_t *data,
- int datalen)
-{
- uint64_t tmp;
- /* FIXME: This routine is broken.
- * This process isn't very well documented. Maybe NOPs need to
- * be shifted into pipeline before data is read out.
- */
- DEBUG("op_debug(0x%08X)\n", op);
- arm7_select_scanchain(t, ARM7_SCANN_DBUS);
- tmp = op;
- jtag_dev_shift_dr(t->jtag, NULL, (const uint8_t*)&tmp, 33);
- while(datalen--) {
- tmp = *data;
- jtag_dev_shift_dr(t->jtag, (uint8_t*)&tmp, (uint8_t*)&tmp, 33);
- *data = (uint32_t)tmp;
- DEBUG("\t0x%08X\n", *data);
- data++;
- }
-}
-
-/* Execute a single instruction at system speed. */
-static void arm7_op_system(struct target_arm7_s *t, uint32_t op)
-{
- uint64_t tmp;
- arm7_select_scanchain(t, ARM7_SCANN_DBUS);
- tmp = op | (1uLL << 32);
- jtag_dev_shift_dr(t->jtag, NULL, (const uint8_t*)&tmp, 33);
-}
-
-static void arm7_halt_request(struct target_s *target)
-{
- struct target_arm7_s *t = (struct target_arm7_s *)target;
-
- arm7_eice_write(t, ARM7_EICE_DEBUG_CTRL, ARM7_EICE_DEBUG_CTRL_DBGRQ);
-}
-
-static int arm7_halt_wait(struct target_s *target)
-{
- struct target_arm7_s *t = (struct target_arm7_s *)target;
- int stat = arm7_eice_read(t, ARM7_EICE_DEBUG_STAT);
-
- if(!(stat & ARM7_EICE_DEBUG_STAT_DBGACK))
- return 0;
-
- /* We are halted, so switch to ARM mode if needed. */
- if(stat & ARM7_EICE_DEBUG_STAT_TBIT) {
- /* This sequence switches to ARM mode:
- * 6000 STR R0, [R0] ; Save R0 before use
- * 4678 MOV R0, PC ; Copy PC into R0
- * 6000 STR R0, [R0] ; Now save the PC in R0
- * 4778 BX PC ; Jump into ARM state
- * 46c0 MOV R8, R8 ; NOP
- * 46c0 MOV R8, R8 ; NOP
- */
- /* FIXME: Switch to ARM mode. */
- }
-
- /* Fetch core register values */
- /* E880FFFF STM R0, {R0-R15} */
- arm7_op_debug(t, 0xE880FFFF, t->reg_cache, 16);
-
- return 1;
-}
-
-static void arm7_halt_resume(struct target_s *target, bool step)
-{
- struct target_arm7_s *t = (struct target_arm7_s *)target;
-
- if(step) {
- /* FIXME: Set breakpoint on any instruction to single step. */
- }
-
- /* Restore core registers. */
- /* E890FFFF LDM R0, {R0-R15} */
- arm7_op_debug(t, 0xE890FFFF, t->reg_cache, 16);
-
- /* Release DBGRQ */
- arm7_eice_write(t, ARM7_EICE_DEBUG_CTRL, 0);
- /* This sequence restores PC if no other instructions issued in
- * debug mode...
- * 0 E1A00000; MOV R0, R0
- * 1 E1A00000; MOV R0, R0
- * 0 EAFFFFFA; B -6
- * FIXME: Add adjustment for other opcodes.
- */
- arm7_op_debug(t, ARM7_OP_NOP, NULL, 0);
- arm7_op_system(t, ARM7_OP_NOP);
- arm7_op_debug(t, 0xEAFFFFF8, NULL, 0);
-
- jtag_dev_write_ir(t->jtag, ARM7_IR_RESTART);
-}
-
-static bool arm7_attach(struct target_s *target)
-{
- int tries = 0;
- target_halt_request(target);
- while(!target_halt_wait(target) && --tries)
- platform_delay(2);
- if(!tries)
- return false;
- return true;
-}
-
-static int arm7_regs_read(struct target_s *target, void *data)
-{
- struct target_arm7_s *t = (struct target_arm7_s *)target;
- memcpy(data, t->reg_cache, target->regs_size);
- return 0;
-}
-
-static int arm7_regs_write(struct target_s *target, const void *data)
-{
- struct target_arm7_s *t = (struct target_arm7_s *)target;
- memcpy(t->reg_cache, data, target->regs_size);
- return 0;
-}
-
diff --git a/src/command.c b/src/command.c
index 46163fd..8e20960 100644
--- a/src/command.c
+++ b/src/command.c
@@ -22,18 +22,19 @@
* commands.
*/
-#include <stdlib.h>
-#include <string.h>
-
#include "general.h"
-
+#include "exception.h"
#include "command.h"
#include "gdb_packet.h"
-
#include "jtag_scan.h"
#include "target.h"
-
+#include "morse.h"
#include "adiv5.h"
+#include "version.h"
+
+#ifdef PLATFORM_HAS_TRACESWO
+# include "traceswo.h"
+#endif
static bool cmd_version(void);
static bool cmd_help(target *t);
@@ -42,6 +43,10 @@ static bool cmd_jtag_scan(target *t, int argc, char **argv);
static bool cmd_swdp_scan(void);
static bool cmd_targets(target *t);
static bool cmd_morse(void);
+static bool cmd_connect_srst(target *t, int argc, const char **argv);
+#ifdef PLATFORM_HAS_POWER_SWITCH
+static bool cmd_target_power(target *t, int argc, const char **argv);
+#endif
#ifdef PLATFORM_HAS_TRACESWO
static bool cmd_traceswo(void);
#endif
@@ -53,6 +58,10 @@ const struct command_s cmd_list[] = {
{"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" },
+ {"connect_srst", (cmd_handler)cmd_connect_srst, "Configure connect under SRST: (enable|disable)" },
+#ifdef PLATFORM_HAS_POWER_SWITCH
+ {"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
+#endif
#ifdef PLATFORM_HAS_TRACESWO
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture" },
#endif
@@ -68,19 +77,19 @@ int command_process(target *t, char *cmd)
const char **argv;
/* Initial estimate for argc */
- for(char *s = cmd; *s; s++)
+ 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");
+ 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++) {
/* Accept a partial match as GDB does.
- * So 'mon ver' will match 'monitor version'
+ * So 'mon ver' will match 'monitor version'
*/
if(!strncmp(argv[0], c->cmd, strlen(argv[0])))
return !c->handler(t, argc, argv);
@@ -90,7 +99,7 @@ int command_process(target *t, char *cmd)
return -1;
for (tc = t->commands; tc; tc = tc->next)
- for(c = tc->cmds; c->cmd; c++)
+ for(c = tc->cmds; c->cmd; c++)
if(!strncmp(argv[0], c->cmd, strlen(argv[0])))
return !c->handler(t, argc, argv);
@@ -99,8 +108,8 @@ int command_process(target *t, char *cmd)
bool cmd_version(void)
{
- gdb_out("Black Magic Probe (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")\n");
- gdb_out("Copyright (C) 2011 Black Sphere Technologies Ltd.\n");
+ gdb_out("Black Magic Probe (Firmware " FIRMWARE_VERSION ")\n");
+ gdb_out("Copyright (C) 2015 Black Sphere Technologies Ltd.\n");
gdb_out("License GPLv3+: GNU GPL version 3 or later "
"<http://gnu.org/licenses/gpl.html>\n\n");
@@ -113,7 +122,7 @@ bool cmd_help(target *t)
const struct command_s *c;
gdb_out("General commands:\n");
- for(c = cmd_list; c->cmd; c++)
+ for(c = cmd_list; c->cmd; c++)
gdb_outf("\t%s -- %s\n", c->cmd, c->help);
if (!t)
@@ -121,7 +130,7 @@ bool cmd_help(target *t)
for (tc = t->commands; tc; tc = tc->next) {
gdb_outf("%s specific commands:\n", tc->specific_name);
- for(c = tc->cmds; c->cmd; c++)
+ for(c = tc->cmds; c->cmd; c++)
gdb_outf("\t%s -- %s\n", c->cmd, c->help);
}
@@ -131,32 +140,43 @@ bool cmd_help(target *t)
static bool cmd_jtag_scan(target *t, int argc, char **argv)
{
(void)t;
- uint8_t *irlens = NULL;
+ uint8_t irlens[argc];
gdb_outf("Target voltage: %s\n", platform_target_voltage());
if (argc > 1) {
/* Accept a list of IR lengths on command line */
- irlens = alloca(argc);
for (int i = 1; i < argc; i++)
irlens[i-1] = atoi(argv[i]);
irlens[argc-1] = 0;
}
- int devs = jtag_scan(irlens);
+ int devs = -1;
+ volatile struct exception e;
+ TRY_CATCH (e, EXCEPTION_ALL) {
+ devs = jtag_scan(argc > 1 ? irlens : NULL);
+ }
+ switch (e.type) {
+ case EXCEPTION_TIMEOUT:
+ gdb_outf("Timeout during scan. Is target stuck in WFI?\n");
+ break;
+ case EXCEPTION_ERROR:
+ gdb_outf("Exception: %s\n", e.msg);
+ break;
+ }
if(devs < 0) {
gdb_out("JTAG device scan failed!\n");
return false;
- }
+ }
if(devs == 0) {
gdb_out("JTAG scan found no devices!\n");
return false;
- }
+ }
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,
+ 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(NULL);
@@ -167,16 +187,28 @@ bool cmd_swdp_scan(void)
{
gdb_outf("Target voltage: %s\n", platform_target_voltage());
- if(adiv5_swdp_scan() < 0) {
+ int devs = -1;
+ volatile struct exception e;
+ TRY_CATCH (e, EXCEPTION_ALL) {
+ devs = adiv5_swdp_scan();
+ }
+ switch (e.type) {
+ case EXCEPTION_TIMEOUT:
+ gdb_outf("Timeout during scan. Is target stuck in WFI?\n");
+ break;
+ case EXCEPTION_ERROR:
+ gdb_outf("Exception: %s\n", e.msg);
+ break;
+ }
+
+ if(devs < 0) {
gdb_out("SW-DP scan failed!\n");
return false;
- }
-
- //gdb_outf("SW-DP detected IDCODE: 0x%08X\n", adiv5_dp_list->idcode);
+ }
cmd_targets(NULL);
return true;
-
+
}
bool cmd_targets(target *cur_target)
@@ -188,11 +220,11 @@ bool cmd_targets(target *cur_target)
gdb_out("No usable targets found.\n");
return false;
}
-
+
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?'*':' ',
+ gdb_outf("%2d %c %s\n", i, t==cur_target?'*':' ',
t->driver);
return true;
@@ -200,11 +232,35 @@ bool cmd_targets(target *cur_target)
bool cmd_morse(void)
{
- if(morse_msg)
+ if(morse_msg)
gdb_outf("%s\n", morse_msg);
return true;
}
+static bool cmd_connect_srst(target *t, int argc, const char **argv)
+{
+ (void)t;
+ if (argc == 1)
+ gdb_outf("Assert SRST during connect: %s\n",
+ connect_assert_srst ? "enabled" : "disabled");
+ else
+ connect_assert_srst = !strcmp(argv[1], "enable");
+ return true;
+}
+
+#ifdef PLATFORM_HAS_POWER_SWITCH
+static bool cmd_target_power(target *t, int argc, const char **argv)
+{
+ (void)t;
+ if (argc == 1)
+ gdb_outf("Target Power: %s\n",
+ platform_target_get_power() ? "enabled" : "disabled");
+ else
+ platform_target_set_power(!strncmp(argv[1], "enable", strlen(argv[1])));
+ return true;
+}
+#endif
+
#ifdef PLATFORM_HAS_TRACESWO
static bool cmd_traceswo(void)
{
diff --git a/src/cortexm.c b/src/cortexm.c
index 9ec96d6..ae1c59d 100644
--- a/src/cortexm.c
+++ b/src/cortexm.c
@@ -24,21 +24,19 @@
* ARM doc DDI0403C.
*
* Also supports Cortex-M0 / ARMv6-M
- *
- * Issues:
- * There are way too many magic numbers used here.
*/
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
#include "general.h"
+#include "exception.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "adiv5.h"
#include "target.h"
#include "command.h"
#include "gdb_packet.h"
+#include "cortexm.h"
+#include "morse.h"
+
+#include <unistd.h>
static char cortexm_driver_str[] = "ARM Cortex-M";
@@ -53,157 +51,35 @@ const struct command_s cortexm_cmd_list[] = {
#define TOPT_FLAVOUR_V6M (1<<0) /* if not set, target is assumed to be v7m */
#define TOPT_FLAVOUR_V7MF (1<<1) /* if set, floating-point enabled. */
-/* Private peripheral bus base address */
-#define CORTEXM_PPB_BASE 0xE0000000
-
-#define CORTEXM_SCS_BASE (CORTEXM_PPB_BASE + 0xE000)
-
-#define CORTEXM_AIRCR (CORTEXM_SCS_BASE + 0xD0C)
-#define CORTEXM_CFSR (CORTEXM_SCS_BASE + 0xD28)
-#define CORTEXM_HFSR (CORTEXM_SCS_BASE + 0xD2C)
-#define CORTEXM_DFSR (CORTEXM_SCS_BASE + 0xD30)
-#define CORTEXM_CPACR (CORTEXM_SCS_BASE + 0xD88)
-#define CORTEXM_DHCSR (CORTEXM_SCS_BASE + 0xDF0)
-#define CORTEXM_DCRSR (CORTEXM_SCS_BASE + 0xDF4)
-#define CORTEXM_DCRDR (CORTEXM_SCS_BASE + 0xDF8)
-#define CORTEXM_DEMCR (CORTEXM_SCS_BASE + 0xDFC)
-
-#define CORTEXM_FPB_BASE (CORTEXM_PPB_BASE + 0x2000)
-
-/* ARM Literature uses FP_*, we use CORTEXM_FPB_* consistently */
-#define CORTEXM_FPB_CTRL (CORTEXM_FPB_BASE + 0x000)
-#define CORTEXM_FPB_REMAP (CORTEXM_FPB_BASE + 0x004)
-#define CORTEXM_FPB_COMP(i) (CORTEXM_FPB_BASE + 0x008 + (4*(i)))
-
-#define CORTEXM_DWT_BASE (CORTEXM_PPB_BASE + 0x1000)
-
-#define CORTEXM_DWT_CTRL (CORTEXM_DWT_BASE + 0x000)
-#define CORTEXM_DWT_COMP(i) (CORTEXM_DWT_BASE + 0x020 + (0x10*(i)))
-#define CORTEXM_DWT_MASK(i) (CORTEXM_DWT_BASE + 0x024 + (0x10*(i)))
-#define CORTEXM_DWT_FUNC(i) (CORTEXM_DWT_BASE + 0x028 + (0x10*(i)))
-
-/* Application Interrupt and Reset Control Register (AIRCR) */
-#define CORTEXM_AIRCR_VECTKEY (0x05FA << 16)
-/* Bits 31:16 - Read as VECTKETSTAT, 0xFA05 */
-#define CORTEXM_AIRCR_ENDIANESS (1 << 15)
-/* Bits 15:11 - Unused, reserved */
-#define CORTEXM_AIRCR_PRIGROUP (7 << 8)
-/* Bits 7:3 - Unused, reserved */
-#define CORTEXM_AIRCR_SYSRESETREQ (1 << 2)
-#define CORTEXM_AIRCR_VECTCLRACTIVE (1 << 1)
-#define CORTEXM_AIRCR_VECTRESET (1 << 0)
-
-/* HardFault Status Register (HFSR) */
-#define CORTEXM_HFSR_DEBUGEVT (1 << 31)
-#define CORTEXM_HFSR_FORCED (1 << 30)
-/* Bits 29:2 - Not specified */
-#define CORTEXM_HFSR_VECTTBL (1 << 1)
-/* Bits 0 - Reserved */
-
-/* Debug Fault Status Register (DFSR) */
-/* Bits 31:5 - Reserved */
-#define CORTEXM_DFSR_RESETALL 0x1F
-#define CORTEXM_DFSR_EXTERNAL (1 << 4)
-#define CORTEXM_DFSR_VCATCH (1 << 3)
-#define CORTEXM_DFSR_DWTTRAP (1 << 2)
-#define CORTEXM_DFSR_BKPT (1 << 1)
-#define CORTEXM_DFSR_HALTED (1 << 0)
-
-/* Debug Halting Control and Status Register (DHCSR) */
-/* This key must be written to bits 31:16 for write to take effect */
-#define CORTEXM_DHCSR_DBGKEY 0xA05F0000
-/* Bits 31:26 - Reserved */
-#define CORTEXM_DHCSR_S_RESET_ST (1 << 25)
-#define CORTEXM_DHCSR_S_RETIRE_ST (1 << 24)
-/* Bits 23:20 - Reserved */
-#define CORTEXM_DHCSR_S_LOCKUP (1 << 19)
-#define CORTEXM_DHCSR_S_SLEEP (1 << 18)
-#define CORTEXM_DHCSR_S_HALT (1 << 17)
-#define CORTEXM_DHCSR_S_REGRDY (1 << 16)
-/* Bits 15:6 - Reserved */
-#define CORTEXM_DHCSR_C_SNAPSTALL (1 << 5) /* v7m only */
-/* Bit 4 - Reserved */
-#define CORTEXM_DHCSR_C_MASKINTS (1 << 3)
-#define CORTEXM_DHCSR_C_STEP (1 << 2)
-#define CORTEXM_DHCSR_C_HALT (1 << 1)
-#define CORTEXM_DHCSR_C_DEBUGEN (1 << 0)
-
-/* Debug Core Register Selector Register (DCRSR) */
-#define CORTEXM_DCRSR_REGWnR 0x00010000
-#define CORTEXM_DCRSR_REGSEL_MASK 0x0000001F
-#define CORTEXM_DCRSR_REGSEL_XPSR 0x00000010
-#define CORTEXM_DCRSR_REGSEL_MSP 0x00000011
-#define CORTEXM_DCRSR_REGSEL_PSP 0x00000012
-
-/* Debug Exception and Monitor Control Register (DEMCR) */
-/* Bits 31:25 - Reserved */
-#define CORTEXM_DEMCR_TRCENA (1 << 24)
-/* Bits 23:20 - Reserved */
-#define CORTEXM_DEMCR_MON_REQ (1 << 19) /* v7m only */
-#define CORTEXM_DEMCR_MON_STEP (1 << 18) /* v7m only */
-#define CORTEXM_DEMCR_VC_MON_PEND (1 << 17) /* v7m only */
-#define CORTEXM_DEMCR_VC_MON_EN (1 << 16) /* v7m only */
-/* Bits 15:11 - Reserved */
-#define CORTEXM_DEMCR_VC_HARDERR (1 << 10)
-#define CORTEXM_DEMCR_VC_INTERR (1 << 9) /* v7m only */
-#define CORTEXM_DEMCR_VC_BUSERR (1 << 8) /* v7m only */
-#define CORTEXM_DEMCR_VC_STATERR (1 << 7) /* v7m only */
-#define CORTEXM_DEMCR_VC_CHKERR (1 << 6) /* v7m only */
-#define CORTEXM_DEMCR_VC_NOCPERR (1 << 5) /* v7m only */
-#define CORTEXM_DEMCR_VC_MMERR (1 << 4) /* v7m only */
-/* Bits 3:1 - Reserved */
-#define CORTEXM_DEMCR_VC_CORERESET (1 << 0)
-
-/* Flash Patch and Breakpoint Control Register (FP_CTRL) */
-/* Bits 32:15 - Reserved */
-/* Bits 14:12 - NUM_CODE2 */ /* v7m only */
-/* Bits 11:8 - NUM_LIT */ /* v7m only */
-/* Bits 7:4 - NUM_CODE1 */
-/* Bits 3:2 - Unspecified */
-#define CORTEXM_FPB_CTRL_KEY (1 << 1)
-#define CORTEXM_FPB_CTRL_ENABLE (1 << 0)
-
-/* Data Watchpoint and Trace Mask Register (DWT_MASKx) */
-#define CORTEXM_DWT_MASK_BYTE (0 << 0)
-#define CORTEXM_DWT_MASK_HALFWORD (1 << 0)
-#define CORTEXM_DWT_MASK_WORD (3 << 0)
-
-/* Data Watchpoint and Trace Function Register (DWT_FUNCTIONx) */
-#define CORTEXM_DWT_FUNC_MATCHED (1 << 24)
-#define CORTEXM_DWT_FUNC_DATAVSIZE_WORD (2 << 10) /* v7m only */
-#define CORTEXM_DWT_FUNC_FUNC_READ (5 << 0)
-#define CORTEXM_DWT_FUNC_FUNC_WRITE (6 << 0)
-#define CORTEXM_DWT_FUNC_FUNC_ACCESS (7 << 0)
-
/* Signals returned by cortexm_halt_wait() */
#define SIGINT 2
#define SIGTRAP 5
#define SIGSEGV 11
+#define SIGLOST 29
-static bool cortexm_attach(struct target_s *target);
-static void cortexm_detach(struct target_s *target);
-
-static int cortexm_regs_read(struct target_s *target, void *data);
-static int cortexm_regs_write(struct target_s *target, const void *data);
-static int cortexm_pc_write(struct target_s *target, const uint32_t val);
+static void cortexm_regs_read(target *t, void *data);
+static void cortexm_regs_write(target *t, const void *data);
+static uint32_t cortexm_pc_read(target *t);
-static void cortexm_reset(struct target_s *target);
-static void cortexm_halt_resume(struct target_s *target, bool step);
-static int cortexm_halt_wait(struct target_s *target);
-static void cortexm_halt_request(struct target_s *target);
-static int cortexm_fault_unwind(struct target_s *target);
+static void cortexm_reset(target *t);
+static int cortexm_halt_wait(target *t);
+static void cortexm_halt_request(target *t);
+static int cortexm_fault_unwind(target *t);
-static int cortexm_set_hw_bp(struct target_s *target, uint32_t addr);
-static int cortexm_clear_hw_bp(struct target_s *target, uint32_t addr);
+static int cortexm_set_hw_bp(target *t, uint32_t addr);
+static int cortexm_clear_hw_bp(target *t, uint32_t addr);
-static int cortexm_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
-static int cortexm_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
+static int cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len);
+static int cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len);
-static int cortexm_check_hw_wp(struct target_s *target, uint32_t *addr);
+static int cortexm_check_hw_wp(target *t, uint32_t *addr);
#define CORTEXM_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */
#define CORTEXM_MAX_BREAKPOINTS 6 /* architecture says up to 127, no implementation has > 6 */
+static int cortexm_hostio_request(target *t);
+static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode);
+
struct cortexm_priv {
bool stepping;
bool on_bkpt;
@@ -219,6 +95,10 @@ struct cortexm_priv {
unsigned hw_breakpoint_max;
/* Copy of DEMCR for vector-catch */
uint32_t demcr;
+ /* Semihosting state */
+ uint32_t syscall;
+ uint32_t errno;
+ uint32_t byte_count;
};
/* Register number tables */
@@ -315,39 +195,47 @@ static const char tdesc_cortex_mf[] =
" </feature>"
"</target>";
-bool
-cortexm_probe(struct target_s *target)
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
+#define REG_XPSR 16
+#define REG_MSP 17
+#define REG_PSP 18
+#define REG_SPECIAL 19
+
+bool cortexm_probe(target *t)
{
- target->driver = cortexm_driver_str;
+ t->driver = cortexm_driver_str;
- target->attach = cortexm_attach;
- target->detach = cortexm_detach;
+ t->attach = cortexm_attach;
+ t->detach = cortexm_detach;
/* Should probe here to make sure it's Cortex-M3 */
- target->tdesc = tdesc_cortex_m;
- target->regs_read = cortexm_regs_read;
- target->regs_write = cortexm_regs_write;
- target->pc_write = cortexm_pc_write;
+ t->tdesc = tdesc_cortex_m;
+ t->regs_read = cortexm_regs_read;
+ t->regs_write = cortexm_regs_write;
+
+ t->reset = cortexm_reset;
+ t->halt_request = cortexm_halt_request;
+ t->halt_wait = cortexm_halt_wait;
+ t->halt_resume = cortexm_halt_resume;
+ t->regs_size = sizeof(regnum_cortex_m);
- target->reset = cortexm_reset;
- target->halt_request = cortexm_halt_request;
- target->halt_wait = cortexm_halt_wait;
- target->halt_resume = cortexm_halt_resume;
- target->regs_size = sizeof(regnum_cortex_m);
+ t->hostio_reply = cortexm_hostio_reply;
- target_add_commands(target, cortexm_cmd_list, cortexm_driver_str);
+ target_add_commands(t, cortexm_cmd_list, cortexm_driver_str);
/* Probe for FP extension */
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint32_t cpacr = adiv5_ap_mem_read(ap, CORTEXM_CPACR);
+ uint32_t cpacr = target_mem_read32(t, CORTEXM_CPACR);
cpacr |= 0x00F00000; /* CP10 = 0b11, CP11 = 0b11 */
- adiv5_ap_mem_write(ap, CORTEXM_CPACR, cpacr);
- if (adiv5_ap_mem_read(ap, CORTEXM_CPACR) == cpacr) {
- target->target_options |= TOPT_FLAVOUR_V7MF;
- target->regs_size += sizeof(regnum_cortex_mf);
- target->tdesc = tdesc_cortex_mf;
+ target_mem_write32(t, CORTEXM_CPACR, cpacr);
+ if (target_mem_read32(t, CORTEXM_CPACR) == cpacr) {
+ t->target_options |= TOPT_FLAVOUR_V7MF;
+ t->regs_size += sizeof(regnum_cortex_mf);
+ t->tdesc = tdesc_cortex_mf;
}
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = calloc(1, sizeof(*priv));
ap->priv = priv;
ap->priv_free = free;
@@ -357,240 +245,272 @@ cortexm_probe(struct target_s *target)
CORTEXM_DEMCR_VC_CORERESET;
#define PROBE(x) \
- do { if ((x)(target)) return true; else target_check_error(target); } while (0)
+ do { if ((x)(t)) return true; else target_check_error(t); } while (0)
PROBE(stm32f1_probe);
PROBE(stm32f4_probe);
- PROBE(stm32l1_probe);
+ PROBE(stm32l0_probe); /* STM32L0xx & STM32L1xx */
PROBE(lpc11xx_probe);
PROBE(lpc43xx_probe);
PROBE(sam3x_probe);
+ PROBE(nrf51_probe);
+ PROBE(samd_probe);
PROBE(lmi_probe);
+ PROBE(kinetis_probe);
#undef PROBE
return true;
}
-static bool
-cortexm_attach(struct target_s *target)
+bool cortexm_attach(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
uint32_t r;
int tries;
/* Clear any pending fault condition */
- target_check_error(target);
+ target_check_error(t);
- target_halt_request(target);
+ target_halt_request(t);
tries = 10;
- while(!target_halt_wait(target) && --tries)
+ while(!connect_assert_srst && !target_halt_wait(t) && --tries)
platform_delay(2);
if(!tries)
return false;
/* Request halt on reset */
- adiv5_ap_mem_write(ap, CORTEXM_DEMCR, priv->demcr);
+ target_mem_write32(t, CORTEXM_DEMCR, priv->demcr);
/* Reset DFSR flags */
- adiv5_ap_mem_write(ap, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
+ target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
/* size the break/watchpoint units */
priv->hw_breakpoint_max = CORTEXM_MAX_BREAKPOINTS;
- r = adiv5_ap_mem_read(ap, CORTEXM_FPB_CTRL);
+ r = target_mem_read32(t, CORTEXM_FPB_CTRL);
if (((r >> 4) & 0xf) < priv->hw_breakpoint_max) /* only look at NUM_COMP1 */
priv->hw_breakpoint_max = (r >> 4) & 0xf;
priv->hw_watchpoint_max = CORTEXM_MAX_WATCHPOINTS;
- r = adiv5_ap_mem_read(ap, CORTEXM_DWT_CTRL);
+ r = target_mem_read32(t, CORTEXM_DWT_CTRL);
if ((r >> 28) > priv->hw_watchpoint_max)
priv->hw_watchpoint_max = r >> 28;
/* Clear any stale breakpoints */
for(i = 0; i < priv->hw_breakpoint_max; i++) {
- adiv5_ap_mem_write(ap, CORTEXM_FPB_COMP(i), 0);
+ target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
priv->hw_breakpoint[i] = 0;
}
/* Clear any stale watchpoints */
for(i = 0; i < priv->hw_watchpoint_max; i++) {
- adiv5_ap_mem_write(ap, CORTEXM_DWT_FUNC(i), 0);
+ target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
priv->hw_watchpoint[i].type = 0;
}
/* Flash Patch Control Register: set ENABLE */
- adiv5_ap_mem_write(ap, CORTEXM_FPB_CTRL,
+ target_mem_write32(t, CORTEXM_FPB_CTRL,
CORTEXM_FPB_CTRL_KEY | CORTEXM_FPB_CTRL_ENABLE);
- target->set_hw_bp = cortexm_set_hw_bp;
- target->clear_hw_bp = cortexm_clear_hw_bp;
+ t->set_hw_bp = cortexm_set_hw_bp;
+ t->clear_hw_bp = cortexm_clear_hw_bp;
/* Data Watchpoint and Trace */
- target->set_hw_wp = cortexm_set_hw_wp;
- target->clear_hw_wp = cortexm_clear_hw_wp;
- target->check_hw_wp = cortexm_check_hw_wp;
+ t->set_hw_wp = cortexm_set_hw_wp;
+ t->clear_hw_wp = cortexm_clear_hw_wp;
+ t->check_hw_wp = cortexm_check_hw_wp;
+
+ if(connect_assert_srst)
+ jtagtap_srst(false);
return true;
}
-static void
-cortexm_detach(struct target_s *target)
+void cortexm_detach(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
/* Clear any stale breakpoints */
for(i = 0; i < priv->hw_breakpoint_max; i++)
- adiv5_ap_mem_write(ap, CORTEXM_FPB_COMP(i), 0);
+ target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
/* Clear any stale watchpoints */
for(i = 0; i < priv->hw_watchpoint_max; i++)
- adiv5_ap_mem_write(ap, CORTEXM_DWT_FUNC(i), 0);
+ target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
/* Disable debug */
- adiv5_ap_mem_write(ap, CORTEXM_DHCSR, CORTEXM_DHCSR_DBGKEY);
+ target_mem_write32(t, CORTEXM_DHCSR, CORTEXM_DHCSR_DBGKEY);
}
-static int
-cortexm_regs_read(struct target_s *target, void *data)
+enum { DB_DHCSR, DB_DCRSR, DB_DCRDR, DB_DEMCR };
+
+static void cortexm_regs_read(target *t, void *data)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
uint32_t *regs = data;
unsigned i;
/* FIXME: Describe what's really going on here */
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
+ adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw | ADIV5_AP_CSW_SIZE_WORD);
/* Map the banked data registers (0x10-0x1c) to the
* debug registers DHCSR, DCRSR, DCRDR and DEMCR respectively */
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_TAR, CORTEXM_DHCSR);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_TAR, CORTEXM_DHCSR);
/* Walk the regnum_cortex_m array, reading the registers it
* calls out. */
- adiv5_ap_write(ap, ADIV5_AP_DB(1), regnum_cortex_m[0]); /* Required to switch banks */
- *regs++ = adiv5_dp_read_ap(ap->dp, ADIV5_AP_DB(2));
+ adiv5_ap_write(ap, ADIV5_AP_DB(DB_DCRSR), regnum_cortex_m[0]); /* Required to switch banks */
+ *regs++ = adiv5_dp_read(ap->dp, ADIV5_AP_DB(DB_DCRDR));
for(i = 1; i < sizeof(regnum_cortex_m) / 4; i++) {
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(1), regnum_cortex_m[i]);
- *regs++ = adiv5_dp_read_ap(ap->dp, ADIV5_AP_DB(2));
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DB(DB_DCRSR),
+ regnum_cortex_m[i]);
+ *regs++ = adiv5_dp_read(ap->dp, ADIV5_AP_DB(DB_DCRDR));
}
- if (target->target_options & TOPT_FLAVOUR_V7MF)
+ if (t->target_options & TOPT_FLAVOUR_V7MF)
for(i = 0; i < sizeof(regnum_cortex_mf) / 4; i++) {
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(1), regnum_cortex_mf[i]);
- *regs++ = adiv5_dp_read_ap(ap->dp, ADIV5_AP_DB(2));
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE,
+ ADIV5_AP_DB(DB_DCRSR),
+ regnum_cortex_mf[i]);
+ *regs++ = adiv5_dp_read(ap->dp, ADIV5_AP_DB(DB_DCRDR));
}
-
- return 0;
}
-static int
-cortexm_regs_write(struct target_s *target, const void *data)
+static void cortexm_regs_write(target *t, const void *data)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
const uint32_t *regs = data;
unsigned i;
/* FIXME: Describe what's really going on here */
- adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw |
- ADIV5_AP_CSW_SIZE_WORD | ADIV5_AP_CSW_ADDRINC_SINGLE);
+ adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw | ADIV5_AP_CSW_SIZE_WORD);
/* Map the banked data registers (0x10-0x1c) to the
* debug registers DHCSR, DCRSR, DCRDR and DEMCR respectively */
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_TAR, CORTEXM_DHCSR);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_TAR, CORTEXM_DHCSR);
/* Walk the regnum_cortex_m array, writing the registers it
* calls out. */
- adiv5_ap_write(ap, ADIV5_AP_DB(2), *regs++); /* Required to switch banks */
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(1), 0x10000 | regnum_cortex_m[0]);
+ adiv5_ap_write(ap, ADIV5_AP_DB(DB_DCRDR), *regs++); /* Required to switch banks */
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DB(DB_DCRSR),
+ 0x10000 | regnum_cortex_m[0]);
for(i = 1; i < sizeof(regnum_cortex_m) / 4; i++) {
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(2), *regs++);
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(1),
- 0x10000 | regnum_cortex_m[i]);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE,
+ ADIV5_AP_DB(DB_DCRDR), *regs++);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DB(DB_DCRSR),
+ 0x10000 | regnum_cortex_m[i]);
}
- if (target->target_options & TOPT_FLAVOUR_V7MF)
+ if (t->target_options & TOPT_FLAVOUR_V7MF)
for(i = 0; i < sizeof(regnum_cortex_mf) / 4; i++) {
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(2), *regs++);
- adiv5_dp_low_access(ap->dp, 1, 0, ADIV5_AP_DB(1),
- 0x10000 | regnum_cortex_mf[i]);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE,
+ ADIV5_AP_DB(DB_DCRDR), *regs++);
+ adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE,
+ ADIV5_AP_DB(DB_DCRSR),
+ 0x10000 | regnum_cortex_mf[i]);
}
-
- return 0;
}
-static uint32_t
-cortexm_pc_read(struct target_s *target)
+static uint32_t cortexm_pc_read(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- adiv5_ap_mem_write(ap, CORTEXM_DCRSR, 0x0F);
- return adiv5_ap_mem_read(ap, CORTEXM_DCRDR);
-
- return 0;
+ target_mem_write32(t, CORTEXM_DCRSR, 0x0F);
+ return target_mem_read32(t, CORTEXM_DCRDR);
}
-static int
-cortexm_pc_write(struct target_s *target, const uint32_t val)
+static void cortexm_pc_write(target *t, const uint32_t val)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- adiv5_ap_mem_write(ap, CORTEXM_DCRDR, val);
- adiv5_ap_mem_write(ap, CORTEXM_DCRSR, CORTEXM_DCRSR_REGWnR | 0x0F);
-
- return 0;
+ target_mem_write32(t, CORTEXM_DCRDR, val);
+ target_mem_write32(t, CORTEXM_DCRSR, CORTEXM_DCRSR_REGWnR | 0x0F);
}
/* The following three routines implement target halt/resume
* using the core debug registers in the NVIC. */
-static void
-cortexm_reset(struct target_s *target)
+static void cortexm_reset(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- jtagtap_srst();
+ jtagtap_srst(true);
+ jtagtap_srst(false);
/* Read DHCSR here to clear S_RESET_ST bit before reset */
- adiv5_ap_mem_read(ap, CORTEXM_DHCSR);
+ target_mem_read32(t, CORTEXM_DHCSR);
/* Request system reset from NVIC: SRST doesn't work correctly */
/* This could be VECTRESET: 0x05FA0001 (reset only core)
* or SYSRESETREQ: 0x05FA0004 (system reset)
*/
- adiv5_ap_mem_write(ap, CORTEXM_AIRCR,
- CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ);
+ target_mem_write32(t, CORTEXM_AIRCR,
+ CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ);
/* Poll for release from reset */
- while(adiv5_ap_mem_read(ap, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST);
+ while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST);
/* Reset DFSR flags */
- adiv5_ap_mem_write(ap, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
+ target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
}
-static void
-cortexm_halt_request(struct target_s *target)
+static void cortexm_halt_request(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- adiv5_ap_mem_write(ap, CORTEXM_DHCSR,
- CORTEXM_DHCSR_DBGKEY | CORTEXM_DHCSR_C_HALT | CORTEXM_DHCSR_C_DEBUGEN);
+ volatile struct exception e;
+ TRY_CATCH (e, EXCEPTION_TIMEOUT) {
+ target_mem_write32(t, CORTEXM_DHCSR, CORTEXM_DHCSR_DBGKEY |
+ CORTEXM_DHCSR_C_HALT |
+ CORTEXM_DHCSR_C_DEBUGEN);
+ }
+ if (e.type) {
+ gdb_out("Timeout sending interrupt, is target in WFI?\n");
+ }
}
-static int
-cortexm_halt_wait(struct target_s *target)
+static int cortexm_halt_wait(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
- if (!(adiv5_ap_mem_read(ap, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_HALT))
+
+ volatile uint32_t dhcsr = 0;
+ volatile struct exception e;
+ TRY_CATCH (e, EXCEPTION_ALL) {
+ /* If this times out because the target is in WFI then
+ * the target is still running. */
+ dhcsr = target_mem_read32(t, CORTEXM_DHCSR);
+ }
+ switch (e.type) {
+ case EXCEPTION_ERROR:
+ /* Oh crap, there's no recovery from this... */
+ target_list_free();
+ morse("TARGET LOST.", 1);
+ return SIGLOST;
+ case EXCEPTION_TIMEOUT:
+ /* Timeout isn't a problem, target could be in WFI */
+ return 0;
+ }
+
+ if (!(dhcsr & CORTEXM_DHCSR_S_HALT))
return 0;
/* We've halted. Let's find out why. */
- uint32_t dfsr = adiv5_ap_mem_read(ap, CORTEXM_DFSR);
- adiv5_ap_mem_write(ap, CORTEXM_DFSR, dfsr); /* write back to reset */
+ uint32_t dfsr = target_mem_read32(t, CORTEXM_DFSR);
+ target_mem_write32(t, CORTEXM_DFSR, dfsr); /* write back to reset */
- if ((dfsr & CORTEXM_DFSR_VCATCH) && cortexm_fault_unwind(target))
+ if ((dfsr & CORTEXM_DFSR_VCATCH) && cortexm_fault_unwind(t))
return SIGSEGV;
/* Remember if we stopped on a breakpoint */
priv->on_bkpt = dfsr & (CORTEXM_DFSR_BKPT);
+ if (priv->on_bkpt) {
+ /* If we've hit a programmed breakpoint, check for semihosting
+ * call. */
+ uint32_t pc = cortexm_pc_read(t);
+ uint16_t bkpt_instr;
+ bkpt_instr = target_mem_read16(t, pc);
+ if (bkpt_instr == 0xBEAB) {
+ int n = cortexm_hostio_request(t);
+ if (n > 0) {
+ target_halt_resume(t, priv->stepping);
+ return 0;
+ } else if (n < 0) {
+ return -1;
+ }
+ }
+ }
+
if (dfsr & (CORTEXM_DFSR_BKPT | CORTEXM_DFSR_DWTTRAP))
return SIGTRAP;
@@ -601,58 +521,71 @@ cortexm_halt_wait(struct target_s *target)
}
-static void
-cortexm_halt_resume(struct target_s *target, bool step)
+void cortexm_halt_resume(target *t, bool step)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
uint32_t dhcsr = CORTEXM_DHCSR_DBGKEY | CORTEXM_DHCSR_C_DEBUGEN;
- if(step) dhcsr |= CORTEXM_DHCSR_C_STEP | CORTEXM_DHCSR_C_MASKINTS;
+ if (step)
+ dhcsr |= CORTEXM_DHCSR_C_STEP | CORTEXM_DHCSR_C_MASKINTS;
/* Disable interrupts while single stepping... */
if(step != priv->stepping) {
- adiv5_ap_mem_write(ap, CORTEXM_DHCSR, dhcsr | CORTEXM_DHCSR_C_HALT);
+ target_mem_write32(t, CORTEXM_DHCSR, dhcsr | CORTEXM_DHCSR_C_HALT);
priv->stepping = step;
}
if (priv->on_bkpt) {
- uint32_t pc = cortexm_pc_read(target);
- if ((adiv5_ap_mem_read_halfword(ap, pc) & 0xFF00) == 0xBE00)
- cortexm_pc_write(target, pc + 2);
+ uint32_t pc = cortexm_pc_read(t);
+ if ((target_mem_read16(t, pc) & 0xFF00) == 0xBE00)
+ cortexm_pc_write(t, pc + 2);
}
- adiv5_ap_mem_write(ap, CORTEXM_DHCSR, dhcsr);
+ target_mem_write32(t, CORTEXM_DHCSR, dhcsr);
}
-static int cortexm_fault_unwind(struct target_s *target)
+static int cortexm_fault_unwind(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint32_t hfsr = adiv5_ap_mem_read(ap, CORTEXM_HFSR);
- uint32_t cfsr = adiv5_ap_mem_read(ap, CORTEXM_CFSR);
- adiv5_ap_mem_write(ap, CORTEXM_HFSR, hfsr);/* write back to reset */
- adiv5_ap_mem_write(ap, CORTEXM_CFSR, cfsr);/* write back to reset */
+ uint32_t hfsr = target_mem_read32(t, CORTEXM_HFSR);
+ uint32_t cfsr = target_mem_read32(t, CORTEXM_CFSR);
+ target_mem_write32(t, CORTEXM_HFSR, hfsr);/* write back to reset */
+ target_mem_write32(t, CORTEXM_CFSR, cfsr);/* write back to reset */
/* We check for FORCED in the HardFault Status Register or
* for a configurable fault to avoid catching core resets */
if((hfsr & CORTEXM_HFSR_FORCED) || cfsr) {
/* Unwind exception */
- uint32_t regs[target->regs_size];
+ uint32_t regs[t->regs_size / 4];
uint32_t stack[8];
uint32_t retcode, framesize;
/* Read registers for post-exception stack pointer */
- target_regs_read(target, regs);
+ target_regs_read(t, regs);
/* save retcode currently in lr */
- retcode = regs[14];
+ retcode = regs[REG_LR];
+ bool spsel = retcode & (1<<2);
+ bool fpca = !(retcode & (1<<4));
/* Read stack for pre-exception registers */
- target_mem_read_words(target, stack, regs[13], sizeof(stack));
- regs[14] = stack[5]; /* restore LR to pre-exception state */
- regs[15] = stack[6]; /* restore PC to pre-exception state */
+ uint32_t sp = spsel ? regs[REG_PSP] : regs[REG_MSP];
+ target_mem_read(t, stack, sp, sizeof(stack));
+ if (target_check_error(t))
+ return 0;
+ regs[REG_LR] = stack[5]; /* restore LR to pre-exception state */
+ regs[REG_PC] = stack[6]; /* restore PC to pre-exception state */
/* adjust stack to pop exception state */
- framesize = (retcode & (1<<4)) ? 0x68 : 0x20; /* check for basic vs. extended frame */
+ framesize = fpca ? 0x68 : 0x20; /* check for basic vs. extended frame */
if (stack[7] & (1<<9)) /* check for stack alignment fixup */
framesize += 4;
- regs[13] += framesize;
+
+ if (spsel) {
+ regs[REG_SPECIAL] |= 0x4000000;
+ regs[REG_SP] = regs[REG_PSP] += framesize;
+ } else {
+ regs[REG_SP] = regs[REG_MSP] += framesize;
+ }
+
+ if (fpca)
+ regs[REG_SPECIAL] |= 0x2000000;
/* FIXME: stack[7] contains xPSR when this is supported */
/* although, if we caught the exception it will be unchanged */
@@ -660,24 +593,55 @@ static int cortexm_fault_unwind(struct target_s *target)
/* Reset exception state to allow resuming from restored
* state.
*/
- adiv5_ap_mem_write(ap, CORTEXM_AIRCR,
+ target_mem_write32(t, CORTEXM_AIRCR,
CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_VECTCLRACTIVE);
/* Write pre-exception registers back to core */
- target_regs_write(target, regs);
+ target_regs_write(t, regs);
return 1;
}
return 0;
}
+int cortexm_run_stub(target *t, uint32_t loadaddr,
+ uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ uint32_t regs[t->regs_size / 4];
+
+ memset(regs, 0, sizeof(regs));
+ regs[0] = r0;
+ regs[1] = r1;
+ regs[2] = r2;
+ regs[3] = r3;
+ regs[15] = loadaddr;
+ regs[16] = 0x1000000;
+ regs[19] = 0;
+
+ cortexm_regs_write(t, regs);
+
+ if (target_check_error(t))
+ return -1;
+
+ /* Execute the stub */
+ cortexm_halt_resume(t, 0);
+ while (!cortexm_halt_wait(t))
+ ;
+
+ uint32_t pc = cortexm_pc_read(t);
+ uint16_t bkpt_instr = target_mem_read16(t, pc);
+ if (bkpt_instr >> 8 != 0xbe)
+ return -2;
+
+ return bkpt_instr & 0xff;
+}
+
/* The following routines implement hardware breakpoints.
* The Flash Patch and Breakpoint (FPB) system is used. */
-static int
-cortexm_set_hw_bp(struct target_s *target, uint32_t addr)
+static int cortexm_set_hw_bp(target *t, uint32_t addr)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
uint32_t val = addr & 0x1FFFFFFC;
unsigned i;
@@ -692,15 +656,14 @@ cortexm_set_hw_bp(struct target_s *target, uint32_t addr)
priv->hw_breakpoint[i] = addr | 1;
- adiv5_ap_mem_write(ap, CORTEXM_FPB_COMP(i), val);
+ target_mem_write32(t, CORTEXM_FPB_COMP(i), val);
return 0;
}
-static int
-cortexm_clear_hw_bp(struct target_s *target, uint32_t addr)
+static int cortexm_clear_hw_bp(target *t, uint32_t addr)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
@@ -711,19 +674,18 @@ cortexm_clear_hw_bp(struct target_s *target, uint32_t addr)
priv->hw_breakpoint[i] = 0;
- adiv5_ap_mem_write(ap, CORTEXM_FPB_COMP(i), 0);
+ target_mem_write32(t, CORTEXM_FPB_COMP(i), 0);
return 0;
}
-
/* The following routines implement hardware watchpoints.
* The Data Watch and Trace (DWT) system is used. */
static int
-cortexm_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
+cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
@@ -745,7 +707,7 @@ cortexm_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t
for(i = 0; i < priv->hw_watchpoint_max; i++)
if((priv->hw_watchpoint[i].type == 0) &&
- ((adiv5_ap_mem_read(ap, CORTEXM_DWT_FUNC(i)) & 0xF) == 0))
+ ((target_mem_read32(t, CORTEXM_DWT_FUNC(i)) & 0xF) == 0))
break;
if(i == priv->hw_watchpoint_max) return -2;
@@ -754,18 +716,18 @@ cortexm_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t
priv->hw_watchpoint[i].addr = addr;
priv->hw_watchpoint[i].size = len;
- adiv5_ap_mem_write(ap, CORTEXM_DWT_COMP(i), addr);
- adiv5_ap_mem_write(ap, CORTEXM_DWT_MASK(i), len);
- adiv5_ap_mem_write(ap, CORTEXM_DWT_FUNC(i), type |
- ((target->target_options & TOPT_FLAVOUR_V6M) ? 0: CORTEXM_DWT_FUNC_DATAVSIZE_WORD));
+ target_mem_write32(t, CORTEXM_DWT_COMP(i), addr);
+ target_mem_write32(t, CORTEXM_DWT_MASK(i), len);
+ target_mem_write32(t, CORTEXM_DWT_FUNC(i), type |
+ ((t->target_options & TOPT_FLAVOUR_V6M) ? 0: CORTEXM_DWT_FUNC_DATAVSIZE_WORD));
return 0;
}
static int
-cortexm_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
+cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
@@ -794,22 +756,21 @@ cortexm_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_
priv->hw_watchpoint[i].type = 0;
- adiv5_ap_mem_write(ap, CORTEXM_DWT_FUNC(i), 0);
+ target_mem_write32(t, CORTEXM_DWT_FUNC(i), 0);
return 0;
}
-static int
-cortexm_check_hw_wp(struct target_s *target, uint32_t *addr)
+static int cortexm_check_hw_wp(target *t, uint32_t *addr)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
struct cortexm_priv *priv = ap->priv;
unsigned i;
for(i = 0; i < priv->hw_watchpoint_max; i++)
/* if SET and MATCHED then break */
if(priv->hw_watchpoint[i].type &&
- (adiv5_ap_mem_read(ap, CORTEXM_DWT_FUNC(i)) &
+ (target_mem_read32(t, CORTEXM_DWT_FUNC(i)) &
CORTEXM_DWT_FUNC_MATCHED))
break;
@@ -826,13 +787,13 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[])
const char *vectors[] = {"reset", NULL, NULL, NULL, "mm", "nocp",
"chk", "stat", "bus", "int", "hard"};
uint32_t tmp = 0;
- unsigned i, j;
+ unsigned i;
if ((argc < 3) || ((argv[1][0] != 'e') && (argv[1][0] != 'd'))) {
gdb_out("usage: monitor vector_catch (enable|disable) "
"(hard|int|bus|stat|chk|nocp|mm|reset)\n");
} else {
- for (j = 0; j < argc; j++)
+ for (int j = 0; j < argc; j++)
for (i = 0; i < sizeof(vectors) / sizeof(char*); i++) {
if (vectors[i] && !strcmp(vectors[i], argv[j]))
tmp |= 1 << i;
@@ -843,7 +804,7 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[])
else
priv->demcr &= ~tmp;
- adiv5_ap_mem_write(ap, CORTEXM_DEMCR, priv->demcr);
+ target_mem_write32(t, CORTEXM_DEMCR, priv->demcr);
}
gdb_out("Catching vectors: ");
@@ -857,3 +818,160 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[])
return true;
}
+
+/* Semihosting support */
+/* ARM Semihosting syscall numbers, from ARM doc DUI0471C, Chapter 8 */
+#define SYS_CLOSE 0x02
+#define SYS_CLOCK 0x10
+#define SYS_ELAPSED 0x30
+#define SYS_ERRNO 0x13
+#define SYS_FLEN 0x0C
+#define SYS_GET_CMDLINE 0x15
+#define SYS_HEAPINFO 0x16
+#define SYS_ISERROR 0x08
+#define SYS_ISTTY 0x09
+#define SYS_OPEN 0x01
+#define SYS_READ 0x06
+#define SYS_READC 0x07
+#define SYS_REMOVE 0x0E
+#define SYS_RENAME 0x0F
+#define SYS_SEEK 0x0A
+#define SYS_SYSTEM 0x12
+#define SYS_TICKFREQ 0x31
+#define SYS_TIME 0x11
+#define SYS_TMPNAM 0x0D
+#define SYS_WRITE 0x05
+#define SYS_WRITEC 0x03
+#define SYS_WRITE0 0x04
+
+#define FILEIO_O_RDONLY 0
+#define FILEIO_O_WRONLY 1
+#define FILEIO_O_RDWR 2
+#define FILEIO_O_APPEND 0x008
+#define FILEIO_O_CREAT 0x200
+#define FILEIO_O_TRUNC 0x400
+
+#define FILEIO_SEEK_SET 0
+#define FILEIO_SEEK_CUR 1
+#define FILEIO_SEEK_END 2
+
+static int cortexm_hostio_request(target *t)
+{
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
+ struct cortexm_priv *priv = ap->priv;
+ uint32_t arm_regs[t->regs_size];
+ uint32_t params[4];
+
+ target_regs_read(t, arm_regs);
+ target_mem_read(t, params, arm_regs[1], sizeof(params));
+ priv->syscall = arm_regs[0];
+
+ DEBUG("syscall 0x%x (%x %x %x %x)\n", priv->syscall,
+ params[0], params[1], params[2], params[3]);
+ switch (priv->syscall) {
+ case SYS_OPEN:{ /* open */
+ /* Translate stupid fopen modes to open flags.
+ * See DUI0471C, Table 8-3 */
+ const uint32_t flags[] = {
+ FILEIO_O_RDONLY, /* r, rb */
+ FILEIO_O_RDWR, /* r+, r+b */
+ FILEIO_O_WRONLY | FILEIO_O_CREAT | FILEIO_O_TRUNC,/*w*/
+ FILEIO_O_RDWR | FILEIO_O_CREAT | FILEIO_O_TRUNC,/*w+*/
+ FILEIO_O_WRONLY | FILEIO_O_CREAT | FILEIO_O_APPEND,/*a*/
+ FILEIO_O_RDWR | FILEIO_O_CREAT | FILEIO_O_APPEND,/*a+*/
+ };
+ uint32_t pflag = flags[params[1] >> 1];
+ char filename[4];
+
+ target_mem_read(t, filename, params[0], sizeof(filename));
+ /* handle requests for console i/o */
+ if (!strcmp(filename, ":tt")) {
+ if (pflag == FILEIO_O_RDONLY)
+ arm_regs[0] = STDIN_FILENO;
+ else if (pflag & FILEIO_O_TRUNC)
+ arm_regs[0] = STDOUT_FILENO;
+ else
+ arm_regs[0] = STDERR_FILENO;
+ arm_regs[0]++;
+ target_regs_write(t, arm_regs);
+ return 1;
+ }
+
+ gdb_putpacket_f("Fopen,%08X/%X,%08X,%08X",
+ params[0], params[2] + 1,
+ pflag, 0644);
+ break;
+ }
+ case SYS_CLOSE: /* close */
+ gdb_putpacket_f("Fclose,%08X", params[0] - 1);
+ break;
+ case SYS_READ: /* read */
+ priv->byte_count = params[2];
+ gdb_putpacket_f("Fread,%08X,%08X,%08X",
+ params[0] - 1, params[1], params[2]);
+ break;
+ case SYS_WRITE: /* write */
+ priv->byte_count = params[2];
+ gdb_putpacket_f("Fwrite,%08X,%08X,%08X",
+ params[0] - 1, params[1], params[2]);
+ break;
+ case SYS_WRITEC: /* writec */
+ gdb_putpacket_f("Fwrite,2,%08X,1", arm_regs[1]);
+ break;
+ case SYS_ISTTY: /* isatty */
+ gdb_putpacket_f("Fisatty,%08X", params[0] - 1);
+ break;
+ case SYS_SEEK: /* lseek */
+ gdb_putpacket_f("Flseek,%08X,%08X,%08X",
+ params[0] - 1, params[1], FILEIO_SEEK_SET);
+ break;
+ case SYS_RENAME:/* rename */
+ gdb_putpacket_f("Frename,%08X/%X,%08X/%X",
+ params[0] - 1, params[1] + 1,
+ params[2], params[3] + 1);
+ break;
+ case SYS_REMOVE:/* unlink */
+ gdb_putpacket_f("Funlink,%08X/%X", params[0] - 1,
+ params[1] + 1);
+ break;
+ case SYS_SYSTEM:/* system */
+ gdb_putpacket_f("Fsystem,%08X/%X", params[0] - 1,
+ params[1] + 1);
+ break;
+
+ case SYS_FLEN: /* Not supported, fake success */
+ priv->errno = 0;
+ return 1;
+
+ case SYS_ERRNO: /* Return last errno from GDB */
+ arm_regs[0] = priv->errno;
+ target_regs_write(t, arm_regs);
+ return 1;
+
+ case SYS_TIME: /* gettimeofday */
+ /* FIXME How do we use gdb's gettimeofday? */
+ default:
+ return 0;
+ }
+
+ return -1;
+}
+
+static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode)
+{
+ ADIv5_AP_t *ap = adiv5_target_ap(t);
+ struct cortexm_priv *priv = ap->priv;
+ uint32_t arm_regs[t->regs_size];
+
+ DEBUG("syscall return ret=%d errno=%d\n", retcode, errcode);
+ target_regs_read(t, arm_regs);
+ if (((priv->syscall == SYS_READ) || (priv->syscall == SYS_WRITE)) &&
+ (retcode > 0))
+ retcode = priv->byte_count - retcode;
+ if ((priv->syscall == SYS_OPEN) && (retcode != -1))
+ retcode++;
+ arm_regs[0] = retcode;
+ target_regs_write(t, arm_regs);
+ priv->errno = errcode;
+}
+
diff --git a/src/crc32.c b/src/crc32.c
index d5c8732..6f1c0e3 100644
--- a/src/crc32.c
+++ b/src/crc32.c
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "platform.h"
+#include "general.h"
#include "target.h"
#if !defined(STM32F1) && !defined(STM32F4)
@@ -94,14 +94,13 @@ uint32_t crc32_calc(uint32_t crc, uint8_t data)
return (crc << 8) ^ crc32_table[((crc >> 24) ^ data) & 255];
}
-uint32_t generic_crc32(struct target_s *target, uint32_t base, int len)
+uint32_t generic_crc32(target *t, uint32_t base, int len)
{
uint32_t crc = -1;
uint8_t byte;
while (len--) {
- if (target_mem_read_bytes(target, &byte, base, 1) != 0)
- return -1;
+ byte = target_mem_read8(t, base);
crc = crc32_calc(crc, byte);
base++;
@@ -110,29 +109,36 @@ uint32_t generic_crc32(struct target_s *target, uint32_t base, int len)
}
#else
#include <libopencm3/stm32/crc.h>
-uint32_t generic_crc32(struct target_s *target, uint32_t base, int len)
+uint32_t generic_crc32(target *t, uint32_t base, int len)
{
uint32_t data;
- uint8_t byte;
+ uint32_t crc;
+ size_t i;
- CRC_CR |= CRC_CR_RESET;
+ CRC_CR |= CRC_CR_RESET;
- while (len >3) {
- if (target_mem_read_words(target, &data, base, 1) != 0)
- return -1;
+ while (len > 3) {
+ data = target_mem_read32(t, base);
- CRC_DR = data;
- base+=4;
- len -= 4;
+ CRC_DR = __builtin_bswap32(data);
+ base += 4;
+ len -= 4;
}
+
+ crc = CRC_DR;
+
while (len--) {
- if (target_mem_read_bytes(target, &byte, base, 1) != 0)
- return -1;
+ data = target_mem_read8(t, base++);
- CRC_DR = byte;
- base++;
+ crc ^= data << 24;
+ for (i = 0; i < 8; i++) {
+ if (crc & 0x80000000)
+ crc = (crc << 1) ^ 0x4C11DB7;
+ else
+ crc <<= 1;
+ }
}
- return CRC_DR;
+ return crc;
}
#endif
diff --git a/src/exception.c b/src/exception.c
new file mode 100644
index 0000000..3d43f99
--- /dev/null
+++ b/src/exception.c
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#include "general.h"
+#include "exception.h"
+
+struct exception *innermost_exception;
+
+void raise_exception(uint32_t type, const char *msg)
+{
+ struct exception *e;
+ for (e = innermost_exception; e; e = e->outer) {
+ if (e->mask & type) {
+ e->type = type;
+ e->msg = msg;
+ innermost_exception = e->outer;
+ longjmp(e->jmpbuf, type);
+ }
+ }
+ abort();
+}
+
diff --git a/src/gdb_main.c b/src/gdb_main.c
index d93a8ce..6b2059f 100644
--- a/src/gdb_main.c
+++ b/src/gdb_main.c
@@ -24,26 +24,15 @@
* Originally written for GDB 6.8, updated and tested with GDB 7.2.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.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"
#include "crc32.h"
@@ -52,7 +41,7 @@
#define ERROR_IF_NO_TARGET() \
if(!cur_target) { gdb_putpacketz("EFF"); break; }
-static unsigned char pbuf[BUF_SIZE];
+static char pbuf[BUF_SIZE];
static target *cur_target;
static target *last_target;
@@ -75,6 +64,7 @@ gdb_main(void)
{
int size;
bool single_step = false;
+ char last_activity = 0;
DEBUG("Entring GDB protocol main loop\n");
/* GDB protocol main loop */
@@ -82,25 +72,24 @@ gdb_main(void)
SET_IDLE_STATE(1);
size = gdb_getpacket(pbuf, BUF_SIZE);
SET_IDLE_STATE(0);
+ continue_activity:
switch(pbuf[0]) {
/* Implementation of these is mandatory! */
case 'g': { /* 'g': Read general registers */
ERROR_IF_NO_TARGET();
- uint32_t arm_regs[cur_target->regs_size];
- target_regs_read(cur_target, (void*)arm_regs);
- gdb_putpacket(hexify(pbuf, (void*)arm_regs, cur_target->regs_size), cur_target->regs_size * 2);
+ uint8_t arm_regs[target_regs_size(cur_target)];
+ target_regs_read(cur_target, arm_regs);
+ gdb_putpacket(hexify(pbuf, arm_regs, sizeof(arm_regs)),
+ sizeof(arm_regs) * 2);
break;
}
case 'm': { /* 'm addr,len': Read len bytes from addr */
uint32_t addr, len;
ERROR_IF_NO_TARGET();
- sscanf(pbuf, "m%08lX,%08lX", &addr, &len);
- DEBUG("m packet: addr = %08lX, len = %08lX\n", addr, len);
+ sscanf(pbuf, "m%" SCNx32 ",%" SCNx32, &addr, &len);
+ DEBUG("m packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
uint8_t mem[len];
- 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);
+ target_mem_read(cur_target, mem, addr, len);
if(target_check_error(cur_target))
gdb_putpacketz("E01");
else
@@ -109,8 +98,8 @@ gdb_main(void)
}
case 'G': { /* 'G XX': Write general registers */
ERROR_IF_NO_TARGET();
- uint32_t arm_regs[cur_target->regs_size];
- unhexify((void*)arm_regs, &pbuf[1], cur_target->regs_size);
+ uint8_t arm_regs[target_regs_size(cur_target)];
+ unhexify(arm_regs, &pbuf[1], sizeof(arm_regs));
target_regs_write(cur_target, arm_regs);
gdb_putpacketz("OK");
break;
@@ -119,14 +108,11 @@ gdb_main(void)
uint32_t addr, len;
int hex;
ERROR_IF_NO_TARGET();
- sscanf(pbuf, "M%08lX,%08lX:%n", &addr, &len, &hex);
- DEBUG("M packet: addr = %08lX, len = %08lX\n", addr, len);
+ sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
+ DEBUG("M packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
uint8_t mem[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);
+ target_mem_write(cur_target, addr, mem, len);
if(target_check_error(cur_target))
gdb_putpacketz("E01");
else
@@ -158,14 +144,27 @@ gdb_main(void)
break;
}
+ last_activity = pbuf[0];
/* Wait for target halt */
while(!(sig = target_halt_wait(cur_target))) {
unsigned char c = gdb_if_getchar_to(0);
if((c == '\x03') || (c == '\x04')) {
target_halt_request(cur_target);
+ last_activity = 's';
}
}
SET_RUN_STATE(0);
+
+ /* Negative signal indicates we're in a syscall */
+ if (sig < 0)
+ break;
+
+ /* Target disappeared */
+ if (cur_target == NULL) {
+ gdb_putpacket_f("X%02X", sig);
+ break;
+ }
+
/* Report reason for halt */
if(target_check_hw_wp(cur_target, &watch_addr)) {
/* Watchpoint hit */
@@ -175,6 +174,28 @@ gdb_main(void)
}
break;
}
+ case 'F': { /* Semihosting call finished */
+ int retcode, errcode, items;
+ char c, *p;
+ if (pbuf[1] == '-')
+ p = &pbuf[2];
+ else
+ p = &pbuf[1];
+ items = sscanf(p, "%x,%x,%c", &retcode, &errcode, &c);
+ if (pbuf[1] == '-')
+ retcode = -retcode;
+
+ target_hostio_reply(cur_target, retcode, errcode);
+
+ /* if break is requested */
+ if (items == 3 && c == 'C') {
+ gdb_putpacketz("T02");
+ break;
+ }
+
+ pbuf[0] = last_activity;
+ goto continue_activity;
+ }
/* Optional GDB packet support */
case '!': /* Enable Extended GDB Protocol. */
@@ -186,7 +207,7 @@ gdb_main(void)
break;
case 0x04:
- case 'D': /* GDB 'detach' command. */
+ case 'D': /* GDB 'detach' command. */
if(cur_target)
target_detach(cur_target);
last_target = cur_target;
@@ -218,12 +239,9 @@ gdb_main(void)
uint32_t addr, len;
int bin;
ERROR_IF_NO_TARGET();
- sscanf(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);
+ sscanf(pbuf, "X%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &bin);
+ DEBUG("X packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
+ target_mem_write(cur_target, addr, pbuf+bin, len);
if(target_check_error(cur_target))
gdb_putpacketz("E01");
else
@@ -258,12 +276,12 @@ handle_q_string_reply(const char *str, const char *param)
{
unsigned long addr, len;
- if (sscanf(param, "%08lX,%08lX", &addr, &len) != 2) {
+ if (sscanf(param, "%08lx,%08lx", &addr, &len) != 2) {
gdb_putpacketz("E01");
return;
}
if (addr < strlen (str)) {
- uint8_t reply[len+2];
+ char reply[len+2];
reply[0] = 'm';
strncpy (reply + 1, &str[addr], len);
if(len > strlen(&str[addr]))
@@ -281,7 +299,7 @@ handle_q_packet(char *packet, int len)
uint32_t addr, alen;
if(!strncmp(packet, "qRcmd,", 6)) {
- unsigned char *data;
+ char *data;
int datalen;
/* calculate size and allocate buffer for command */
@@ -310,11 +328,11 @@ handle_q_packet(char *packet, int len)
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
}
- if((!cur_target) || (!cur_target->xml_mem_map)) {
+ if (!cur_target) {
gdb_putpacketz("E01");
return;
}
- handle_q_string_reply(cur_target->xml_mem_map, packet + 23);
+ handle_q_string_reply(target_mem_map(cur_target), packet + 23);
} else if (strncmp (packet, "qXfer:features:read:target.xml:", 31) == 0) {
/* Read target description */
@@ -323,12 +341,12 @@ handle_q_packet(char *packet, int len)
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
}
- if((!cur_target) || (!cur_target->tdesc)) {
+ if (!cur_target) {
gdb_putpacketz("E01");
return;
}
- handle_q_string_reply(cur_target->tdesc, packet + 31);
- } else if (sscanf(packet, "qCRC:%08lX,%08lX", &addr, &alen) == 2) {
+ handle_q_string_reply(target_tdesc(cur_target), packet + 31);
+ } else if (sscanf(packet, "qCRC:%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) {
if(!cur_target) {
gdb_putpacketz("E01");
return;
@@ -348,7 +366,7 @@ handle_v_packet(char *packet, int plen)
int bin;
static uint8_t flash_mode = 0;
- if (sscanf(packet, "vAttach;%08lX", &addr) == 1) {
+ if (sscanf(packet, "vAttach;%08lx", &addr) == 1) {
/* Attach to remote target processor */
target *t;
uint32_t i;
@@ -371,11 +389,16 @@ handle_v_packet(char *packet, int plen)
} else if(last_target) {
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
- target_reset(cur_target);
- gdb_putpacketz("T05");
+
+ /* If we were able to attach to the target again */
+ if (cur_target) {
+ target_reset(cur_target);
+ gdb_putpacketz("T05");
+ } else gdb_putpacketz("E01");
+
} else gdb_putpacketz("E01");
- } else if (sscanf(packet, "vFlashErase:%08lX,%08lX", &addr, &len) == 2) {
+ } else if (sscanf(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; }
@@ -391,7 +414,7 @@ handle_v_packet(char *packet, int plen)
else
gdb_putpacketz("EFF");
- } else if (sscanf(packet, "vFlashWrite:%08lX:%n", &addr, &bin) == 1) {
+ } else if (sscanf(packet, "vFlashWrite:%08lx:%n", &addr, &bin) == 1) {
/* Write Flash Memory */
len = plen - bin;
DEBUG("Flash Write %08lX %08lX\n", addr, len);
@@ -402,7 +425,7 @@ handle_v_packet(char *packet, int plen)
} else if (!strcmp(packet, "vFlashDone")) {
/* Commit flash operations. */
- gdb_putpacketz("OK");
+ gdb_putpacketz(target_flash_done(cur_target) ? "EFF" : "OK");
flash_mode = 0;
} else {
@@ -425,13 +448,9 @@ handle_z_packet(char *packet, int plen)
* with real sscanf() though... */
//sscanf(packet, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
type = packet[1] - '0';
- sscanf(packet + 2, ",%08lX,%d", &addr, &len);
+ sscanf(packet + 2, ",%" PRIx32 ",%d", &addr, &len);
switch(type) {
case 1: /* Hardware breakpoint */
- if(!cur_target->set_hw_bp) { /* Not supported */
- gdb_putpacketz("");
- return;
- }
if(set)
ret = target_set_hw_bp(cur_target, addr);
else
@@ -441,10 +460,6 @@ handle_z_packet(char *packet, int plen)
case 2:
case 3:
case 4:
- if(!cur_target->set_hw_wp) { /* Not supported */
- gdb_putpacketz("");
- return;
- }
if(set)
ret = target_set_hw_wp(cur_target, type, addr, len);
else
diff --git a/src/gdb_packet.c b/src/gdb_packet.c
index 958572c..9ce63f2 100644
--- a/src/gdb_packet.c
+++ b/src/gdb_packet.c
@@ -22,20 +22,14 @@
* 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 "general.h"
#include "gdb_if.h"
#include "gdb_packet.h"
#include "hex_utils.h"
-int
-gdb_getpacket(unsigned char *packet, int size)
+#include <stdarg.h>
+
+int gdb_getpacket(char *packet, int size)
{
unsigned char c;
unsigned char csum;
@@ -54,9 +48,9 @@ gdb_getpacket(unsigned char *packet, int size)
if(i == size) break; /* Oh shit */
if(c == '$') { /* Restart capture */
- i = 0;
- csum = 0;
- continue;
+ i = 0;
+ csum = 0;
+ continue;
}
if(c == '}') { /* escaped char */
c = gdb_if_getchar();
@@ -70,7 +64,7 @@ gdb_getpacket(unsigned char *packet, int size)
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;
@@ -84,7 +78,7 @@ gdb_getpacket(unsigned char *packet, int size)
DEBUG("%s : ", __func__);
for(int j = 0; j < i; j++) {
c = packet[j];
- if ((c >= 32) && (c < 127))
+ if ((c >= 32) && (c < 127))
DEBUG("%c", c);
else
DEBUG("\\x%02X", c);
@@ -94,14 +88,14 @@ gdb_getpacket(unsigned char *packet, int size)
return i;
}
-void gdb_putpacket(unsigned char *packet, int size)
+void gdb_putpacket(const char *packet, int size)
{
int i;
unsigned char csum;
unsigned char c;
char xmit_csum[3];
int tries = 0;
-
+
do {
#ifdef DEBUG_GDBPACKET
DEBUG("%s : ", __func__);
@@ -111,7 +105,7 @@ void gdb_putpacket(unsigned char *packet, int size)
for(i = 0; i < size; i++) {
c = packet[i];
#ifdef DEBUG_GDBPACKET
- if ((c >= 32) && (c < 127))
+ if ((c >= 32) && (c < 127))
DEBUG("%c", c);
else
DEBUG("\\x%02X", c);
@@ -135,7 +129,7 @@ void gdb_putpacket(unsigned char *packet, int size)
} while((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
}
-void gdb_putpacket_f(const unsigned char *fmt, ...)
+void gdb_putpacket_f(const char *fmt, ...)
{
va_list ap;
char *buf;
@@ -165,7 +159,8 @@ void gdb_outf(const char *fmt, ...)
char *buf;
va_start(ap, fmt);
- vasprintf(&buf, fmt, ap);
+ if (vasprintf(&buf, fmt, ap) < 0)
+ return;
gdb_out(buf);
free(buf);
va_end(ap);
diff --git a/src/hex_utils.c b/src/hex_utils.c
index 6629f10..e18df58 100644
--- a/src/hex_utils.c
+++ b/src/hex_utils.c
@@ -18,23 +18,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* Convenience function to convert to/from ascii strings of hex digits.
+/* Convenience function to convert to/from ascii strings of hex digits.
*/
-#include <stdio.h>
-#include <stdint.h>
-
+#include "general.h"
#include "hex_utils.h"
-static char hexdigits[] = "0123456789abcdef";
+static const char hexdigits[] = "0123456789abcdef";
-char * hexify(char *hex, const unsigned char *buf, int size)
+char * hexify(char *hex, const void *buf, size_t size)
{
char *tmp = hex;
+ const uint8_t *b = buf;
- while(size--) {
- *tmp++ = hexdigits[*buf >> 4];
- *tmp++ = hexdigits[*buf++ & 0xF];
+ while (size--) {
+ *tmp++ = hexdigits[*b >> 4];
+ *tmp++ = hexdigits[*b++ & 0xF];
}
*tmp++ = 0;
@@ -44,18 +43,19 @@ char * hexify(char *hex, const unsigned char *buf, int size)
static uint8_t unhex_digit(char hex)
{
uint8_t tmp = hex - '0';
- if(tmp > 9)
+ if(tmp > 9)
tmp -= 'A' - '0' - 10;
- if(tmp > 16)
+ if(tmp > 16)
tmp -= 'a' - 'A';
return tmp;
}
-char * unhexify(unsigned char *buf, const char *hex, int size)
+char * unhexify(void *buf, const char *hex, size_t size)
{
- while(size--) {
- *buf = unhex_digit(*hex++) << 4;
- *buf++ |= unhex_digit(*hex++);
+ uint8_t *b = buf;
+ while (size--) {
+ *b = unhex_digit(*hex++) << 4;
+ *b++ |= unhex_digit(*hex++);
}
return buf;
}
diff --git a/src/include/adiv5.h b/src/include/adiv5.h
index d2d4be0..12d3bf4 100644
--- a/src/include/adiv5.h
+++ b/src/include/adiv5.h
@@ -21,16 +21,19 @@
#ifndef __ADIV5_H
#define __ADIV5_H
-#include "general.h"
#include "jtag_scan.h"
#include "target.h"
+#define ADIV5_APnDP 0x100
+#define ADIV5_DP_REG(x) (x)
+#define ADIV5_AP_REG(x) (ADIV5_APnDP | (x))
+
/* ADIv5 DP Register addresses */
-#define ADIV5_DP_IDCODE 0x0
-#define ADIV5_DP_ABORT 0x0
-#define ADIV5_DP_CTRLSTAT 0x4
-#define ADIV5_DP_SELECT 0x8
-#define ADIV5_DP_RDBUFF 0xC
+#define ADIV5_DP_IDCODE ADIV5_DP_REG(0x0)
+#define ADIV5_DP_ABORT ADIV5_DP_REG(0x0)
+#define ADIV5_DP_CTRLSTAT ADIV5_DP_REG(0x4)
+#define ADIV5_DP_SELECT ADIV5_DP_REG(0x8)
+#define ADIV5_DP_RDBUFF ADIV5_DP_REG(0xC)
/* AP Abort Register (ABORT) */
/* Bits 31:5 - Reserved */
@@ -64,15 +67,15 @@
/* ADIv5 MEM-AP Registers */
-#define ADIV5_AP_CSW 0x00
-#define ADIV5_AP_TAR 0x04
+#define ADIV5_AP_CSW ADIV5_AP_REG(0x00)
+#define ADIV5_AP_TAR ADIV5_AP_REG(0x04)
/* 0x08 - Reserved */
-#define ADIV5_AP_DRW 0x0C
-#define ADIV5_AP_DB(x) (0x10 + (4*(x)))
+#define ADIV5_AP_DRW ADIV5_AP_REG(0x0C)
+#define ADIV5_AP_DB(x) ADIV5_AP_REG(0x10 + (4*(x)))
/* 0x20:0xF0 - Reserved */
-#define ADIV5_AP_CFG 0xF4
-#define ADIV5_AP_BASE 0xF8
-#define ADIV5_AP_IDR 0xFC
+#define ADIV5_AP_CFG ADIV5_AP_REG(0xF4)
+#define ADIV5_AP_BASE ADIV5_AP_REG(0xF8)
+#define ADIV5_AP_IDR ADIV5_AP_REG(0xFC)
/* AP Control and Status Word (CSW) */
#define ADIV5_AP_CSW_DBGSWENABLE (1u << 31)
@@ -94,11 +97,9 @@
#define ADIV5_AP_CSW_SIZE_WORD (2u << 0)
#define ADIV5_AP_CSW_SIZE_MASK (7u << 0)
-/* Constants to make RnW and APnDP parameters more clear in code */
+/* Constants to make RnW parameters more clear in code */
#define ADIV5_LOW_WRITE 0
#define ADIV5_LOW_READ 1
-#define ADIV5_LOW_DP 0
-#define ADIV5_LOW_AP 1
/* Try to keep this somewhat absract for later adding SW-DP */
typedef struct ADIv5_DP_s {
@@ -106,13 +107,10 @@ typedef struct ADIv5_DP_s {
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);
-
+ uint32_t (*dp_read)(struct ADIv5_DP_s *dp, uint16_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);
+ uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value);
union {
jtag_dev_t *dev;
@@ -120,12 +118,7 @@ typedef struct ADIv5_DP_s {
};
} 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)
+static inline uint32_t adiv5_dp_read(ADIv5_DP_t *dp, uint16_t addr)
{
return dp->dp_read(dp, addr);
}
@@ -135,10 +128,10 @@ 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)
+static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW,
+ uint16_t addr, uint32_t value)
{
- return dp->low_access(dp, APnDP, RnW, addr, value);
+ return dp->low_access(dp, RnW, addr, value);
}
typedef struct ADIv5_AP_s {
@@ -157,22 +150,15 @@ typedef struct ADIv5_AP_s {
} ADIv5_AP_t;
void adiv5_dp_init(ADIv5_DP_t *dp);
+void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value);
void adiv5_dp_ref(ADIv5_DP_t *dp);
void adiv5_ap_ref(ADIv5_AP_t *ap);
void adiv5_dp_unref(ADIv5_DP_t *dp);
void adiv5_ap_unref(ADIv5_AP_t *ap);
-void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
-uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
-
-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);
-uint16_t adiv5_ap_mem_read_halfword(ADIv5_AP_t *ap, uint32_t addr);
-void adiv5_ap_mem_write_halfword(ADIv5_AP_t *ap, uint32_t addr, uint16_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_ap_write(ADIv5_AP_t *ap, uint16_t addr, uint32_t value);
+uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint16_t addr);
void adiv5_jtag_dp_handler(jtag_dev_t *dev);
int adiv5_swdp_scan(void);
diff --git a/src/include/arm7tdmi.h b/src/include/arm7tdmi.h
index ffcd6e9..fa7d586 100644
--- a/src/include/arm7tdmi.h
+++ b/src/include/arm7tdmi.h
@@ -21,7 +21,6 @@
#ifndef __ARM7TDMI_H
#define __ARM7TDMI_H
-#include "general.h"
#include "jtag_scan.h"
void arm7tdmi_jtag_handler(jtag_dev_t *dev);
diff --git a/src/include/command.h b/src/include/command.h
index 32b0d7b..3910bbb 100644
--- a/src/include/command.h
+++ b/src/include/command.h
@@ -21,7 +21,6 @@
#ifndef __COMMAND_H
#define __COMMAND_H
-#include "general.h"
#include "target.h"
int command_process(target *t, char *cmd);
diff --git a/src/include/cortexm.h b/src/include/cortexm.h
new file mode 100644
index 0000000..f7396bf
--- /dev/null
+++ b/src/include/cortexm.h
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __CORTEXM_H
+#define __CORTEXM_H
+
+#include "target.h"
+
+/* Private peripheral bus base address */
+#define CORTEXM_PPB_BASE 0xE0000000
+
+#define CORTEXM_SCS_BASE (CORTEXM_PPB_BASE + 0xE000)
+
+#define CORTEXM_AIRCR (CORTEXM_SCS_BASE + 0xD0C)
+#define CORTEXM_CFSR (CORTEXM_SCS_BASE + 0xD28)
+#define CORTEXM_HFSR (CORTEXM_SCS_BASE + 0xD2C)
+#define CORTEXM_DFSR (CORTEXM_SCS_BASE + 0xD30)
+#define CORTEXM_CPACR (CORTEXM_SCS_BASE + 0xD88)
+#define CORTEXM_DHCSR (CORTEXM_SCS_BASE + 0xDF0)
+#define CORTEXM_DCRSR (CORTEXM_SCS_BASE + 0xDF4)
+#define CORTEXM_DCRDR (CORTEXM_SCS_BASE + 0xDF8)
+#define CORTEXM_DEMCR (CORTEXM_SCS_BASE + 0xDFC)
+
+#define CORTEXM_FPB_BASE (CORTEXM_PPB_BASE + 0x2000)
+
+/* ARM Literature uses FP_*, we use CORTEXM_FPB_* consistently */
+#define CORTEXM_FPB_CTRL (CORTEXM_FPB_BASE + 0x000)
+#define CORTEXM_FPB_REMAP (CORTEXM_FPB_BASE + 0x004)
+#define CORTEXM_FPB_COMP(i) (CORTEXM_FPB_BASE + 0x008 + (4*(i)))
+
+#define CORTEXM_DWT_BASE (CORTEXM_PPB_BASE + 0x1000)
+
+#define CORTEXM_DWT_CTRL (CORTEXM_DWT_BASE + 0x000)
+#define CORTEXM_DWT_COMP(i) (CORTEXM_DWT_BASE + 0x020 + (0x10*(i)))
+#define CORTEXM_DWT_MASK(i) (CORTEXM_DWT_BASE + 0x024 + (0x10*(i)))
+#define CORTEXM_DWT_FUNC(i) (CORTEXM_DWT_BASE + 0x028 + (0x10*(i)))
+
+/* Application Interrupt and Reset Control Register (AIRCR) */
+#define CORTEXM_AIRCR_VECTKEY (0x05FA << 16)
+/* Bits 31:16 - Read as VECTKETSTAT, 0xFA05 */
+#define CORTEXM_AIRCR_ENDIANESS (1 << 15)
+/* Bits 15:11 - Unused, reserved */
+#define CORTEXM_AIRCR_PRIGROUP (7 << 8)
+/* Bits 7:3 - Unused, reserved */
+#define CORTEXM_AIRCR_SYSRESETREQ (1 << 2)
+#define CORTEXM_AIRCR_VECTCLRACTIVE (1 << 1)
+#define CORTEXM_AIRCR_VECTRESET (1 << 0)
+
+/* HardFault Status Register (HFSR) */
+#define CORTEXM_HFSR_DEBUGEVT (1 << 31)
+#define CORTEXM_HFSR_FORCED (1 << 30)
+/* Bits 29:2 - Not specified */
+#define CORTEXM_HFSR_VECTTBL (1 << 1)
+/* Bits 0 - Reserved */
+
+/* Debug Fault Status Register (DFSR) */
+/* Bits 31:5 - Reserved */
+#define CORTEXM_DFSR_RESETALL 0x1F
+#define CORTEXM_DFSR_EXTERNAL (1 << 4)
+#define CORTEXM_DFSR_VCATCH (1 << 3)
+#define CORTEXM_DFSR_DWTTRAP (1 << 2)
+#define CORTEXM_DFSR_BKPT (1 << 1)
+#define CORTEXM_DFSR_HALTED (1 << 0)
+
+/* Debug Halting Control and Status Register (DHCSR) */
+/* This key must be written to bits 31:16 for write to take effect */
+#define CORTEXM_DHCSR_DBGKEY 0xA05F0000
+/* Bits 31:26 - Reserved */
+#define CORTEXM_DHCSR_S_RESET_ST (1 << 25)
+#define CORTEXM_DHCSR_S_RETIRE_ST (1 << 24)
+/* Bits 23:20 - Reserved */
+#define CORTEXM_DHCSR_S_LOCKUP (1 << 19)
+#define CORTEXM_DHCSR_S_SLEEP (1 << 18)
+#define CORTEXM_DHCSR_S_HALT (1 << 17)
+#define CORTEXM_DHCSR_S_REGRDY (1 << 16)
+/* Bits 15:6 - Reserved */
+#define CORTEXM_DHCSR_C_SNAPSTALL (1 << 5) /* v7m only */
+/* Bit 4 - Reserved */
+#define CORTEXM_DHCSR_C_MASKINTS (1 << 3)
+#define CORTEXM_DHCSR_C_STEP (1 << 2)
+#define CORTEXM_DHCSR_C_HALT (1 << 1)
+#define CORTEXM_DHCSR_C_DEBUGEN (1 << 0)
+
+/* Debug Core Register Selector Register (DCRSR) */
+#define CORTEXM_DCRSR_REGWnR 0x00010000
+#define CORTEXM_DCRSR_REGSEL_MASK 0x0000001F
+#define CORTEXM_DCRSR_REGSEL_XPSR 0x00000010
+#define CORTEXM_DCRSR_REGSEL_MSP 0x00000011
+#define CORTEXM_DCRSR_REGSEL_PSP 0x00000012
+
+/* Debug Exception and Monitor Control Register (DEMCR) */
+/* Bits 31:25 - Reserved */
+#define CORTEXM_DEMCR_TRCENA (1 << 24)
+/* Bits 23:20 - Reserved */
+#define CORTEXM_DEMCR_MON_REQ (1 << 19) /* v7m only */
+#define CORTEXM_DEMCR_MON_STEP (1 << 18) /* v7m only */
+#define CORTEXM_DEMCR_VC_MON_PEND (1 << 17) /* v7m only */
+#define CORTEXM_DEMCR_VC_MON_EN (1 << 16) /* v7m only */
+/* Bits 15:11 - Reserved */
+#define CORTEXM_DEMCR_VC_HARDERR (1 << 10)
+#define CORTEXM_DEMCR_VC_INTERR (1 << 9) /* v7m only */
+#define CORTEXM_DEMCR_VC_BUSERR (1 << 8) /* v7m only */
+#define CORTEXM_DEMCR_VC_STATERR (1 << 7) /* v7m only */
+#define CORTEXM_DEMCR_VC_CHKERR (1 << 6) /* v7m only */
+#define CORTEXM_DEMCR_VC_NOCPERR (1 << 5) /* v7m only */
+#define CORTEXM_DEMCR_VC_MMERR (1 << 4) /* v7m only */
+/* Bits 3:1 - Reserved */
+#define CORTEXM_DEMCR_VC_CORERESET (1 << 0)
+
+/* Flash Patch and Breakpoint Control Register (FP_CTRL) */
+/* Bits 32:15 - Reserved */
+/* Bits 14:12 - NUM_CODE2 */ /* v7m only */
+/* Bits 11:8 - NUM_LIT */ /* v7m only */
+/* Bits 7:4 - NUM_CODE1 */
+/* Bits 3:2 - Unspecified */
+#define CORTEXM_FPB_CTRL_KEY (1 << 1)
+#define CORTEXM_FPB_CTRL_ENABLE (1 << 0)
+
+/* Data Watchpoint and Trace Mask Register (DWT_MASKx) */
+#define CORTEXM_DWT_MASK_BYTE (0 << 0)
+#define CORTEXM_DWT_MASK_HALFWORD (1 << 0)
+#define CORTEXM_DWT_MASK_WORD (3 << 0)
+
+/* Data Watchpoint and Trace Function Register (DWT_FUNCTIONx) */
+#define CORTEXM_DWT_FUNC_MATCHED (1 << 24)
+#define CORTEXM_DWT_FUNC_DATAVSIZE_WORD (2 << 10) /* v7m only */
+#define CORTEXM_DWT_FUNC_FUNC_READ (5 << 0)
+#define CORTEXM_DWT_FUNC_FUNC_WRITE (6 << 0)
+#define CORTEXM_DWT_FUNC_FUNC_ACCESS (7 << 0)
+
+bool cortexm_attach(target *t);
+void cortexm_detach(target *t);
+void cortexm_halt_resume(target *t, bool step);
+int cortexm_run_stub(target *t, uint32_t loadaddr,
+ uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3);
+
+#endif
+
diff --git a/src/include/crc32.h b/src/include/crc32.h
index 6cc00ea..bd8d5c4 100644
--- a/src/include/crc32.h
+++ b/src/include/crc32.h
@@ -21,9 +21,7 @@
#ifndef __CRC32_H
#define __CRC32_H
-#include "platform.h"
-
uint32_t crc32_calc(uint32_t crc, uint8_t data);
-uint32_t generic_crc32(struct target_s *target, uint32_t base, int len);
+uint32_t generic_crc32(target *t, uint32_t base, int len);
#endif
diff --git a/src/include/exception.h b/src/include/exception.h
new file mode 100644
index 0000000..180398d
--- /dev/null
+++ b/src/include/exception.h
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+/* Exception handling to escape deep nesting.
+ * Used for the case of communicaiton failure and timeouts.
+ */
+
+/* Example usage:
+ *
+ * volatile struct exception e;
+ * TRY_CATCH (e, EXCEPTION_TIMEOUT) {
+ * ...
+ * raise_exception(EXCEPTION_TIMEOUT, "Timeout occurred");
+ * ...
+ * }
+ * if (e.type == EXCEPTION_TIMEOUT) {
+ * printf("timeout: %s\n", e.msg);
+ * }
+ */
+
+/* Limitations:
+ * Can't use break, return, goto, etc from inside the TRY_CATCH block.
+ */
+
+#ifndef __EXCEPTION_H
+#define __EXCEPTION_H
+
+#include <setjmp.h>
+#include <stdint.h>
+
+#define EXCEPTION_ERROR 0x01
+#define EXCEPTION_TIMEOUT 0x02
+#define EXCEPTION_ALL -1
+
+struct exception {
+ uint32_t type;
+ const char *msg;
+ /* private */
+ uint32_t mask;
+ jmp_buf jmpbuf;
+ struct exception *outer;
+};
+
+extern struct exception *innermost_exception;
+
+#define TRY_CATCH(e, type_mask) \
+ (e).type = 0; \
+ (e).mask = (type_mask); \
+ (e).outer = innermost_exception; \
+ innermost_exception = (void*)&(e); \
+ if (setjmp(innermost_exception->jmpbuf) == 0) \
+ for (;innermost_exception == &(e); innermost_exception = (e).outer)
+
+void raise_exception(uint32_t type, const char *msg);
+
+#endif
+
diff --git a/src/include/gdb_packet.h b/src/include/gdb_packet.h
index 9f5430f..aa1a654 100644
--- a/src/include/gdb_packet.h
+++ b/src/include/gdb_packet.h
@@ -21,12 +21,10 @@
#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);
+int gdb_getpacket(char *packet, int size);
+void gdb_putpacket(const char *packet, int size);
#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
-void gdb_putpacket_f(const unsigned char *packet, ...);
+void gdb_putpacket_f(const char *packet, ...);
void gdb_out(const char *buf);
void gdb_outf(const char *fmt, ...);
diff --git a/src/include/general.h b/src/include/general.h
index b0721ac..c82c750 100644
--- a/src/include/general.h
+++ b/src/include/general.h
@@ -21,15 +21,26 @@
#ifndef __GENERAL_H
#define __GENERAL_H
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <inttypes.h>
+
#include "platform.h"
+#include "platform_support.h"
#ifndef DEBUG
#include <stdio.h>
#define DEBUG printf
#endif
-#include <stdint.h>
-#include <stdbool.h>
+#define ALIGN(x, n) (((x) + (n) - 1) & ~((n) - 1))
+#undef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
diff --git a/src/include/hex_utils.h b/src/include/hex_utils.h
index 3aa210b..8a0d092 100644
--- a/src/include/hex_utils.h
+++ b/src/include/hex_utils.h
@@ -21,9 +21,8 @@
#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);
+char * hexify(char *hex, const void *buf, size_t size);
+char * unhexify(void *buf, const char *hex, size_t size);
#endif
diff --git a/src/include/jtag_scan.h b/src/include/jtag_scan.h
index b425b95..aa006e7 100644
--- a/src/include/jtag_scan.h
+++ b/src/include/jtag_scan.h
@@ -21,8 +21,6 @@
#ifndef __JTAG_SCAN_H
#define __JTAG_SCAN_H
-#include "general.h"
-
#define JTAG_MAX_DEVS 5
#define JTAG_MAX_IR_LEN 16
diff --git a/src/include/jtagtap.h b/src/include/jtagtap.h
index 9fe3e0d..0072594 100644
--- a/src/include/jtagtap.h
+++ b/src/include/jtagtap.h
@@ -21,15 +21,13 @@
#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);
+void jtagtap_srst(bool assert);
uint8_t jtagtap_next(const uint8_t TMS, const uint8_t TDI);
/* tap_next executes one state transision in the JTAG TAP state machine:
@@ -44,9 +42,9 @@ 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.
+ * - 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 */
diff --git a/src/include/morse.h b/src/include/morse.h
new file mode 100644
index 0000000..5ba39b1
--- /dev/null
+++ b/src/include/morse.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __MORSE_H
+#define __MORSE_H
+
+extern const char *morse_msg;
+
+void morse(const char *msg, char repeat);
+bool morse_update(void);
+
+#endif
+
diff --git a/src/include/platform_support.h b/src/include/platform_support.h
new file mode 100644
index 0000000..1d853be
--- /dev/null
+++ b/src/include/platform_support.h
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __PLATFORM_SUPPORT_H
+#define __PLATFORM_SUPPORT_H
+
+#ifndef __GENERAL_H
+# error "Include 'general.h' instead"
+#endif
+
+#if defined(HOST)
+void platform_init(int argc, char **argv);
+#else
+void platform_init(void);
+#endif
+
+const char *platform_target_voltage(void);
+int platform_hwversion(void);
+void platform_timeout_set(uint32_t ms);
+bool platform_timeout_is_expired(void);
+void platform_delay(uint32_t delay);
+void platform_srst_set_val(bool assert);
+bool platform_target_get_power(void);
+void platform_target_set_power(bool power);
+void platform_request_boot(void);
+
+#endif
+
diff --git a/src/include/stm32lx-nvm.h b/src/include/stm32lx-nvm.h
new file mode 100644
index 0000000..2e9d8c5
--- /dev/null
+++ b/src/include/stm32lx-nvm.h
@@ -0,0 +1,190 @@
+/* @file stm32lx-nvm.h
+ *
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2014 Woollysoft
+ * Written by Marc Singer <elf@woollysoft.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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/>.
+ */
+
+#if !defined (STM32Lx_NVM_H_INCLUDED)
+# define STM32Lx_NVM_H_INCLUDED
+
+/* ----- Includes */
+
+#include <stdint.h>
+
+/* ----- Macros */
+
+/* ----- Types */
+
+enum {
+ STM32Lx_STUB_PHYS = 0x20000000ul,
+ STM32Lx_STUB_INFO_PHYS = 0x20000004ul,
+ STM32Lx_STUB_DATA_PHYS = (0x20000000ul + 1024),
+ STM32Lx_STUB_DATA_MAX = 2048,
+
+ STM32Lx_NVM_OPT_PHYS = 0x1ff80000ul,
+ STM32Lx_NVM_EEPROM_PHYS = 0x08080000ul,
+
+ STM32L0_NVM_PHYS = 0x40022000ul,
+ STM32L0_NVM_PROG_PAGE_SIZE = 128,
+ STM32L0_NVM_DATA_PAGE_SIZE = 4,
+ STM32L0_NVM_OPT_SIZE = 12,
+ STM32L0_NVM_EEPROM_SIZE = 2*1024,
+
+ STM32L1_NVM_PHYS = 0x40023c00ul,
+ STM32L1_NVM_PROG_PAGE_SIZE = 256,
+ STM32L1_NVM_DATA_PAGE_SIZE = 4,
+ STM32L1_NVM_OPT_SIZE = 32,
+ STM32L1_NVM_EEPROM_SIZE = 16*1024,
+
+ STM32Lx_NVM_PEKEY1 = 0x89abcdeful,
+ STM32Lx_NVM_PEKEY2 = 0x02030405ul,
+ STM32Lx_NVM_PRGKEY1 = 0x8c9daebful,
+ STM32Lx_NVM_PRGKEY2 = 0x13141516ul,
+ STM32Lx_NVM_OPTKEY1 = 0xfbead9c8ul,
+ STM32Lx_NVM_OPTKEY2 = 0x24252627ul,
+
+ STM32Lx_NVM_PECR_OBL_LAUNCH = (1<<18),
+ STM32Lx_NVM_PECR_ERRIE = (1<<17),
+ STM32Lx_NVM_PECR_EOPIE = (1<<16),
+ STM32Lx_NVM_PECR_FPRG = (1<<10),
+ STM32Lx_NVM_PECR_ERASE = (1<< 9),
+ STM32Lx_NVM_PECR_FIX = (1<< 8), /* FTDW */
+ STM32Lx_NVM_PECR_DATA = (1<< 4),
+ STM32Lx_NVM_PECR_PROG = (1<< 3),
+ STM32Lx_NVM_PECR_OPTLOCK = (1<< 2),
+ STM32Lx_NVM_PECR_PRGLOCK = (1<< 1),
+ STM32Lx_NVM_PECR_PELOCK = (1<< 0),
+
+ STM32Lx_NVM_SR_FWWERR = (1<<17),
+ STM32Lx_NVM_SR_NOTZEROERR = (1<<16),
+ STM32Lx_NVM_SR_RDERR = (1<<13),
+ STM32Lx_NVM_SR_OPTVER = (1<<11),
+ STM32Lx_NVM_SR_SIZERR = (1<<10),
+ STM32Lx_NVM_SR_PGAERR = (1<<9),
+ STM32Lx_NVM_SR_WRPERR = (1<<8),
+ STM32Lx_NVM_SR_READY = (1<<3),
+ STM32Lx_NVM_SR_HWOFF = (1<<2),
+ STM32Lx_NVM_SR_EOP = (1<<1),
+ STM32Lx_NVM_SR_BSY = (1<<0),
+ STM32Lx_NVM_SR_ERR_M = ( STM32Lx_NVM_SR_WRPERR
+ | STM32Lx_NVM_SR_PGAERR
+ | STM32Lx_NVM_SR_SIZERR
+ | STM32Lx_NVM_SR_NOTZEROERR),
+
+ STM32L0_NVM_OPTR_BOOT1 = (1<<31),
+ STM32L0_NVM_OPTR_WDG_SW = (1<<20),
+ STM32L0_NVM_OPTR_WPRMOD = (1<<8),
+ STM32L0_NVM_OPTR_RDPROT_S = (0),
+ STM32L0_NVM_OPTR_RDPROT_M = (0xff),
+ STM32L0_NVM_OPTR_RDPROT_0 = (0xaa),
+ STM32L0_NVM_OPTR_RDPROT_2 = (0xcc),
+
+ STM32L1_NVM_OPTR_nBFB2 = (1<<23),
+ STM32L1_NVM_OPTR_nRST_STDBY = (1<<22),
+ STM32L1_NVM_OPTR_nRST_STOP = (1<<21),
+ STM32L1_NVM_OPTR_WDG_SW = (1<<20),
+ STM32L1_NVM_OPTR_BOR_LEV_S = (16),
+ STM32L1_NVM_OPTR_BOR_LEV_M = (0xf),
+ STM32L1_NVM_OPTR_SPRMOD = (1<<8),
+ STM32L1_NVM_OPTR_RDPROT_S = (0),
+ STM32L1_NVM_OPTR_RDPROT_M = (0xff),
+ STM32L1_NVM_OPTR_RDPROT_0 = (0xaa),
+ STM32L1_NVM_OPTR_RDPROT_2 = (0xcc),
+
+};
+
+#if defined (__cplusplus)
+
+namespace STM32 {
+ struct NVM {
+ volatile uint32_t acr;
+ volatile uint32_t pecr;
+ volatile uint32_t pdkeyr;
+ volatile uint32_t pkeyr;
+ volatile uint32_t prgkeyr;
+ volatile uint32_t optkeyr;
+ volatile uint32_t sr;
+ volatile uint32_t optr;
+ volatile uint32_t wrprot;
+
+ static constexpr uint32_t PKEY1 = 0x89abcdef;
+ static constexpr uint32_t PKEY2 = 0x02030405;
+ static constexpr uint32_t PRGKEY1 = 0x8c9daebf;
+ static constexpr uint32_t PRGKEY2 = 0x13141516;
+ static constexpr uint32_t OPTKEY1 = 0xfbead9c8;
+ static constexpr uint32_t OPTKEY2 = 0x24252627;
+ static constexpr uint32_t PDKEY1 = 0x04152637;
+ static constexpr uint32_t PDKEY2 = 0xfafbfcfd;
+ };
+
+ static_assert(sizeof (NVM) == 9*4, "NVM size error");
+}
+using stm32lx_stub_pointer_t = uint32_t*;
+
+#define Nvm(nvm) (*reinterpret_cast<STM32::NVM*>(nvm))
+#define Info (*reinterpret_cast<stm32lx_nvm_stub_info*>(STM32Lx_STUB_INFO_PHYS))
+
+namespace {
+ inline __attribute((always_inline)) bool unlock (STM32::NVM& nvm) {
+ // Lock guarantees unlock
+ nvm.pecr = STM32Lx_NVM_PECR_PELOCK;
+
+ nvm.pkeyr = STM32::NVM::PKEY1;
+ nvm.pkeyr = STM32::NVM::PKEY2;
+ nvm.prgkeyr = STM32::NVM::PRGKEY1;
+ nvm.prgkeyr = STM32::NVM::PRGKEY2;
+ return !(nvm.pecr & STM32Lx_NVM_PECR_PRGLOCK);
+ }
+ inline __attribute((always_inline)) void lock (STM32::NVM& nvm) {
+ nvm.pecr = STM32Lx_NVM_PECR_PELOCK; }
+
+}
+
+#else
+
+typedef uint32_t stm32lx_stub_pointer_t;
+
+#define STM32Lx_NVM_PECR(p) ((p) + 0x04)
+#define STM32Lx_NVM_PEKEYR(p) ((p) + 0x0C)
+#define STM32Lx_NVM_PRGKEYR(p) ((p) + 0x10)
+#define STM32Lx_NVM_OPTKEYR(p) ((p) + 0x14)
+#define STM32Lx_NVM_SR(p) ((p) + 0x18)
+#define STM32Lx_NVM_OPTR(p) ((p) + 0x1C)
+
+#endif
+
+enum {
+ OPT_STM32L1 = 1<<1,
+};
+
+struct stm32lx_nvm_stub_info {
+ stm32lx_stub_pointer_t destination;
+ int32_t size;
+ stm32lx_stub_pointer_t source;
+ uint32_t nvm;
+ uint16_t page_size;
+ uint16_t options;
+} __attribute__((packed));
+
+/* ----- Globals */
+
+/* ----- Prototypes */
+
+
+
+#endif /* STM32Lx_NVM_H_INCLUDED */
diff --git a/src/include/swdptap.h b/src/include/swdptap.h
index 5bb0545..3263a1d 100644
--- a/src/include/swdptap.h
+++ b/src/include/swdptap.h
@@ -21,8 +21,6 @@
#ifndef __SWDPTAP_H
#define __SWDPTAP_H
-#include "general.h"
-
int swdptap_init(void);
void swdptap_reset(void);
diff --git a/src/include/target.h b/src/include/target.h
index 31501f6..940a360 100644
--- a/src/include/target.h
+++ b/src/include/target.h
@@ -25,8 +25,6 @@
#ifndef __TARGET_H
#define __TARGET_H
-#include "general.h"
-
typedef struct target_s target;
/* The destroy callback function will be called by target_list_free() just
@@ -34,7 +32,7 @@ typedef struct target_s target;
* targets, or because of a communication failure. The target data may
* be assumed to be intact, but the communication medium may not be available,
* so access methods shouldn't be called.
- *
+ *
* The callback is installed by target_attach() and only removed by attaching
* with a different callback. It remains intact after target_detach().
*/
@@ -50,18 +48,11 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
(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))
+#define target_mem_read(target, dest, src, len) \
+ (target)->mem_read((target), (dest), (src), (len))
+#define target_mem_write(target, dest, src, len) \
+ (target)->mem_write((target), (dest), (src), (len))
/* Register access functions */
#define target_regs_read(target, data) \
@@ -70,12 +61,6 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
#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) \
@@ -116,50 +101,58 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
#define target_flash_write(target, dest, src, len) \
(target)->flash_write((target), (dest), (src), (len))
+#define target_flash_done(target) \
+ ((target)->flash_done ? (target)->flash_done(target) : 0)
+
+/* Host I/O */
+#define target_hostio_reply(target, recode, errcode) \
+ (target)->hostio_reply((target), (retcode), (errcode))
+
+/* Accessor functions */
+#define target_regs_size(target) \
+ ((target)->regs_size)
+
+#define target_mem_map(target) \
+ ((target)->xml_mem_map ? (target)->xml_mem_map : "")
+
+#define target_tdesc(target) \
+ ((target)->tdesc ? (target)->tdesc : "")
struct target_s {
/* Notify controlling debugger if target is lost */
target_destroy_callback destroy_callback;
/* Attach/Detach funcitons */
- bool (*attach)(struct target_s *target);
- void (*detach)(struct target_s *target);
- int (*check_error)(struct target_s *target);
+ bool (*attach)(target *t);
+ void (*detach)(target *t);
+ bool (*check_error)(target *t);
/* 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);
+ void (*mem_read)(target *t, void *dest, uint32_t src,
+ size_t len);
+ void (*mem_write)(target *t, uint32_t dest,
+ const void *src, size_t len);
/* Register access functions */
int regs_size;
const char *tdesc;
- 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);
+ void (*regs_read)(target *t, void *data);
+ void (*regs_write)(target *t, const void *data);
/* 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, bool step);
+ void (*reset)(target *t);
+ void (*halt_request)(target *t);
+ int (*halt_wait)(target *t);
+ void (*halt_resume)(target *t, bool 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_bp)(target *t, uint32_t addr);
+ int (*clear_hw_bp)(target *t, 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 (*set_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len);
+ int (*clear_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len);
- int (*check_hw_wp)(struct target_s *target, uint32_t *addr);
+ int (*check_hw_wp)(target *t, uint32_t *addr);
/* target-defined options */
unsigned target_options;
@@ -167,9 +160,13 @@ struct target_s {
/* Flash memory access functions */
const char *xml_mem_map;
- int (*flash_erase)(struct target_s *target, uint32_t addr, int len);
- int (*flash_write)(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
+ int (*flash_erase)(target *t, uint32_t addr, size_t len);
+ int (*flash_write)(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
+ int (*flash_done)(target *t);
+
+ /* Host I/O support */
+ void (*hostio_reply)(target *t, int32_t retcode, uint32_t errcode);
const char *driver;
struct target_command_s *commands;
@@ -188,22 +185,64 @@ struct target_command_s {
};
extern target *target_list;
+extern bool connect_assert_srst;
target *target_new(unsigned size);
void target_list_free(void);
void target_add_commands(target *t, const struct command_s *cmds, const char *name);
+static inline uint32_t target_mem_read32(target *t, uint32_t addr)
+{
+ uint32_t ret;
+ target_mem_read(t, &ret, addr, sizeof(ret));
+ return ret;
+}
+
+static inline void target_mem_write32(target *t, uint32_t addr, uint32_t value)
+{
+ target_mem_write(t, addr, &value, sizeof(value));
+}
+
+static inline uint16_t target_mem_read16(target *t, uint32_t addr)
+{
+ uint16_t ret;
+ target_mem_read(t, &ret, addr, sizeof(ret));
+ return ret;
+}
+
+static inline void target_mem_write16(target *t, uint32_t addr, uint16_t value)
+{
+ target_mem_write(t, addr, &value, sizeof(value));
+}
+
+static inline uint8_t target_mem_read8(target *t, uint32_t addr)
+{
+ uint8_t ret;
+ target_mem_read(t, &ret, addr, sizeof(ret));
+ return ret;
+}
+
+static inline void target_mem_write8(target *t, uint32_t addr, uint8_t value)
+{
+ target_mem_write(t, addr, &value, sizeof(value));
+}
+
+
/* Probe for various targets.
* Actual functions implemented in their respective drivers.
*/
-bool cortexm_probe(struct target_s *target);
-bool stm32f1_probe(struct target_s *target);
-bool stm32f4_probe(struct target_s *target);
-bool stm32l1_probe(struct target_s *target);
-bool lmi_probe(struct target_s *target);
-bool lpc11xx_probe(struct target_s *target);
-bool lpc43xx_probe(struct target_s *target);
-bool sam3x_probe(struct target_s *target);
+bool cortexm_probe(target *t);
+bool stm32f1_probe(target *t);
+bool stm32f4_probe(target *t);
+bool stm32l0_probe(target *t);
+bool stm32l1_probe(target *t);
+bool lmi_probe(target *t);
+bool lpc11xx_probe(target *t);
+bool lpc43xx_probe(target *t);
+bool sam3x_probe(target *t);
+bool nrf51_probe(target *t);
+bool samd_probe(target *t);
+bool kinetis_probe(target *t);
#endif
diff --git a/src/jtag_scan.c b/src/jtag_scan.c
index bfce1ec..f46a148 100644
--- a/src/jtag_scan.c
+++ b/src/jtag_scan.c
@@ -22,18 +22,12 @@
* 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 "general.h"
#include "jtagtap.h"
+#include "morse.h"
#include "jtag_scan.h"
-
#include "gdb_packet.h"
-
#include "adiv5.h"
#include "arm7tdmi.h"
@@ -49,9 +43,6 @@ static struct jtag_dev_descr_s {
{.idcode = 0x0BA00477, .idmask = 0x0FFF0FFF,
.descr = "ARM Limited: ADIv5 JTAG-DP port.",
.handler = adiv5_jtag_dp_handler},
- {.idcode = 0x3F0F0F0F, .idmask = 0xFFFFFFFF,
- .descr = "ST Microelectronics: STR730",
- .handler = arm7tdmi_jtag_handler},
{.idcode = 0x06410041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Medium density."},
{.idcode = 0x06412041, .idmask = 0x0FFFFFFF,
@@ -80,7 +71,7 @@ static struct jtag_dev_descr_s {
};
/* bucket of ones for don't care TDI */
-static const char ones[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
+static const uint8_t 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.
@@ -114,6 +105,8 @@ int jtag_scan(const uint8_t *irlens)
*/
DEBUG("Resetting TAP\n");
jtagtap_init();
+ if(connect_assert_srst)
+ jtagtap_srst(true); /* will be deasserted after attach */
jtagtap_reset();
if (irlens) {
diff --git a/src/jtagtap_generic.c b/src/jtagtap_generic.c
index a2e723b..63c8e44 100644
--- a/src/jtagtap_generic.c
+++ b/src/jtagtap_generic.c
@@ -28,14 +28,14 @@ jtagtap_tms_seq(uint32_t MS, int ticks)
{
while(ticks--) {
jtagtap_next(MS & 1, 1);
- MS >>= 1;
+ MS >>= 1;
}
}
#endif
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
-void
+void
jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
{
uint8_t index = 1;
@@ -55,7 +55,7 @@ jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
-void
+void
jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
{
uint8_t index = 1;
@@ -63,7 +63,7 @@ jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
jtagtap_next(ticks?0:final_tms, *DI & index);
if(!(index <<= 1)) {
index = 1;
- DI++;
+ DI++;
}
}
}
diff --git a/src/kinetis.c b/src/kinetis.c
new file mode 100644
index 0000000..7ccb28d
--- /dev/null
+++ b/src/kinetis.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 KL25 target specific functions providing
+ * the XML memory map and Flash memory programming.
+ */
+
+#include "general.h"
+#include "adiv5.h"
+#include "target.h"
+
+#define SIM_SDID 0x40048024
+
+#define FTFA_BASE 0x40020000
+#define FTFA_FSTAT (FTFA_BASE + 0x00)
+#define FTFA_FCNFG (FTFA_BASE + 0x01)
+#define FTFA_FSEC (FTFA_BASE + 0x02)
+#define FTFA_FOPT (FTFA_BASE + 0x03)
+#define FTFA_FCCOB(x) (FTFA_BASE + 0x04 + ((x) ^ 3))
+
+#define FTFA_FSTAT_CCIF (1 << 7)
+#define FTFA_FSTAT_RDCOLERR (1 << 6)
+#define FTFA_FSTAT_ACCERR (1 << 5)
+#define FTFA_FSTAT_FPVIOL (1 << 4)
+#define FTFA_FSTAT_MGSTAT0 (1 << 0)
+
+#define FTFA_CMD_CHECK_ERASE 0x01
+#define FTFA_CMD_PROGRAM_CHECK 0x02
+#define FTFA_CMD_READ_RESOURCE 0x03
+#define FTFA_CMD_PROGRAM_LONGWORD 0x06
+#define FTFA_CMD_ERASE_SECTOR 0x09
+#define FTFA_CMD_CHECK_ERASE_ALL 0x40
+#define FTFA_CMD_READ_ONCE 0x41
+#define FTFA_CMD_PROGRAM_ONCE 0x43
+#define FTFA_CMD_ERASE_ALL 0x44
+#define FTFA_CMD_BACKDOOR_ACCESS 0x45
+
+#define KL25_PAGESIZE 0x400
+
+static int kl25_flash_erase(target *t, uint32_t addr, size_t len);
+static int kl25_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
+
+static const char kl25_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=\"0x1ffff000\" length=\"0x1000\"/>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x3000\"/>"
+ "</memory-map>";
+
+bool kinetis_probe(target *t)
+{
+ uint32_t sdid = target_mem_read32(t, SIM_SDID);
+ switch (sdid >> 20) {
+ case 0x251:
+ t->driver = "KL25";
+ t->xml_mem_map = kl25_xml_memory_map;
+ t->flash_erase = kl25_flash_erase;
+ t->flash_write = kl25_flash_write;
+ return true;
+ }
+ return false;
+}
+
+static bool
+kl25_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8])
+{
+ uint8_t fstat;
+
+ /* Wait for CCIF to be high */
+ do {
+ fstat = target_mem_read8(t, FTFA_FSTAT);
+ /* Check ACCERR and FPVIOL are zero in FSTAT */
+ if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
+ return false;
+ } while (!(fstat & FTFA_FSTAT_CCIF));
+
+ /* Write command to FCCOB */
+ addr &= 0xffffff;
+ addr |= (uint32_t)cmd << 24;
+ target_mem_write32(t, FTFA_FCCOB(0), addr);
+ if (data) {
+ target_mem_write32(t, FTFA_FCCOB(4), *(uint32_t*)&data[0]);
+ target_mem_write32(t, FTFA_FCCOB(8), *(uint32_t*)&data[4]);
+ }
+
+ /* Enable execution by clearing CCIF */
+ target_mem_write8(t, FTFA_FSTAT, FTFA_FSTAT_CCIF);
+
+ /* Wait for execution to complete */
+ do {
+ fstat = target_mem_read8(t, FTFA_FSTAT);
+ /* Check ACCERR and FPVIOL are zero in FSTAT */
+ if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
+ return false;
+ } while (!(fstat & FTFA_FSTAT_CCIF));
+
+ return true;
+}
+
+static int kl25_flash_erase(target *t, uint32_t addr, size_t len)
+{
+ addr &= ~(KL25_PAGESIZE - 1);
+ len = (len + KL25_PAGESIZE - 1) & ~(KL25_PAGESIZE - 1);
+
+ while (len) {
+ kl25_command(t, FTFA_CMD_ERASE_SECTOR, addr, NULL);
+ len -= KL25_PAGESIZE;
+ addr += KL25_PAGESIZE;
+ }
+ return 0;
+}
+
+static int kl25_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
+{
+ /* FIXME handle misaligned start and end of sections */
+ if ((dest & 3) || (len & 3))
+ return -1;
+
+ while (len) {
+ kl25_command(t, FTFA_CMD_PROGRAM_LONGWORD, dest, src);
+ len -= 4;
+ dest += 4;
+ src += 4;
+ }
+ return 0;
+}
diff --git a/src/lmi.c b/src/lmi.c
index 09a9dd5..fb425a2 100644
--- a/src/lmi.c
+++ b/src/lmi.c
@@ -18,27 +18,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* This file implements TI/LMI LM3S target specific functions providing
+/* 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.
+ * According to: TivaTM TM4C123GH6PM Microcontroller Datasheet
*/
-#include <stdlib.h>
-#include <string.h>
-
#include "general.h"
-#include "adiv5.h"
#include "target.h"
+#include "cortexm.h"
+
+#define SRAM_BASE 0x20000000
+#define STUB_BUFFER_BASE (SRAM_BASE + 0x30)
+
+#define BLOCK_SIZE 0x400
+
+#define LMI_SCB_BASE 0x400FE000
+#define LMI_SCB_DID1 (LMI_SCB_BASE + 0x004)
+
+#define LMI_FLASH_BASE 0x400FD000
+#define LMI_FLASH_FMA (LMI_FLASH_BASE + 0x000)
+#define LMI_FLASH_FMC (LMI_FLASH_BASE + 0x008)
-static int lmi_flash_erase(struct target_s *target, uint32_t addr, int len);
-static int lmi_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
+#define LMI_FLASH_FMC_WRITE (1 << 0)
+#define LMI_FLASH_FMC_ERASE (1 << 1)
+#define LMI_FLASH_FMC_MERASE (1 << 2)
+#define LMI_FLASH_FMC_COMT (1 << 3)
+#define LMI_FLASH_FMC_WRKEY 0xA4420000
-static const char lmi_driver_str[] = "LuminaryMicro Stellaris";
+static int lmi_flash_erase(target *t, uint32_t addr, size_t len);
+static int lmi_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
+
+static const char lmi_driver_str[] = "TI Stellaris/Tiva";
static const char lmi_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
@@ -51,107 +63,71 @@ static const char lmi_xml_memory_map[] = "<?xml version=\"1.0\"?>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
"</memory-map>";
+static const char tm4c123gh6pm_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=\"0x40000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x8000\"/>"
+ "</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:
-// ...
+static const uint16_t lmi_flash_write_stub[] = {
+#include "../flashstub/lmi.stub"
};
-bool lmi_probe(struct target_s *target)
+bool lmi_probe(target *t)
{
- uint32_t did1 = adiv5_ap_mem_read(adiv5_target_ap(target), 0x400FE004);
+ uint32_t did1 = target_mem_read32(t, LMI_SCB_DID1);
switch (did1 >> 16) {
case 0x1049: /* LM3S3748 */
- target->driver = lmi_driver_str;
- target->xml_mem_map = lmi_xml_memory_map;
- target->flash_erase = lmi_flash_erase;
- target->flash_write = lmi_flash_write;
+ t->driver = lmi_driver_str;
+ t->xml_mem_map = lmi_xml_memory_map;
+ t->flash_erase = lmi_flash_erase;
+ t->flash_write = lmi_flash_write;
+ return true;
+
+ case 0x10A1: /* TM4C123GH6PM */
+ t->driver = lmi_driver_str;
+ t->xml_mem_map = tm4c123gh6pm_xml_memory_map;
+ t->flash_erase = lmi_flash_erase;
+ t->flash_write = lmi_flash_write;
return true;
}
return false;
}
-int lmi_flash_erase(struct target_s *target, uint32_t addr, int len)
+int lmi_flash_erase(target *t, uint32_t addr, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint32_t tmp;
-
- addr &= 0xFFFFFC00;
- len &= 0xFFFFFC00;
-
- /* setup word access */
- adiv5_ap_write(ap, 0x00, 0xA2000052);
-
- /* select Flash Control */
- adiv5_dp_low_access(ap->dp, 1, 0, 0x04, 0x400FD000);
+ addr &= ~(BLOCK_SIZE - 1);
+ len &= ~(BLOCK_SIZE - 1);
while(len) {
- /* write address to FMA */
- adiv5_ap_write(ap, 0x10, addr); /* Required to switch banks */
- /* set ERASE bit in FMC */
- adiv5_dp_low_access(ap->dp, 1, 0, 0x08, 0xA4420002);
- /* Read FMC to poll for ERASE bit */
- adiv5_dp_low_access(ap->dp, 1, 1, 0x08, 0);
- do {
- tmp = adiv5_dp_low_access(ap->dp, 1, 1, 0x08, 0);
- } while (tmp & 2);
-
- len -= 0x400;
- addr += 0x400;
+ target_mem_write32(t, LMI_FLASH_FMA, addr);
+ target_mem_write32(t, LMI_FLASH_FMC,
+ LMI_FLASH_FMC_WRKEY | LMI_FLASH_FMC_ERASE);
+ while (target_mem_read32(t, LMI_FLASH_FMC) &
+ LMI_FLASH_FMC_ERASE);
+
+ len -= BLOCK_SIZE;
+ addr += BLOCK_SIZE;
}
return 0;
}
-int lmi_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len)
+int lmi_flash_write(target *t, uint32_t dest, const uint8_t *src, size_t len)
{
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));
- return 0;
+ target_mem_write(t, SRAM_BASE, lmi_flash_write_stub,
+ sizeof(lmi_flash_write_stub));
+ target_mem_write(t, STUB_BUFFER_BASE, data, len + 8);
+ return cortexm_run_stub(t, SRAM_BASE, 0, 0, 0, 0);
}
diff --git a/src/lpc11xx.c b/src/lpc11xx.c
new file mode 100644
index 0000000..3a85ded
--- /dev/null
+++ b/src/lpc11xx.c
@@ -0,0 +1,321 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * 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 "general.h"
+#include "adiv5.h"
+#include "target.h"
+
+#define IAP_PGM_CHUNKSIZE 256 /* should fit in RAM on any device */
+
+struct flash_param {
+ uint16_t opcodes[2]; /* two opcodes to return to after calling the ROM */
+ uint32_t command[5]; /* command operands */
+ uint32_t result[4]; /* result data */
+};
+
+struct flash_program {
+ struct flash_param p;
+ uint8_t data[IAP_PGM_CHUNKSIZE];
+};
+
+static struct flash_program flash_pgm;
+
+#define MSP 17 /* Main stack pointer register number */
+#define MIN_RAM_SIZE_FOR_LPC8xx 1024
+#define MIN_RAM_SIZE_FOR_LPC1xxx 2048
+#define RAM_USAGE_FOR_IAP_ROUTINES 32 /* IAP routines use 32 bytes at top of ram */
+
+#define IAP_ENTRYPOINT 0x1fff1ff1
+#define IAP_RAM_BASE 0x10000000
+
+#define IAP_CMD_PREPARE 50
+#define IAP_CMD_PROGRAM 51
+#define IAP_CMD_ERASE 52
+#define IAP_CMD_BLANKCHECK 53
+
+#define IAP_STATUS_CMD_SUCCESS 0
+#define IAP_STATUS_INVALID_COMMAND 1
+#define IAP_STATUS_SRC_ADDR_ERROR 2
+#define IAP_STATUS_DST_ADDR_ERROR 3
+#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
+#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
+#define IAP_STATUS_COUNT_ERROR 6
+#define IAP_STATUS_INVALID_SECTOR 7
+#define IAP_STATUS_SECTOR_NOT_BLANK 8
+#define IAP_STATUS_SECTOR_NOT_PREPARED 9
+#define IAP_STATUS_COMPARE_ERROR 10
+#define IAP_STATUS_BUSY 11
+
+static const char lpc8xx_driver[] = "lpc8xx";
+static const char lpc11xx_driver[] = "lpc11xx";
+static void lpc11x_iap_call(target *t, struct flash_param *param, unsigned param_len);
+static int lpc11xx_flash_prepare(target *t, uint32_t addr, int len);
+static int lpc11xx_flash_erase(target *t, uint32_t addr, size_t len);
+static int lpc11xx_flash_write(target *t, uint32_t dest, const uint8_t *src,
+ size_t len);
+
+/*
+ * Note that this memory map is actually for the largest of the lpc11xx devices;
+ * There seems to be no good way to decode the part number to determine the RAM
+ * and flash sizes.
+ */
+static const char lpc11xx_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=\"0x00000000\" length=\"0x20000\">"
+ " <property name=\"blocksize\">0x1000</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x10000000\" length=\"0x2000\"/>"
+ "</memory-map>";
+
+/*
+ * Memory map for the lpc8xx devices, which otherwise look much like the lpc11xx.
+ *
+ * We could decode the RAM/flash sizes, but we just encode the largest possible here.
+ *
+ * Note that the LPC810 and LPC811 map their flash oddly; see the NXP LPC800 user
+ * manual (UM10601) for more details.
+ */
+static const char lpc8xx_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=\"0x00000000\" length=\"0x4000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x10000000\" length=\"0x1000\"/>"
+ "</memory-map>";
+
+bool
+lpc11xx_probe(target *t)
+{
+ uint32_t idcode;
+
+ /* read the device ID register */
+ idcode = target_mem_read32(t, 0x400483F4);
+
+ switch (idcode) {
+
+ case 0x041E502B:
+ case 0x2516D02B:
+ case 0x0416502B:
+ case 0x2516902B: /* lpc1111 */
+ case 0x2524D02B:
+ case 0x0425502B:
+ case 0x2524902B:
+ case 0x1421102B: /* lpc1112 */
+ case 0x0434502B:
+ case 0x2532902B:
+ case 0x0434102B:
+ case 0x2532102B: /* lpc1113 */
+ case 0x0444502B:
+ case 0x2540902B:
+ case 0x0444102B:
+ case 0x2540102B:
+ case 0x1440102B: /* lpc1114 */
+ case 0x0A40902B:
+ case 0x1A40902B:
+ case 0x2058002B: /* lpc1115 */
+ case 0x1431102B: /* lpc11c22 */
+ case 0x1430102B: /* lpc11c24 */
+ case 0x095C802B: /* lpc11u12x/201 */
+ case 0x295C802B:
+ case 0x097A802B: /* lpc11u13/201 */
+ case 0x297A802B:
+ case 0x0998802B: /* lpc11u14x/201 */
+ case 0x2998802B:
+ case 0x2972402B: /* lpc11u23/301 */
+ case 0x2988402B: /* lpc11u24x/301 */
+ case 0x2980002B: /* lpc11u24x/401 */
+ t->driver = lpc11xx_driver;
+ t->xml_mem_map = lpc11xx_xml_memory_map;
+ t->flash_erase = lpc11xx_flash_erase;
+ t->flash_write = lpc11xx_flash_write;
+
+ return true;
+
+ case 0x1812202b: /* LPC812M101FDH20 */
+ t->driver = lpc8xx_driver;
+ t->xml_mem_map = lpc8xx_xml_memory_map;
+ t->flash_erase = lpc11xx_flash_erase;
+ t->flash_write = lpc11xx_flash_write;
+
+ return true;
+ }
+
+ return false;
+}
+
+static void
+lpc11x_iap_call(target *t, struct flash_param *param, unsigned param_len)
+{
+ uint32_t regs[t->regs_size / sizeof(uint32_t)];
+
+ /* fill out the remainder of the parameters and copy the structure to RAM */
+ param->opcodes[0] = 0xbe00;
+ param->opcodes[1] = 0x0000;
+ target_mem_write(t, IAP_RAM_BASE, param, param_len);
+
+ /* set up for the call to the IAP ROM */
+ target_regs_read(t, regs);
+ regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
+ regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
+
+ // stack pointer - top of the smallest ram less 32 for IAP usage
+ if (t->driver == lpc8xx_driver)
+ regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC8xx - RAM_USAGE_FOR_IAP_ROUTINES;
+ else
+ regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC1xxx - RAM_USAGE_FOR_IAP_ROUTINES;
+ regs[14] = IAP_RAM_BASE | 1;
+ regs[15] = IAP_ENTRYPOINT;
+ target_regs_write(t, regs);
+
+ /* start the target and wait for it to halt again */
+ target_halt_resume(t, 0);
+ while (!target_halt_wait(t));
+
+ /* copy back just the parameters structure */
+ target_mem_read(t, param, IAP_RAM_BASE, sizeof(struct flash_param));
+}
+
+static int flash_page_size(target *t)
+{
+ if (t->driver == lpc8xx_driver)
+ return 1024;
+ else
+ return 4096;
+}
+
+static int
+lpc11xx_flash_prepare(target *t, uint32_t addr, int len)
+{
+ /* prepare the sector(s) to be erased */
+ memset(&flash_pgm.p, 0, sizeof(flash_pgm.p));
+ flash_pgm.p.command[0] = IAP_CMD_PREPARE;
+ flash_pgm.p.command[1] = addr / flash_page_size(t);
+ flash_pgm.p.command[2] = (addr + len - 1) / flash_page_size(t);
+
+ lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+lpc11xx_flash_erase(target *t, uint32_t addr, size_t len)
+{
+
+ if (addr % flash_page_size(t))
+ return -1;
+
+ /* prepare... */
+ if (lpc11xx_flash_prepare(t, addr, len))
+ return -1;
+
+ /* and now erase them */
+ flash_pgm.p.command[0] = IAP_CMD_ERASE;
+ flash_pgm.p.command[1] = addr / flash_page_size(t);
+ flash_pgm.p.command[2] = (addr + len - 1) / flash_page_size(t);
+ flash_pgm.p.command[3] = 12000; /* XXX safe to assume this? */
+ lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+ flash_pgm.p.command[0] = IAP_CMD_BLANKCHECK;
+ lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+lpc11xx_flash_write(target *t, uint32_t dest, const uint8_t *src, size_t len)
+{
+ unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
+ unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
+ unsigned chunk_offset = dest % IAP_PGM_CHUNKSIZE;
+ unsigned chunk;
+
+ for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
+
+ DEBUG("chunk %u len %zu\n", chunk, len);
+ /* first and last chunk may require special handling */
+ if ((chunk == first_chunk) || (chunk == last_chunk)) {
+
+ /* fill with all ff to avoid sector rewrite corrupting other writes */
+ memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
+
+ /* copy as much as fits */
+ size_t copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
+ if (copylen > len)
+ copylen = len;
+ memcpy(&flash_pgm.data[chunk_offset], src, copylen);
+
+ /* if we are programming the vectors, calculate the magic number */
+ if ((chunk == 0) && (chunk_offset == 0)) {
+ if (copylen < 32) {
+ /* we have to be programming at least the first 8 vectors... */
+ return -1;
+ }
+
+ uint32_t *w = (uint32_t *)(&flash_pgm.data[0]);
+ uint32_t sum = 0;
+
+ for (unsigned i = 0; i < 7; i++)
+ sum += w[i];
+ w[7] = ~sum + 1;
+ }
+
+ /* update to suit */
+ len -= copylen;
+ src += copylen;
+ chunk_offset = 0;
+
+ } else {
+
+ /* interior chunk, must be aligned and full-sized */
+ memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
+ len -= IAP_PGM_CHUNKSIZE;
+ src += IAP_PGM_CHUNKSIZE;
+ }
+
+ /* prepare... */
+ if (lpc11xx_flash_prepare(t, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE))
+ return -1;
+
+ /* set the destination address and program */
+ flash_pgm.p.command[0] = IAP_CMD_PROGRAM;
+ flash_pgm.p.command[1] = chunk * IAP_PGM_CHUNKSIZE;
+ flash_pgm.p.command[2] = IAP_RAM_BASE + offsetof(struct flash_program, data);
+ flash_pgm.p.command[3] = IAP_PGM_CHUNKSIZE;
+ /* assuming we are running off IRC - safe lower bound */
+ flash_pgm.p.command[4] = 12000; /* XXX safe to presume this? */
+ lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+}
diff --git a/src/lpc43xx.c b/src/lpc43xx.c
index ca1b351..1519306 100644
--- a/src/lpc43xx.c
+++ b/src/lpc43xx.c
@@ -18,32 +18,193 @@
*/
#include "general.h"
+#include "command.h"
#include "adiv5.h"
#include "target.h"
+#include "gdb_packet.h"
#define LPC43XX_CHIPID 0x40043200
#define ARM_CPUID 0xE000ED00
+#define ARM_THUMB_BREAKPOINT 0xBE00
-bool lpc43xx_probe(struct target_s *target)
+#define R_MSP 17 // Main stack pointer register number
+#define R_PC 15 // Program counter register number
+#define R_LR 14 // Link register number
+
+#define IAP_ENTRYPOINT_LOCATION 0x10400100
+
+#define LPC43XX_ETBAHB_SRAM_BASE 0x2000C000
+#define LPC43XX_ETBAHB_SRAM_SIZE (16*1024)
+
+#define LPC43XX_WDT_MODE 0x40080000
+#define LPC43XX_WDT_CNT 0x40080004
+#define LPC43XX_WDT_FEED 0x40080008
+#define LPC43XX_WDT_PERIOD_MAX 0xFFFFFF
+#define LPC43XX_WDT_PROTECT (1 << 4)
+
+#define IAP_RAM_SIZE LPC43XX_ETBAHB_SRAM_SIZE
+#define IAP_RAM_BASE LPC43XX_ETBAHB_SRAM_BASE
+
+#define IAP_PGM_CHUNKSIZE 4096
+
+#define IAP_CMD_INIT 49
+#define IAP_CMD_PREPARE 50
+#define IAP_CMD_PROGRAM 51
+#define IAP_CMD_ERASE 52
+#define IAP_CMD_BLANKCHECK 53
+#define IAP_CMD_SET_ACTIVE_BANK 60
+
+#define IAP_STATUS_CMD_SUCCESS 0
+#define IAP_STATUS_INVALID_COMMAND 1
+#define IAP_STATUS_SRC_ADDR_ERROR 2
+#define IAP_STATUS_DST_ADDR_ERROR 3
+#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
+#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
+#define IAP_STATUS_COUNT_ERROR 6
+#define IAP_STATUS_INVALID_SECTOR 7
+#define IAP_STATUS_SECTOR_NOT_BLANK 8
+#define IAP_STATUS_SECTOR_NOT_PREPARED 9
+#define IAP_STATUS_COMPARE_ERROR 10
+#define IAP_STATUS_BUSY 11
+
+#define FLASH_BANK_A_BASE 0x1A000000
+#define FLASH_BANK_A_SIZE 0x80000
+#define FLASH_BANK_B_BASE 0x1B000000
+#define FLASH_BANK_B_SIZE 0x80000
+#define FLASH_NUM_BANK 2
+#define FLASH_NUM_SECTOR 15
+#define FLASH_LARGE_SECTOR_OFFSET 0x00010000
+
+/* CPU Frequency */
+#define CPU_CLK_KHZ 12000
+
+struct flash_param {
+ uint16_t opcode; /* opcode to return to after calling the ROM */
+ uint16_t pad0;
+ uint32_t command; /* IAP command */
+ union {
+ uint32_t words[5]; /* command parameters */
+ struct {
+ uint32_t start_sector;
+ uint32_t end_sector;
+ uint32_t flash_bank;
+ } prepare;
+ struct {
+ uint32_t start_sector;
+ uint32_t end_sector;
+ uint32_t cpu_clk_khz;
+ uint32_t flash_bank;
+ } erase;
+ struct {
+ uint32_t dest;
+ uint32_t source;
+ uint32_t byte_count;
+ uint32_t cpu_clk_khz;
+ } program;
+ struct {
+ uint32_t start_sector;
+ uint32_t end_sector;
+ uint32_t flash_bank;
+ } blank_check;
+ struct {
+ uint32_t flash_bank;
+ uint32_t cpu_clk_khz;
+ } make_active;
+ } params;
+ uint32_t result[5]; /* result data */
+} __attribute__((aligned(4)));
+
+struct flash_program {
+ struct flash_param p;
+ uint8_t data[IAP_PGM_CHUNKSIZE];
+};
+
+static bool lpc43xx_cmd_erase(target *t, int argc, const char *argv[]);
+static bool lpc43xx_cmd_reset(target *t, int argc, const char *argv[]);
+static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[]);
+static int lpc43xx_flash_init(target *t);
+static void lpc43xx_iap_call(target *t, struct flash_param *param,
+ unsigned param_len);
+static int lpc43xx_flash_prepare(target *t,
+ uint32_t addr, int len);
+static int lpc43xx_flash_erase(target *t, uint32_t addr, size_t len);
+static int lpc43xx_flash_write(target *t,
+ uint32_t dest, const uint8_t *src, size_t len);
+static void lpc43xx_set_internal_clock(target *t);
+static void lpc43xx_wdt_set_period(target *t);
+static void lpc43xx_wdt_pet(target *t);
+
+const struct command_s lpc43xx_cmd_list[] = {
+ {"erase_mass", lpc43xx_cmd_erase, "Erase entire flash memory"},
+ {"reset", lpc43xx_cmd_reset, "Reset target"},
+ {"mkboot", lpc43xx_cmd_mkboot, "Make flash bank bootable"},
+ {NULL, NULL, NULL}
+};
+
+/* blocksize is the erasure block size */
+static const char lpc4337_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=\"ram\" start=\"0x0\" length=\"0x1A000000\"/>"
+" <memory type=\"flash\" start=\"0x1A000000\" length=\"0x10000\">"
+" <property name=\"blocksize\">0x2000</property>"
+" </memory>"
+" <memory type=\"flash\" start=\"0x1A010000\" length=\"0x70000\">"
+" <property name=\"blocksize\">0x10000</property>"
+" </memory>"
+" <memory type=\"ram\" start=\"0x1A080000\" length=\"0x00F80000\"/>"
+" <memory type=\"flash\" start=\"0x1B000000\" length=\"0x10000\">"
+" <property name=\"blocksize\">0x2000</property>"
+" </memory>"
+" <memory type=\"flash\" start=\"0x1B010000\" length=\"0x70000\">"
+" <property name=\"blocksize\">0x10000</property>"
+" </memory>"
+" <memory type=\"ram\" start=\"0x1B080000\" length=\"0xE4F80000\"/>"
+"</memory-map>";
+
+bool lpc43xx_probe(target *t)
{
uint32_t chipid, cpuid;
- chipid = adiv5_ap_mem_read(adiv5_target_ap(target), LPC43XX_CHIPID);
- cpuid = adiv5_ap_mem_read(adiv5_target_ap(target), ARM_CPUID);
+ chipid = target_mem_read32(t, LPC43XX_CHIPID);
+ cpuid = target_mem_read32(t, ARM_CPUID);
switch(chipid) {
case 0x4906002B: /* Parts with on-chip flash */
+ switch (cpuid & 0xFF00FFF0) {
+ case 0x4100C240:
+ t->driver = "LPC43xx Cortex-M4";
+ if (cpuid == 0x410FC241)
+ {
+ /* LPC4337 */
+ t->xml_mem_map = lpc4337_xml_memory_map;
+ t->flash_erase = lpc43xx_flash_erase;
+ t->flash_write = lpc43xx_flash_write;
+ target_add_commands(t, lpc43xx_cmd_list, "LPC43xx");
+ }
+ break;
+ case 0x4100C200:
+ t->driver = "LPC43xx Cortex-M0";
+ break;
+ default:
+ t->driver = "LPC43xx <Unknown>";
+ }
+ return true;
case 0x5906002B: /* Flashless parts */
case 0x6906002B:
switch (cpuid & 0xFF00FFF0) {
case 0x4100C240:
- target->driver = "LPC43xx Cortex-M4";
+ t->driver = "LPC43xx Cortex-M4";
break;
case 0x4100C200:
- target->driver = "LPC43xx Cortex-M0";
+ t->driver = "LPC43xx Cortex-M0";
break;
default:
- target->driver = "LPC43xx <Unknown>";
+ t->driver = "LPC43xx <Unknown>";
}
return true;
}
@@ -51,3 +212,360 @@ bool lpc43xx_probe(struct target_s *target)
return false;
}
+/* Reset all major systems _except_ debug */
+static bool lpc43xx_cmd_reset(target *t, int argc, const char *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ /* Cortex-M4 Application Interrupt and Reset Control Register */
+ static const uint32_t AIRCR = 0xE000ED0C;
+ /* Magic value key */
+ static const uint32_t reset_val = 0x05FA0004;
+
+ /* System reset on target */
+ target_mem_write(t, AIRCR, &reset_val, sizeof(reset_val));
+
+ return true;
+}
+
+static bool lpc43xx_cmd_erase(target *t, int argc, const char *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ uint32_t bank = 0;
+ struct flash_program flash_pgm;
+
+ lpc43xx_flash_init(t);
+
+ for (bank = 0; bank < FLASH_NUM_BANK; bank++)
+ {
+ flash_pgm.p.command = IAP_CMD_PREPARE;
+ flash_pgm.p.params.prepare.start_sector = 0;
+ flash_pgm.p.params.prepare.end_sector = FLASH_NUM_SECTOR-1;
+ flash_pgm.p.params.prepare.flash_bank = bank;
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return false;
+ }
+
+ flash_pgm.p.command = IAP_CMD_ERASE;
+ flash_pgm.p.params.erase.start_sector = 0;
+ flash_pgm.p.params.prepare.end_sector = FLASH_NUM_SECTOR-1;
+ flash_pgm.p.params.erase.cpu_clk_khz = CPU_CLK_KHZ;
+ flash_pgm.p.params.erase.flash_bank = bank;
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS)
+ {
+ return false;
+ }
+ }
+
+ gdb_outf("Erase OK.\n");
+
+ return true;
+}
+
+static int lpc43xx_flash_init(target *t)
+{
+ /* Deal with WDT */
+ lpc43xx_wdt_set_period(t);
+
+ /* Force internal clock */
+ lpc43xx_set_internal_clock(t);
+
+ struct flash_program flash_pgm;
+
+ /* Initialize flash IAP */
+ flash_pgm.p.command = IAP_CMD_INIT;
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS)
+ return -1;
+
+ return 0;
+}
+
+
+
+/**
+ * @brief find a sector number given linear offset
+ */
+static int32_t flash_bank(uint32_t addr)
+{
+ if ((addr >= FLASH_BANK_A_BASE) &&
+ (addr < (FLASH_BANK_A_BASE + FLASH_BANK_A_SIZE)))
+ return 0;
+ if ((addr >= FLASH_BANK_B_BASE) &&
+ (addr < (FLASH_BANK_B_BASE + FLASH_BANK_B_SIZE)))
+ return 1;
+
+ return -1;
+}
+
+/**
+ * @brief find a sector number given linear offset
+ */
+static int32_t sector_number(uint32_t addr)
+{
+ int32_t bank = flash_bank(addr);
+
+ switch (bank) {
+ case 0:
+ addr = addr - FLASH_BANK_A_BASE;
+ break;
+ case 1:
+ addr = addr - FLASH_BANK_B_BASE;
+ break;
+ default:
+ return -1;
+ }
+
+ /* from 47.5 "Sector numbers" (page 1218) UM10503.pdf (Rev 1.6) */
+ if (addr < FLASH_LARGE_SECTOR_OFFSET) {
+ return addr >> 13;
+ } else {
+ return 8 + ((addr - FLASH_LARGE_SECTOR_OFFSET) >> 16);
+ }
+}
+
+static void lpc43xx_iap_call(target *t, struct flash_param *param, unsigned param_len)
+{
+ uint32_t regs[t->regs_size / sizeof(uint32_t)];
+ uint32_t iap_entry;
+
+ /* Pet WDT before each IAP call, if it is on */
+ lpc43xx_wdt_pet(t);
+
+ target_mem_read(t, &iap_entry, IAP_ENTRYPOINT_LOCATION, sizeof(iap_entry));
+
+ /* fill out the remainder of the parameters and copy the structure to RAM */
+ param->opcode = ARM_THUMB_BREAKPOINT; /* breakpoint */
+ param->pad0 = 0x0000; /* pad */
+ target_mem_write(t, IAP_RAM_BASE, param, param_len);
+
+ /* set up for the call to the IAP ROM */
+ target_regs_read(t, regs);
+ regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
+ regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
+
+ regs[R_MSP] = IAP_RAM_BASE + IAP_RAM_SIZE;
+ regs[R_LR] = IAP_RAM_BASE | 1;
+ regs[R_PC] = iap_entry;
+ target_regs_write(t, regs);
+
+ /* start the target and wait for it to halt again */
+ target_halt_resume(t, 0);
+ while (!target_halt_wait(t));
+
+ /* copy back just the parameters structure */
+ target_mem_read(t, param, IAP_RAM_BASE, sizeof(struct flash_param));
+}
+
+static int lpc43xx_flash_prepare(target *t, uint32_t addr, int len)
+{
+ struct flash_program flash_pgm;
+
+ /* prepare the sector(s) to be erased */
+ flash_pgm.p.command = IAP_CMD_PREPARE;
+ flash_pgm.p.params.prepare.start_sector = sector_number(addr);
+ flash_pgm.p.params.prepare.end_sector = sector_number(addr+len);
+ flash_pgm.p.params.prepare.flash_bank = flash_bank(addr);
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lpc43xx_flash_erase(target *t, uint32_t addr, size_t len)
+{
+ struct flash_program flash_pgm;
+
+ /* min block size */
+ if (addr % 8192)
+ return -1;
+
+ /* init */
+ if (lpc43xx_flash_init(t))
+ return -1;
+
+ /* prepare... */
+ if (lpc43xx_flash_prepare(t, addr, len))
+ return -1;
+
+ /* and now erase them */
+ flash_pgm.p.command = IAP_CMD_ERASE;
+ flash_pgm.p.params.erase.start_sector = sector_number(addr);
+ flash_pgm.p.params.erase.end_sector = sector_number(addr+len);
+ flash_pgm.p.params.erase.cpu_clk_khz = CPU_CLK_KHZ;
+ flash_pgm.p.params.erase.flash_bank = flash_bank(addr);
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ /* check erase ok */
+ flash_pgm.p.command = IAP_CMD_BLANKCHECK;
+ flash_pgm.p.params.blank_check.start_sector = sector_number(addr);
+ flash_pgm.p.params.blank_check.end_sector = sector_number(addr+len);
+ flash_pgm.p.params.blank_check.flash_bank = flash_bank(addr);
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void lpc43xx_set_internal_clock(target *t)
+{
+ const uint32_t val2 = (1 << 11) | (1 << 24);
+ target_mem_write(t, 0x40050000 + 0x06C, &val2, sizeof(val2));
+}
+
+static int lpc43xx_flash_write(target *t,
+ uint32_t dest, const uint8_t *src, size_t len)
+{
+ unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
+ unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
+ unsigned chunk_offset;
+ unsigned chunk;
+ struct flash_program flash_pgm;
+
+ for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
+ if (chunk == first_chunk) {
+ chunk_offset = dest % IAP_PGM_CHUNKSIZE;
+ } else {
+ chunk_offset = 0;
+ }
+
+ /* first and last chunk may require special handling */
+ if ((chunk == first_chunk) || (chunk == last_chunk)) {
+
+ /* fill with all ff to avoid sector rewrite corrupting other writes */
+ memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
+
+ /* copy as much as fits */
+ size_t copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
+ if (copylen > len)
+ copylen = len;
+
+ memcpy(flash_pgm.data + chunk_offset, src, copylen);
+
+ /* update to suit */
+ len -= copylen;
+ src += copylen;
+ } else {
+ /* interior chunk, must be aligned and full-sized */
+ memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
+ len -= IAP_PGM_CHUNKSIZE;
+ src += IAP_PGM_CHUNKSIZE;
+ }
+
+ /* prepare... */
+ if (lpc43xx_flash_prepare(t, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE)) {
+ return -1;
+ }
+
+ /* copy buffer into target memory */
+ target_mem_write(t,
+ IAP_RAM_BASE + offsetof(struct flash_program, data),
+ flash_pgm.data, sizeof(flash_pgm.data));
+
+ /* set the destination address and program */
+ flash_pgm.p.command = IAP_CMD_PROGRAM;
+ flash_pgm.p.params.program.dest = chunk * IAP_PGM_CHUNKSIZE;
+ flash_pgm.p.params.program.source = IAP_RAM_BASE + offsetof(struct flash_program, data);
+ flash_pgm.p.params.program.byte_count = IAP_PGM_CHUNKSIZE;
+ flash_pgm.p.params.program.cpu_clk_khz = CPU_CLK_KHZ;
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Call Boot ROM code to make a flash bank bootable by computing and writing the
+ * correct signature into the exception table near the start of the bank.
+ *
+ * This is done indepently of writing to give the user a chance to verify flash
+ * before changing it.
+ */
+static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[])
+{
+ /* Usage: mkboot 0 or mkboot 1 */
+ if (argc != 2) {
+ gdb_outf("Expected bank argument 0 or 1.\n");
+ return false;
+ }
+
+ const long int bank = strtol(argv[1], NULL, 0);
+
+ if ((bank != 0) && (bank != 1)) {
+ gdb_outf("Unexpected bank number, should be 0 or 1.\n");
+ return false;
+ }
+
+ lpc43xx_flash_init(t);
+ struct flash_program flash_pgm;
+
+ /* special command to compute/write magic vector for signature */
+ flash_pgm.p.command = IAP_CMD_SET_ACTIVE_BANK;
+ flash_pgm.p.params.make_active.flash_bank = bank;
+ flash_pgm.p.params.make_active.cpu_clk_khz = CPU_CLK_KHZ;
+ flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
+ lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
+ if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
+ gdb_outf("Set bootable failed.\n");
+ return false;
+ }
+
+ gdb_outf("Set bootable OK.\n");
+ return true;
+}
+
+static void lpc43xx_wdt_set_period(target *t)
+{
+ uint32_t wdt_mode = 0;
+ /* Check if WDT is on */
+ target_mem_read(t, &wdt_mode, LPC43XX_WDT_MODE, sizeof(wdt_mode));
+
+ /* If WDT on, we can't disable it, but we may be able to set a long period */
+ if (wdt_mode && !(wdt_mode & LPC43XX_WDT_PROTECT))
+ {
+ const uint32_t wdt_period = LPC43XX_WDT_PERIOD_MAX;
+
+
+ target_mem_write(t, LPC43XX_WDT_CNT, &wdt_period, sizeof(wdt_period));
+ }
+}
+
+static void lpc43xx_wdt_pet(target *t)
+{
+ uint32_t wdt_mode = 0;
+ /* Check if WDT is on */
+ target_mem_read(t, &wdt_mode, LPC43XX_WDT_MODE, sizeof(wdt_mode));
+
+ /* If WDT on, pet */
+ if (wdt_mode)
+ {
+ const uint32_t feed1 = 0xAA;;
+ const uint32_t feed2 = 0x55;;
+
+ target_mem_write(t, LPC43XX_WDT_FEED, &feed1, sizeof(feed1));
+ target_mem_write(t, LPC43XX_WDT_FEED, &feed2, sizeof(feed2));
+ }
+}
diff --git a/src/main.c b/src/main.c
index aed3e80..6109df9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,34 +18,42 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* Provides main entry point. Initialise subsystems and enter GDB
+/* Provides main entry point. Initialise subsystems and enter GDB
* protocol loop.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
+#include "general.h"
#include "gdb_if.h"
#include "gdb_main.h"
#include "jtagtap.h"
#include "jtag_scan.h"
-
#include "target.h"
+#include "exception.h"
+#include "gdb_packet.h"
+#include "morse.h"
int
main(int argc, char **argv)
{
#if defined(HOST)
- assert(platform_init(argc, argv) == 0);
+ platform_init(argc, argv);
#else
(void) argc;
(void) argv;
- assert(platform_init() == 0);
+ platform_init();
#endif
- PLATFORM_SET_FATAL_ERROR_RECOVERY();
- gdb_main();
+ while (true) {
+ volatile struct exception e;
+ TRY_CATCH(e, EXCEPTION_ALL) {
+ gdb_main();
+ }
+ if (e.type) {
+ gdb_putpacketz("EFF");
+ target_list_free();
+ morse("TARGET LOST.", 1);
+ }
+ }
/* Should never get here */
return 0;
diff --git a/src/morse.c b/src/morse.c
new file mode 100644
index 0000000..6b17c16
--- /dev/null
+++ b/src/morse.c
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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/>.
+ */
+#include "general.h"
+#include "morse.h"
+
+/* 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;
+}
+
+bool morse_update(void)
+{
+ static uint16_t code;
+ static uint8_t bits;
+
+ if (!morse_ptr)
+ return false;
+
+ if (!bits) {
+ char c = *morse_ptr++;
+ if (!c) {
+ if(morse_repeat) {
+ morse_ptr = morse_msg;
+ c = *morse_ptr++;
+ } else {
+ morse_ptr = 0;
+ return false;
+ }
+ }
+ if ((c >= 'A') && (c <= 'Z')) {
+ c -= 'A';
+ code = morse_letter[c].code;
+ bits = morse_letter[c].bits;
+ } else {
+ code = 0; bits = 4;
+ }
+ }
+
+ bool ret = code & 1;
+ code >>= 1;
+ bits--;
+
+ return ret;
+}
+
diff --git a/src/nrf51.c b/src/nrf51.c
new file mode 100644
index 0000000..e302de7
--- /dev/null
+++ b/src/nrf51.c
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2014 Mike Walters <mike@flomp.net>
+ *
+ * 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 nRF51 target specific functions for detecting
+ * the device, providing the XML memory map and Flash memory programming.
+ */
+
+#include "general.h"
+#include "adiv5.h"
+#include "target.h"
+#include "command.h"
+#include "gdb_packet.h"
+#include "cortexm.h"
+
+static int nrf51_flash_erase(target *t, uint32_t addr, size_t len);
+static int nrf51_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
+
+static bool nrf51_cmd_erase_all(target *t);
+static bool nrf51_cmd_read_hwid(target *t);
+static bool nrf51_cmd_read_fwid(target *t);
+static bool nrf51_cmd_read_deviceid(target *t);
+static bool nrf51_cmd_read_deviceaddr(target *t);
+static bool nrf51_cmd_read_help();
+static bool nrf51_cmd_read(target *t, int argc, const char *argv[]);
+
+const struct command_s nrf51_cmd_list[] = {
+ {"erase_mass", (cmd_handler)nrf51_cmd_erase_all, "Erase entire flash memory"},
+ {"read", (cmd_handler)nrf51_cmd_read, "Read device parameters"},
+ {NULL, NULL, NULL}
+};
+const struct command_s nrf51_read_cmd_list[] = {
+ {"help", (cmd_handler)nrf51_cmd_read_help, "Display help for read commands"},
+ {"hwid", (cmd_handler)nrf51_cmd_read_hwid, "Read hardware identification number"},
+ {"fwid", (cmd_handler)nrf51_cmd_read_fwid, "Read pre-loaded firmware ID"},
+ {"deviceid", (cmd_handler)nrf51_cmd_read_deviceid, "Read unique device ID"},
+ {"deviceaddr", (cmd_handler)nrf51_cmd_read_deviceaddr, "Read device address"},
+ {NULL, NULL, NULL}
+};
+
+static const char nrf51_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=\"0x0\" length=\"0x40000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"flash\" start=\"0x10001000\" length=\"0x100\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x4000\"/>"
+ "</memory-map>";
+
+/* Non-Volatile Memory Controller (NVMC) Registers */
+#define NRF51_NVMC 0x4001E000
+#define NRF51_NVMC_READY (NRF51_NVMC + 0x400)
+#define NRF51_NVMC_CONFIG (NRF51_NVMC + 0x504)
+#define NRF51_NVMC_ERASEPAGE (NRF51_NVMC + 0x508)
+#define NRF51_NVMC_ERASEALL (NRF51_NVMC + 0x50C)
+#define NRF51_NVMC_ERASEUICR (NRF51_NVMC + 0x514)
+
+#define NRF51_NVMC_CONFIG_REN 0x0 // Read only access
+#define NRF51_NVMC_CONFIG_WEN 0x1 // Write enable
+#define NRF51_NVMC_CONFIG_EEN 0x2 // Erase enable
+
+/* Factory Information Configuration Registers (FICR) */
+#define NRF51_FICR 0x10000000
+#define NRF51_FICR_CODEPAGESIZE (NRF51_FICR + 0x010)
+#define NRF51_FICR_CODESIZE (NRF51_FICR + 0x014)
+#define NRF51_FICR_CONFIGID (NRF51_FICR + 0x05C)
+#define NRF51_FICR_DEVICEID_LOW (NRF51_FICR + 0x060)
+#define NRF51_FICR_DEVICEID_HIGH (NRF51_FICR + 0x064)
+#define NRF51_FICR_DEVICEADDRTYPE (NRF51_FICR + 0x0A0)
+#define NRF51_FICR_DEVICEADDR_LOW (NRF51_FICR + 0x0A4)
+#define NRF51_FICR_DEVICEADDR_HIGH (NRF51_FICR + 0x0A8)
+
+/* User Information Configuration Registers (UICR) */
+#define NRF51_UICR 0x10001000
+
+#define NRF51_PAGE_SIZE 1024
+
+#define SRAM_BASE 0x20000000
+#define STUB_BUFFER_BASE (SRAM_BASE + 0x28)
+
+static const uint16_t nrf51_flash_write_stub[] = {
+#include "../flashstub/nrf51.stub"
+};
+
+bool nrf51_probe(target *t)
+{
+ t->idcode = target_mem_read32(t, NRF51_FICR_CONFIGID) & 0xFFFF;
+
+ switch (t->idcode) {
+ case 0x001D:
+ case 0x002A:
+ case 0x0044:
+ case 0x003C:
+ case 0x0020:
+ case 0x002F:
+ case 0x0040:
+ case 0x0047:
+ case 0x004D:
+ case 0x0026:
+ case 0x004C:
+ t->driver = "Nordic nRF51";
+ t->xml_mem_map = nrf51_xml_memory_map;
+ t->flash_erase = nrf51_flash_erase;
+ t->flash_write = nrf51_flash_write;
+ target_add_commands(t, nrf51_cmd_list, "nRF51");
+ return true;
+ }
+
+ return false;
+}
+
+static int nrf51_flash_erase(target *t, uint32_t addr, size_t len)
+{
+ addr &= ~(NRF51_PAGE_SIZE - 1);
+ len &= ~(NRF51_PAGE_SIZE - 1);
+
+ /* Enable erase */
+ target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN);
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return -1;
+
+ while (len) {
+ if (addr == NRF51_UICR) { // Special Case
+ /* Write to the ERASE_UICR register to erase */
+ target_mem_write32(t, NRF51_NVMC_ERASEUICR, 0x1);
+
+ } else { // Standard Flash Page
+ /* Write address of first word in page to erase it */
+ target_mem_write32(t, NRF51_NVMC_ERASEPAGE, addr);
+ }
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return -1;
+
+ addr += NRF51_PAGE_SIZE;
+ len -= NRF51_PAGE_SIZE;
+ }
+
+ /* Return to read-only */
+ target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_REN);
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return -1;
+
+ return 0;
+}
+
+static int nrf51_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
+{
+ uint32_t offset = dest % 4;
+ uint32_t words = (offset + len + 3) / 4;
+ uint32_t data[2 + words];
+
+ /* Construct data buffer used by stub */
+ data[0] = dest - offset;
+ data[1] = words * 4; /* length must always be a multiple of 4 */
+ data[2] = 0xFFFFFFFF; /* pad partial words with all 1s to avoid */
+ data[words + 1] = 0xFFFFFFFF; /* damaging overlapping areas */
+ memcpy((uint8_t *)&data[2] + offset, src, len);
+
+ /* Enable write */
+ target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_WEN);
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return -1;
+
+ /* Write stub and data to target ram and call stub */
+ target_mem_write(t, SRAM_BASE, nrf51_flash_write_stub,
+ sizeof(nrf51_flash_write_stub));
+ target_mem_write(t, STUB_BUFFER_BASE, data, len + 8);
+ cortexm_run_stub(t, SRAM_BASE, 0, 0, 0, 0);
+
+ /* Return to read-only */
+ target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_REN);
+
+ return 0;
+}
+
+static bool nrf51_cmd_erase_all(target *t)
+{
+ gdb_out("erase..\n");
+
+ /* Enable erase */
+ target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN);
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return false;
+
+ /* Erase all */
+ target_mem_write32(t, NRF51_NVMC_ERASEALL, 1);
+
+ /* Poll for NVMC_READY */
+ while (target_mem_read32(t, NRF51_NVMC_READY) == 0)
+ if(target_check_error(t))
+ return false;
+
+ return true;
+}
+
+static bool nrf51_cmd_read_hwid(target *t)
+{
+ uint32_t hwid = target_mem_read32(t, NRF51_FICR_CONFIGID) & 0xFFFF;
+ gdb_outf("Hardware ID: 0x%04X\n", hwid);
+
+ return true;
+}
+static bool nrf51_cmd_read_fwid(target *t)
+{
+ uint32_t fwid = (target_mem_read32(t, NRF51_FICR_CONFIGID) >> 16) & 0xFFFF;
+ gdb_outf("Firmware ID: 0x%04X\n", fwid);
+
+ return true;
+}
+static bool nrf51_cmd_read_deviceid(target *t)
+{
+ uint32_t deviceid_low = target_mem_read32(t, NRF51_FICR_DEVICEID_LOW);
+ uint32_t deviceid_high = target_mem_read32(t, NRF51_FICR_DEVICEID_HIGH);
+
+ gdb_outf("Device ID: 0x%08X%08X\n", deviceid_high, deviceid_low);
+
+ return true;
+}
+static bool nrf51_cmd_read_deviceaddr(target *t)
+{
+ uint32_t addr_type = target_mem_read32(t, NRF51_FICR_DEVICEADDRTYPE);
+ uint32_t addr_low = target_mem_read32(t, NRF51_FICR_DEVICEADDR_LOW);
+ uint32_t addr_high = target_mem_read32(t, NRF51_FICR_DEVICEADDR_HIGH) & 0xFFFF;
+
+ if ((addr_type & 1) == 0) {
+ gdb_outf("Publicly Listed Address: 0x%04X%08X\n", addr_high, addr_low);
+ } else {
+ gdb_outf("Randomly Assigned Address: 0x%04X%08X\n", addr_high, addr_low);
+ }
+
+ return true;
+}
+static bool nrf51_cmd_read_help()
+{
+ const struct command_s *c;
+
+ gdb_out("Read commands:\n");
+ for(c = nrf51_read_cmd_list; c->cmd; c++)
+ gdb_outf("\t%s -- %s\n", c->cmd, c->help);
+
+ return true;
+}
+static bool nrf51_cmd_read(target *t, int argc, const char *argv[])
+{
+ const struct command_s *c;
+
+ for(c = nrf51_read_cmd_list; c->cmd; c++) {
+ /* Accept a partial match as GDB does.
+ * So 'mon ver' will match 'monitor version'
+ */
+ if(!strncmp(argv[1], c->cmd, strlen(argv[1])))
+ return !c->handler(t, argc - 1, &argv[1]);
+ }
+
+ return nrf51_cmd_read_help(t);
+}
+
diff --git a/src/nxp_tgt.c b/src/nxp_tgt.c
deleted file mode 100644
index a22eda8..0000000
--- a/src/nxp_tgt.c
+++ /dev/null
@@ -1,252 +0,0 @@
-
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-
-#include "general.h"
-#include "adiv5.h"
-#include "target.h"
-
-#define IAP_PGM_CHUNKSIZE 256 /* should fit in RAM on any device */
-
-struct flash_param {
- uint16_t opcodes[2]; /* two opcodes to return to after calling the ROM */
- uint32_t command[5]; /* command operands */
- uint32_t result[4]; /* result data */
-};
-
-struct flash_program {
- struct flash_param p;
- uint8_t data[IAP_PGM_CHUNKSIZE];
-};
-
-static struct flash_program flash_pgm;
-
-#define MSP 17 // Main stack pointer register number
-#define MIN_RAM_SIZE_FOR_LPC1xxx 2048
-#define RAM_USAGE_FOR_IAP_ROUTINES 32 // IAP routines use 32 bytes at top of ram
-
-#define IAP_ENTRYPOINT 0x1fff1ff1
-#define IAP_RAM_BASE 0x10000000
-
-
-#define IAP_CMD_PREPARE 50
-#define IAP_CMD_PROGRAM 51
-#define IAP_CMD_ERASE 52
-#define IAP_CMD_BLANKCHECK 53
-
-#define IAP_STATUS_CMD_SUCCESS 0
-#define IAP_STATUS_INVALID_COMMAND 1
-#define IAP_STATUS_SRC_ADDR_ERROR 2
-#define IAP_STATUS_DST_ADDR_ERROR 3
-#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
-#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
-#define IAP_STATUS_COUNT_ERROR 6
-#define IAP_STATUS_INVALID_SECTOR 7
-#define IAP_STATUS_SECTOR_NOT_BLANK 8
-#define IAP_STATUS_SECTOR_NOT_PREPARED 9
-#define IAP_STATUS_COMPARE_ERROR 10
-#define IAP_STATUS_BUSY 11
-
-static void lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len);
-static int lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len);
-static int lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len);
-static int lpc11xx_flash_write(struct target_s *target, uint32_t dest, const uint8_t *src,
- int len);
-
-/*
- * Note that this memory map is actually for the largest of the lpc11xx devices;
- * There seems to be no good way to decode the part number to determine the RAM
- * and flash sizes.
- */
-static const char lpc11xx_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=\"0x00000000\" length=\"0x8000\">"
- " <property name=\"blocksize\">0x1000</property>"
- " </memory>"
- " <memory type=\"ram\" start=\"0x10000000\" length=\"0x2000\"/>"
- "</memory-map>";
-
-
-bool
-lpc11xx_probe(struct target_s *target)
-{
- uint32_t idcode;
-
- /* read the device ID register */
- idcode = adiv5_ap_mem_read(adiv5_target_ap(target), 0x400483F4);
-
- switch (idcode) {
-
- case 0x041E502B:
- case 0x2516D02B:
- case 0x0416502B:
- case 0x2516902B: /* lpc1111 */
- case 0x2524D02B:
- case 0x0425502B:
- case 0x2524902B:
- case 0x1421102B: /* lpc1112 */
- case 0x0434502B:
- case 0x2532902B:
- case 0x0434102B:
- case 0x2532102B: /* lpc1113 */
- case 0x0444502B:
- case 0x2540902B:
- case 0x0444102B:
- case 0x2540102B:
- case 0x1440102B: /* lpc1114 */
- case 0x1431102B: /* lpc11c22 */
- case 0x1430102B: /* lpc11c24 */
- target->driver = "lpc11xx";
- target->xml_mem_map = lpc11xx_xml_memory_map;
- target->flash_erase = lpc11xx_flash_erase;
- target->flash_write = lpc11xx_flash_write;
-
- return true;
- }
-
- return false;
-}
-
-static void
-lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len)
-{
- uint32_t regs[target->regs_size];
-
- /* fill out the remainder of the parameters and copy the structure to RAM */
- param->opcodes[0] = 0xbe00;
- param->opcodes[1] = 0x0000;
- target_mem_write_words(target, IAP_RAM_BASE, (void *)param, param_len);
-
- /* set up for the call to the IAP ROM */
- target_regs_read(target, regs);
- regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
- regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
-
- regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC1xxx - RAM_USAGE_FOR_IAP_ROUTINES;// stack pointer - top of the smallest ram less 32 for IAP usage
- regs[14] = IAP_RAM_BASE | 1;
- regs[15] = IAP_ENTRYPOINT;
- target_regs_write(target, regs);
-
- /* start the target and wait for it to halt again */
- target_halt_resume(target, 0);
- while (!target_halt_wait(target));
-
- /* copy back just the parameters structure */
- target_mem_read_words(target, (void *)param, IAP_RAM_BASE, sizeof(struct flash_param));
-}
-
-static int
-lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len)
-{
- /* prepare the sector(s) to be erased */
- memset(&flash_pgm.p, 0, sizeof(flash_pgm.p));
- flash_pgm.p.command[0] = IAP_CMD_PREPARE;
- flash_pgm.p.command[1] = addr / 4096;
- flash_pgm.p.command[2] = (addr + len - 1) / 4096;
-
- lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
- if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
- return -1;
- }
-
- return 0;
-}
-
-static int
-lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len)
-{
-
- if (addr % 4096)
- return -1;
-
- /* prepare... */
- if (lpc11xx_flash_prepare(target, addr, len))
- return -1;
-
- /* and now erase them */
- flash_pgm.p.command[0] = IAP_CMD_ERASE;
- flash_pgm.p.command[1] = addr / 4096;
- flash_pgm.p.command[2] = (addr + len - 1) / 4096;
- flash_pgm.p.command[3] = 12000; /* XXX safe to assume this? */
- lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
- if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
- return -1;
- }
- flash_pgm.p.command[0] = IAP_CMD_BLANKCHECK;
- lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
- if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
- return -1;
- }
-
- return 0;
-}
-
-static int
-lpc11xx_flash_write(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
-{
- unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
- unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
- unsigned chunk_offset = dest % IAP_PGM_CHUNKSIZE;
- unsigned chunk;
-
- for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
-
- DEBUG("chunk %u len %d\n", chunk, len);
- /* first and last chunk may require special handling */
- if ((chunk == first_chunk) || (chunk == last_chunk)) {
-
- /* fill with all ff to avoid sector rewrite corrupting other writes */
- memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
-
- /* copy as much as fits */
- int copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
- if (copylen > len)
- copylen = len;
- memcpy(&flash_pgm.data[chunk_offset], src, copylen);
-
- /* update to suit */
- len -= copylen;
- src += copylen;
- chunk_offset = 0;
-
- /* if we are programming the vectors, calculate the magic number */
- if (chunk == 0) {
- uint32_t *w = (uint32_t *)(&flash_pgm.data[0]);
- uint32_t sum = 0;
-
- for (unsigned i = 0; i < 7; i++)
- sum += w[i];
- w[7] = 0 - sum;
- }
-
- } else {
-
- /* interior chunk, must be aligned and full-sized */
- memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
- len -= IAP_PGM_CHUNKSIZE;
- src += IAP_PGM_CHUNKSIZE;
- }
-
- /* prepare... */
- if (lpc11xx_flash_prepare(target, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE))
- return -1;
-
- /* set the destination address and program */
- flash_pgm.p.command[0] = IAP_CMD_PROGRAM;
- flash_pgm.p.command[1] = chunk * IAP_PGM_CHUNKSIZE;
- flash_pgm.p.command[2] = IAP_RAM_BASE + offsetof(struct flash_program, data);
- flash_pgm.p.command[3] = IAP_PGM_CHUNKSIZE;
- flash_pgm.p.command[4] = 12000; /* XXX safe to presume this? */
- lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm));
- if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
- return -1;
- }
-
- }
-
- return 0;
-}
diff --git a/src/platforms/stm32/cdcacm.c b/src/platforms/common/cdcacm.c
index 93b0f33..ab8294e 100644
--- a/src/platforms/stm32/cdcacm.c
+++ b/src/platforms/common/cdcacm.c
@@ -26,6 +26,15 @@
* The device's unique id is used as the USB serial number string.
*/
+#include "general.h"
+#include "gdb_if.h"
+#include "cdcacm.h"
+#if defined(PLATFORM_HAS_TRACESWO)
+# include "traceswo.h"
+#endif
+#include "usbuart.h"
+#include "serialno.h"
+
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
@@ -33,38 +42,30 @@
#include <libopencm3/usb/dfu.h>
#include <stdlib.h>
-#include "platform.h"
-#include "gdb_if.h"
-#if defined(PLATFORM_HAS_TRACESWO)
-#include <traceswo.h>
-#endif
-#include <usbuart.h>
-
#define DFU_IF_NO 4
usbd_device * usbdev;
-static char *get_dev_unique_id(char *serial_no);
-
static int configured;
static int cdcacm_gdb_dtr = 1;
+static void cdcacm_set_modem_state(usbd_device *dev, int iface, bool dsr, bool dcd);
static const struct usb_device_descriptor dev = {
- .bLength = USB_DT_DEVICE_SIZE,
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = 0x0200,
- .bDeviceClass = 0xEF, /* Miscellaneous Device */
- .bDeviceSubClass = 2, /* Common Class */
- .bDeviceProtocol = 1, /* Interface Association */
- .bMaxPacketSize0 = 64,
- .idVendor = 0x1D50,
- .idProduct = 0x6018,
- .bcdDevice = 0x0100,
- .iManufacturer = 1,
- .iProduct = 2,
- .iSerialNumber = 3,
- .bNumConfigurations = 1,
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0xEF, /* Miscellaneous Device */
+ .bDeviceSubClass = 2, /* Common Class */
+ .bDeviceProtocol = 1, /* Interface Association */
+ .bMaxPacketSize0 = 64,
+ .idVendor = 0x1D50,
+ .idProduct = 0x6018,
+ .bcdDevice = 0x0100,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 1,
};
/* This notification endpoint isn't implemented. According to CDC spec its
@@ -408,11 +409,7 @@ static void dfu_detach_complete(usbd_device *dev, struct usb_setup_data *req)
(void)dev;
(void)req;
- /* Disconnect USB cable */
- disconnect_usb();
-
- /* Assert boot-request pin */
- assert_boot_pin();
+ platform_request_boot();
/* Reset core to enter bootloader */
scb_reset_core();
@@ -429,6 +426,7 @@ static int cdcacm_control_request(usbd_device *dev,
switch(req->bRequest) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ cdcacm_set_modem_state(dev, req->wIndex, true, true);
/* Ignore if not for GDB interface */
if(req->wIndex != 0)
return 1;
@@ -480,22 +478,42 @@ int cdcacm_get_dtr(void)
return cdcacm_gdb_dtr;
}
-static void cdcacm_set_config(usbd_device *dev, u16 wValue)
+static void cdcacm_set_modem_state(usbd_device *dev, int iface, bool dsr, bool dcd)
+{
+ char buf[10];
+ struct usb_cdc_notification *notif = (void*)buf;
+ /* We echo signals back to host as notification */
+ notif->bmRequestType = 0xA1;
+ notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
+ notif->wValue = 0;
+ notif->wIndex = iface;
+ notif->wLength = 2;
+ buf[8] = (dsr ? 2 : 0) | (dcd ? 1 : 0);
+ buf[9] = 0;
+ usbd_ep_write_packet(dev, 0x82 + iface, buf, 10);
+}
+
+static void cdcacm_set_config(usbd_device *dev, uint16_t wValue)
{
configured = wValue;
/* GDB interface */
+#ifdef STM32F4
usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK,
- CDCACM_PACKET_SIZE, gdb_usb_out_cb);
+ CDCACM_PACKET_SIZE, gdb_usb_out_cb);
+#else
+ usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK,
+ CDCACM_PACKET_SIZE, NULL);
+#endif
usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_BULK,
- CDCACM_PACKET_SIZE, NULL);
+ CDCACM_PACKET_SIZE, NULL);
usbd_ep_setup(dev, 0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
/* Serial interface */
usbd_ep_setup(dev, 0x03, USB_ENDPOINT_ATTR_BULK,
- CDCACM_PACKET_SIZE, usbuart_usb_out_cb);
+ CDCACM_PACKET_SIZE, usbuart_usb_out_cb);
usbd_ep_setup(dev, 0x83, USB_ENDPOINT_ATTR_BULK,
- CDCACM_PACKET_SIZE, usbuart_usb_in_cb);
+ CDCACM_PACKET_SIZE, usbuart_usb_in_cb);
usbd_ep_setup(dev, 0x84, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
#if defined(PLATFORM_HAS_TRACESWO)
@@ -512,19 +530,8 @@ static void cdcacm_set_config(usbd_device *dev, u16 wValue)
/* Notify the host that DCD is asserted.
* Allows the use of /dev/tty* devices on *BSD/MacOS
*/
- char buf[10];
- struct usb_cdc_notification *notif = (void*)buf;
- /* We echo signals back to host as notification */
- notif->bmRequestType = 0xA1;
- notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
- notif->wValue = 0;
- notif->wIndex = 0;
- notif->wLength = 2;
- buf[8] = 3; /* DCD | DSR */
- buf[9] = 0;
- usbd_ep_write_packet(dev, 0x82, buf, 10);
- notif->wIndex = 2;
- usbd_ep_write_packet(dev, 0x84, buf, 10);
+ cdcacm_set_modem_state(dev, 0, true, true);
+ cdcacm_set_modem_state(dev, 2, true, true);
}
/* We need a special large control buffer for this device: */
@@ -534,15 +541,16 @@ void cdcacm_init(void)
{
void exti15_10_isr(void);
- get_dev_unique_id(serial_no);
+ serialno_read(serial_no);
+
+ usbdev = usbd_init(&USB_DRIVER, &dev, &config, usb_strings,
+ sizeof(usb_strings)/sizeof(char *),
+ usbd_control_buffer, sizeof(usbd_control_buffer));
- usbdev = usbd_init(&USB_DRIVER, &dev, &config, usb_strings, sizeof(usb_strings)/sizeof(char *));
- usbd_set_control_buffer_size(usbdev, sizeof(usbd_control_buffer));
usbd_register_set_config_callback(usbdev, cdcacm_set_config);
nvic_set_priority(USB_IRQ, IRQ_PRI_USB);
nvic_enable_irq(USB_IRQ);
- setup_vbus_irq();
}
void USB_ISR(void)
@@ -550,26 +558,3 @@ void USB_ISR(void)
usbd_poll(usbdev);
}
-static char *get_dev_unique_id(char *s)
-{
-#if defined(STM32F4)
- volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFF7A10;
-#else
- volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8;
-#endif
- uint32_t unique_id = *unique_id_p +
- *(unique_id_p + 1) +
- *(unique_id_p + 2);
- int i;
-
- /* Fetch serial number from chip's unique ID */
- for(i = 0; i < 8; i++) {
- s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
- }
- for(i = 0; i < 8; i++)
- if(s[i] > '9')
- s[i] += 'A' - '9' - 1;
- s[8] = 0;
-
- return s;
-}
diff --git a/src/platforms/common/cdcacm.h b/src/platforms/common/cdcacm.h
new file mode 100644
index 0000000..f64399d
--- /dev/null
+++ b/src/platforms/common/cdcacm.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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.
+ */
+#ifndef __CDCACM_H
+#define __CDCACM_H
+
+#include <libopencm3/usb/usbd.h>
+
+#define CDCACM_PACKET_SIZE 64
+
+#define CDCACM_GDB_ENDPOINT 1
+#define CDCACM_UART_ENDPOINT 3
+
+extern usbd_device *usbdev;
+
+void cdcacm_init(void);
+/* Returns current usb configuration, or 0 if not configured. */
+int cdcacm_get_config(void);
+int cdcacm_get_dtr(void);
+
+#endif
diff --git a/src/platforms/common/serialno.h b/src/platforms/common/serialno.h
new file mode 100644
index 0000000..946dc20
--- /dev/null
+++ b/src/platforms/common/serialno.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __SERIALNO_H
+#define __SERIALNO_H
+
+char *serialno_read(char *s);
+
+#endif
+
diff --git a/src/include/traceswo.h b/src/platforms/common/traceswo.h
index 5566f94..5566f94 100644
--- a/src/include/traceswo.h
+++ b/src/platforms/common/traceswo.h
diff --git a/src/include/usbuart.h b/src/platforms/common/usbuart.h
index 39f7dcd..51f5702 100644
--- a/src/include/usbuart.h
+++ b/src/platforms/common/usbuart.h
@@ -22,7 +22,6 @@
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
-#include "general.h"
void usbuart_init(void);
diff --git a/src/platforms/dev2/jtagtap.c b/src/platforms/dev2/jtagtap.c
index 30cf9f8..329d4b6 100644
--- a/src/platforms/dev2/jtagtap.c
+++ b/src/platforms/dev2/jtagtap.c
@@ -20,12 +20,9 @@
/* This file implements the low-level JTAG TAP interface. */
-#include <stdio.h>
-
#include "general.h"
#include "jtagtap.h"
-#include "platform.h"
int jtagtap_init(void)
{
@@ -46,8 +43,9 @@ void jtagtap_reset(void)
jtagtap_soft_reset();
}
-void jtagtap_srst(void)
+void jtagtap_srst(bool assert)
{
+ (void)assert;
}
inline uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO)
diff --git a/src/platforms/dev2/platform.c b/src/platforms/dev2/platform.c
index 3d5929b..a62c49e 100644
--- a/src/platforms/dev2/platform.c
+++ b/src/platforms/dev2/platform.c
@@ -17,13 +17,11 @@
* 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 "platform.h"
+#include "general.h"
#include "gdb_if.h"
-#include "jtag_scan.h"
+#include "version.h"
-#include <stdlib.h>
#include <stdarg.h>
-#include <string.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
@@ -31,6 +29,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/time.h>
static int serial_fd = -1;
static int debug;
@@ -68,7 +67,7 @@ int serial_open(const char *name)
return fd;
}
-int platform_init(int argc, char **argv)
+void platform_init(int argc, char **argv)
{
int c;
char *serial = NULL;
@@ -105,11 +104,12 @@ int platform_init(int argc, char **argv)
};
platform_buffer_write (init_str, sizeof (init_str));
- assert(gdb_if_init() == 0);
-
- jtag_scan(NULL);
+ printf("\nBlack Magic Probe (" FIRMWARE_VERSION ")\n");
+ printf("Copyright (C) 2015 Black Sphere Technologies Ltd.\n");
+ printf("License GPLv3+: GNU GPL version 3 or later "
+ "<http://gnu.org/licenses/gpl.html>\n\n");
- return 0;
+ assert(gdb_if_init() == 0);
}
void platform_buffer_flush(void)
@@ -169,3 +169,22 @@ void platform_debug(const char *fmt, ...)
va_end(ap);
}
}
+
+static uint32_t timeout_time;
+static uint32_t time_ms(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+void platform_timeout_set(uint32_t ms)
+{
+ timeout_time = time_ms() + ms;
+}
+
+bool platform_timeout_is_expired(void)
+{
+ return time_ms() > timeout_time;
+}
+
diff --git a/src/platforms/dev2/platform.h b/src/platforms/dev2/platform.h
index b87e4d9..e768d3d 100644
--- a/src/platforms/dev2/platform.h
+++ b/src/platforms/dev2/platform.h
@@ -21,9 +21,6 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <stdint.h>
-#include <stdio.h>
-
#ifndef WIN32
# include <alloca.h>
#else
@@ -83,18 +80,8 @@
#define SET_IDLE_STATE(state)
#define SET_ERROR_STATE(state)
-#define PLATFORM_FATAL_ERROR(error) abort()
-#define PLATFORM_SET_FATAL_ERROR_RECOVERY()
-
#define DEBUG(...) platform_debug(__VA_ARGS__)
-#define morse(x, y) do { if (x) fprintf(stderr,"%s\n", x); } while (0)
-#define morse_msg 0
-
-int platform_init(int argc, char **argv);
-const char *platform_target_voltage(void);
-void platform_delay(uint32_t delay);
-
void platform_buffer_flush(void);
int platform_buffer_write(const uint8_t *data, int size);
int platform_buffer_read(uint8_t *data, int size);
diff --git a/src/platforms/dev2/swdptap.c b/src/platforms/dev2/swdptap.c
index e50a612..2f7c73d 100644
--- a/src/platforms/dev2/swdptap.c
+++ b/src/platforms/dev2/swdptap.c
@@ -20,8 +20,6 @@
/* This file implements the low-level SW-DP interface. */
-#include <stdio.h>
-
#include "general.h"
#include "swdptap.h"
@@ -89,7 +87,7 @@ static uint64_t swdptap_seq_in_64(int ticks)
for(i = 0; i < bytes; i++)
ret |= (uint64_t) rep[i] << (8 * i);
- DEBUG("(0x%0*llx,%d)", (ticks + 3) / 4, ret, ticks);
+ DEBUG("(0x%0*" PRIx64 ",%d)", (ticks + 3) / 4, ret, ticks);
return ret;
}
@@ -124,7 +122,7 @@ static void swdptap_seq_out_64(uint64_t MS, int ticks)
{
swdptap_turnaround(0);
- DEBUG("(0x%0*llx,%d)", (ticks + 3) / 4, MS, ticks);
+ DEBUG("(0x%0*" PRIx64 ",%d)", (ticks + 3) / 4, MS, ticks);
uint8_t cmd[12];
int i = 0;
diff --git a/src/platforms/f4discovery/Makefile.inc b/src/platforms/f4discovery/Makefile.inc
index 5470d65..e57bb0e 100644
--- a/src/platforms/f4discovery/Makefile.inc
+++ b/src/platforms/f4discovery/Makefile.inc
@@ -7,33 +7,21 @@ CFLAGS += -Istm32/include -mcpu=cortex-m4 -mthumb \
-DSTM32F4 -DF4DISCOVERY -I../libopencm3/include \
-Iplatforms/stm32
-LDFLAGS_BOOT = -lopencm3_stm32f4 -Wl,--defsym,_stack=0x20020000 \
+LDFLAGS = -lopencm3_stm32f4 -Wl,--defsym,_stack=0x20006000 \
-Wl,-T,platforms/stm32/f4discovery.ld -nostartfiles -lc -lnosys \
-Wl,-Map=mapfile -mthumb -mcpu=cortex-m4 -Wl,-gc-sections \
-mfloat-abi=hard -mfpu=fpv4-sp-d16 \
-L../libopencm3/lib
-LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8010000
VPATH += platforms/stm32
SRC += cdcacm.c \
- platform.c \
traceswo.c \
usbuart.c \
+ serialno.c \
+ timing.c \
-all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex
-
-blackmagic.bin: blackmagic
- $(OBJCOPY) -O binary $^ $@
-
-blackmagic_dfu: usbdfu.o dfucore.o dfu_f4.o
- $(CC) $^ -o $@ $(LDFLAGS_BOOT)
-
-blackmagic_dfu.bin: blackmagic_dfu
- $(OBJCOPY) -O binary $^ $@
-
-blackmagic_dfu.hex: blackmagic_dfu
- $(OBJCOPY) -O ihex $^ $@
+all: blackmagic.bin
host_clean:
- -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
+ -$(Q)$(RM) blackmagic.bin
diff --git a/src/platforms/f4discovery/Readme b/src/platforms/f4discovery/Readme
index 6035917..9bc1557 100644
--- a/src/platforms/f4discovery/Readme
+++ b/src/platforms/f4discovery/Readme
@@ -1,17 +1,28 @@
-Application start address:
-=========================
+System vs BMP Bootloader
+========================
+For the BMP bootloader, flashing qas not reliable. So we use the system
+bootloder unconditional.
-Use 0x8010000
-- lower 3 16 k pages may be used for parameter storage
-- Erasing a single 64 k Page is faster then erasing 2 16 k Pages
- eventual the 64 k page
+Connections:
+====================
+PA0: User button to force system bootloader entry with reset
+PA2/PA3 eventual connected to the STLINK/ STM32F103C8
-Internal boot loader:
-====================
+PC2: TDI
+PC4: TMS/SWDIO
+PC5: TCK/SWCLK
+PC6: TDO/TRACESWO
+
+PC1: TRST
+PC8: SRST
-When we request invokation of a bootloader from inside the application,
-we boot the device internal bootloader with the blue botton pressed.
+Blue Led: Indicator that system bootloader is entered via BMP
-That way, we can easy exchange the custom bootloader via the device
-internale bootloader \ No newline at end of file
+Bootstrapping the F4Discovery on-board ST-Link
+==============================================
+http://embdev.net/articles/STM_Discovery_as_Black_Magic_Probe has some hints
+how to modify the F4Discovery on-board ST-Link. If you try to do so and hit
+a problem that stands some test like that you load the right firmware to the
+right device via the right BMP probe, explain, report and ask on the
+blackmagic mailing list http://sourceforge.net/mail/?group_id=407419.
diff --git a/src/platforms/f4discovery/platform.c b/src/platforms/f4discovery/platform.c
index 4aa14a6..e85ebd5 100644
--- a/src/platforms/f4discovery/platform.c
+++ b/src/platforms/f4discovery/platform.c
@@ -22,8 +22,12 @@
* implementation.
*/
+#include "general.h"
+#include "cdcacm.h"
+#include "usbuart.h"
+#include "morse.h"
+
#include <libopencm3/stm32/f4/rcc.h>
-#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/exti.h>
@@ -31,37 +35,32 @@
#include <libopencm3/stm32/syscfg.h>
#include <libopencm3/usb/usbd.h>
-#include "platform.h"
-#include "jtag_scan.h"
-#include <usbuart.h>
-
-#include <ctype.h>
-
-uint8_t running_status;
-volatile uint32_t timeout_counter;
-
jmp_buf fatal_error_jmpbuf;
-static void morse_update(void);
-
-int platform_init(void)
+void platform_init(void)
{
- rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
+ /* Check the USER button*/
+ rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN);
+ if(gpio_get(GPIOA, GPIO0)) {
+ platform_request_boot();
+ scb_reset_core();
+ }
+
+ rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_48MHZ]);
/* Enable peripherals */
rcc_peripheral_enable_clock(&RCC_AHB2ENR, RCC_AHB2ENR_OTGFSEN);
- rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN);
- rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN);
+ rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPCEN);
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN);
-
+ rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_CRCEN);
/* Set up USB Pins and alternate function*/
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE,
GPIO9 | GPIO11 | GPIO12);
gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO11 | GPIO12);
- GPIOA_OSPEEDR &=~0xfc;
- GPIOA_OSPEEDR |= 0xa8;
+ GPIOC_OSPEEDR &=~0xF30;
+ GPIOC_OSPEEDR |= 0xA20;
gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT,
GPIO_PUPD_NONE,
TMS_PIN | TCK_PIN | TDI_PIN);
@@ -72,118 +71,11 @@ int platform_init(void)
gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT,
GPIO_PUPD_NONE,
- LED_UART | LED_IDLE_RUN | LED_ERROR | LED_SPARE1);
-
- /* Setup heartbeat timer */
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
- systick_set_reload(168000000/(10*8)); /* Interrupt us at 10 Hz */
- SCB_SHPR(11) &= ~((15 << 4) & 0xff);
- SCB_SHPR(11) |= ((14 << 4) & 0xff);
- systick_interrupt_enable();
- systick_counter_enable();
+ LED_UART | LED_IDLE_RUN | LED_ERROR | LED_BOOTLOADER);
+ platform_timing_init();
usbuart_init();
-
- SCB_VTOR = 0x10000; // Relocate interrupt vector table here
-
cdcacm_init();
-
- jtag_scan(NULL);
-
- return 0;
-}
-
-void platform_delay(uint32_t delay)
-{
- timeout_counter = delay;
- while(timeout_counter);
-}
-
-void sys_tick_handler(void)
-{
- if(running_status)
- gpio_toggle(LED_PORT, LED_IDLE_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--;
}
const char *platform_target_voltage(void)
@@ -191,18 +83,20 @@ const char *platform_target_voltage(void)
return "ABSENT!";
}
-void assert_boot_pin(void)
+void platform_request_boot(void)
{
- if (gpio_get(GPIOA, GPIO0)) {
- /* Jump to the built in bootloader by mapping System flash */
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SYSCFGEN);
- SYSCFG_MEMRM &= ~3;
- SYSCFG_MEMRM |= 1;
- }
- else {
- /* Flag Bootloader Request by mimicing a pushed USER button*/
- gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT,
- GPIO_PUPD_NONE, GPIO0);
- gpio_set(GPIOA, GPIO0);
- }
+ /* Disconnect USB cable */
+ usbd_disconnect(usbdev, 1);
+ nvic_disable_irq(USB_IRQ);
+
+ /* Assert blue LED as indicator we are in the bootloader */
+ rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN);
+ gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT,
+ GPIO_PUPD_NONE, LED_BOOTLOADER);
+ gpio_set(LED_PORT, LED_BOOTLOADER);
+
+ /* Jump to the built in bootloader by mapping System flash */
+ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SYSCFGEN);
+ SYSCFG_MEMRM &= ~3;
+ SYSCFG_MEMRM |= 1;
}
diff --git a/src/platforms/f4discovery/platform.h b/src/platforms/f4discovery/platform.h
index a1deb48..16bd169 100644
--- a/src/platforms/f4discovery/platform.h
+++ b/src/platforms/f4discovery/platform.h
@@ -24,40 +24,33 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <libopencm3/stm32/f4/gpio.h>
-#include <libopencm3/usb/usbd.h>
+#include "gpio.h"
+#include "timing.h"
+#include "version.h"
#include <setjmp.h>
-#include <alloca.h>
-#include "gdb_packet.h"
-
-#define INLINE_GPIO
-#define CDCACM_PACKET_SIZE 64
#define PLATFORM_HAS_TRACESWO
-#define BOARD_IDENT "Black Magic Probe (F4Discovery), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define BOARD_IDENT_DFU "Black Magic (Upgrade) for F4Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define DFU_IDENT "Black Magic Firmware Upgrade (F4Discovery"
-#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,7*128Kg"
-
-extern usbd_device *usbdev;
-#define CDCACM_GDB_ENDPOINT 1
-#define CDCACM_UART_ENDPOINT 3
+#define BOARD_IDENT "Black Magic Probe (F4Discovery), (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_DFU "Black Magic (Upgrade) for F4Discovery, (Firmware " FIRMWARE_VERSION ")"
+#define DFU_IDENT "Black Magic Firmware Upgrade (F4Discovery"
+#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,7*128Kg"
/* Important pin mappings for STM32 implementation:
*
* LED0 = PD12 (Green LED : Running)
* LED1 = PD13 (Orange LED : Idle)
* LED2 = PD12 (Red LED : Error)
+ * LED3 = PD15 (Blue LED : Bootloader active)
*
* TPWR = XXX (input) -- analogue on mini design ADC1, ch8
- * nTRST = PD0
- * SRST_OUT = PD1
- * TDI = PA1
- * TMS = PA3 (input for SWDP)
- * TCK = PA8
- * TDO = PB4 (input)
- * nSRST = PD2 (input)
+ * nTRST = PC1
+ * SRST_OUT = PC8
+ * TDI = PC2
+ * TMS = PC4 (input for SWDP)
+ * TCK = PC5/SWCLK
+ * TDO = PC6 (input for TRACESWO
+ * nSRST =
*
* USB cable pull-up: PA8
* USB VBUS detect: PB13 -- New on mini design.
@@ -66,40 +59,43 @@ extern usbd_device *usbdev;
*/
/* Hardware definitions... */
-#define JTAG_PORT GPIOA
+#define JTAG_PORT GPIOC
#define TDI_PORT JTAG_PORT
#define TMS_PORT JTAG_PORT
#define TCK_PORT JTAG_PORT
-#define TDO_PORT GPIOB
-#define TDI_PIN GPIO1
-#define TMS_PIN GPIO3
-#define TCK_PIN GPIO2
-#define TDO_PIN GPIO4
+#define TDO_PORT GPIOC
+#define TDI_PIN GPIO2
+#define TMS_PIN GPIO4
+#define TCK_PIN GPIO5
+#define TDO_PIN GPIO6
#define SWDIO_PORT JTAG_PORT
#define SWCLK_PORT JTAG_PORT
#define SWDIO_PIN TMS_PIN
#define SWCLK_PIN TCK_PIN
-#define TRST_PORT GPIOD
-#define TRST_PIN GPIO0
-#define SRST_PORT GPIOD
-#define SRST_PIN GPIO1
+#define TRST_PORT GPIOC
+#define TRST_PIN GPIO1
+#define SRST_PORT GPIOC
+#define SRST_PIN GPIO8
#define LED_PORT GPIOD
#define LED_PORT_UART GPIOD
#define LED_UART GPIO12
#define LED_IDLE_RUN GPIO13
#define LED_ERROR GPIO14
-#define LED_SPARE1 GPIO15
+#define LED_BOOTLOADER GPIO15
-#define TMS_SET_MODE() gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, \
- GPIO_PUPD_NONE, TMS_PIN);
-#define SWDIO_MODE_FLOAT() gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, \
- GPIO_PUPD_NONE, SWDIO_PIN);
+#define TMS_SET_MODE() \
+ gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, \
+ GPIO_PUPD_NONE, TMS_PIN);
+#define SWDIO_MODE_FLOAT() \
+ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, \
+ GPIO_PUPD_NONE, SWDIO_PIN);
-#define SWDIO_MODE_DRIVE() gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \
- GPIO_PUPD_NONE, SWDIO_PIN);
+#define SWDIO_MODE_DRIVE() \
+ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \
+ GPIO_PUPD_NONE, SWDIO_PIN);
#define USB_DRIVER stm32f107_usb_driver
@@ -111,42 +107,39 @@ extern usbd_device *usbdev;
*/
#define IRQ_PRI_USB (2 << 4)
#define IRQ_PRI_USBUSART (1 << 4)
+#define IRQ_PRI_USBUSART_TIM (3 << 4)
#define IRQ_PRI_TRACE (0 << 4)
#define USBUSART USART3
#define USBUSART_CR1 USART3_CR1
#define USBUSART_IRQ NVIC_USART3_IRQ
-#define USBUSART_APB_ENR RCC_APB1ENR
-#define USBUSART_CLK_ENABLE RCC_APB1ENR_USART3EN
+#define USBUSART_CLK RCC_USART3
#define USBUSART_TX_PORT GPIOD
#define USBUSART_TX_PIN GPIO8
#define USBUSART_RX_PORT GPIOD
#define USBUSART_RX_PIN GPIO9
#define USBUSART_ISR usart3_isr
-
-#define UART_PIN_SETUP() do { \
- gpio_mode_setup(USBUSART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \
- USBUSART_TX_PIN); \
- gpio_mode_setup(USBUSART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \
- USBUSART_RX_PIN); \
- gpio_set_af(USBUSART_TX_PORT, GPIO_AF7, USBUSART_TX_PIN); \
- gpio_set_af(USBUSART_RX_PORT, GPIO_AF7, USBUSART_RX_PIN); \
+#define USBUSART_TIM TIM4
+#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4)
+#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ
+#define USBUSART_TIM_ISR tim4_isr
+
+#define UART_PIN_SETUP() do { \
+ gpio_mode_setup(USBUSART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \
+ USBUSART_TX_PIN); \
+ gpio_mode_setup(USBUSART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \
+ USBUSART_RX_PIN); \
+ gpio_set_af(USBUSART_TX_PORT, GPIO_AF7, USBUSART_TX_PIN); \
+ gpio_set_af(USBUSART_RX_PORT, GPIO_AF7, USBUSART_RX_PIN); \
} while(0)
#define TRACE_TIM TIM3
-#define TRACE_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN)
+#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3)
#define TRACE_IRQ NVIC_TIM3_IRQ
#define TRACE_ISR tim3_isr
#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)); \
@@ -158,58 +151,15 @@ extern const char *morse_msg;
#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, 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(); \
- morse("TARGET LOST.", 1); \
- longjmp(fatal_error_jmpbuf, (error)); \
+static inline int platform_hwversion(void)
+{
+ return 0;
}
-int platform_init(void);
-void morse(const char *msg, char repeat);
-const char *platform_target_voltage(void);
-int platform_hwversion(void);
-void platform_delay(uint32_t delay);
-
-/* <cdcacm.c> */
-void cdcacm_init(void);
-/* Returns current usb configuration, or 0 if not configured. */
-int cdcacm_get_config(void);
-int cdcacm_get_dtr(void);
-
-/* <platform.h> */
-void uart_usb_buf_drain(uint8_t ep);
-
/* Use newlib provided integer only stdio functions */
#define sscanf siscanf
#define sprintf siprintf
#define vasprintf vasiprintf
-#ifdef INLINE_GPIO
-static inline void _gpio_set(u32 gpioport, u16 gpios)
-{
- GPIO_BSRR(gpioport) = gpios;
-}
-#define gpio_set _gpio_set
-
-static inline void _gpio_clear(u32 gpioport, u16 gpios)
-{
- GPIO_BSRR(gpioport) = gpios<<16;
-}
-#define gpio_clear _gpio_clear
-
-static inline u16 _gpio_get(u32 gpioport, u16 gpios)
-{
- return (u16)GPIO_IDR(gpioport) & gpios;
-}
-#define gpio_get _gpio_get
-#endif
-
#endif
-#define disconnect_usb() do {usbd_disconnect(usbdev,1); nvic_disable_irq(USB_IRQ);} while(0)
-void assert_boot_pin(void);
-#define setup_vbus_irq()
diff --git a/src/platforms/f4discovery/usbdfu.c b/src/platforms/f4discovery/usbdfu.c
index 998628b..6a40295 100644
--- a/src/platforms/f4discovery/usbdfu.c
+++ b/src/platforms/f4discovery/usbdfu.c
@@ -17,17 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
+#include "general.h"
+#include "usbdfu.h"
+
#include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/scb.h>
-#include "usbdfu.h"
-
void dfu_detach(void)
{
- /* USB device must detach, we just reset... */
+ /* USB device must detach, we just reset... */
scb_reset_system();
}
@@ -40,9 +40,9 @@ int main(void)
dfu_protect_enable();
- /* Set up clock*/
- rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ /* Set up clock*/
+ rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
systick_set_reload(2100000);
systick_interrupt_enable();
diff --git a/src/platforms/launchpad-icdi/Makefile.inc b/src/platforms/launchpad-icdi/Makefile.inc
new file mode 100644
index 0000000..e46b727
--- /dev/null
+++ b/src/platforms/launchpad-icdi/Makefile.inc
@@ -0,0 +1,21 @@
+CROSS_COMPILE ?= arm-none-eabi-
+CC = $(CROSS_COMPILE)gcc
+OBJCOPY = $(CROSS_COMPILE)objcopy
+
+INCLUDES = -I../libopencm3/include
+
+CPU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
+CFLAGS += $(INCLUDES) $(CPU_FLAGS) -DTARGET_IS_BLIZZARD_RB1 -DLM4F -DPART_TM4C123GH6PM
+
+LINKER_SCRIPT="platforms/tm4c/tm4c.ld"
+LDFLAGS = -nostartfiles -lc $(CPU_FLAGS) -nodefaultlibs -T$(LINKER_SCRIPT) -Wl,--gc-sections \
+ -L../libopencm3/lib -lopencm3_lm4f -lnosys -lm -lgcc
+
+VPATH += platforms/tm4c
+
+SRC += cdcacm.c \
+ usbuart.c \
+ traceswo.o
+
+all: blackmagic.bin
+
diff --git a/src/platforms/launchpad-icdi/platform.c b/src/platforms/launchpad-icdi/platform.c
new file mode 100644
index 0000000..ff67fa0
--- /dev/null
+++ b/src/platforms/launchpad-icdi/platform.c
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * 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 "general.h"
+#include "gdb_if.h"
+#include "cdcacm.h"
+#include "usbuart.h"
+
+#include <libopencm3/lm4f/rcc.h>
+#include <libopencm3/lm4f/nvic.h>
+#include <libopencm3/lm4f/uart.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/lm4f/usb.h>
+
+#define SYSTICKHZ 100
+#define SYSTICKMS (1000 / SYSTICKHZ)
+
+#define PLL_DIV_80MHZ 5
+#define PLL_DIV_25MHZ 16
+
+extern void trace_tick(void);
+
+uint8_t running_status;
+volatile uint32_t timeout_counter;
+
+void sys_tick_handler(void)
+{
+ if(timeout_counter)
+ timeout_counter--;
+ trace_tick();
+}
+
+void
+platform_init(void)
+{
+ int i;
+ for(i=0; i<1000000; i++);
+
+ rcc_sysclk_config(OSCSRC_MOSC, XTAL_16M, PLL_DIV_80MHZ);
+
+ // Enable all JTAG ports and set pins to output
+ periph_clock_enable(RCC_GPIOA);
+ periph_clock_enable(RCC_GPIOB);
+
+ gpio_enable_ahb_aperture();
+
+ gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN);
+ gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN);
+ gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN);
+ gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN);
+ gpio_mode_setup(SRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SRST_PIN);
+ gpio_set_output_config(SRST_PORT, GPIO_OTYPE_OD, GPIO_DRIVE_2MA, SRST_PIN);
+ gpio_set(SRST_PORT, SRST_PIN);
+
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(rcc_get_system_clock_frequency() / (SYSTICKHZ * 8));
+
+ systick_interrupt_enable();
+ systick_counter_enable();
+
+ nvic_enable_irq(NVIC_SYSTICK_IRQ);
+ nvic_enable_irq(NVIC_UART0_IRQ);
+
+ periph_clock_enable(RCC_GPIOD);
+ __asm__("nop"); __asm__("nop"); __asm__("nop");
+ gpio_mode_setup(GPIOD_BASE, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO4|GPIO5);
+ usb_enable_interrupts(USB_INT_RESET | USB_INT_DISCON |
+ USB_INT_RESUME | USB_INT_SUSPEND,
+ 0xff, 0xff);
+
+ usbuart_init();
+ cdcacm_init();
+}
+
+void platform_timeout_set(uint32_t ms)
+{
+ timeout_counter = ms / 10;
+}
+
+bool platform_timeout_is_expired(void)
+{
+ return timeout_counter == 0;
+}
+
+void platform_delay(uint32_t delay)
+{
+ platform_timeout_set(delay);
+ while (platform_timeout_is_expired());
+}
+
+const char *platform_target_voltage(void)
+{
+ return "not supported";
+}
+
+char *serialno_read(char *s)
+{
+ /* FIXME: Store a unique serial number somewhere and retreive here */
+ uint32_t unique_id = 1;
+ int i;
+
+ /* Fetch serial number from chip's unique ID */
+ for(i = 0; i < 8; i++) {
+ s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
+ }
+ for(i = 0; i < 8; i++)
+ if(s[i] > '9')
+ s[i] += 'A' - '9' - 1;
+ s[8] = 0;
+
+ return s;
+}
+
+void platform_request_boot(void)
+{
+}
+
diff --git a/src/platforms/launchpad-icdi/platform.h b/src/platforms/launchpad-icdi/platform.h
new file mode 100644
index 0000000..52b963e
--- /dev/null
+++ b/src/platforms/launchpad-icdi/platform.h
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * 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 __PLATFORM_H
+#define __PLATFORM_H
+
+#include <libopencm3/lm4f/gpio.h>
+#include <libopencm3/usb/usbd.h>
+
+#include "version.h"
+
+#define BOARD_IDENT "Black Magic Probe (Launchpad ICDI), (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_DFU "Black Magic (Upgrade) for Launchpad, (Firmware " FIRMWARE_VERSION ")"
+#define DFU_IDENT "Black Magic Firmware Upgrade (Launchpad)"
+#define DFU_IFACE_STRING "lolwut"
+
+extern uint8_t running_status;
+extern volatile uint32_t timeout_counter;
+
+#define TMS_PORT GPIOA_BASE
+#define TMS_PIN GPIO3
+
+#define TCK_PORT GPIOA_BASE
+#define TCK_PIN GPIO2
+
+#define TDI_PORT GPIOA_BASE
+#define TDI_PIN GPIO5
+
+#define TDO_PORT GPIOA_BASE
+#define TDO_PIN GPIO4
+
+#define SWO_PORT GPIOD_BASE
+#define SWO_PIN GPIO6
+
+#define SWDIO_PORT TMS_PORT
+#define SWDIO_PIN TMS_PIN
+
+#define SWCLK_PORT TCK_PORT
+#define SWCLK_PIN TCK_PIN
+
+#define SRST_PORT GPIOA_BASE
+#define SRST_PIN GPIO6
+
+#define TMS_SET_MODE() { \
+ gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); \
+ gpio_set_output_config(TMS_PORT, GPIO_OTYPE_PP, GPIO_DRIVE_2MA, TMS_PIN); \
+}
+
+#define SWDIO_MODE_FLOAT() { \
+ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWDIO_PIN); \
+}
+
+#define SWDIO_MODE_DRIVE() { \
+ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); \
+ gpio_set_output_config(SWDIO_PORT, GPIO_OTYPE_PP, GPIO_DRIVE_2MA, SWDIO_PIN); \
+}
+
+extern usbd_driver lm4f_usb_driver;
+#define USB_DRIVER lm4f_usb_driver
+#define USB_IRQ NVIC_USB0_IRQ
+#define USB_ISR usb0_isr
+
+#define IRQ_PRI_USB (2 << 4)
+
+#define USBUART UART0
+#define USBUART_CLK RCC_UART0
+#define USBUART_IRQ NVIC_UART0_IRQ
+#define USBUART_ISR uart0_isr
+#define UART_PIN_SETUP() do { \
+ periph_clock_enable(RCC_GPIOA); \
+ __asm__("nop"); __asm__("nop"); __asm__("nop"); \
+ gpio_set_af(GPIOA_BASE, 0x1, GPIO0 | GPIO1); \
+ gpio_mode_setup(GPIOA_BASE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0); \
+ gpio_mode_setup(GPIOA_BASE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO1); \
+ } while (0)
+
+#define TRACEUART UART2
+#define TRACEUART_CLK RCC_UART2
+#define TRACEUART_IRQ NVIC_UART2_IRQ
+#define TRACEUART_ISR uart2_isr
+
+/* Use newlib provided integer only stdio functions */
+#define sscanf siscanf
+#define sprintf siprintf
+#define vasprintf vasiprintf
+
+#define DEBUG(...)
+
+#define SET_RUN_STATE(state) {running_status = (state);}
+#define SET_IDLE_STATE(state) {}
+#define SET_ERROR_STATE(state) SET_IDLE_STATE(state)
+
+#define PLATFORM_HAS_TRACESWO
+
+inline static void gpio_set_val(uint32_t port, uint8_t pin, uint8_t val) {
+ gpio_write(port, pin, val == 0 ? 0 : 0xff);
+}
+
+inline static uint8_t gpio_get(uint32_t port, uint8_t pin) {
+ return !(gpio_read(port, pin) == 0);
+}
+
+#define disconnect_usb() do { usbd_disconnect(usbdev,1); nvic_disable_irq(USB_IRQ);} while(0)
+
+#endif
diff --git a/src/platforms/libftdi/gdb_if.c b/src/platforms/libftdi/gdb_if.c
index 4b4f4a5..ff49b57 100644
--- a/src/platforms/libftdi/gdb_if.c
+++ b/src/platforms/libftdi/gdb_if.c
@@ -108,7 +108,9 @@ unsigned char gdb_if_getchar_to(int timeout)
void gdb_if_putchar(unsigned char c, int flush)
{
- if(gdb_if_conn > 0)
+ (void)flush;
+
+ if (gdb_if_conn > 0)
send(gdb_if_conn, &c, 1, 0);
}
diff --git a/src/platforms/libftdi/jtagtap.c b/src/platforms/libftdi/jtagtap.c
index cd58707..c416892 100644
--- a/src/platforms/libftdi/jtagtap.c
+++ b/src/platforms/libftdi/jtagtap.c
@@ -51,12 +51,11 @@
int jtagtap_init(void)
{
- int err;
-
assert(ftdic != NULL);
/* Go to JTAG mode for SWJ-DP */
- for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-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();
@@ -68,8 +67,9 @@ void jtagtap_reset(void)
jtagtap_soft_reset();
}
-void jtagtap_srst(void)
+void jtagtap_srst(bool assert)
{
+ (void)assert;
platform_buffer_flush();
//ftdi_write_data(ftdic, "\x80\x88\xAB", 3);
//usleep(1000);
@@ -85,7 +85,7 @@ jtagtap_tms_seq(uint32_t MS, int ticks)
//jtagtap_next(MS & 1, 1);
tmp[1] = ticks<7?ticks-1:6;
tmp[2] = 0x80 | (MS & 0x7F);
-
+
// assert(ftdi_write_data(ftdic, tmp, 3) == 3);
platform_buffer_write(tmp, 3);
MS >>= 7; ticks -= 7;
@@ -94,10 +94,10 @@ jtagtap_tms_seq(uint32_t MS, int ticks)
#endif
#ifndef PROVIDE_GENERIC_TAP_TDI_SEQ
-void
+void
jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
{
- char *tmp;
+ uint8_t *tmp;
int index = 0;
int rticks;
@@ -120,7 +120,7 @@ jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
tmp[index++] = rticks - 1;
tmp[index++] = *DI;
}
-
+
if(final_tms) {
tmp[index++] = 0x4B;
tmp[index++] = 0;
@@ -132,7 +132,7 @@ jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
#endif
#ifndef PROVIDE_GENERIC_TAP_TDI_TDO_SEQ
-void
+void
jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
{
uint8_t *tmp;
@@ -160,7 +160,7 @@ jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int
tmp[index++] = rticks - 1;
tmp[index++] = *DI;
}
-
+
if(final_tms) {
rsize++;
tmp[index++] = 0x6B;
@@ -177,7 +177,7 @@ jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int
printf("\n");*/
index = 0;
if(final_tms) rsize--;
-
+
while(rsize--) {
/*if(rsize) printf("%02X ", tmp[index]);*/
*DO++ = tmp[index++];
diff --git a/src/platforms/libftdi/platform.c b/src/platforms/libftdi/platform.c
index d7f11dd..37f876b 100644
--- a/src/platforms/libftdi/platform.c
+++ b/src/platforms/libftdi/platform.c
@@ -17,14 +17,12 @@
* 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 "platform.h"
+#include "general.h"
#include "gdb_if.h"
-#include "jtag_scan.h"
+#include "version.h"
-#include <stdio.h>
-#include <string.h>
#include <assert.h>
-#include <unistd.h>
+#include <sys/time.h>
struct ftdi_context *ftdic;
@@ -49,6 +47,15 @@ static struct cable_desc_s {
.interface = INTERFACE_A,
.dbus_data = 0x08,
.dbus_ddr = 0x1B,
+ .description = "FLOSS-JTAG",
+ .name = "flossjtag"
+ },
+ {
+ .vendor = 0x0403,
+ .product = 0x6010,
+ .interface = INTERFACE_A,
+ .dbus_data = 0x08,
+ .dbus_ddr = 0x1B,
.description = "FTDIJTAG",
.name = "ftdijtag"
},
@@ -112,11 +119,11 @@ static struct cable_desc_s {
},
};
-int platform_init(int argc, char **argv)
-{
+void platform_init(int argc, char **argv)
+{
int err;
int c;
- int index = 0;
+ unsigned index = 0;
char *serial = NULL;
char * cablename = "ftdi";
uint8_t ftdi_init[9] = {TCK_DIVISOR, 0x01, 0x00, SET_BITS_LOW, 0,0,
@@ -140,7 +147,7 @@ int platform_init(int argc, char **argv)
if (index == sizeof(cable_desc)/sizeof(cable_desc[0])){
fprintf(stderr, "No cable matching %s found\n",cablename);
- return -1;
+ exit(-1);
}
if (cable_desc[index].dbus_data)
@@ -152,46 +159,51 @@ int platform_init(int argc, char **argv)
if(cable_desc[index].cbus_ddr)
ftdi_init[8]= cable_desc[index].cbus_ddr;
+ printf("\nBlack Magic Probe (" FIRMWARE_VERSION ")\n");
+ printf("Copyright (C) 2015 Black Sphere Technologies Ltd.\n");
+ printf("License GPLv3+: GNU GPL version 3 or later "
+ "<http://gnu.org/licenses/gpl.html>\n\n");
+
if(ftdic) {
ftdi_usb_close(ftdic);
ftdi_free(ftdic);
ftdic = NULL;
}
if((ftdic = ftdi_new()) == NULL) {
- fprintf(stderr, "ftdi_new: %s\n",
+ fprintf(stderr, "ftdi_new: %s\n",
ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_set_interface(ftdic, cable_desc[index].interface)) != 0) {
- fprintf(stderr, "ftdi_set_interface: %d: %s\n",
+ fprintf(stderr, "ftdi_set_interface: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_usb_open_desc(
ftdic, cable_desc[index].vendor, cable_desc[index].product,
cable_desc[index].description, serial)) != 0) {
- fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
+ fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
err, ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_set_latency_timer(ftdic, 1)) != 0) {
- fprintf(stderr, "ftdi_set_latency_timer: %d: %s\n",
+ fprintf(stderr, "ftdi_set_latency_timer: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_set_baudrate(ftdic, 1000000)) != 0) {
- fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
+ fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_usb_purge_buffers(ftdic)) != 0) {
- fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
+ fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
if((err = ftdi_write_data_set_chunksize(ftdic, BUF_SIZE)) != 0) {
- fprintf(stderr, "ftdi_write_data_set_chunksize: %d: %s\n",
+ fprintf(stderr, "ftdi_write_data_set_chunksize: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
@@ -203,12 +215,7 @@ int platform_init(int argc, char **argv)
}
assert(ftdi_write_data(ftdic, ftdi_init, 9) == 9);
-
assert(gdb_if_init() == 0);
-
- jtag_scan(NULL);
-
- return 0;
}
void platform_buffer_flush(void)
@@ -241,9 +248,9 @@ int vasprintf(char **strp, const char *fmt, va_list ap)
int size = 128, ret = 0;
*strp = malloc(size);
- while(*strp && ((ret = vsnprintf(*strp, size, fmt, ap)) == size))
+ while(*strp && ((ret = vsnprintf(*strp, size, fmt, ap)) == size))
*strp = realloc(*strp, size <<= 1);
-
+
return ret;
}
#endif
@@ -258,3 +265,21 @@ void platform_delay(uint32_t delay)
usleep(delay * 100000);
}
+static uint32_t timeout_time;
+static uint32_t time_ms(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+void platform_timeout_set(uint32_t ms)
+{
+ timeout_time = time_ms() + ms;
+}
+
+bool platform_timeout_is_expired(void)
+{
+ return time_ms() > timeout_time;
+}
+
diff --git a/src/platforms/libftdi/platform.h b/src/platforms/libftdi/platform.h
index e23bf96..8611674 100644
--- a/src/platforms/libftdi/platform.h
+++ b/src/platforms/libftdi/platform.h
@@ -21,7 +21,6 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <stdint.h>
#include <ftdi.h>
#ifndef WIN32
@@ -37,18 +36,8 @@
#define SET_IDLE_STATE(state)
#define SET_ERROR_STATE(state)
-#define PLATFORM_FATAL_ERROR(error) abort()
-#define PLATFORM_SET_FATAL_ERROR_RECOVERY()
-
-#define morse(x, y) fprintf(stderr,"%s\n", x)
-#define morse_msg 0
-
extern struct ftdi_context *ftdic;
-int platform_init(int argc, char **argv);
-const char *platform_target_voltage(void);
-void platform_delay(uint32_t delay);
-
void platform_buffer_flush(void);
int platform_buffer_write(const uint8_t *data, int size);
int platform_buffer_read(uint8_t *data, int size);
diff --git a/src/platforms/libftdi/swdptap.c b/src/platforms/libftdi/swdptap.c
index be329d8..6aafa7d 100644
--- a/src/platforms/libftdi/swdptap.c
+++ b/src/platforms/libftdi/swdptap.c
@@ -26,7 +26,7 @@
#include <assert.h>
#include <ftdi.h>
-#include "platform.h"
+#include "general.h"
#include "swdptap.h"
static void swdptap_turnaround(uint8_t dir);
@@ -40,36 +40,36 @@ int swdptap_init(void)
assert(ftdic != NULL);
if((err = ftdi_set_bitmode(ftdic, 0xAB, BITMODE_BITBANG)) != 0) {
- fprintf(stderr, "ftdi_set_bitmode: %d: %s\n",
+ fprintf(stderr, "ftdi_set_bitmode: %d: %s\n",
err, ftdi_get_error_string(ftdic));
abort();
}
- assert(ftdi_write_data(ftdic, "\xAB\xA8", 2) == 2);
+ assert(ftdi_write_data(ftdic, (void*)"\xAB\xA8", 2) == 2);
/* This must be investigated in more detail.
* As described in STM32 Reference Manual... */
- swdptap_seq_out(0xFFFF, 16);
+ swdptap_seq_out(0xFFFF, 16);
swdptap_reset();
- swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */
+ swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */
swdptap_reset();
- swdptap_seq_out(0, 16);
+ 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);
+ swdptap_turnaround(0);
+ /* 50 clocks with TMS high */
+ for(int i = 0; i < 50; i++)
+ swdptap_bit_out(1);
}
static void swdptap_turnaround(uint8_t dir)
{
static uint8_t olddir = 0;
- //DEBUG("%s", dir ? "\n-> ":"\n<- ");
platform_buffer_flush();
if(dir == olddir) return;
@@ -79,22 +79,19 @@ static void swdptap_turnaround(uint8_t dir)
assert(ftdi_set_bitmode(ftdic, 0xA3, BITMODE_BITBANG) == 0);
/* One clock cycle */
- ftdi_write_data(ftdic, "\xAB\xA8", 2);
+ ftdi_write_data(ftdic, (void *)"\xAB\xA8", 2);
if(!dir) /* SWDIO goes to output */
assert(ftdi_set_bitmode(ftdic, 0xAB, BITMODE_BITBANG) == 0);
}
-static uint8_t swdptap_bit_in(void)
+static uint8_t swdptap_bit_in(void)
{
uint8_t ret;
- //ftdi_read_data(ftdic, &ret, 1);
ftdi_read_pins(ftdic, &ret);
ret &= 0x08;
- ftdi_write_data(ftdic, "\xA1\xA0", 2);
-
- //DEBUG("%d", ret?1:0);
+ ftdi_write_data(ftdic, (void *)"\xA1\xA0", 2);
return ret;
}
@@ -103,13 +100,10 @@ static void swdptap_bit_out(uint8_t val)
{
uint8_t buf[3] = "\xA0\xA1\xA0";
- //DEBUG("%d", val);
-
if(val) {
for(int i = 0; i < 3; i++)
buf[i] |= 0x08;
}
- //ftdi_write_data(ftdic, buf, 3);
platform_buffer_write(buf, 3);
}
diff --git a/src/platforms/native/Makefile.inc b/src/platforms/native/Makefile.inc
index 77c07e9..8365555 100644
--- a/src/platforms/native/Makefile.inc
+++ b/src/platforms/native/Makefile.inc
@@ -6,7 +6,7 @@ CFLAGS += -Istm32/include -mcpu=cortex-m3 -mthumb \
-DSTM32F1 -DBLACKMAGIC -I../libopencm3/include \
-Iplatforms/stm32
-LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
+LDFLAGS_BOOT := $(LDFLAGS) -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
-Wl,-T,platforms/stm32/blackmagic.ld -nostartfiles -lc -lnosys \
-Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections \
-L../libopencm3/lib
@@ -15,24 +15,17 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
VPATH += platforms/stm32
SRC += cdcacm.c \
- platform.c \
traceswo.c \
usbuart.c \
+ serialno.c \
+ timing.c \
all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex
-blackmagic.bin: blackmagic
- $(OBJCOPY) -O binary $^ $@
-
blackmagic_dfu: usbdfu.o dfucore.o dfu_f1.o
- $(CC) $^ -o $@ $(LDFLAGS_BOOT)
-
-blackmagic_dfu.bin: blackmagic_dfu
- $(OBJCOPY) -O binary $^ $@
-
-blackmagic_dfu.hex: blackmagic_dfu
- $(OBJCOPY) -O ihex $^ $@
+ @echo " LD $@"
+ $(Q)$(CC) $^ -o $@ $(LDFLAGS_BOOT)
host_clean:
- -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
+ -$(Q)$(RM) -f blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c
index 8ded81a..babb498 100644
--- a/src/platforms/native/platform.c
+++ b/src/platforms/native/platform.c
@@ -22,8 +22,12 @@
* implementation.
*/
+#include "general.h"
+#include "cdcacm.h"
+#include "usbuart.h"
+#include "morse.h"
+
#include <libopencm3/stm32/f1/rcc.h>
-#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/exti.h>
@@ -31,20 +35,8 @@
#include <libopencm3/usb/usbd.h>
#include <libopencm3/stm32/f1/adc.h>
-#include "platform.h"
-#include "jtag_scan.h"
-#include <usbuart.h>
-
-#include <ctype.h>
-
-uint8_t running_status;
-volatile uint32_t timeout_counter;
-
-jmp_buf fatal_error_jmpbuf;
-
-static void morse_update(void);
-
static void adc_init(void);
+static void setup_vbus_irq(void);
/* Pins PB[7:5] are used to detect hardware revision.
* 000 - Original production build.
@@ -63,15 +55,16 @@ int platform_hwversion(void)
return hwversion;
}
-int platform_init(void)
+void platform_init(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz();
/* Enable peripherals */
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- 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_AFIOEN);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOB);
+ rcc_periph_clock_enable(RCC_AFIO);
+ rcc_periph_clock_enable(RCC_CRC);
/* Setup GPIO ports */
gpio_clear(USB_PU_PORT, USB_PU_PIN);
@@ -81,7 +74,6 @@ int platform_init(void)
gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
TMS_PIN | TCK_PIN | TDI_PIN);
-
/* This needs some fixing... */
/* Toggle required to sort out line drivers... */
gpio_port_write(GPIOA, 0x8100);
@@ -98,16 +90,24 @@ int platform_init(void)
* to release the device from reset if this floats. */
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO7);
-
- /* Setup heartbeat timer */
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
- systick_set_reload(900000); /* Interrupt us at 10 Hz */
- SCB_SHPR(11) &= ~((15 << 4) & 0xff);
- SCB_SHPR(11) |= ((14 << 4) & 0xff);
- systick_interrupt_enable();
- systick_counter_enable();
-
- usbuart_init();
+ /* Enable SRST output. Original uses a NPN to pull down, so setting the
+ * output HIGH asserts. Mini is directly connected so use open drain output
+ * and set LOW to assert.
+ */
+ platform_srst_set_val(false);
+ gpio_set_mode(SRST_PORT, GPIO_MODE_OUTPUT_50_MHZ,
+ (platform_hwversion() == 0
+ ? GPIO_CNF_OUTPUT_PUSHPULL
+ : GPIO_CNF_OUTPUT_OPENDRAIN),
+ SRST_PIN);
+
+ /* Enable internal pull-up on PWR_BR so that we don't drive
+ TPWR locally or inadvertently supply power to the target. */
+ if (platform_hwversion () > 0) {
+ gpio_set (PWR_BR_PORT, PWR_BR_PIN);
+ gpio_set_mode(PWR_BR_PORT, GPIO_MODE_INPUT,
+ GPIO_CNF_INPUT_PULL_UPDOWN, PWR_BR_PIN);
+ }
if (platform_hwversion() > 0) {
adc_init();
@@ -116,112 +116,42 @@ int platform_init(void)
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_PULL_UPDOWN, GPIO0);
}
+ /* Relocate interrupt vector table here */
+ SCB_VTOR = 0x2000;
- SCB_VTOR = 0x2000; // Relocate interrupt vector table here
-
+ platform_timing_init();
cdcacm_init();
-
- jtag_scan(NULL);
-
- return 0;
-}
-
-void platform_delay(uint32_t delay)
-{
- timeout_counter = delay;
- while(timeout_counter);
+ usbuart_init();
+ setup_vbus_irq();
}
-void sys_tick_handler(void)
+void platform_srst_set_val(bool assert)
{
- if(running_status)
- gpio_toggle(LED_PORT, LED_IDLE_RUN);
-
- if(timeout_counter)
- timeout_counter--;
-
- morse_update();
+ if (platform_hwversion() == 0) {
+ gpio_set_val(SRST_PORT, SRST_PIN, assert);
+ } else {
+ gpio_set_val(SRST_PORT, SRST_PIN, !assert);
+ }
}
-
-/* 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)
+bool platform_target_get_power(void)
{
- morse_msg = morse_ptr = msg;
- morse_repeat = repeat;
- SET_ERROR_STATE(0);
+ if (platform_hwversion() > 0) {
+ return !gpio_get(PWR_BR_PORT, PWR_BR_PIN);
+ }
+ return 0;
}
-static void morse_update(void)
+void platform_target_set_power(bool power)
{
- 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;
- }
+ if (platform_hwversion() > 0) {
+ gpio_set_val(PWR_BR_PORT, PWR_BR_PIN, !power);
}
- SET_ERROR_STATE(code & 1);
- code >>= 1; bits--;
}
static void adc_init(void)
{
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_ADC1EN);
+ rcc_periph_clock_enable(RCC_ADC1);
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_ANALOG, GPIO0);
@@ -231,7 +161,7 @@ static void adc_init(void)
adc_set_single_conversion_mode(ADC1);
adc_disable_external_trigger_regular(ADC1);
adc_set_right_aligned(ADC1);
- adc_set_conversion_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC);
+ adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC);
adc_power_on(ADC1);
@@ -249,23 +179,27 @@ const char *platform_target_voltage(void)
return gpio_get(GPIOB, GPIO0) ? "OK" : "ABSENT!";
static char ret[] = "0.0V";
- const u8 channel = 8;
- adc_set_regular_sequence(ADC1, 1, (u8*)&channel);
+ const uint8_t channel = 8;
+ adc_set_regular_sequence(ADC1, 1, (uint8_t*)&channel);
- adc_on(ADC1);
+ adc_start_conversion_direct(ADC1);
/* Wait for end of conversion. */
- while (!(ADC_SR(ADC1) & ADC_SR_EOC));
+ while (!adc_eoc(ADC1));
- u32 val = ADC_DR(ADC1) * 99; /* 0-4095 */
+ uint32_t val = adc_read_regular(ADC1) * 99; /* 0-4095 */
ret[0] = '0' + val / 81910;
ret[2] = '0' + (val / 8191) % 10;
return ret;
}
-void assert_boot_pin(void)
+void platform_request_boot(void)
{
+ /* Disconnect USB cable */
+ gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, 0, USB_PU_PIN);
+
+ /* Drive boot request pin */
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
gpio_clear(GPIOB, GPIO12);
@@ -286,7 +220,7 @@ void exti15_10_isr(void)
exti_reset_request(USB_VBUS_PIN);
}
-void setup_vbus_irq(void)
+static void setup_vbus_irq(void)
{
nvic_set_priority(USB_VBUS_IRQ, IRQ_PRI_USB_VBUS);
nvic_enable_irq(USB_VBUS_IRQ);
@@ -304,3 +238,4 @@ void setup_vbus_irq(void)
exti15_10_isr();
}
+
diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h
index fbd048a..6958585 100644
--- a/src/platforms/native/platform.h
+++ b/src/platforms/native/platform.h
@@ -24,25 +24,17 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <libopencm3/stm32/f1/gpio.h>
-#include <libopencm3/usb/usbd.h>
+#include "gpio.h"
+#include "timing.h"
-#include <setjmp.h>
-#include <alloca.h>
-
-#include "gdb_packet.h"
-
-#define INLINE_GPIO
-#define CDCACM_PACKET_SIZE 64
#define PLATFORM_HAS_TRACESWO
-#define BOARD_IDENT "Black Magic Probe, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define BOARD_IDENT_DFU "Black Magic Probe (Upgrade), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
+#define PLATFORM_HAS_POWER_SWITCH
+#define BOARD_IDENT "Black Magic Probe"
+#define BOARD_IDENT_DFU "Black Magic Probe (Upgrade)"
+#define BOARD_IDENT_UPD "Black Magic Probe (DFU Upgrade)"
#define DFU_IDENT "Black Magic Firmware Upgrade"
-#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,120*001Kg"
-
-extern usbd_device *usbdev;
-#define CDCACM_GDB_ENDPOINT 1
-#define CDCACM_UART_ENDPOINT 3
+#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,120*001Kg"
+#define UPD_IFACE_STRING "@Internal Flash /0x08000000/8*001Kg"
/* Important pin mappings for STM32 implementation:
*
@@ -51,7 +43,8 @@ extern usbd_device *usbdev;
* LED2 = PB11 (Red LED : Error)
*
* TPWR = RB0 (input) -- analogue on mini design ADC1, ch8
- * nTRST = PB1
+ * nTRST = PB1 [blackmagic]
+ * PWR_BR = PB1 [blackmagic_mini] -- supply power to the target, active low
* SRST_OUT = PA2
* TDI = PA3
* TMS = PA4 (input for SWDP)
@@ -60,7 +53,7 @@ extern usbd_device *usbdev;
* nSRST = PA7 (input)
*
* USB cable pull-up: PA8
- * USB VBUS detect: PB13 -- New on mini design.
+ * USB VBUS detect: PB13 -- New on mini design.
* Enable pull up for compatibility.
* Force DFU mode button: PB12
*/
@@ -83,6 +76,8 @@ extern usbd_device *usbdev;
#define TRST_PORT GPIOB
#define TRST_PIN GPIO1
+#define PWR_BR_PORT GPIOB
+#define PWR_BR_PIN GPIO1
#define SRST_PORT GPIOA
#define SRST_PIN GPIO2
@@ -99,118 +94,63 @@ extern usbd_device *usbdev;
#define LED_IDLE_RUN GPIO10
#define LED_ERROR GPIO11
-#define TMS_SET_MODE() \
- gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
-#define SWDIO_MODE_FLOAT() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
- GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
-#define SWDIO_MODE_DRIVE() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
-
-#define UART_PIN_SETUP() \
- gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
- GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN);
-
-#define USB_DRIVER stm32f103_usb_driver
-#define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ
-#define USB_ISR usb_lp_can_rx0_isr
+#define TMS_SET_MODE() \
+ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
+#define SWDIO_MODE_FLOAT() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
+ GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
+#define SWDIO_MODE_DRIVE() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
+
+#define UART_PIN_SETUP() \
+ gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
+ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN);
+
+#define SRST_SET_VAL(x) \
+ platform_srst_set_val(x)
+
+#define USB_DRIVER stm32f103_usb_driver
+#define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ
+#define USB_ISR usb_lp_can_rx0_isr
/* Interrupt priorities. Low numbers are high priority.
* For now USART1 preempts USB which may spin while buffer is drained.
- * TIM3 is used for traceswo capture and must be highest priority.
+ * TIM3 is used for traceswo capture and must be highest priority.
*/
-#define IRQ_PRI_USB (2 << 4)
-#define IRQ_PRI_USBUSART (1 << 4)
-#define IRQ_PRI_USB_VBUS (14 << 4)
-#define IRQ_PRI_TRACE (0 << 4)
+#define IRQ_PRI_USB (2 << 4)
+#define IRQ_PRI_USBUSART (1 << 4)
+#define IRQ_PRI_USBUSART_TIM (3 << 4)
+#define IRQ_PRI_USB_VBUS (14 << 4)
+#define IRQ_PRI_TRACE (0 << 4)
#define USBUSART USART1
#define USBUSART_CR1 USART1_CR1
#define USBUSART_IRQ NVIC_USART1_IRQ
-#define USBUSART_APB_ENR RCC_APB2ENR
-#define USBUSART_CLK_ENABLE RCC_APB2ENR_USART1EN
+#define USBUSART_CLK RCC_USART1
#define USBUSART_PORT GPIOA
#define USBUSART_TX_PIN GPIO9
#define USBUSART_ISR usart1_isr
+#define USBUSART_TIM TIM4
+#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4)
+#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ
+#define USBUSART_TIM_ISR tim4_isr
#define TRACE_TIM TIM3
-#define TRACE_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN)
+#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3)
#define TRACE_IRQ NVIC_TIM3_IRQ
#define TRACE_ISR tim3_isr
#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_RUN, 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(); \
- morse("TARGET LOST.", 1); \
- longjmp(fatal_error_jmpbuf, (error)); \
-}
-
-int platform_init(void);
-void morse(const char *msg, char repeat);
-const char *platform_target_voltage(void);
-int platform_hwversion(void);
-void platform_delay(uint32_t delay);
-
-/* <cdcacm.c> */
-void cdcacm_init(void);
-/* Returns current usb configuration, or 0 if not configured. */
-int cdcacm_get_config(void);
-int cdcacm_get_dtr(void);
-
-/* <platform.h> */
-void uart_usb_buf_drain(uint8_t ep);
-
/* Use newlib provided integer only stdio functions */
#define sscanf siscanf
#define sprintf siprintf
#define vasprintf vasiprintf
-#ifdef INLINE_GPIO
-static inline void _gpio_set(u32 gpioport, u16 gpios)
-{
- GPIO_BSRR(gpioport) = gpios;
-}
-#define gpio_set _gpio_set
-
-static inline void _gpio_clear(u32 gpioport, u16 gpios)
-{
- GPIO_BRR(gpioport) = gpios;
-}
-#define gpio_clear _gpio_clear
-
-static inline u16 _gpio_get(u32 gpioport, u16 gpios)
-{
- return (u16)GPIO_IDR(gpioport) & gpios;
-}
-#define gpio_get _gpio_get
-#endif
-
#endif
-#define disconnect_usb() gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, 0, USB_PU_PIN);
-void assert_boot_pin(void);
-void setup_vbus_irq(void);
diff --git a/src/platforms/native/usbdfu.c b/src/platforms/native/usbdfu.c
index 35572a5..39643da 100644
--- a/src/platforms/native/usbdfu.c
+++ b/src/platforms/native/usbdfu.c
@@ -25,27 +25,29 @@
#include "usbdfu.h"
+uint32_t app_address = 0x08002000;
+
void dfu_detach(void)
{
- /* USB device must detach, we just reset... */
+ /* USB device must detach, we just reset... */
scb_reset_system();
}
int main(void)
{
/* Check the force bootloader pin*/
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
+ rcc_periph_clock_enable(RCC_GPIOB);
if(gpio_get(GPIOB, GPIO12))
dfu_jump_app_if_valid();
- dfu_protect_enable();
+ dfu_protect(DFU_MODE);
rcc_clock_setup_in_hse_8mhz_out_72mhz();
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
systick_set_reload(900000);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_USB);
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO8);
systick_interrupt_enable();
@@ -56,7 +58,7 @@ int main(void)
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, GPIO2 | GPIO10);
- dfu_init(&stm32f103_usb_driver);
+ dfu_init(&stm32f103_usb_driver, DFU_MODE);
gpio_set(GPIOA, GPIO8);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
diff --git a/src/platforms/stlink/Makefile.inc b/src/platforms/stlink/Makefile.inc
index 627b14b..fdee700 100644
--- a/src/platforms/stlink/Makefile.inc
+++ b/src/platforms/stlink/Makefile.inc
@@ -2,10 +2,12 @@ CROSS_COMPILE ?= arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
+OPT_FLAGS = -Os
CFLAGS += -mcpu=cortex-m3 -mthumb \
-DSTM32F1 -DDISCOVERY_STLINK -I../libopencm3/include \
-I platforms/stm32
-LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
+LDFLAGS_BOOT := $(LDFLAGS) --specs=nano.specs \
+ -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
-Wl,-T,platforms/stm32/stlink.ld -nostartfiles -lc -lnosys \
-Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections \
-L../libopencm3/lib
@@ -14,23 +16,20 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
VPATH += platforms/stm32
SRC += cdcacm.c \
- platform.c \
- usbuart.c \
+ usbuart.c \
+ serialno.c \
+ timing.c \
-all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex
-
-blackmagic.bin: blackmagic
- $(OBJCOPY) -O binary $^ $@
+all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex dfu_upgrade.bin dfu_upgrade.hex
blackmagic_dfu: usbdfu.o dfucore.o dfu_f1.o
- $(CC) $^ -o $@ $(LDFLAGS_BOOT)
-
-blackmagic_dfu.bin: blackmagic_dfu
- $(OBJCOPY) -O binary $^ $@
+ @echo " LD $@"
+ $(Q)$(CC) $^ -o $@ $(LDFLAGS_BOOT)
-blackmagic_dfu.hex: blackmagic_dfu
- $(OBJCOPY) -O ihex $^ $@
+dfu_upgrade: dfu_upgrade.o dfucore.o dfu_f1.o
+ @echo " LD $@"
+ $(Q)$(CC) $^ -o $@ $(LDFLAGS)
host_clean:
- -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
+ -$(Q)$(RM) blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex dfu_upgrade dfu_upgrade.bin dfu_upgrade.hex
diff --git a/src/platforms/stlink/dfu_upgrade.c b/src/platforms/stlink/dfu_upgrade.c
new file mode 100644
index 0000000..4a3648a
--- /dev/null
+++ b/src/platforms/stlink/dfu_upgrade.c
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2013 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/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/cm3/scb.h>
+
+#include "usbdfu.h"
+uint32_t app_address = 0x08000000;
+
+static uint8_t rev;
+static uint16_t led_idle_run;
+static uint32_t led2_state = 0;
+
+void dfu_detach(void)
+{
+ /* Disconnect USB cable by resetting USB Device
+ and pulling USB_DP low*/
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ gpio_clear(GPIOA, GPIO12);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
+ /* Pull PB0 (T_NRST) low
+ */
+ rcc_periph_clock_enable(RCC_GPIOB);
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0);
+ gpio_clear(GPIOB, GPIO0);
+ SCB_VTOR = 0;
+ scb_reset_core();
+}
+
+void stlink_set_rev(void)
+{
+ int i;
+
+ /* First, get Board revision by pulling PC13/14 up. Read
+ * 11 for ST-Link V1, e.g. on VL Discovery, tag as rev 0
+ * 10 for ST-Link V2, e.g. on F4 Discovery, tag as rev 1
+ */
+ rcc_periph_clock_enable(RCC_GPIOC);
+ gpio_set_mode(GPIOC, GPIO_MODE_INPUT,
+ GPIO_CNF_INPUT_PULL_UPDOWN, GPIO14 | GPIO13);
+ gpio_set(GPIOC, GPIO14 | GPIO13);
+ for (i = 0; i < 100; i++)
+ rev = (~(gpio_get(GPIOC, GPIO14 | GPIO13)) >> 13) & 3;
+
+ switch (rev) {
+ case 0:
+ led_idle_run = GPIO8;
+ break;
+ default:
+ led_idle_run = GPIO9;
+ }
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
+}
+
+int main(void)
+{
+
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ stlink_set_rev();
+
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(900000);
+
+ dfu_protect(UPD_MODE);
+
+ /* Handle USB disconnect/connect */
+ /* Just in case: Disconnect USB cable by resetting USB Device
+ * and pulling USB_DP low
+ * Device will reconnect automatically as Pull-Up is hard wired*/
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ gpio_clear(GPIOA, GPIO12);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
+
+ systick_interrupt_enable();
+ systick_counter_enable();
+
+ dfu_init(&stm32f103_usb_driver, UPD_MODE);
+
+ dfu_main();
+}
+
+void sys_tick_handler(void)
+{
+ if (rev == 0) {
+ gpio_toggle(GPIOA, led_idle_run);
+ } else {
+ if (led2_state & 1) {
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
+ gpio_set(GPIOA, led_idle_run);
+ } else {
+ gpio_set_mode(GPIOA, GPIO_MODE_INPUT,
+ GPIO_CNF_INPUT_ANALOG, led_idle_run);
+ }
+ led2_state++;
+ }
+}
diff --git a/src/platforms/stlink/platform.c b/src/platforms/stlink/platform.c
index 5eaf5fc..dd501a4 100644
--- a/src/platforms/stlink/platform.c
+++ b/src/platforms/stlink/platform.c
@@ -22,25 +22,20 @@
* implementation.
*/
-#include <libopencm3/stm32/f1/rcc.h>
-#include <libopencm3/cm3/systick.h>
+#include "general.h"
+#include "cdcacm.h"
+#include "usbuart.h"
+
+#include <libopencm3/stm32/rcc.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/usb/usbd.h>
-#include <libopencm3/stm32/f1/adc.h>
-
-#include "platform.h"
-#include "jtag_scan.h"
-#include <usbuart.h>
-
-#include <ctype.h>
+#include <libopencm3/stm32/adc.h>
uint8_t running_status;
volatile uint32_t timeout_counter;
-jmp_buf fatal_error_jmpbuf;
-
uint16_t led_idle_run;
/* Pins PC[14:13] are used to detect hardware revision. Read
* 11 for STLink V1 e.g. on VL Discovery, tag as hwversion 0
@@ -49,98 +44,78 @@ uint16_t led_idle_run;
int platform_hwversion(void)
{
static int hwversion = -1;
- int i;
+ int i;
if (hwversion == -1) {
gpio_set_mode(GPIOC, GPIO_MODE_INPUT,
- GPIO_CNF_INPUT_PULL_UPDOWN,
- GPIO14 | GPIO13);
+ GPIO_CNF_INPUT_PULL_UPDOWN, GPIO14 | GPIO13);
gpio_set(GPIOC, GPIO14 | GPIO13);
- for (i = 0; i<10; i++)
- hwversion = ~(gpio_get(GPIOC, GPIO14 | GPIO13) >> 13) & 3;
- switch (hwversion)
- {
- case 0:
- led_idle_run = GPIO8;
- break;
- default:
- led_idle_run = GPIO9;
- }
+ for (i = 0; i<10; i++)
+ hwversion = ~(gpio_get(GPIOC, GPIO14 | GPIO13) >> 13) & 3;
+ switch (hwversion)
+ {
+ case 0:
+ led_idle_run = GPIO8;
+ break;
+ default:
+ led_idle_run = GPIO9;
+ }
}
return hwversion;
}
-int platform_init(void)
+void platform_init(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz();
/* Enable peripherals */
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- 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_IOPCEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOB);
+ rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_AFIO);
+ rcc_periph_clock_enable(RCC_CRC);
/* On Rev 1 unconditionally activate MCO on PORTA8 with HSE
- * platform_hwversion() also needed to initialize led_idle_run!
- */
- if (platform_hwversion() == 1)
- {
- RCC_CFGR &= ~( 0xf<< 24);
- RCC_CFGR |= (RCC_CFGR_MCO_HSECLK << 24);
- gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
- GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO8);
- }
+ * platform_hwversion() also needed to initialize led_idle_run!
+ */
+ if (platform_hwversion() == 1)
+ {
+ RCC_CFGR &= ~(0xf << 24);
+ RCC_CFGR |= (RCC_CFGR_MCO_HSECLK << 24);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
+ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO8);
+ }
/* Setup GPIO ports */
- gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
- gpio_set_mode(TCK_PORT, GPIO_MODE_OUTPUT_50_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, TCK_PIN);
- gpio_set_mode(TDI_PORT, GPIO_MODE_OUTPUT_50_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, TDI_PIN);
-
- gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
-
- /* Setup heartbeat timer */
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
- systick_set_reload(900000); /* Interrupt us at 10 Hz */
- SCB_SHPR(11) &= ~((15 << 4) & 0xff);
- SCB_SHPR(11) |= ((14 << 4) & 0xff);
- systick_interrupt_enable();
- systick_counter_enable();
-
- usbuart_init();
-
- SCB_VTOR = 0x2000; // Relocate interrupt vector table here
-
+ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
+ gpio_set_mode(TCK_PORT, GPIO_MODE_OUTPUT_50_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, TCK_PIN);
+ gpio_set_mode(TDI_PORT, GPIO_MODE_OUTPUT_50_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, TDI_PIN);
+ uint16_t srst_pin = platform_hwversion() == 0 ?
+ SRST_PIN_V1 : SRST_PIN_V2;
+ gpio_set(SRST_PORT, srst_pin);
+ gpio_set_mode(SRST_PORT, GPIO_MODE_OUTPUT_50_MHZ,
+ GPIO_CNF_OUTPUT_OPENDRAIN, srst_pin);
+
+ gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ,
+ GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
+
+ SCB_VTOR = 0x2000; /* Relocate interrupt vector table here */
+
+ platform_timing_init();
cdcacm_init();
-
- jtag_scan(NULL);
-
- return 0;
-}
-
-void platform_delay(uint32_t delay)
-{
- timeout_counter = delay;
- while(timeout_counter);
-}
-
-void sys_tick_handler(void)
-{
- if(running_status)
- gpio_toggle(LED_PORT, led_idle_run);
-
- if(timeout_counter)
- timeout_counter--;
+ usbuart_init();
}
-const char *morse_msg;
-
-void morse(const char *msg, char repeat)
+void platform_srst_set_val(bool assert)
{
- (void)repeat;
- morse_msg = msg;
+ uint16_t pin;
+ pin = platform_hwversion() == 0 ? SRST_PIN_V1 : SRST_PIN_V2;
+ if (assert)
+ gpio_clear(SRST_PORT, pin);
+ else
+ gpio_set(SRST_PORT, pin);
}
const char *platform_target_voltage(void)
@@ -148,22 +123,19 @@ const char *platform_target_voltage(void)
return "unknown";
}
-void disconnect_usb(void)
+void platform_request_boot(void)
{
/* Disconnect USB cable by resetting USB Device and pulling USB_DP low*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
- GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
-}
+ GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
-void assert_boot_pin(void)
-{
+ /* Assert bootloader pin */
uint32_t crl = GPIOA_CRL;
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_clock_enable(RCC_GPIOA);
/* Enable Pull on GPIOA1. We don't rely on the external pin
* really pulled, but only on the value of the CNF register
* changed from the reset value
@@ -172,4 +144,4 @@ void assert_boot_pin(void)
crl |= 0x80;
GPIOA_CRL = crl;
}
-void setup_vbus_irq(void){};
+
diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h
index cbfc0ba..498e512 100644
--- a/src/platforms/stlink/platform.h
+++ b/src/platforms/stlink/platform.h
@@ -24,24 +24,20 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <libopencm3/stm32/f1/gpio.h>
-#include <libopencm3/usb/usbd.h>
-
-#include <setjmp.h>
-#include <alloca.h>
+#include "gpio.h"
+#include "timing.h"
+#include "version.h"
-#include "gdb_packet.h"
-
-#define INLINE_GPIO
-#define CDCACM_PACKET_SIZE 64
-#define BOARD_IDENT "Black Magic Probe (STLINK), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define BOARD_IDENT_DFU "Black Magic (Upgrade) for STLink/Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define DFU_IDENT "Black Magic Firmware Upgrade (STLINK)"
-#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/stm32/f1/memorymap.h>
+#include <libopencm3/usb/usbd.h>
-extern usbd_device *usbdev;
-#define CDCACM_GDB_ENDPOINT 1
-#define CDCACM_UART_ENDPOINT 3
+#define BOARD_IDENT "Black Magic Probe (STLINK), (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_DFU "Black Magic (Upgrade) for STLink/Discovery, (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_UPD "Black Magic (DFU Upgrade) for STLink/Discovery, (Firmware " FIRMWARE_VERSION ")"
+#define DFU_IDENT "Black Magic Firmware Upgrade (STLINK)"
+#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
+#define UPD_IFACE_STRING "@Internal Flash /0x08000000/8*001Kg"
/* Important pin mappings for STM32 implementation:
*
@@ -59,7 +55,7 @@ extern usbd_device *usbdev;
* nSRST = PA7 (input)
*
* USB cable pull-up: PA8
- * USB VBUS detect: PB13 -- New on mini design.
+ * USB VBUS detect: PB13 -- New on mini design.
* Enable pull up for compatibility.
* Force DFU mode button: PB12
*/
@@ -79,115 +75,69 @@ extern usbd_device *usbdev;
#define SWDIO_PIN TMS_PIN
#define SWCLK_PIN TCK_PIN
+#define SRST_PORT GPIOB
+#define SRST_PIN_V1 GPIO1
+#define SRST_PIN_V2 GPIO0
+
#define LED_PORT GPIOA
/* Use PC14 for a "dummy" uart led. So we can observere at least with scope*/
#define LED_PORT_UART GPIOC
#define LED_UART GPIO14
-#define TMS_SET_MODE() \
- gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
-#define SWDIO_MODE_FLOAT() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
- GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
-#define SWDIO_MODE_DRIVE() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
+#define TMS_SET_MODE() \
+ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
+#define SWDIO_MODE_FLOAT() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
+ GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
+#define SWDIO_MODE_DRIVE() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
+
+#define UART_PIN_SETUP() \
+ gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
+ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN);
-#define UART_PIN_SETUP() \
- gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
- GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN);
+#define SRST_SET_VAL(x) \
+ platform_srst_set_val(x)
#define USB_DRIVER stm32f103_usb_driver
-#define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ
-#define USB_ISR usb_lp_can_rx0_isr
+#define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ
+#define USB_ISR usb_lp_can_rx0_isr
/* Interrupt priorities. Low numbers are high priority.
* For now USART2 preempts USB which may spin while buffer is drained.
* TIM3 is used for traceswo capture and must be highest priority.
*/
#define IRQ_PRI_USB (2 << 4)
#define IRQ_PRI_USBUSART (1 << 4)
+#define IRQ_PRI_USBUSART_TIM (3 << 4)
#define IRQ_PRI_USB_VBUS (14 << 4)
#define IRQ_PRI_TIM3 (0 << 4)
#define USBUSART USART2
#define USBUSART_CR1 USART2_CR1
#define USBUSART_IRQ NVIC_USART2_IRQ
-#define USBUSART_APB_ENR RCC_APB1ENR
-#define USBUSART_CLK_ENABLE RCC_APB1ENR_USART2EN
+#define USBUSART_CLK RCC_USART2
#define USBUSART_PORT GPIOA
#define USBUSART_TX_PIN GPIO2
#define USBUSART_ISR usart2_isr
+#define USBUSART_TIM TIM4
+#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4)
+#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ
+#define USBUSART_TIM_ISR tim4_isr
#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)
-
extern uint16_t led_idle_run;
+#define LED_IDLE_RUN led_idle_run
#define SET_RUN_STATE(state) {running_status = (state);}
#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, led_idle_run, 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(); \
- longjmp(fatal_error_jmpbuf, (error)); \
-}
-
-int platform_init(void);
-void morse(const char *msg, char repeat);
-const char *platform_target_voltage(void);
-void platform_delay(uint32_t delay);
-
-/* <cdcacm.c> */
-void cdcacm_init(void);
-/* Returns current usb configuration, or 0 if not configured. */
-int cdcacm_get_config(void);
-int cdcacm_get_dtr(void);
-
-/* <platform.h> */
-void uart_usb_buf_drain(uint8_t ep);
+#define SET_ERROR_STATE(x)
/* Use newlib provided integer only stdio functions */
#define sscanf siscanf
#define sprintf siprintf
#define vasprintf vasiprintf
-#ifdef INLINE_GPIO
-static inline void _gpio_set(u32 gpioport, u16 gpios)
-{
- GPIO_BSRR(gpioport) = gpios;
-}
-#define gpio_set _gpio_set
-
-static inline void _gpio_clear(u32 gpioport, u16 gpios)
-{
- GPIO_BRR(gpioport) = gpios;
-}
-#define gpio_clear _gpio_clear
-
-static inline u16 _gpio_get(u32 gpioport, u16 gpios)
-{
- return (u16)GPIO_IDR(gpioport) & gpios;
-}
-#define gpio_get _gpio_get
-#endif
-
#endif
-void disconnect_usb(void);
-void assert_boot_pin(void);
diff --git a/src/platforms/stlink/usbdfu.c b/src/platforms/stlink/usbdfu.c
index f688c1c..b42ec20 100644
--- a/src/platforms/stlink/usbdfu.c
+++ b/src/platforms/stlink/usbdfu.c
@@ -29,24 +29,31 @@ static uint8_t rev;
static uint16_t led_idle_run;
static uint32_t led2_state = 0;
+uint32_t app_address = 0x08002000;
+
static int stlink_test_nrst(void)
{
/* Test if JRST/NRST is pulled down*/
- int i;
uint16_t nrst;
uint16_t pin;
+ uint32_t systick_value;
+
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(0xffffff); /* no underflow for about 16.7 seconds*/
+ systick_counter_enable();
+ /* systick ist now running with 1 MHz, systick counts down */
/* First, get Board revision by pulling PC13/14 up. Read
* 11 for ST-Link V1, e.g. on VL Discovery, tag as rev 0
* 10 for ST-Link V2, e.g. on F4 Discovery, tag as rev 1
*/
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN);
+ rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_mode(GPIOC, GPIO_MODE_INPUT,
- GPIO_CNF_INPUT_PULL_UPDOWN, GPIO14 | GPIO13);
+ GPIO_CNF_INPUT_PULL_UPDOWN, GPIO14 | GPIO13);
gpio_set(GPIOC, GPIO14 | GPIO13);
- for (i = 0; i < 100; i++)
- rev = (~(gpio_get(GPIOC, GPIO14 | GPIO13)) >> 13) & 3;
-
+ systick_value = systick_get_value();
+ while (systick_get_value() > (systick_value - 1000)); /* Wait 1 msec*/
+ rev = (~(gpio_get(GPIOC, GPIO14 | GPIO13)) >> 13) & 3;
switch (rev) {
case 0:
pin = GPIO1;
@@ -58,12 +65,14 @@ static int stlink_test_nrst(void)
}
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
+ rcc_periph_clock_enable(RCC_GPIOB);
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_PULL_UPDOWN, pin);
gpio_set(GPIOB, pin);
- for (i = 0; i < 100; i++)
- nrst = gpio_get(GPIOB, pin);
+ systick_value = systick_get_value();
+ while (systick_get_value() > (systick_value - 20000)); /* Wait 20 msec*/
+ nrst = gpio_get(GPIOB, pin);
+ systick_counter_disable();
return (nrst) ? 1 : 0;
}
@@ -71,10 +80,9 @@ void dfu_detach(void)
{
/* Disconnect USB cable by resetting USB Device
and pulling USB_DP low*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
@@ -84,7 +92,7 @@ void dfu_detach(void)
int main(void)
{
/* Check the force bootloader pin*/
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_clock_enable(RCC_GPIOA);
/* Check value of GPIOA1 configuration. This pin is unconnected on
* STLink V1 and V2. If we have a value other than the reset value (0x4),
* we have a warm start and request Bootloader entry
@@ -92,20 +100,19 @@ int main(void)
if(((GPIOA_CRL & 0x40) == 0x40) && stlink_test_nrst())
dfu_jump_app_if_valid();
- dfu_protect_enable();
+ dfu_protect(DFU_MODE);
rcc_clock_setup_in_hse_8mhz_out_72mhz();
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
systick_set_reload(900000);
- /* Handle USB disconnect/connect */
+ /* Handle USB disconnect/connect */
/* Just in case: Disconnect USB cable by resetting USB Device
- * and pulling USB_DP low
- * Device will reconnect automatically as Pull-Up is hard wired*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ * and pulling USB_DP low
+ * Device will reconnect automatically as Pull-Up is hard wired*/
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
@@ -113,7 +120,7 @@ int main(void)
systick_interrupt_enable();
systick_counter_enable();
- dfu_init(&stm32f103_usb_driver);
+ dfu_init(&stm32f103_usb_driver, DFU_MODE);
dfu_main();
}
diff --git a/src/platforms/stm32/dfu_f1.c b/src/platforms/stm32/dfu_f1.c
index 9d82bec..abbdbe6 100644
--- a/src/platforms/stm32/dfu_f1.c
+++ b/src/platforms/stm32/dfu_f1.c
@@ -16,11 +16,11 @@
* 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 "general.h"
+#include "usbdfu.h"
#include <libopencm3/stm32/f1/flash.h>
#include <libopencm3/cm3/scb.h>
-#include "usbdfu.h"
#define FLASH_OBP_RDP 0x1FFFF800
#define FLASH_OBP_WRP10 0x1FFFF808
@@ -48,7 +48,7 @@ void dfu_flash_program_buffer(uint32_t baseaddr, void *buf, int len)
{
for(int i = 0; i < len; i += 2)
flash_program_half_word(baseaddr + i,
- *(u16*)(buf+i));
+ *(uint16_t*)(buf+i));
}
uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum)
@@ -59,8 +59,10 @@ uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum)
return 100;
}
-void dfu_protect_enable(void)
+void dfu_protect(dfu_mode_t mode)
{
+ if (mode == DFU_MODE) {
+#ifdef DFU_SELF_PROTECT
if ((FLASH_WRPR & 0x03) != 0x00) {
flash_unlock();
FLASH_CR = 0;
@@ -70,19 +72,26 @@ void dfu_protect_enable(void)
/* MD Device: Protect 2 bits with (4 * 1k pages each)*/
flash_program_option_bytes(FLASH_OBP_WRP10, 0x03FC);
}
+#endif
+ }
+ else if (mode == UPD_MODE) {
+ flash_unlock();
+ FLASH_CR = 0;
+ flash_erase_option_bytes();
+ }
}
void dfu_jump_app_if_valid(void)
{
/* Boot the application if it's valid */
- if((*(volatile u32*)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
+ if((*(volatile uint32_t*)app_address & 0x2FFE0000) == 0x20000000) {
/* Set vector table base address */
- SCB_VTOR = APP_ADDRESS & 0x1FFFFF; /* Max 2 MByte Flash*/
+ SCB_VTOR = app_address & 0x1FFFFF; /* Max 2 MByte Flash*/
/* Initialise master stack pointer */
asm volatile ("msr msp, %0"::"g"
- (*(volatile u32*)APP_ADDRESS));
+ (*(volatile uint32_t*)app_address));
/* Jump to application */
- (*(void(**)())(APP_ADDRESS + 4))();
+ (*(void(**)())(app_address + 4))();
}
}
diff --git a/src/platforms/stm32/dfu_f4.c b/src/platforms/stm32/dfu_f4.c
index 4f47c99..2ececa0 100644
--- a/src/platforms/stm32/dfu_f4.c
+++ b/src/platforms/stm32/dfu_f4.c
@@ -16,6 +16,8 @@
* 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 "general.h"
+#include "usbdfu.h"
#if defined(STM32F2)
# include <libopencm3/stm32/f2/flash.h>
@@ -24,26 +26,28 @@
#endif
#include <libopencm3/cm3/scb.h>
-#include "usbdfu.h"
-
-static u32 sector_addr[] = {0x8000000, 0x8004000, 0x8008000, 0x800c000,
- 0x8010000, 0x8020000, 0x8040000, 0x8060000,
- 0x8080000, 0x80a0000, 0x80c0000, 0x80e0000,
- 0x8100000, 0};
-static u16 sector_erase_time[12]= {500, 500, 500, 500,
- 1100,
- 2600, 2600, 2600, 2600, 2600, 2600, 2600};
-static u8 sector_num = 0xff;
+static uint32_t sector_addr[] = {
+ 0x8000000, 0x8004000, 0x8008000, 0x800c000,
+ 0x8010000, 0x8020000, 0x8040000, 0x8060000,
+ 0x8080000, 0x80a0000, 0x80c0000, 0x80e0000,
+ 0x8100000, 0
+};
+static uint16_t sector_erase_time[12]= {
+ 500, 500, 500, 500,
+ 1100, 2600, 2600, 2600,
+ 2600, 2600, 2600, 2600
+};
+static uint8_t sector_num = 0xff;
/* Find the sector number for a given address*/
-static void get_sector_num(u32 addr)
+static void get_sector_num(uint32_t addr)
{
int i = 0;
while(sector_addr[i+1]) {
if (addr < sector_addr[i+1])
break;
i++;
- }
+ }
if (!sector_addr[i])
return;
sector_num = i;
@@ -59,9 +63,8 @@ void dfu_check_and_do_sector_erase(uint32_t addr)
void dfu_flash_program_buffer(uint32_t baseaddr, void *buf, int len)
{
for(int i = 0; i < len; i += 4)
- flash_program_word(baseaddr + i,
- *(u32*)(buf+i),
- FLASH_PROGRAM_X32);
+ flash_program_word(baseaddr + i, *(uint32_t*)(buf+i),
+ FLASH_PROGRAM_X32);
}
uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum)
@@ -80,10 +83,12 @@ uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum)
void dfu_protect_enable(void)
{
+#ifdef DFU_SELF_PROTECT
if ((FLASH_OPTCR & 0x10000) != 0) {
flash_program_option_bytes(FLASH_OPTCR & ~0x10000);
flash_lock_option_bytes();
}
+#endif
}
void dfu_jump_app_if_valid(void)
@@ -91,12 +96,12 @@ void dfu_jump_app_if_valid(void)
/* Boot the application if it's valid */
/* Vector table may be anywhere in 128 kByte RAM
CCM not handled*/
- if((*(volatile u32*)APP_ADDRESS & 0x2FFC0000) == 0x20000000) {
+ if((*(volatile uint32_t*)APP_ADDRESS & 0x2FFC0000) == 0x20000000) {
/* Set vector table base address */
SCB_VTOR = APP_ADDRESS & 0x1FFFFF; /* Max 2 MByte Flash*/
/* Initialise master stack pointer */
asm volatile ("msr msp, %0"::"g"
- (*(volatile u32*)APP_ADDRESS));
+ (*(volatile uint32_t*)APP_ADDRESS));
/* Jump to application */
(*(void(**)())(APP_ADDRESS + 4))();
}
diff --git a/src/platforms/stm32/dfucore.c b/src/platforms/stm32/dfucore.c
index c1d5cfd..7b733d7 100644
--- a/src/platforms/stm32/dfucore.c
+++ b/src/platforms/stm32/dfucore.c
@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "platform.h"
+#include "general.h"
#include <string.h>
#if defined(STM32F1)
@@ -34,36 +34,36 @@
usbd_device *usbdev;
/* We need a special large control buffer for this device: */
-u8 usbd_control_buffer[1024];
+uint8_t usbd_control_buffer[1024];
-static u32 max_address;
+static uint32_t max_address;
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;
+ uint8_t buf[sizeof(usbd_control_buffer)];
+ uint16_t len;
+ uint32_t addr;
+ uint16_t 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 = 0x1D50,
- .idProduct = 0x6017,
- .bcdDevice = 0x0100,
- .iManufacturer = 1,
- .iProduct = 2,
- .iSerialNumber = 3,
- .bNumConfigurations = 1,
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = 0x1D50,
+ .idProduct = 0x6017,
+ .bcdDevice = 0x0100,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 1,
};
const struct usb_dfu_descriptor dfu_function = {
@@ -121,13 +121,27 @@ static const char *usb_strings[] = {
DFU_IFACE_STRING,
};
-static u8 usbdfu_getstatus(u32 *bwPollTimeout)
+static const char *usb_strings_upd[] = {
+ "Black Sphere Technologies",
+ BOARD_IDENT_UPD,
+ serial_no,
+ /* This string is used by ST Microelectronics' DfuSe utility */
+ UPD_IFACE_STRING,
+};
+
+static uint32_t get_le32(const void *vp)
+{
+ const uint8_t *p = vp;
+ return ((uint32_t)p[3] << 24) + ((uint32_t)p[2] << 16) + (p[1] << 8) + p[0];
+}
+
+static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout)
{
switch(usbdfu_state) {
case STATE_DFU_DNLOAD_SYNC:
usbdfu_state = STATE_DFU_DNBUSY;
*bwPollTimeout = dfu_poll_timeout(prog.buf[0],
- *(u32 *)(prog.buf + 1),
+ get_le32(prog.buf + 1),
prog.blocknum);
return DFU_STATUS_OK;
@@ -151,9 +165,8 @@ usbdfu_getstatus_complete(usbd_device *dev, struct usb_setup_data *req)
flash_unlock();
if(prog.blocknum == 0) {
- u32 addr = *(u32 *)(prog.buf + 1);
- if (addr < APP_ADDRESS ||
- (addr >= max_address)) {
+ uint32_t addr = get_le32(prog.buf + 1);
+ if ((addr < app_address) || (addr >= max_address)) {
flash_lock();
usbd_ep_stall_set(dev, 0, 1);
return;
@@ -165,7 +178,7 @@ usbdfu_getstatus_complete(usbd_device *dev, struct usb_setup_data *req)
prog.addr = addr;
}
} else {
- u32 baseaddr = prog.addr +
+ uint32_t baseaddr = prog.addr +
((prog.blocknum - 2) *
dfu_function.wTransferSize);
dfu_flash_program_buffer(baseaddr, prog.buf, prog.len);
@@ -187,7 +200,7 @@ usbdfu_getstatus_complete(usbd_device *dev, struct usb_setup_data *req)
}
static int usbdfu_control_request(usbd_device *dev,
- struct usb_setup_data *req, u8 **buf, u16 *len,
+ struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
void (**complete)(usbd_device *dev, struct usb_setup_data *req))
{
(void)dev;
@@ -221,7 +234,7 @@ static int usbdfu_control_request(usbd_device *dev,
/* Upload not supported for now */
return 0;
case DFU_GETSTATUS: {
- u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
+ uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
(*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
(*buf)[1] = bwPollTimeout & 0xFF;
@@ -245,12 +258,14 @@ static int usbdfu_control_request(usbd_device *dev,
return 0;
}
-void dfu_init(const usbd_driver *driver)
+void dfu_init(const usbd_driver *driver, dfu_mode_t mode)
{
get_dev_unique_id(serial_no);
- usbdev = usbd_init(driver, &dev, &config, usb_strings, 4);
- usbd_set_control_buffer_size(usbdev, sizeof(usbd_control_buffer));
+ usbdev = usbd_init(driver, &dev, &config,
+ (mode == DFU_MODE)?usb_strings:usb_strings_upd, 4,
+ usbd_control_buffer, sizeof(usbd_control_buffer));
+
usbd_register_control_callback(usbdev,
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
@@ -266,34 +281,34 @@ void dfu_main(void)
static char *get_dev_unique_id(char *s)
{
#if defined(STM32F4) || defined(STM32F2)
-#define UNIQUE_SERIAL_R 0x1FFF7A10
-#define FLASH_SIZE_R 0x1fff7A22
+# define UNIQUE_SERIAL_R 0x1FFF7A10
+# define FLASH_SIZE_R 0x1fff7A22
#elif defined(STM32F3)
-#define UNIQUE_SERIAL_R 0x1FFFF7AC
-#define FLASH_SIZE_R 0x1fff77cc
+# define UNIQUE_SERIAL_R 0x1FFFF7AC
+# define FLASH_SIZE_R 0x1fff77cc
#elif defined(STM32L1)
-#define UNIQUE_SERIAL_R 0x1ff80050
-#define FLASH_SIZE_R 0x1FF8004C
+# define UNIQUE_SERIAL_R 0x1ff80050
+# define FLASH_SIZE_R 0x1FF8004C
#else
-#define UNIQUE_SERIAL_R 0x1FFFF7E8;
-#define FLASH_SIZE_R 0x1ffff7e0
+# define UNIQUE_SERIAL_R 0x1FFFF7E8;
+# define FLASH_SIZE_R 0x1ffff7e0
#endif
- volatile uint32_t *unique_id_p = (volatile uint32_t *)UNIQUE_SERIAL_R;
+ volatile uint32_t *unique_id_p = (volatile uint32_t *)UNIQUE_SERIAL_R;
uint32_t unique_id = *unique_id_p +
*(unique_id_p + 1) +
*(unique_id_p + 2);
- int i;
-
- /* Calculated the upper flash limit from the exported data
- in theparameter block*/
- max_address = (*(u32 *) FLASH_SIZE_R) <<10;
- /* Fetch serial number from chip's unique ID */
- for(i = 0; i < 8; i++) {
- s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
- }
- for(i = 0; i < 8; i++)
- if(s[i] > '9')
- s[i] += 'A' - '9' - 1;
+ int i;
+
+ /* Calculated the upper flash limit from the exported data
+ in theparameter block*/
+ max_address = (*(uint32_t *) FLASH_SIZE_R) <<10;
+ /* Fetch serial number from chip's unique ID */
+ for(i = 0; i < 8; i++) {
+ s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
+ }
+ for(i = 0; i < 8; i++)
+ if(s[i] > '9')
+ s[i] += 'A' - '9' - 1;
s[8] = 0;
return s;
diff --git a/src/platforms/stm32/gdb_if.c b/src/platforms/stm32/gdb_if.c
index 3ea7d14..52922e7 100644
--- a/src/platforms/stm32/gdb_if.c
+++ b/src/platforms/stm32/gdb_if.c
@@ -22,18 +22,19 @@
* 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 <libopencm3/usb/usbd.h>
-
+#include "general.h"
+#include "cdcacm.h"
#include "gdb_if.h"
static uint32_t count_out;
-static uint32_t count_new;
static uint32_t count_in;
static uint32_t out_ptr;
static uint8_t buffer_out[CDCACM_PACKET_SIZE];
-static uint8_t double_buffer_out[CDCACM_PACKET_SIZE];
static uint8_t buffer_in[CDCACM_PACKET_SIZE];
+#ifdef STM32F4
+static volatile uint32_t count_new;
+static uint8_t double_buffer_out[CDCACM_PACKET_SIZE];
+#endif
void gdb_if_putchar(unsigned char c, int flush)
{
@@ -47,37 +48,64 @@ void gdb_if_putchar(unsigned char c, int flush)
}
while(usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT,
buffer_in, count_in) <= 0);
+
+ if (flush && (count_in == CDCACM_PACKET_SIZE)) {
+ /* We need to send an empty packet for some hosts
+ * to accept this as a complete transfer. */
+ /* libopencm3 needs a change for us to confirm when
+ * that transfer is complete, so we just send a packet
+ * containing a null byte for now.
+ */
+ while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT,
+ "\0", 1) <= 0);
+ }
+
count_in = 0;
}
}
+#ifdef STM32F4
void gdb_usb_out_cb(usbd_device *dev, uint8_t ep)
{
(void)ep;
usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1);
- count_new = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT,
- double_buffer_out, CDCACM_PACKET_SIZE);
+ count_new = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT,
+ double_buffer_out, CDCACM_PACKET_SIZE);
if(!count_new) {
usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 0);
}
}
+#endif
+
+static void gdb_if_update_buf(void)
+{
+ while (cdcacm_get_config() != 1);
+#ifdef STM32F4
+ asm volatile ("cpsid i; isb");
+ if (count_new) {
+ memcpy(buffer_out, double_buffer_out, count_new);
+ count_out = count_new;
+ count_new = 0;
+ out_ptr = 0;
+ usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0);
+ }
+ asm volatile ("cpsie i; isb");
+#else
+ count_out = usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT,
+ buffer_out, CDCACM_PACKET_SIZE);
+ out_ptr = 0;
+#endif
+}
unsigned char gdb_if_getchar(void)
{
- while(!(out_ptr < count_out)) {
+ while (!(out_ptr < count_out)) {
/* Detach if port closed */
- if(!cdcacm_get_dtr())
+ if (!cdcacm_get_dtr())
return 0x04;
- while(cdcacm_get_config() != 1);
- if (count_new) {
- memcpy(buffer_out, double_buffer_out,count_new);
- count_out = count_new;
- count_new = 0;
- out_ptr = 0;
- usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0);
- }
+ gdb_if_update_buf();
}
return buffer_out[out_ptr++];
@@ -85,22 +113,15 @@ unsigned char gdb_if_getchar(void)
unsigned char gdb_if_getchar_to(int timeout)
{
- timeout_counter = timeout/100;
+ platform_timeout_set(timeout);
- if(!(out_ptr < count_out)) do {
+ if (!(out_ptr < count_out)) do {
/* Detach if port closed */
- if(!cdcacm_get_dtr())
+ if (!cdcacm_get_dtr())
return 0x04;
- while(cdcacm_get_config() != 1);
- if (count_new) {
- memcpy(buffer_out, double_buffer_out,count_new);
- count_out = count_new;
- count_new = 0;
- out_ptr = 0;
- usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0);
- }
- } while(timeout_counter && !(out_ptr < count_out));
+ gdb_if_update_buf();
+ } while (!platform_timeout_is_expired() && !(out_ptr < count_out));
if(out_ptr < count_out)
return gdb_if_getchar();
diff --git a/src/platforms/stm32/gpio.h b/src/platforms/stm32/gpio.h
new file mode 100644
index 0000000..d39fd4f
--- /dev/null
+++ b/src/platforms/stm32/gpio.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __GPIO_H
+#define __GPIO_H
+
+#include <libopencm3/cm3/common.h>
+
+#ifndef STM32F4
+# include <libopencm3/stm32/f1/memorymap.h>
+# include <libopencm3/stm32/f1/gpio.h>
+#else
+# include <libopencm3/stm32/f4/memorymap.h>
+# include <libopencm3/stm32/f4/gpio.h>
+#endif
+
+#define INLINE_GPIO
+
+#define gpio_set_val(port, pin, val) do { \
+ if(val) \
+ gpio_set((port), (pin)); \
+ else \
+ gpio_clear((port), (pin)); \
+} while(0)
+
+#ifdef INLINE_GPIO
+static inline void _gpio_set(uint32_t gpioport, uint16_t gpios)
+{
+ GPIO_BSRR(gpioport) = gpios;
+#ifdef STM32F4
+ GPIO_BSRR(gpioport) = gpios;
+#endif
+}
+#define gpio_set _gpio_set
+
+static inline void _gpio_clear(uint32_t gpioport, uint16_t gpios)
+{
+#ifndef STM32F4
+ GPIO_BRR(gpioport) = gpios;
+#else
+ GPIO_BSRR(gpioport) = gpios<<16;
+ GPIO_BSRR(gpioport) = gpios<<16;
+#endif
+}
+#define gpio_clear _gpio_clear
+
+static inline uint16_t _gpio_get(uint32_t gpioport, uint16_t gpios)
+{
+ return (uint16_t)GPIO_IDR(gpioport) & gpios;
+}
+#define gpio_get _gpio_get
+#endif
+
+#endif
+
diff --git a/src/platforms/stm32/jtagtap.c b/src/platforms/stm32/jtagtap.c
index d6e298b..3c45a81 100644
--- a/src/platforms/stm32/jtagtap.c
+++ b/src/platforms/stm32/jtagtap.c
@@ -23,9 +23,7 @@
#include <stdio.h>
#include "general.h"
-
#include "jtagtap.h"
-#include "platform.h"
int jtagtap_init(void)
{
@@ -42,21 +40,26 @@ int jtagtap_init(void)
void jtagtap_reset(void)
{
#ifdef TRST_PORT
- volatile int i;
- gpio_clear(TRST_PORT, TRST_PIN);
- for(i = 0; i < 10000; i++) asm("nop");
- gpio_set(TRST_PORT, TRST_PIN);
+ if (platform_hwversion() == 0) {
+ volatile int i;
+ gpio_clear(TRST_PORT, TRST_PIN);
+ for(i = 0; i < 10000; i++) asm("nop");
+ gpio_set(TRST_PORT, TRST_PIN);
+ }
#endif
jtagtap_soft_reset();
}
-void jtagtap_srst(void)
+void jtagtap_srst(bool assert)
{
-#ifdef SRST_PORT
- volatile int i;
- gpio_set(SRST_PORT, SRST_PIN);
- for(i = 0; i < 10000; i++) asm("nop");
- gpio_clear(SRST_PORT, SRST_PIN);
+ (void)assert;
+#ifdef SRST_SET_VAL
+ SRST_SET_VAL(assert);
+ if(assert) {
+ int i;
+ for(i = 0; i < 10000; i++)
+ asm volatile("nop");
+ }
#endif
}
diff --git a/src/platforms/stm32/serialno.c b/src/platforms/stm32/serialno.c
new file mode 100644
index 0000000..ef28ed2
--- /dev/null
+++ b/src/platforms/stm32/serialno.c
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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/>.
+ */
+#include "general.h"
+
+char *serialno_read(char *s)
+{
+#if defined(STM32F4)
+ volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFF7A10;
+#else
+ volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8;
+#endif
+ uint32_t unique_id = *unique_id_p +
+ *(unique_id_p + 1) +
+ *(unique_id_p + 2);
+ int i;
+
+ /* Fetch serial number from chip's unique ID */
+ for(i = 0; i < 8; i++) {
+ s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
+ }
+ for(i = 0; i < 8; i++)
+ if(s[i] > '9')
+ s[i] += 'A' - '9' - 1;
+ s[8] = 0;
+
+ return s;
+}
+
diff --git a/src/platforms/stm32/swdptap.c b/src/platforms/stm32/swdptap.c
index 72fb0f9..e7049d3 100644
--- a/src/platforms/stm32/swdptap.c
+++ b/src/platforms/stm32/swdptap.c
@@ -73,9 +73,9 @@ 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_seq_out(0xE79E, 16); /* 0b0111100111100111 */
swdptap_reset();
- swdptap_seq_out(0, 16);
+ swdptap_seq_out(0, 16);
return 0;
}
@@ -132,7 +132,7 @@ void swdptap_seq_out(uint32_t MS, int ticks)
while(ticks--) {
swdptap_bit_out(MS & 1);
- MS >>= 1;
+ MS >>= 1;
}
}
@@ -146,7 +146,7 @@ void swdptap_seq_out_parity(uint32_t MS, int ticks)
while(ticks--) {
swdptap_bit_out(MS & 1);
parity ^= MS;
- MS >>= 1;
+ MS >>= 1;
}
swdptap_bit_out(parity & 1);
}
diff --git a/src/platforms/stm32/timing.c b/src/platforms/stm32/timing.c
new file mode 100644
index 0000000..55a217a
--- /dev/null
+++ b/src/platforms/stm32/timing.c
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 "general.h"
+#include "morse.h"
+
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/cm3/scb.h>
+
+uint8_t running_status;
+
+static volatile uint32_t timeout_counter;
+
+void platform_timing_init(void)
+{
+ /* Setup heartbeat timer */
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
+ systick_set_reload(900000); /* Interrupt us at 10 Hz */
+ SCB_SHPR(11) &= ~((15 << 4) & 0xff);
+ SCB_SHPR(11) |= ((14 << 4) & 0xff);
+ systick_interrupt_enable();
+ systick_counter_enable();
+}
+
+void platform_timeout_set(uint32_t ms)
+{
+ timeout_counter = ms / 100;
+}
+
+bool platform_timeout_is_expired(void)
+{
+ return timeout_counter == 0;
+}
+
+void platform_delay(uint32_t delay)
+{
+ platform_timeout_set(delay);
+ while (platform_timeout_is_expired());
+}
+
+void sys_tick_handler(void)
+{
+ if(running_status)
+ gpio_toggle(LED_PORT, LED_IDLE_RUN);
+
+ if(timeout_counter)
+ timeout_counter--;
+
+ SET_ERROR_STATE(morse_update());
+}
+
diff --git a/src/platforms/stm32/timing.h b/src/platforms/stm32/timing.h
new file mode 100644
index 0000000..0178ff5
--- /dev/null
+++ b/src/platforms/stm32/timing.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2015 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 __TIMING_H
+#define __TIMING_H
+
+extern uint8_t running_status;
+
+void platform_timing_init(void);
+
+#endif
+
diff --git a/src/platforms/stm32/traceswo.c b/src/platforms/stm32/traceswo.c
index 01ad728..a2e704d 100644
--- a/src/platforms/stm32/traceswo.c
+++ b/src/platforms/stm32/traceswo.c
@@ -32,16 +32,12 @@
* The core can then process the buffer to extract the frame.
*/
#include "general.h"
+#include "cdcacm.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/f1/rcc.h>
-#include <libopencm3/usb/usbd.h>
-
-#include <string.h>
-#include "platform.h"
-
void traceswo_init(void)
{
TRACE_TIM_CLK_EN();
diff --git a/src/platforms/stm32/usbdfu.h b/src/platforms/stm32/usbdfu.h
index d331ca4..e460082 100644
--- a/src/platforms/stm32/usbdfu.h
+++ b/src/platforms/stm32/usbdfu.h
@@ -19,25 +19,25 @@
#include <libopencm3/usb/usbd.h>
-#ifdef STM32F4
-# define APP_ADDRESS 0x08010000
-#else
-# define APP_ADDRESS 0x08002000
-#endif
-
/* Commands sent with wBlockNum == 0 as per ST implementation. */
#define CMD_SETADDR 0x21
#define CMD_ERASE 0x41
+extern uint32_t app_address;
+
+typedef enum {
+ DFU_MODE = 0,
+ UPD_MODE = 1
+} dfu_mode_t;
/* dfucore.c - DFU core, common to libopencm3 platforms. */
-void dfu_init(const usbd_driver *driver);
+void dfu_init(const usbd_driver *driver, dfu_mode_t mode);
void dfu_main(void);
/* Device specific functions */
void dfu_check_and_do_sector_erase(uint32_t sector);
void dfu_flash_program_buffer(uint32_t baseaddr, void *buf, int len);
uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum);
-void dfu_protect_enable(void);
+void dfu_protect(dfu_mode_t mode);
void dfu_jump_app_if_valid(void);
/* Platform specific function */
diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c
index 1c71105..cce2904 100644
--- a/src/platforms/stm32/usbuart.c
+++ b/src/platforms/stm32/usbuart.c
@@ -21,12 +21,28 @@
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/timer.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/scs.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
-#include <platform.h>
+#include "general.h"
+#include "cdcacm.h"
+
+#define USBUART_TIMER_FREQ_HZ 1000000U /* 1us per tick */
+#define USBUART_RUN_FREQ_HZ 5000U /* 200us (or 100 characters at 2Mbps) */
+
+#define FIFO_SIZE 128
+
+/* RX Fifo buffer */
+static uint8_t buf_rx[FIFO_SIZE];
+/* Fifo in pointer, writes assumed to be atomic, should be only incremented within RX ISR */
+static uint8_t buf_rx_in;
+/* Fifo out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */
+static uint8_t buf_rx_out;
+
+static void usbuart_run(void);
void usbuart_init(void)
{
@@ -37,7 +53,7 @@ void usbuart_init(void)
return;
#endif
- rcc_peripheral_enable_clock(&USBUSART_APB_ENR, USBUSART_CLK_ENABLE);
+ rcc_periph_clock_enable(USBUSART_CLK);
UART_PIN_SETUP();
@@ -56,12 +72,79 @@ void usbuart_init(void)
USBUSART_CR1 |= USART_CR1_RXNEIE;
nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART);
nvic_enable_irq(USBUSART_IRQ);
+
+ /* Setup timer for running deferred FIFO processing */
+ USBUSART_TIM_CLK_EN();
+ timer_reset(USBUSART_TIM);
+ timer_set_mode(USBUSART_TIM, TIM_CR1_CKD_CK_INT,
+ TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+ timer_set_prescaler(USBUSART_TIM,
+ rcc_ppre2_frequency / USBUART_TIMER_FREQ_HZ * 2 - 1);
+ timer_set_period(USBUSART_TIM,
+ USBUART_TIMER_FREQ_HZ / USBUART_RUN_FREQ_HZ - 1);
+
+ /* Setup update interrupt in NVIC */
+ nvic_set_priority(USBUSART_TIM_IRQ, IRQ_PRI_USBUSART_TIM);
+ nvic_enable_irq(USBUSART_TIM_IRQ);
+
+ /* turn the timer on */
+ timer_enable_counter(USBUSART_TIM);
+}
+
+/*
+ * Runs deferred processing for usb uart rx, draining RX FIFO by sending
+ * characters to host PC via CDCACM. Allowed to read from FIFO in pointer,
+ * but not write to it. Allowed to write to FIFO out pointer.
+ */
+static void usbuart_run(void)
+{
+ /* forcibly empty fifo if no USB endpoint */
+ if (cdcacm_get_config() != 1)
+ {
+ buf_rx_out = buf_rx_in;
+ }
+
+ /* if fifo empty, nothing further to do */
+ if (buf_rx_in == buf_rx_out) {
+ /* turn off LED, disable IRQ */
+ timer_disable_irq(USBUSART_TIM, TIM_DIER_UIE);
+ gpio_clear(LED_PORT_UART, LED_UART);
+ }
+ else
+ {
+ uint8_t packet_buf[CDCACM_PACKET_SIZE];
+ uint8_t packet_size = 0;
+ uint8_t buf_out = buf_rx_out;
+
+ /* copy from uart FIFO into local usb packet buffer */
+ while (buf_rx_in != buf_out && packet_size < CDCACM_PACKET_SIZE)
+ {
+ packet_buf[packet_size++] = buf_rx[buf_out++];
+
+ /* wrap out pointer */
+ if (buf_out >= FIFO_SIZE)
+ {
+ buf_out = 0;
+ }
+
+ }
+
+ /* advance fifo out pointer by amount written */
+ buf_rx_out += usbd_ep_write_packet(usbdev,
+ CDCACM_UART_ENDPOINT, packet_buf, packet_size);
+ buf_rx_out %= FIFO_SIZE;
+ }
}
void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
{
usart_set_baudrate(USBUSART, coding->dwDTERate);
- usart_set_databits(USBUSART, coding->bDataBits);
+
+ if (coding->bParityType)
+ usart_set_databits(USBUSART, coding->bDataBits + 1);
+ else
+ usart_set_databits(USBUSART, coding->bDataBits);
+
switch(coding->bCharFormat) {
case 0:
usart_set_stopbits(USBUSART, USART_STOPBITS_1);
@@ -73,6 +156,7 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
usart_set_stopbits(USBUSART, USART_STOPBITS_2);
break;
}
+
switch(coding->bParityType) {
case 0:
usart_set_parity(USBUSART, USART_PARITY_NONE);
@@ -108,41 +192,50 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
gpio_clear(LED_PORT_UART, LED_UART);
}
-static uint8_t uart_usb_buf[CDCACM_PACKET_SIZE];
-static uint8_t uart_usb_buf_size;
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
{
- if (!uart_usb_buf_size) {
- gpio_clear(LED_PORT_UART, LED_UART);
- return;
- }
-
- usbd_ep_write_packet(dev, ep, uart_usb_buf, uart_usb_buf_size);
- uart_usb_buf_size = 0;
+ (void) dev;
+ (void) ep;
}
+/*
+ * Read a character from the UART RX and stuff it in a software FIFO.
+ * Allowed to read from FIFO out pointer, but not write to it.
+ * Allowed to write to FIFO in pointer.
+ */
void USBUSART_ISR(void)
{
char c = usart_recv(USBUSART);
- /* Don't try to write until we are configured.
- * Otherwise enumeration hanged in some cases.
- */
- if (cdcacm_get_config() != 1)
- return;
-
+ /* Turn on LED */
gpio_set(LED_PORT_UART, LED_UART);
- /* Try to send now */
- if (usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, &c, 1) == 1)
- return;
-
- /* We failed, so queue for later */
- if (uart_usb_buf_size == CDCACM_PACKET_SIZE) {
- /* Drop if the buffer's full: we have no flow control */
- return;
+ /* If the next increment of rx_in would put it at the same point
+ * as rx_out, the FIFO is considered full.
+ */
+ if (((buf_rx_in + 1) % FIFO_SIZE) != buf_rx_out)
+ {
+ /* insert into FIFO */
+ buf_rx[buf_rx_in++] = c;
+
+ /* wrap out pointer */
+ if (buf_rx_in >= FIFO_SIZE)
+ {
+ buf_rx_in = 0;
+ }
+
+ /* enable deferred processing if we put data in the FIFO */
+ timer_enable_irq(USBUSART_TIM, TIM_DIER_UIE);
}
+}
+
+void USBUSART_TIM_ISR(void)
+{
+ /* need to clear timer update event */
+ timer_clear_flag(USBUSART_TIM, TIM_SR_UIF);
- uart_usb_buf[uart_usb_buf_size++] = c;
+ /* process FIFO */
+ usbuart_run();
}
+
diff --git a/src/platforms/swlink/Makefile.inc b/src/platforms/swlink/Makefile.inc
index df0b648..62f90bb 100644
--- a/src/platforms/swlink/Makefile.inc
+++ b/src/platforms/swlink/Makefile.inc
@@ -2,10 +2,12 @@ CROSS_COMPILE ?= arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
+OPT_FLAGS = -Os
CFLAGS += -mcpu=cortex-m3 -mthumb \
-DSTM32F1 -DDISCOVERY_SWLINK -I../libopencm3/include \
-I platforms/stm32
-LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
+LDFLAGS_BOOT := $(LDFLAGS) --specs=nano.specs \
+ -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \
-Wl,-T,platforms/stm32/stlink.ld -nostartfiles -lc -lnosys \
-Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections \
-L../libopencm3/lib
@@ -14,23 +16,16 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
VPATH += platforms/stm32
SRC += cdcacm.c \
- platform.c \
- usbuart.c \
+ usbuart.c \
+ serialno.c \
+ timing.c \
all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex
-blackmagic.bin: blackmagic
- $(OBJCOPY) -O binary $^ $@
-
blackmagic_dfu: usbdfu.o dfucore.o dfu_f1.o
- $(CC) $^ -o $@ $(LDFLAGS_BOOT)
-
-blackmagic_dfu.bin: blackmagic_dfu
- $(OBJCOPY) -O binary $^ $@
-
-blackmagic_dfu.hex: blackmagic_dfu
- $(OBJCOPY) -O ihex $^ $@
+ @echo " LD $@"
+ $(Q)$(CC) $^ -o $@ $(LDFLAGS_BOOT)
host_clean:
- -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
+ -$(Q)$(RM) blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex
diff --git a/src/platforms/swlink/platform.c b/src/platforms/swlink/platform.c
index 33d4f12..02cde19 100644
--- a/src/platforms/swlink/platform.c
+++ b/src/platforms/swlink/platform.c
@@ -22,41 +22,34 @@
* implementation.
*/
+#include "general.h"
+#include "cdcacm.h"
+#include "usbuart.h"
+
#include <libopencm3/stm32/f1/rcc.h>
-#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/stm32/f1/adc.h>
-#include "platform.h"
-#include "jtag_scan.h"
-#include <usbuart.h>
-
-#include <ctype.h>
-
-uint8_t running_status;
-volatile uint32_t timeout_counter;
-
-jmp_buf fatal_error_jmpbuf;
-
-int platform_init(void)
+void platform_init(void)
{
uint32_t data;
rcc_clock_setup_in_hse_8mhz_out_72mhz();
/* Enable peripherals */
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- 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_AFIOEN);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOB);
+ rcc_periph_clock_enable(RCC_AFIO);
+ rcc_periph_clock_enable(RCC_CRC);
/* Unmap JTAG Pins so we can reuse as GPIO */
- data = AFIO_MAPR;
- data &= ~AFIO_MAPR_SWJ_MASK;
- data |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF;
- AFIO_MAPR = data;
+ data = AFIO_MAPR;
+ data &= ~AFIO_MAPR_SWJ_MASK;
+ data |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF;
+ AFIO_MAPR = data;
/* Setup JTAG GPIO ports */
gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_10_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
@@ -68,62 +61,27 @@ int platform_init(void)
gpio_set_mode(TDO_PORT, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, TDO_PIN);
- gpio_set(NRST_PORT,NRST_PIN);
+ gpio_set(NRST_PORT,NRST_PIN);
gpio_set_mode(NRST_PORT, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_PULL_UPDOWN, NRST_PIN);
gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, led_idle_run);
-
- /* Remap TIM2 TIM2_REMAP[1]
- * TIM2_CH1_ETR -> PA15 (TDI, set as output above)
- * TIM2_CH2 -> PB3 (TDO)
- */
- data = AFIO_MAPR;
- data &= ~AFIO_MAPR_TIM2_REMAP_FULL_REMAP;
- data |= AFIO_MAPR_TIM2_REMAP_PARTIAL_REMAP1;
- AFIO_MAPR = data;
-
- /* Setup heartbeat timer */
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
- systick_set_reload(900000); /* Interrupt us at 10 Hz */
- SCB_SHPR(11) &= ~((15 << 4) & 0xff);
- SCB_SHPR(11) |= ((14 << 4) & 0xff);
- systick_interrupt_enable();
- systick_counter_enable();
+ GPIO_CNF_OUTPUT_PUSHPULL, LED_IDLE_RUN);
- usbuart_init();
+ /* Remap TIM2 TIM2_REMAP[1]
+ * TIM2_CH1_ETR -> PA15 (TDI, set as output above)
+ * TIM2_CH2 -> PB3 (TDO)
+ */
+ data = AFIO_MAPR;
+ data &= ~AFIO_MAPR_TIM2_REMAP_FULL_REMAP;
+ data |= AFIO_MAPR_TIM2_REMAP_PARTIAL_REMAP1;
+ AFIO_MAPR = data;
SCB_VTOR = 0x2000; // Relocate interrupt vector table here
+ platform_timing_init();
cdcacm_init();
-
- jtag_scan(NULL);
-
- return 0;
-}
-
-void platform_delay(uint32_t delay)
-{
- timeout_counter = delay;
- while(timeout_counter);
-}
-
-void sys_tick_handler(void)
-{
- if(running_status)
- gpio_toggle(LED_PORT, led_idle_run);
-
- if(timeout_counter)
- timeout_counter--;
-}
-
-const char *morse_msg;
-
-void morse(const char *msg, char repeat)
-{
- (void)repeat;
- morse_msg = msg;
+ usbuart_init();
}
const char *platform_target_voltage(void)
@@ -131,22 +89,19 @@ const char *platform_target_voltage(void)
return "unknown";
}
-void disconnect_usb(void)
+void platform_request_boot(void)
{
/* Disconnect USB cable by resetting USB Device and pulling USB_DP low*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
-}
-void assert_boot_pin(void)
-{
+ /* Assert bootloader pin */
uint32_t crl = GPIOA_CRL;
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_clock_enable(RCC_GPIOA);
/* Enable Pull on GPIOA1. We don't rely on the external pin
* really pulled, but only on the value of the CNF register
* changed from the reset value
@@ -155,4 +110,4 @@ void assert_boot_pin(void)
crl |= 0x80;
GPIOA_CRL = crl;
}
-void setup_vbus_irq(void){};
+
diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h
index 469499f..e31f543 100644
--- a/src/platforms/swlink/platform.h
+++ b/src/platforms/swlink/platform.h
@@ -24,24 +24,16 @@
#ifndef __PLATFORM_H
#define __PLATFORM_H
-#include <libopencm3/stm32/f1/gpio.h>
-#include <libopencm3/usb/usbd.h>
+#include "gpio.h"
+#include "timing.h"
+#include "version.h"
-#include <setjmp.h>
-#include <alloca.h>
-
-#include "gdb_packet.h"
-
-#define INLINE_GPIO
-#define CDCACM_PACKET_SIZE 64
-#define BOARD_IDENT "Black Magic Probe (SWLINK), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define BOARD_IDENT_DFU "Black Magic (Upgrade), STM8S Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")"
-#define DFU_IDENT "Black Magic Firmware Upgrade (SWLINK)"
-#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
-
-extern usbd_device *usbdev;
-#define CDCACM_GDB_ENDPOINT 1
-#define CDCACM_UART_ENDPOINT 3
+#define BOARD_IDENT "Black Magic Probe (SWLINK), (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_DFU "Black Magic (Upgrade), STM8S Discovery, (Firmware " FIRMWARE_VERSION ")"
+#define BOARD_IDENT_UPD "Black Magic (DFU Upgrade), STM8S Discovery, (Firmware " FIRMWARE_VERSION ")"
+#define DFU_IDENT "Black Magic Firmware Upgrade (SWLINK)"
+#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
+#define UPD_IFACE_STRING "@Internal Flash /0x08000000/8*001Kg"
/* Pin mappings:
*
@@ -53,7 +45,7 @@ extern usbd_device *usbdev;
* nSRST = PA7 (input)
*
* USB cable pull-up: PA8
- * USB VBUS detect: PB13 -- New on mini design.
+ * USB VBUS detect: PB13 -- New on mini design.
* Enable pull up for compatibility.
* Force DFU mode button: PB9 Low
*/
@@ -76,50 +68,54 @@ extern usbd_device *usbdev;
#define SWCLK_PIN TCK_PIN
#define LED_PORT GPIOA
+#define LED_IDLE_RUN GPIO8
/* Use PC14 for a "dummy" uart led. So we can observere at least with scope*/
#define LED_PORT_UART GPIOC
#define LED_UART GPIO14
-#define TMS_SET_MODE() \
- gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
-#define SWDIO_MODE_FLOAT() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
- GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
-#define SWDIO_MODE_DRIVE() \
- gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
- GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
-
-#define UART_PIN_SETUP() do \
- { \
- AFIO_MAPR |= AFIO_MAPR_USART1_REMAP; \
- gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
- GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); \
- } while (0)
+#define TMS_SET_MODE() \
+ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
+#define SWDIO_MODE_FLOAT() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_INPUT, \
+ GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
+#define SWDIO_MODE_DRIVE() \
+ gpio_set_mode(SWDIO_PORT, GPIO_MODE_OUTPUT_50_MHZ, \
+ GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
+
+#define UART_PIN_SETUP() do { \
+ AFIO_MAPR |= AFIO_MAPR_USART1_REMAP; \
+ gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \
+ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); \
+} while (0)
#define USB_DRIVER stm32f103_usb_driver
#define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ
-#define USB_ISR usb_lp_can_rx0_isr
+#define USB_ISR usb_lp_can_rx0_isr
/* Interrupt priorities. Low numbers are high priority.
* For now USART1 preempts USB which may spin while buffer is drained.
* TIM2 is used for traceswo capture and must be highest priority.
*/
#define IRQ_PRI_USB (2 << 4)
#define IRQ_PRI_USBUSART (1 << 4)
+#define IRQ_PRI_USBUSART_TIM (3 << 4)
#define IRQ_PRI_USB_VBUS (14 << 4)
#define IRQ_PRI_TRACE (0 << 4)
#define USBUSART USART1
#define USBUSART_CR1 USART1_CR1
#define USBUSART_IRQ NVIC_USART1_IRQ
-#define USBUSART_APB_ENR RCC_APB2ENR
-#define USBUSART_CLK_ENABLE RCC_APB2ENR_USART1EN
+#define USBUSART_CLK RCC_USART1
#define USBUSART_PORT GPIOB
#define USBUSART_TX_PIN GPIO6
#define USBUSART_ISR usart1_isr
+#define USBUSART_TIM TIM4
+#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4)
+#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ
+#define USBUSART_TIM_ISR tim4_isr
#define TRACE_TIM TIM2
-#define TRACE_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN)
+#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM2)
#define TRACE_IRQ NVIC_TIM2_IRQ
#define TRACE_ISR tim2_isr
#define TRACE_IC_IN TIM_IC_IN_TI2
@@ -127,73 +123,14 @@ extern usbd_device *usbdev;
#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 led_idle_run GPIO8
#define SET_RUN_STATE(state) {running_status = (state);}
-#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, led_idle_run, 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(); \
- longjmp(fatal_error_jmpbuf, (error)); \
-}
-
-int platform_init(void);
-void morse(const char *msg, char repeat);
-const char *platform_target_voltage(void);
-void platform_delay(uint32_t delay);
-
-/* <cdcacm.c> */
-void cdcacm_init(void);
-/* Returns current usb configuration, or 0 if not configured. */
-int cdcacm_get_config(void);
-int cdcacm_get_dtr(void);
-
-/* <platform.h> */
-void uart_usb_buf_drain(uint8_t ep);
+#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);}
+#define SET_ERROR_STATE(x)
/* Use newlib provided integer only stdio functions */
#define sscanf siscanf
#define sprintf siprintf
#define vasprintf vasiprintf
-#ifdef INLINE_GPIO
-static inline void _gpio_set(u32 gpioport, u16 gpios)
-{
- GPIO_BSRR(gpioport) = gpios;
-}
-#define gpio_set _gpio_set
-
-static inline void _gpio_clear(u32 gpioport, u16 gpios)
-{
- GPIO_BRR(gpioport) = gpios;
-}
-#define gpio_clear _gpio_clear
-
-static inline u16 _gpio_get(u32 gpioport, u16 gpios)
-{
- return (u16)GPIO_IDR(gpioport) & gpios;
-}
-#define gpio_get _gpio_get
-#endif
-
#endif
-void disconnect_usb(void);
-void assert_boot_pin(void);
diff --git a/src/platforms/swlink/usbdfu.c b/src/platforms/swlink/usbdfu.c
index 29c3809..9360981 100644
--- a/src/platforms/swlink/usbdfu.c
+++ b/src/platforms/swlink/usbdfu.c
@@ -25,14 +25,15 @@
#include "usbdfu.h"
+uint32_t app_address = 0x08002000;
+
void dfu_detach(void)
{
/* Disconnect USB cable by resetting USB Device
and pulling USB_DP low*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
@@ -42,53 +43,52 @@ void dfu_detach(void)
int main(void)
{
/* Check the force bootloader pin*/
- uint16_t pin_b;
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
+ uint16_t pin_b;
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOB);
/* Switch PB5 (SWIM_RST_IN) up */
gpio_set(GPIOB, GPIO5);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
- GPIO_CNF_OUTPUT_PUSHPULL, GPIO5);
+ GPIO_CNF_OUTPUT_PUSHPULL, GPIO5);
gpio_set(GPIOB, GPIO5);
- pin_b = gpio_get(GPIOB, GPIO6);
+ pin_b = gpio_get(GPIOB, GPIO6);
/* Check state on PB6 ((SWIM_RST) and release PB5*/
- pin_b = gpio_get(GPIOB, GPIO6);
+ pin_b = gpio_get(GPIOB, GPIO6);
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
- GPIO_CNF_INPUT_FLOAT, GPIO5);
+ GPIO_CNF_INPUT_FLOAT, GPIO5);
if(((GPIOA_CRL & 0x40) == 0x40) && pin_b)
dfu_jump_app_if_valid();
- dfu_protect_enable();
+ dfu_protect(DFU_MODE);
rcc_clock_setup_in_hse_8mhz_out_72mhz();
- systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
+ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
systick_set_reload(900000);
- /* Handle USB disconnect/connect */
+ /* Handle USB disconnect/connect */
/* Just in case: Disconnect USB cable by resetting USB Device
- * and pulling USB_DP low
- * Device will reconnect automatically as Pull-Up is hard wired*/
- rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
+ * and pulling USB_DP low
+ * Device will reconnect automatically as Pull-Up is hard wired*/
+ rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_GPIOA);
gpio_clear(GPIOA, GPIO12);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12);
- /* Handle LED*/
+ /* Handle LED*/
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
systick_interrupt_enable();
systick_counter_enable();
- dfu_init(&stm32f103_usb_driver);
+ dfu_init(&stm32f103_usb_driver, DFU_MODE);
dfu_main();
}
void sys_tick_handler(void)
{
- gpio_toggle(GPIOA, GPIO8);
+ gpio_toggle(GPIOA, GPIO8);
}
diff --git a/src/platforms/tm4c/gdb_if.c b/src/platforms/tm4c/gdb_if.c
new file mode 100644
index 0000000..7119638
--- /dev/null
+++ b/src/platforms/tm4c/gdb_if.c
@@ -0,0 +1,102 @@
+/*
+ * 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 "general.h"
+#include "gdb_if.h"
+#include "cdcacm.h"
+
+#include <libopencm3/usb/usbd.h>
+
+static volatile uint32_t head_out, tail_out;
+static volatile uint32_t count_in;
+static volatile uint8_t buffer_out[16*CDCACM_PACKET_SIZE];
+static volatile uint8_t buffer_in[CDCACM_PACKET_SIZE];
+
+void gdb_if_putchar(unsigned char c, int flush)
+{
+ buffer_in[count_in++] = c;
+ if(flush || (count_in == CDCACM_PACKET_SIZE)) {
+ /* Refuse to send if USB isn't configured, and
+ * don't bother if nobody's listening */
+ if((cdcacm_get_config() != 1) || !cdcacm_get_dtr()) {
+ count_in = 0;
+ return;
+ }
+ while(usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT,
+ (uint8_t *)buffer_in, count_in) <= 0);
+ count_in = 0;
+ }
+}
+
+void gdb_usb_out_cb(usbd_device *dev, uint8_t ep)
+{
+ (void)ep;
+ static uint8_t buf[CDCACM_PACKET_SIZE];
+
+ usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1);
+ uint32_t count = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT,
+ (uint8_t *)buf, CDCACM_PACKET_SIZE);
+
+
+ uint32_t idx;
+ for (idx=0; idx<count; idx++) {
+ buffer_out[head_out++ % sizeof(buffer_out)] = buf[idx];
+ }
+
+ usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 0);
+}
+
+unsigned char gdb_if_getchar(void)
+{
+
+ while(tail_out == head_out) {
+ /* Detach if port closed */
+ if(!cdcacm_get_dtr())
+ return 0x04;
+
+ while(cdcacm_get_config() != 1);
+ }
+
+ return buffer_out[tail_out++ % sizeof(buffer_out)];
+}
+
+unsigned char gdb_if_getchar_to(int timeout)
+{
+ timeout_counter = timeout/100;
+
+ if(head_out == tail_out) do {
+ /* Detach if port closed */
+ if(!cdcacm_get_dtr())
+ return 0x04;
+
+ while(cdcacm_get_config() != 1);
+ } while(timeout_counter && head_out == tail_out);
+
+ if(head_out != tail_out)
+ return gdb_if_getchar();
+
+ return -1;
+}
+
diff --git a/src/platforms/tm4c/jtagtap.c b/src/platforms/tm4c/jtagtap.c
new file mode 100644
index 0000000..e0bdd68
--- /dev/null
+++ b/src/platforms/tm4c/jtagtap.c
@@ -0,0 +1,59 @@
+#include "general.h"
+#include "jtagtap.h"
+
+int
+jtagtap_init(void)
+{
+ TMS_SET_MODE();
+
+ for(int i = 0; i <= 50; i++) jtagtap_next(1,0);
+ jtagtap_tms_seq(0xE73C, 16);
+ jtagtap_soft_reset();
+
+ return 0;
+}
+
+void
+jtagtap_reset(void)
+{
+#ifdef TRST_PORT
+ volatile int i;
+ gpio_clear(TRST_PORT, TRST_PIN);
+ for(i = 0; i < 10000; i++) asm("nop");
+ gpio_set(TRST_PORT, TRST_PIN);
+#endif
+ jtagtap_soft_reset();
+}
+
+void
+jtagtap_srst(bool assert)
+{
+ volatile int i;
+ if (assert) {
+ gpio_clear(SRST_PORT, SRST_PIN);
+ for(i = 0; i < 10000; i++) asm("nop");
+ } else {
+ gpio_set(SRST_PORT, SRST_PIN);
+ }
+}
+
+uint8_t
+jtagtap_next(const uint8_t dTMS, const uint8_t dTDO)
+{
+ uint16_t ret;
+
+ gpio_set_val(TMS_PORT, TMS_PIN, dTMS);
+ gpio_set_val(TDI_PORT, TDI_PIN, dTDO);
+ gpio_set(TCK_PORT, TCK_PIN);
+ ret = gpio_get(TDO_PORT, TDO_PIN);
+ gpio_clear(TCK_PORT, TCK_PIN);
+
+ DEBUG("jtagtap_next(TMS = %d, TDO = %d) = %d\n", dTMS, dTDO, ret);
+
+ return ret != 0;
+}
+
+#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/platforms/tm4c/swdptap.c b/src/platforms/tm4c/swdptap.c
new file mode 100644
index 0000000..058f6ba
--- /dev/null
+++ b/src/platforms/tm4c/swdptap.c
@@ -0,0 +1,124 @@
+#include "general.h"
+#include "swdptap.h"
+
+static 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)
+ SWDIO_MODE_FLOAT();
+ gpio_set(SWCLK_PORT, SWCLK_PIN);
+ gpio_clear(SWCLK_PORT, SWCLK_PIN);
+ if(!dir)
+ SWDIO_MODE_DRIVE();
+}
+
+static uint8_t swdptap_bit_in(void)
+{
+ uint16_t ret;
+
+ ret = gpio_get(SWDIO_PORT, SWDIO_PIN);
+ gpio_set(SWCLK_PORT, SWCLK_PIN);
+ gpio_clear(SWCLK_PORT, SWCLK_PIN);
+
+ DEBUG("%d", ret?1:0);
+
+ return ret != 0;
+}
+
+static void swdptap_bit_out(uint8_t val)
+{
+ DEBUG("%d", val);
+
+ gpio_set_val(SWDIO_PORT, SWDIO_PIN, val);
+ gpio_set(SWCLK_PORT, SWCLK_PIN);
+ gpio_clear(SWCLK_PORT, SWCLK_PIN);
+}
+
+int
+swdptap_init(void)
+{
+ 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/platforms/tm4c/tm4c.ld b/src/platforms/tm4c/tm4c.ld
new file mode 100644
index 0000000..8fe93a4
--- /dev/null
+++ b/src/platforms/tm4c/tm4c.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 = 0x00000000, LENGTH = 256K
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
+}
+
+/* Include the common ld script from libopenstm32. */
+INCLUDE libopencm3_lm4f.ld
+
diff --git a/src/platforms/tm4c/traceswo.c b/src/platforms/tm4c/traceswo.c
new file mode 100644
index 0000000..3e43a3b
--- /dev/null
+++ b/src/platforms/tm4c/traceswo.c
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2012 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * Copyright (C) 2014 Fredrik Ahlberg <fredrik@z80.se>
+ *
+ * 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 capture of the TRACESWO output.
+ *
+ * ARM DDI 0403D - ARMv7M Architecture Reference Manual
+ * ARM DDI 0337I - Cortex-M3 Technical Reference Manual
+ * ARM DDI 0314H - CoreSight Components Technical Reference Manual
+ */
+
+#include "general.h"
+#include "cdcacm.h"
+
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/lm4f/rcc.h>
+#include <libopencm3/lm4f/nvic.h>
+#include <libopencm3/lm4f/uart.h>
+#include <libopencm3/usb/usbd.h>
+
+void traceswo_init(void)
+{
+ periph_clock_enable(RCC_GPIOD);
+ periph_clock_enable(TRACEUART_CLK);
+ __asm__("nop"); __asm__("nop"); __asm__("nop");
+
+ gpio_mode_setup(SWO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWO_PIN);
+ gpio_set_af(SWO_PORT, 1, SWO_PIN); /* U2RX */
+
+ uart_disable(TRACEUART);
+
+ /* Setup UART parameters. */
+ uart_clock_from_sysclk(TRACEUART);
+ uart_set_baudrate(TRACEUART, 800000);
+ uart_set_databits(TRACEUART, 8);
+ uart_set_stopbits(TRACEUART, 1);
+ uart_set_parity(TRACEUART, UART_PARITY_NONE);
+
+ // Enable FIFO
+ uart_enable_fifo(TRACEUART);
+
+ // Set FIFO interrupt trigger levels to 4/8 full for RX buffer and
+ // 7/8 empty (1/8 full) for TX buffer
+ uart_set_fifo_trigger_levels(TRACEUART, UART_FIFO_RX_TRIG_1_2, UART_FIFO_TX_TRIG_7_8);
+
+ uart_clear_interrupt_flag(TRACEUART, UART_INT_RX | UART_INT_RT);
+
+ /* Enable interrupts */
+ uart_enable_interrupts(TRACEUART, UART_INT_RX | UART_INT_RT);
+
+ /* Finally enable the USART. */
+ uart_enable(TRACEUART);
+
+ nvic_set_priority(TRACEUART_IRQ, 0);
+ nvic_enable_irq(TRACEUART_IRQ);
+
+ /* Un-stall USB endpoint */
+ usbd_ep_stall_set(usbdev, 0x85, 0);
+
+ gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO3);
+}
+
+void traceswo_baud(unsigned int baud)
+{
+ uart_set_baudrate(TRACEUART, baud);
+ uart_set_databits(TRACEUART, 8);
+}
+
+#define FIFO_SIZE 256
+
+/* RX Fifo buffer */
+static volatile uint8_t buf_rx[FIFO_SIZE];
+/* Fifo in pointer, writes assumed to be atomic, should be only incremented within RX ISR */
+static volatile uint32_t buf_rx_in = 0;
+/* Fifo out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */
+static volatile uint32_t buf_rx_out = 0;
+
+void trace_buf_push(void)
+{
+ size_t len;
+
+ if (buf_rx_in == buf_rx_out) {
+ return;
+ } else if (buf_rx_in > buf_rx_out) {
+ len = buf_rx_in - buf_rx_out;
+ } else {
+ len = FIFO_SIZE - buf_rx_out;
+ }
+
+ if (len > 64) {
+ len = 64;
+ }
+
+ if (usbd_ep_write_packet(usbdev, 0x85, (uint8_t *)&buf_rx[buf_rx_out], len) == len) {
+ buf_rx_out += len;
+ buf_rx_out %= FIFO_SIZE;
+ }
+}
+
+void trace_buf_drain(usbd_device *dev, uint8_t ep)
+{
+ (void) dev;
+ (void) ep;
+ trace_buf_push();
+}
+
+void trace_tick(void)
+{
+ trace_buf_push();
+}
+
+void TRACEUART_ISR(void)
+{
+ uint32_t flush = uart_is_interrupt_source(TRACEUART, UART_INT_RT);
+
+ while (!uart_is_rx_fifo_empty(TRACEUART)) {
+ uint32_t c = uart_recv(TRACEUART);
+
+ /* If the next increment of rx_in would put it at the same point
+ * as rx_out, the FIFO is considered full.
+ */
+ if (((buf_rx_in + 1) % FIFO_SIZE) != buf_rx_out)
+ {
+ /* insert into FIFO */
+ buf_rx[buf_rx_in++] = c;
+
+ /* wrap out pointer */
+ if (buf_rx_in >= FIFO_SIZE)
+ {
+ buf_rx_in = 0;
+ }
+ } else {
+ flush = 1;
+ break;
+ }
+ }
+
+ if (flush) {
+ /* advance fifo out pointer by amount written */
+ trace_buf_push();
+ }
+}
+
diff --git a/src/platforms/tm4c/usbuart.c b/src/platforms/tm4c/usbuart.c
new file mode 100644
index 0000000..da82198
--- /dev/null
+++ b/src/platforms/tm4c/usbuart.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2012 Black Sphere Technologies Ltd.
+ * Written by Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * Copyright (C) 2014 Fredrik Ahlberg <fredrik@z80.se>
+ *
+ * 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 "general.h"
+#include "cdcacm.h"
+
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/scs.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/cdc.h>
+#include <libopencm3/lm4f/rcc.h>
+#include <libopencm3/lm4f/uart.h>
+
+#define FIFO_SIZE 128
+
+/* RX Fifo buffer */
+static uint8_t buf_rx[FIFO_SIZE];
+/* Fifo in pointer, writes assumed to be atomic, should be only incremented within RX ISR */
+static uint8_t buf_rx_in;
+/* Fifo out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */
+static uint8_t buf_rx_out;
+
+void usbuart_init(void)
+{
+ UART_PIN_SETUP();
+
+ periph_clock_enable(USBUART_CLK);
+ __asm__("nop"); __asm__("nop"); __asm__("nop");
+
+ uart_disable(USBUART);
+
+ /* Setup UART parameters. */
+ uart_clock_from_sysclk(USBUART);
+ uart_set_baudrate(USBUART, 38400);
+ uart_set_databits(USBUART, 8);
+ uart_set_stopbits(USBUART, 1);
+ uart_set_parity(USBUART, UART_PARITY_NONE);
+
+ // Enable FIFO
+ uart_enable_fifo(USBUART);
+
+ // Set FIFO interrupt trigger levels to 1/8 full for RX buffer and
+ // 7/8 empty (1/8 full) for TX buffer
+ uart_set_fifo_trigger_levels(USBUART, UART_FIFO_RX_TRIG_1_8, UART_FIFO_TX_TRIG_7_8);
+
+ uart_clear_interrupt_flag(USBUART, UART_INT_RX | UART_INT_RT);
+
+ /* Enable interrupts */
+ uart_enable_interrupts(UART0, UART_INT_RX| UART_INT_RT);
+
+ /* Finally enable the USART. */
+ uart_enable(USBUART);
+
+ //nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART);
+ nvic_enable_irq(USBUART_IRQ);
+}
+
+void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
+{
+ uart_set_baudrate(USBUART, coding->dwDTERate);
+ uart_set_databits(USBUART, coding->bDataBits);
+ switch(coding->bCharFormat) {
+ case 0:
+ case 1:
+ uart_set_stopbits(USBUART, 1);
+ break;
+ case 2:
+ uart_set_stopbits(USBUART, 2);
+ break;
+ }
+ switch(coding->bParityType) {
+ case 0:
+ uart_set_parity(USBUART, UART_PARITY_NONE);
+ break;
+ case 1:
+ uart_set_parity(USBUART, UART_PARITY_ODD);
+ break;
+ case 2:
+ uart_set_parity(USBUART, UART_PARITY_EVEN);
+ break;
+ }
+}
+
+void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
+{
+ (void)ep;
+
+ char buf[CDCACM_PACKET_SIZE];
+ int len = usbd_ep_read_packet(dev, CDCACM_UART_ENDPOINT,
+ buf, CDCACM_PACKET_SIZE);
+
+ for(int i = 0; i < len; i++)
+ uart_send_blocking(USBUART, buf[i]);
+}
+
+
+void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
+{
+ (void) dev;
+ (void) ep;
+}
+
+/*
+ * Read a character from the UART RX and stuff it in a software FIFO.
+ * Allowed to read from FIFO out pointer, but not write to it.
+ * Allowed to write to FIFO in pointer.
+ */
+void USBUART_ISR(void)
+{
+ int flush = uart_is_interrupt_source(USBUART, UART_INT_RT);
+
+ while (!uart_is_rx_fifo_empty(USBUART)) {
+ char c = uart_recv(USBUART);
+
+ /* If the next increment of rx_in would put it at the same point
+ * as rx_out, the FIFO is considered full.
+ */
+ if (((buf_rx_in + 1) % FIFO_SIZE) != buf_rx_out)
+ {
+ /* insert into FIFO */
+ buf_rx[buf_rx_in++] = c;
+
+ /* wrap out pointer */
+ if (buf_rx_in >= FIFO_SIZE)
+ {
+ buf_rx_in = 0;
+ }
+ } else {
+ flush = 1;
+ }
+ }
+
+ if (flush) {
+ /* forcibly empty fifo if no USB endpoint */
+ if (cdcacm_get_config() != 1)
+ {
+ buf_rx_out = buf_rx_in;
+ return;
+ }
+
+ uint8_t packet_buf[CDCACM_PACKET_SIZE];
+ uint8_t packet_size = 0;
+ uint8_t buf_out = buf_rx_out;
+
+ /* copy from uart FIFO into local usb packet buffer */
+ while (buf_rx_in != buf_out && packet_size < CDCACM_PACKET_SIZE)
+ {
+ packet_buf[packet_size++] = buf_rx[buf_out++];
+
+ /* wrap out pointer */
+ if (buf_out >= FIFO_SIZE)
+ {
+ buf_out = 0;
+ }
+
+ }
+
+ /* advance fifo out pointer by amount written */
+ buf_rx_out += usbd_ep_write_packet(usbdev,
+ CDCACM_UART_ENDPOINT, packet_buf, packet_size);
+ buf_rx_out %= FIFO_SIZE;
+ }
+}
+
diff --git a/src/sam3x.c b/src/sam3x.c
index d348701..0566718 100644
--- a/src/sam3x.c
+++ b/src/sam3x.c
@@ -22,24 +22,19 @@
* the device, providing the XML memory map and Flash memory programming.
*/
-#include <stdlib.h>
-#include <string.h>
-
#include "general.h"
#include "adiv5.h"
#include "target.h"
#include "command.h"
#include "gdb_packet.h"
-static int sam3x_flash_erase(struct target_s *target, uint32_t addr, int len);
-static int sam3x_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
+static int sam3x_flash_erase(target *t, uint32_t addr, size_t len);
+static int sam3x_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
static bool sam3x_cmd_gpnvm_get(target *t);
static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[]);
-static const char sam3x_driver_str[] = "Atmel SAM3X";
-
const struct command_s sam3x_cmd_list[] = {
{"gpnvm_get", (cmd_handler)sam3x_cmd_gpnvm_get, "Get GPVNM value"},
{"gpnvm_set", (cmd_handler)sam3x_cmd_gpnvm_set, "Set GPVNM bit"},
@@ -58,13 +53,51 @@ static const char sam3x_xml_memory_map[] = "<?xml version=\"1.0\"?>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
"</memory-map>";
+static const char sam3n_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=\"0x400000\" length=\"0x400000\">"
+ " <property name=\"blocksize\">0x100</property>"
+ " </memory>"
+ " <memory type=\"rom\" start=\"0x800000\" length=\"0x400000\"/>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
+ "</memory-map>";
+
+static const char sam3u_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=\"0x80000\" length=\"0x100000\">"
+ " <property name=\"blocksize\">0x100</property>"
+ " </memory>"
+ " <memory type=\"rom\" start=\"0x180000\" length=\"0x200000\"/>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
+ "</memory-map>";
+
+static const char sam4s_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=\"0x400000\" length=\"0x400000\">"
+ " <property name=\"blocksize\">0x200</property>"
+ " </memory>"
+ " <memory type=\"rom\" start=\"0x800000\" length=\"0x400000\"/>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x400000\"/>"
+ "</memory-map>";
/* Enhanced Embedded Flash Controller (EEFC) Register Map */
-#define EEFC_BASE(x) (0x400E0A00+((x)*0x400))
-#define EEFC_FMR(x) (EEFC_BASE(x)+0x00)
-#define EEFC_FCR(x) (EEFC_BASE(x)+0x04)
-#define EEFC_FSR(x) (EEFC_BASE(x)+0x08)
-#define EEFC_FRR(x) (EEFC_BASE(x)+0x0C)
+#define SAM3N_EEFC_BASE 0x400E0A00
+#define SAM3X_EEFC_BASE(x) (0x400E0A00+((x)*0x400))
+#define SAM3U_EEFC_BASE(x) (0x400E0800+((x)*0x200))
+#define SAM4S_EEFC_BASE(x) (0x400E0A00+((x)*0x200))
+#define EEFC_FMR(base) ((base)+0x00)
+#define EEFC_FCR(base) ((base)+0x04)
+#define EEFC_FSR(base) ((base)+0x08)
+#define EEFC_FRR(base) ((base)+0x0C)
#define EEFC_FCR_FKEY (0x5A << 24)
#define EEFC_FCR_FCMD_GETD 0x00
@@ -73,6 +106,7 @@ static const char sam3x_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define EEFC_FCR_FCMD_EWP 0x03
#define EEFC_FCR_FCMD_EWPL 0x04
#define EEFC_FCR_FCMD_EA 0x05
+#define EEFC_FCR_FCMD_EPA 0x07
#define EEFC_FCR_FCMD_SLB 0x08
#define EEFC_FCR_FCMD_CLB 0x09
#define EEFC_FCR_FCMD_GLB 0x0A
@@ -87,142 +121,292 @@ static const char sam3x_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define EEFC_FSR_FLOCKE (1 << 2)
#define EEFC_FSR_ERROR (EEFC_FSR_FCMDE | EEFC_FSR_FLOCKE)
-#define CHIPID_CIDR 0x400E0940
+#define SAM3X_CHIPID_CIDR 0x400E0940
+#define SAM3N_CHIPID_CIDR 0x400E0740
+#define SAM3S_CHIPID_CIDR 0x400E0740
+#define SAM3U_CHIPID_CIDR 0x400E0740
+#define SAM4S_CHIPID_CIDR 0x400E0740
#define CHIPID_CIDR_VERSION_MASK (0x1F << 0)
#define CHIPID_CIDR_EPROC_CM3 (0x03 << 5)
+#define CHIPID_CIDR_EPROC_CM4 (0x07 << 5)
#define CHIPID_CIDR_EPROC_MASK (0x07 << 5)
#define CHIPID_CIDR_NVPSIZ_MASK (0x0F << 8)
#define CHIPID_CIDR_NVPSIZ_128K (0x07 << 8)
#define CHIPID_CIDR_NVPSIZ_256K (0x09 << 8)
#define CHIPID_CIDR_NVPSIZ_512K (0x0A << 8)
+#define CHIPID_CIDR_NVPSIZ_1024K (0x0C << 8)
+#define CHIPID_CIDR_NVPSIZ_2048K (0x0E << 8)
#define CHIPID_CIDR_NVPSIZ2_MASK (0x0F << 12)
#define CHIPID_CIDR_SRAMSIZ_MASK (0x0F << 16)
#define CHIPID_CIDR_ARCH_MASK (0xFF << 20)
+#define CHIPID_CIDR_ARCH_SAM3UxC (0x80 << 20)
+#define CHIPID_CIDR_ARCH_SAM3UxE (0x81 << 20)
#define CHIPID_CIDR_ARCH_SAM3XxC (0x84 << 20)
#define CHIPID_CIDR_ARCH_SAM3XxE (0x85 << 20)
#define CHIPID_CIDR_ARCH_SAM3XxG (0x86 << 20)
+#define CHIPID_CIDR_ARCH_SAM3NxA (0x93 << 20)
+#define CHIPID_CIDR_ARCH_SAM3NxB (0x94 << 20)
+#define CHIPID_CIDR_ARCH_SAM3NxC (0x95 << 20)
+#define CHIPID_CIDR_ARCH_SAM3SxA (0x88 << 20)
+#define CHIPID_CIDR_ARCH_SAM3SxB (0x89 << 20)
+#define CHIPID_CIDR_ARCH_SAM3SxC (0x8A << 20)
+#define CHIPID_CIDR_ARCH_SAM4SxA (0x88 << 20)
+#define CHIPID_CIDR_ARCH_SAM4SxB (0x89 << 20)
+#define CHIPID_CIDR_ARCH_SAM4SxC (0x8A << 20)
#define CHIPID_CIDR_NVPTYP_MASK (0x07 << 28)
#define CHIPID_CIDR_NVPTYP_FLASH (0x02 << 28)
#define CHIPID_CIDR_NVPTYP_ROM_FLASH (0x03 << 28)
#define CHIPID_CIDR_EXT (0x01 << 31)
-#define PAGE_SIZE 256
+#define SAM3_PAGE_SIZE 256
+#define SAM4_PAGE_SIZE 512
-bool sam3x_probe(struct target_s *target)
+bool sam3x_probe(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- target->idcode = adiv5_ap_mem_read(ap, CHIPID_CIDR);
+ t->idcode = target_mem_read32(t, SAM3X_CHIPID_CIDR);
/* FIXME: Check for all variants with similar flash interface */
- switch (target->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
+ switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM3XxC | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3XxE | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3XxG | CHIPID_CIDR_EPROC_CM3:
- target->driver = sam3x_driver_str;
- target->xml_mem_map = sam3x_xml_memory_map;
- target->flash_erase = sam3x_flash_erase;
- target->flash_write = sam3x_flash_write;
- target_add_commands(target, sam3x_cmd_list, sam3x_driver_str);
+ t->driver = "Atmel SAM3X";
+ t->xml_mem_map = sam3x_xml_memory_map;
+ t->flash_erase = sam3x_flash_erase;
+ t->flash_write = sam3x_flash_write;
+ target_add_commands(t, sam3x_cmd_list, "SAM3X");
+ return true;
+ }
+
+ t->idcode = target_mem_read32(t, SAM3N_CHIPID_CIDR);
+ switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
+ case CHIPID_CIDR_ARCH_SAM3NxA | CHIPID_CIDR_EPROC_CM3:
+ case CHIPID_CIDR_ARCH_SAM3NxB | CHIPID_CIDR_EPROC_CM3:
+ case CHIPID_CIDR_ARCH_SAM3NxC | CHIPID_CIDR_EPROC_CM3:
+ t->driver = "Atmel SAM3N";
+ t->xml_mem_map = sam3n_xml_memory_map;
+ t->flash_erase = sam3x_flash_erase;
+ t->flash_write = sam3x_flash_write;
+ target_add_commands(t, sam3x_cmd_list, "SAM3N");
+ return true;
+ }
+
+ t->idcode = target_mem_read32(t, SAM3S_CHIPID_CIDR);
+ switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
+ case CHIPID_CIDR_ARCH_SAM3SxA | CHIPID_CIDR_EPROC_CM3:
+ case CHIPID_CIDR_ARCH_SAM3SxB | CHIPID_CIDR_EPROC_CM3:
+ case CHIPID_CIDR_ARCH_SAM3SxC | CHIPID_CIDR_EPROC_CM3:
+ t->driver = "Atmel SAM3S";
+ t->xml_mem_map = sam3n_xml_memory_map;
+ t->flash_erase = sam3x_flash_erase;
+ t->flash_write = sam3x_flash_write;
+ target_add_commands(t, sam3x_cmd_list, "SAM3S");
+ return true;
+ }
+
+ t->idcode = target_mem_read32(t, SAM3U_CHIPID_CIDR);
+ switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
+ case CHIPID_CIDR_ARCH_SAM3UxC | CHIPID_CIDR_EPROC_CM3:
+ case CHIPID_CIDR_ARCH_SAM3UxE | CHIPID_CIDR_EPROC_CM3:
+ t->driver = "Atmel SAM3U";
+ t->xml_mem_map = sam3u_xml_memory_map;
+ t->flash_erase = sam3x_flash_erase;
+ t->flash_write = sam3x_flash_write;
+ target_add_commands(t, sam3x_cmd_list, "SAM3U");
+ return true;
+ }
+
+ t->idcode = target_mem_read32(t, SAM4S_CHIPID_CIDR);
+ switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
+ case CHIPID_CIDR_ARCH_SAM4SxA | CHIPID_CIDR_EPROC_CM4:
+ case CHIPID_CIDR_ARCH_SAM4SxB | CHIPID_CIDR_EPROC_CM4:
+ case CHIPID_CIDR_ARCH_SAM4SxC | CHIPID_CIDR_EPROC_CM4:
+ t->driver = "Atmel SAM4S";
+ t->xml_mem_map = sam4s_xml_memory_map;
+ t->flash_erase = sam3x_flash_erase;
+ t->flash_write = sam3x_flash_write;
+ target_add_commands(t, sam3x_cmd_list, "SAM4S");
return true;
}
+
return false;
}
static int
-sam3x_flash_cmd(struct target_s *target, int bank, uint8_t cmd, uint16_t arg)
+sam3x_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
-
- adiv5_ap_mem_write(ap, EEFC_FCR(bank),
- EEFC_FCR_FKEY | cmd | ((uint32_t)arg << 8));
+ DEBUG("%s: base = 0x%08x cmd = 0x%02X, arg = 0x%06X\n",
+ __func__, base, cmd, arg);
+ target_mem_write32(t, EEFC_FCR(base),
+ EEFC_FCR_FKEY | cmd | ((uint32_t)arg << 8));
- while(!(adiv5_ap_mem_read(ap, EEFC_FSR(bank)) & EEFC_FSR_FRDY))
- if(target_check_error(target))
+ while (!(target_mem_read32(t, EEFC_FSR(base)) & EEFC_FSR_FRDY))
+ if(target_check_error(t))
return -1;
- uint32_t sr = adiv5_ap_mem_read(ap, EEFC_FSR(bank));
+ uint32_t sr = target_mem_read32(t, EEFC_FSR(base));
return sr & EEFC_FSR_ERROR;
}
-static int
-sam3x_flash_bank(struct target_s *target, uint32_t addr, uint32_t *offset)
+static uint32_t
+sam3x_flash_base(target *t, uint32_t addr, uint32_t *offset)
{
- uint32_t half = -1;
- switch (target->idcode & CHIPID_CIDR_NVPSIZ_MASK) {
- case CHIPID_CIDR_NVPSIZ_128K:
- half = 0x00090000;
- break;
- case CHIPID_CIDR_NVPSIZ_256K:
- half = 0x000A0000;
- break;
- case CHIPID_CIDR_NVPSIZ_512K:
- half = 0x000C0000;
- break;
+ if (strcmp(t->driver, "Atmel SAM3X") == 0) {
+ uint32_t half = -1;
+ switch (t->idcode & CHIPID_CIDR_NVPSIZ_MASK) {
+ case CHIPID_CIDR_NVPSIZ_128K:
+ half = 0x00090000;
+ break;
+ case CHIPID_CIDR_NVPSIZ_256K:
+ half = 0x000A0000;
+ break;
+ case CHIPID_CIDR_NVPSIZ_512K:
+ half = 0x000C0000;
+ break;
+ }
+ if (addr > half) {
+ if (offset)
+ *offset = addr - half;
+ return SAM3X_EEFC_BASE(1);
+ } else {
+ if (offset)
+ *offset = addr - 0x80000;
+ return SAM3X_EEFC_BASE(0);
+ }
+ }
+
+ /* The SAM3U has a constant split between both banks */
+ if (strcmp(t->driver, "Atmel SAM3U") == 0) {
+ if (addr >= 0x100000) {
+ if(offset)
+ *offset = addr - 0x100000;
+
+ return SAM3U_EEFC_BASE(1);
+ } else {
+ if(offset)
+ *offset = addr - 0x80000;
+
+ return SAM3U_EEFC_BASE(0);
+ }
}
- if (addr > half) {
- if (offset)
- *offset = addr - half;
- return 1;
+ if (strcmp(t->driver, "Atmel SAM4S") == 0) {
+ uint32_t half = -1;
+ switch (t->idcode & CHIPID_CIDR_NVPSIZ_MASK) {
+ case CHIPID_CIDR_NVPSIZ_128K:
+ case CHIPID_CIDR_NVPSIZ_256K:
+ case CHIPID_CIDR_NVPSIZ_512K:
+ if (offset)
+ *offset = addr - 0x400000;
+ return SAM4S_EEFC_BASE(0);
+ case CHIPID_CIDR_NVPSIZ_1024K:
+ half = 0x480000;
+ break;
+ case CHIPID_CIDR_NVPSIZ_2048K:
+ half = 0x500000;
+ break;
+ }
+ if (addr >= half) {
+ if (offset)
+ *offset = addr - half;
+ return SAM4S_EEFC_BASE(1);
+ } else {
+ if (offset)
+ *offset = addr - 0x400000;
+ return SAM4S_EEFC_BASE(0);
+ }
}
+ /* SAM3N device */
if (offset)
- *offset = addr - 0x80000;
- return 0;
+ *offset = addr - 0x400000;
+ return SAM3N_EEFC_BASE;
}
-static int sam3x_flash_erase(struct target_s *target, uint32_t addr, int len)
+static int sam3x_flash_erase(target *t, uint32_t addr, size_t len)
{
uint32_t offset;
- uint8_t bank = sam3x_flash_bank(target, addr, &offset);
- unsigned chunk = offset / PAGE_SIZE;
- uint8_t buf[PAGE_SIZE];
+ uint32_t base = sam3x_flash_base(t, addr, &offset);
- /* This device doesn't really have a page erase function.
+ /* The SAM4S is the only supported device with a page erase command.
+ * Erasing is done in 8-page chunks. arg[15:2] contains the page
+ * number and arg[1:0] contains 0x1, indicating 8-page chunks.
+ */
+ if (strcmp(t->driver, "Atmel SAM4S") == 0) {
+ unsigned chunk = offset / SAM4_PAGE_SIZE;
+
+ /* Fail if the start address is not 8-page-aligned. */
+ if (chunk % 8 != 0)
+ return -1;
+
+ /* Note that the length might not be a multiple of 8 pages.
+ * In this case, we will erase a few extra pages at the end.
+ */
+ while (len > 0) {
+ int16_t arg = chunk | 0x1;
+ if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg))
+ return -1;
+
+ len -= SAM4_PAGE_SIZE * 8;
+ addr += SAM4_PAGE_SIZE * 8;
+ chunk += 8;
+ }
+
+ return 0;
+ }
+
+ /* The SAM3X/SAM3N don't really have a page erase function.
* This Erase/Write page is the best we have, so we write with all
* ones. This does waste time, but what can we do?
*/
+ unsigned chunk = offset / SAM3_PAGE_SIZE;
+ uint8_t buf[SAM3_PAGE_SIZE];
memset(buf, 0xff, sizeof(buf));
/* Only do this once, since it doesn't change. */
- target_mem_write_words(target, addr, (void*)buf, PAGE_SIZE);
+ target_mem_write(t, addr, buf, SAM3_PAGE_SIZE);
while (len) {
- if(sam3x_flash_cmd(target, bank, EEFC_FCR_FCMD_EWP, chunk))
+ if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EWP, chunk))
return -1;
- len -= PAGE_SIZE;
- addr += PAGE_SIZE;
+ len -= SAM3_PAGE_SIZE;
+ addr += SAM3_PAGE_SIZE;
chunk++;
}
return 0;
}
-static int sam3x_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len)
+static int sam3x_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
{
+ unsigned page_size;
+ if (strcmp(t->driver, "Atmel SAM4S") == 0) {
+ page_size = SAM4_PAGE_SIZE;
+ } else {
+ page_size = SAM3_PAGE_SIZE;
+ }
uint32_t offset;
- uint8_t bank = sam3x_flash_bank(target, dest, &offset);
- uint8_t buf[PAGE_SIZE];
- unsigned first_chunk = offset / PAGE_SIZE;
- unsigned last_chunk = (offset + len - 1) / PAGE_SIZE;
- offset %= PAGE_SIZE;
+ uint32_t base = sam3x_flash_base(t, dest, &offset);
+ uint8_t buf[page_size];
+ unsigned first_chunk = offset / page_size;
+ unsigned last_chunk = (offset + len - 1) / page_size;
+ offset %= page_size;
dest -= offset;
for (unsigned chunk = first_chunk; chunk <= last_chunk; chunk++) {
-
- DEBUG("chunk %u len %d\n", chunk, len);
+
+ DEBUG("chunk %u len %zu\n", chunk, len);
/* first and last chunk may require special handling */
if ((chunk == first_chunk) || (chunk == last_chunk)) {
/* fill with all ff to avoid sector rewrite corrupting other writes */
memset(buf, 0xff, sizeof(buf));
-
- /* copy as much as fits */
- int copylen = PAGE_SIZE - offset;
+
+ /* copy as much as fits */
+ size_t copylen = page_size - offset;
if (copylen > len)
copylen = len;
memcpy(&buf[offset], src, copylen);
@@ -234,13 +418,13 @@ static int sam3x_flash_write(struct target_s *target, uint32_t dest,
} else {
/* interior chunk, must be aligned and full-sized */
- memcpy(buf, src, PAGE_SIZE);
- len -= PAGE_SIZE;
- src += PAGE_SIZE;
+ memcpy(buf, src, page_size);
+ len -= page_size;
+ src += page_size;
}
- target_mem_write_words(target, dest, (void*)buf, PAGE_SIZE);
- if(sam3x_flash_cmd(target, bank, EEFC_FCR_FCMD_WP, chunk))
+ target_mem_write(t, dest, buf, page_size);
+ if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_WP, chunk))
return -1;
}
@@ -249,10 +433,10 @@ static int sam3x_flash_write(struct target_s *target, uint32_t dest,
static bool sam3x_cmd_gpnvm_get(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
- sam3x_flash_cmd(t, 0, EEFC_FCR_FCMD_GGPB, 0);
- gdb_outf("GPNVM: 0x%08X\n", adiv5_ap_mem_read(ap, EEFC_FRR(0)));
+ uint32_t base = sam3x_flash_base(t, 0, NULL);
+
+ sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_GGPB, 0);
+ gdb_outf("GPNVM: 0x%08X\n", target_mem_read32(t, EEFC_FRR(base)));
return true;
}
@@ -260,7 +444,8 @@ static bool sam3x_cmd_gpnvm_get(target *t)
static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[])
{
uint32_t bit, cmd;
-
+ uint32_t base = sam3x_flash_base(t, 0, NULL);
+
if (argc != 3) {
gdb_out("usage: monitor gpnvm_set <bit> <val>\n");
return false;
@@ -268,7 +453,7 @@ static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[])
bit = atol(argv[1]);
cmd = atol(argv[2]) ? EEFC_FCR_FCMD_SGPB : EEFC_FCR_FCMD_CGPB;
- sam3x_flash_cmd(t, 0, cmd, bit);
+ sam3x_flash_cmd(t, base, cmd, bit);
sam3x_cmd_gpnvm_get(t);
return true;
diff --git a/src/samd.c b/src/samd.c
new file mode 100644
index 0000000..c8517aa
--- /dev/null
+++ b/src/samd.c
@@ -0,0 +1,737 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2014 Richard Meadows <richardeoin>
+ *
+ * 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 Atmel SAM D target specific functions for
+ * detecting the device, providing the XML memory map and Flash memory
+ * programming.
+ *
+ * Tested with
+ * * SAMD20E17A (rev C)
+ * * SAMD20J18A (rev B)
+ * * SAMD21J18A (rev B)
+ * *
+ */
+/* Refer to the SAM D20 Datasheet:
+ * http://www.atmel.com/Images/Atmel-42129-SAM-D20_Datasheet.pdf
+ * particularly Sections 12. DSU and 20. NVMCTRL
+ */
+
+#include "general.h"
+#include "jtagtap.h"
+#include "adiv5.h"
+#include "target.h"
+#include "command.h"
+#include "gdb_packet.h"
+#include "cortexm.h"
+
+static int samd_flash_erase(target *t, uint32_t addr, size_t len);
+static int samd_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
+
+static bool samd_cmd_erase_all(target *t);
+static bool samd_cmd_lock_flash(target *t);
+static bool samd_cmd_unlock_flash(target *t);
+static bool samd_cmd_read_userrow(target *t);
+static bool samd_cmd_serial(target *t);
+static bool samd_cmd_mbist(target *t);
+static bool samd_cmd_ssb(target *t);
+
+const struct command_s samd_cmd_list[] = {
+ {"erase_mass", (cmd_handler)samd_cmd_erase_all, "Erase entire flash memory"},
+ {"lock_flash", (cmd_handler)samd_cmd_lock_flash, "Locks flash against spurious commands"},
+ {"unlock_flash", (cmd_handler)samd_cmd_unlock_flash, "Unlocks flash"},
+ {"user_row", (cmd_handler)samd_cmd_read_userrow, "Prints user row from flash"},
+ {"serial", (cmd_handler)samd_cmd_serial, "Prints serial number"},
+ {"mbist", (cmd_handler)samd_cmd_mbist, "Runs the built-in memory test"},
+ {"set_security_bit", (cmd_handler)samd_cmd_ssb, "Sets the Security Bit"},
+ {NULL, NULL, NULL}
+};
+
+/**
+ * 256KB Flash Max., 32KB RAM Max. The smallest unit of erase is the
+ * one row = 256 bytes.
+ */
+static const char samd_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=\"0x0\" length=\"0x40000\">"
+ " <property name=\"blocksize\">0x100</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x8000\"/>"
+ "</memory-map>";
+
+/* Non-Volatile Memory Controller (NVMC) Parameters */
+#define SAMD_ROW_SIZE 256
+#define SAMD_PAGE_SIZE 64
+
+/* -------------------------------------------------------------------------- */
+/* Non-Volatile Memory Controller (NVMC) Registers */
+/* -------------------------------------------------------------------------- */
+
+#define SAMD_NVMC 0x41004000
+#define SAMD_NVMC_CTRLA (SAMD_NVMC + 0x0)
+#define SAMD_NVMC_CTRLB (SAMD_NVMC + 0x04)
+#define SAMD_NVMC_PARAM (SAMD_NVMC + 0x08)
+#define SAMD_NVMC_INTFLAG (SAMD_NVMC + 0x14)
+#define SAMD_NVMC_STATUS (SAMD_NVMC + 0x18)
+#define SAMD_NVMC_ADDRESS (SAMD_NVMC + 0x1C)
+
+/* Control A Register (CTRLA) */
+#define SAMD_CTRLA_CMD_KEY 0xA500
+#define SAMD_CTRLA_CMD_ERASEROW 0x0002
+#define SAMD_CTRLA_CMD_WRITEPAGE 0x0004
+#define SAMD_CTRLA_CMD_ERASEAUXROW 0x0005
+#define SAMD_CTRLA_CMD_WRITEAUXPAGE 0x0006
+#define SAMD_CTRLA_CMD_LOCK 0x0040
+#define SAMD_CTRLA_CMD_UNLOCK 0x0041
+#define SAMD_CTRLA_CMD_PAGEBUFFERCLEAR 0x0044
+#define SAMD_CTRLA_CMD_SSB 0x0045
+#define SAMD_CTRLA_CMD_INVALL 0x0046
+
+/* Interrupt Flag Register (INTFLAG) */
+#define SAMD_NVMC_READY (1 << 0)
+
+/* Non-Volatile Memory Calibration and Auxiliary Registers */
+#define SAMD_NVM_USER_ROW_LOW 0x00804000
+#define SAMD_NVM_USER_ROW_HIGH 0x00804004
+#define SAMD_NVM_CALIBRATION 0x00806020
+#define SAMD_NVM_SERIAL(n) (0x0080A00C + (0x30 * ((n + 3) / 4)) + \
+ (0x4 * n))
+
+/* -------------------------------------------------------------------------- */
+/* Device Service Unit (DSU) Registers */
+/* -------------------------------------------------------------------------- */
+
+#define SAMD_DSU 0x41002000
+#define SAMD_DSU_EXT_ACCESS (SAMD_DSU + 0x100)
+#define SAMD_DSU_CTRLSTAT (SAMD_DSU_EXT_ACCESS + 0x0)
+#define SAMD_DSU_ADDRESS (SAMD_DSU_EXT_ACCESS + 0x4)
+#define SAMD_DSU_LENGTH (SAMD_DSU_EXT_ACCESS + 0x8)
+#define SAMD_DSU_DID (SAMD_DSU_EXT_ACCESS + 0x018)
+#define SAMD_DSU_PID(n) (SAMD_DSU + 0x1FE0 + \
+ (0x4 * (n % 4)) - (0x10 * (n / 4)))
+#define SAMD_DSU_CID(n) (SAMD_DSU + 0x1FF0 + \
+ (0x4 * (n % 4)))
+
+/* Control and Status Register (CTRLSTAT) */
+#define SAMD_CTRL_CHIP_ERASE (1 << 4)
+#define SAMD_CTRL_MBIST (1 << 3)
+#define SAMD_CTRL_CRC (1 << 2)
+#define SAMD_STATUSA_PERR (1 << 12)
+#define SAMD_STATUSA_FAIL (1 << 11)
+#define SAMD_STATUSA_BERR (1 << 10)
+#define SAMD_STATUSA_CRSTEXT (1 << 9)
+#define SAMD_STATUSA_DONE (1 << 8)
+#define SAMD_STATUSB_PROT (1 << 16)
+
+/* Device Identification Register (DID) */
+#define SAMD_DID_MASK 0xFFBC0000
+#define SAMD_DID_CONST_VALUE 0x10000000
+#define SAMD_DID_DEVSEL_MASK 0x0F
+#define SAMD_DID_DEVSEL_POS 0
+#define SAMD_DID_REVISION_MASK 0x0F
+#define SAMD_DID_REVISION_POS 8
+#define SAMD_DID_SERIES_MASK 0x03
+#define SAMD_DID_SERIES_POS 16
+
+/* Peripheral ID */
+#define SAMD_PID_MASK 0x00F7FFFF
+#define SAMD_PID_CONST_VALUE 0x0001FCD0
+
+/* Component ID */
+#define SAMD_CID_VALUE 0xB105100D
+
+/**
+ * Reads the SAM D20 Peripheral ID
+ */
+uint64_t samd_read_pid(target *t)
+{
+ uint64_t pid = 0;
+ uint8_t i, j;
+
+ /* Five PID registers to read LSB first */
+ for (i = 0, j = 0; i < 5; i++, j += 8)
+ pid |= (target_mem_read32(t, SAMD_DSU_PID(i)) & 0xFF) << j;
+
+ return pid;
+}
+/**
+ * Reads the SAM D20 Component ID
+ */
+uint32_t samd_read_cid(target *t)
+{
+ uint64_t cid = 0;
+ uint8_t i, j;
+
+ /* Four CID registers to read LSB first */
+ for (i = 0, j = 0; i < 4; i++, j += 8)
+ cid |= (target_mem_read32(t, SAMD_DSU_CID(i)) & 0xFF) << j;
+
+ return cid;
+}
+
+/**
+ * Overloads the default cortexm reset function with a version that
+ * removes the target from extended reset where required.
+ */
+static void
+samd_reset(target *t)
+{
+ /**
+ * SRST is not asserted here as it appears to reset the adiv5
+ * logic, meaning that subsequent adiv5_* calls PLATFORM_FATAL_ERROR.
+ *
+ * This is ok as normally you can just connect the debugger and go,
+ * but if that's not possible (protection or SWCLK being used for
+ * something else) then having SWCLK low on reset should get you
+ * debug access (cold-plugging). TODO: Confirm this
+ *
+ * See the SAM D20 datasheet §12.6 Debug Operation for more
+ * details.
+ *
+ * jtagtap_srst(true);
+ * jtagtap_srst(false);
+ */
+
+ /* Read DHCSR here to clear S_RESET_ST bit before reset */
+ target_mem_read32(t, CORTEXM_DHCSR);
+
+ /* Request system reset from NVIC: SRST doesn't work correctly */
+ /* This could be VECTRESET: 0x05FA0001 (reset only core)
+ * or SYSRESETREQ: 0x05FA0004 (system reset)
+ */
+ target_mem_write32(t, CORTEXM_AIRCR,
+ CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ);
+
+ /* Exit extended reset */
+ if (target_mem_read32(t, SAMD_DSU_CTRLSTAT) &
+ SAMD_STATUSA_CRSTEXT) {
+ /* Write bit to clear from extended reset */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_STATUSA_CRSTEXT);
+ }
+
+ /* Poll for release from reset */
+ while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST);
+
+ /* Reset DFSR flags */
+ target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
+
+ /* Clear any target errors */
+ target_check_error(t);
+}
+
+/**
+ * Overloads the default cortexm detached function with a version that
+ * removes the target from extended reset where required.
+ *
+ * Only required for SAM D20 _Revision B_ Silicon
+ */
+static void
+samd20_revB_detach(target *t)
+{
+ cortexm_detach(t);
+
+ /* ---- Additional ---- */
+ /* Exit extended reset */
+ if (target_mem_read32(t, SAMD_DSU_CTRLSTAT) &
+ SAMD_STATUSA_CRSTEXT) {
+ /* Write bit to clear from extended reset */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT,
+ SAMD_STATUSA_CRSTEXT);
+ }
+}
+
+/**
+ * Overloads the default cortexm halt_resume function with a version
+ * that removes the target from extended reset where required.
+ *
+ * Only required for SAM D20 _Revision B_ Silicon
+ */
+static void
+samd20_revB_halt_resume(target *t, bool step)
+{
+ cortexm_halt_resume(t, step);
+
+ /* ---- Additional ---- */
+ /* Exit extended reset */
+ if (target_mem_read32(t, SAMD_DSU_CTRLSTAT) & SAMD_STATUSA_CRSTEXT) {
+ /* Write bit to clear from extended reset */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT,
+ SAMD_STATUSA_CRSTEXT);
+ }
+}
+
+/**
+ * Overload the default cortexm attach for when the samd is protected.
+ *
+ * If the samd is protected then the default cortexm attach will
+ * fail as the S_HALT bit in the DHCSR will never go high. This
+ * function allows users to attach on a temporary basis so they can
+ * rescue the device.
+ */
+static bool
+samd_protected_attach(target *t)
+{
+ /**
+ * TODO: Notify the user that we're not really attached and
+ * they should issue the 'monitor erase_mass' command to
+ * regain access to the chip.
+ */
+
+ /* Patch back in the normal cortexm attach for next time */
+ t->attach = cortexm_attach;
+
+ /* Allow attach this time */
+ return true;
+}
+
+/**
+ * Use the DSU Device Indentification Register to populate a struct
+ * describing the SAM D device.
+ */
+struct samd_descr {
+ uint8_t series;
+ char revision;
+ char pin;
+ uint8_t mem;
+ char package[3];
+};
+struct samd_descr samd_parse_device_id(uint32_t did)
+{
+ struct samd_descr samd;
+ memset(samd.package, 0, 3);
+
+ uint8_t series = (did >> SAMD_DID_SERIES_POS)
+ & SAMD_DID_SERIES_MASK;
+ uint8_t revision = (did >> SAMD_DID_REVISION_POS)
+ & SAMD_DID_REVISION_MASK;
+ uint8_t devsel = (did >> SAMD_DID_DEVSEL_POS)
+ & SAMD_DID_DEVSEL_MASK;
+
+ /* Series */
+ switch (series) {
+ case 0: samd.series = 20; break;
+ case 1: samd.series = 21; break;
+ case 2: samd.series = 10; break;
+ case 3: samd.series = 11; break;
+ }
+ /* Revision */
+ samd.revision = 'A' + revision;
+
+ switch (samd.series) {
+ case 20: /* SAM D20 */
+ case 21: /* SAM D21 */
+ switch (devsel / 5) {
+ case 0: samd.pin = 'J'; break;
+ case 1: samd.pin = 'G'; break;
+ case 2: samd.pin = 'E'; break;
+ default: samd.pin = 'u'; break;
+ }
+ samd.mem = 18 - (devsel % 5);
+ break;
+ case 10: /* SAM D10 */
+ case 11: /* SAM D11 */
+ switch (devsel / 3) {
+ case 0: samd.package[0] = 'M'; break;
+ case 1: samd.package[0] = 'S'; samd.package[1] = 'S'; break;
+ }
+ samd.pin = 'D';
+ samd.mem = 14 - (devsel % 3);
+ break;
+ }
+
+ return samd;
+}
+
+
+char variant_string[40];
+bool samd_probe(target *t)
+{
+ uint32_t cid = samd_read_cid(t);
+ uint32_t pid = samd_read_pid(t);
+
+ /* Check the ARM Coresight Component and Perhiperal IDs */
+ if ((cid != SAMD_CID_VALUE) ||
+ ((pid & SAMD_PID_MASK) != SAMD_PID_CONST_VALUE))
+ return false;
+
+ /* Read the Device ID */
+ uint32_t did = target_mem_read32(t, SAMD_DSU_DID);
+
+ /* If the Device ID matches */
+ if ((did & SAMD_DID_MASK) != SAMD_DID_CONST_VALUE)
+ return false;
+
+ uint32_t ctrlstat = target_mem_read32(t, SAMD_DSU_CTRLSTAT);
+ struct samd_descr samd = samd_parse_device_id(did);
+
+ /* Protected? */
+ bool protected = (ctrlstat & SAMD_STATUSB_PROT);
+
+ /* Part String */
+ if (protected) {
+ sprintf(variant_string,
+ "Atmel SAMD%d%c%dA%s (rev %c) (PROT=1)",
+ samd.series, samd.pin, samd.mem,
+ samd.package, samd.revision);
+ } else {
+ sprintf(variant_string,
+ "Atmel SAMD%d%c%dA%s (rev %c)",
+ samd.series, samd.pin, samd.mem,
+ samd.package, samd.revision);
+ }
+
+ /* Setup Target */
+ t->driver = variant_string;
+ t->reset = samd_reset;
+
+ if (samd.series == 20 && samd.revision == 'B') {
+ /**
+ * These functions check for and
+ * extended reset. Appears to be
+ * related to Errata 35.4.1 ref 12015
+ */
+ t->detach = samd20_revB_detach;
+ t->halt_resume = samd20_revB_halt_resume;
+ }
+ if (protected) {
+ /**
+ * Overload the default cortexm attach
+ * for when the samd is protected.
+ * This function allows users to
+ * attach on a temporary basis so they
+ * can rescue the device.
+ */
+ t->attach = samd_protected_attach;
+ }
+
+ t->xml_mem_map = samd_xml_memory_map;
+ t->flash_erase = samd_flash_erase;
+ t->flash_write = samd_flash_write;
+ target_add_commands(t, samd_cmd_list, "SAMD");
+
+ /* If we're not in reset here */
+ if (!connect_assert_srst) {
+ /* We'll have to release the target from
+ * extended reset to make attach possible */
+ if (target_mem_read32(t, SAMD_DSU_CTRLSTAT) &
+ SAMD_STATUSA_CRSTEXT) {
+
+ /* Write bit to clear from extended reset */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT,
+ SAMD_STATUSA_CRSTEXT);
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Temporary (until next reset) flash memory locking / unlocking
+ */
+static void samd_lock_current_address(target *t)
+{
+ /* Issue the unlock command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_LOCK);
+}
+static void samd_unlock_current_address(target *t)
+{
+ /* Issue the unlock command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_UNLOCK);
+}
+
+/**
+ * Erase flash row by row
+ */
+static int samd_flash_erase(target *t, uint32_t addr, size_t len)
+{
+ addr &= ~(SAMD_ROW_SIZE - 1);
+ len &= ~(SAMD_ROW_SIZE - 1);
+
+ while (len) {
+ /* Write address of first word in row to erase it */
+ /* Must be shifted right for 16-bit address, see Datasheet §20.8.8 Address */
+ target_mem_write32(t, SAMD_NVMC_ADDRESS, addr >> 1);
+
+ /* Unlock */
+ samd_unlock_current_address(t);
+
+ /* Issue the erase command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_ERASEROW);
+ /* Poll for NVM Ready */
+ while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
+ if (target_check_error(t))
+ return -1;
+
+ /* Lock */
+ samd_lock_current_address(t);
+
+ addr += SAMD_ROW_SIZE;
+ len -= SAMD_ROW_SIZE;
+ }
+
+ return 0;
+}
+
+/**
+ * Write flash page by page
+ */
+static int samd_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
+{
+ /* Find the size of our 32-bit data buffer */
+ uint32_t offset = dest % 4;
+ uint32_t words = (offset + len + 3) / 4;
+ uint32_t data[words], i = 0;
+
+ /* Populate the data buffer */
+ memset((uint8_t *)data, 0xFF, words * 4);
+ memcpy((uint8_t *)data + offset, src, len);
+
+ /* The address of the first word involved in the write */
+ uint32_t addr = dest & ~0x3;
+ /* The address of the last word involved in the write */
+ uint32_t end = (dest + len - 1) & ~0x3;
+
+ /* The start address of the first page involved in the write */
+ uint32_t first_page = dest & ~(SAMD_PAGE_SIZE - 1);
+ /* The start address of the last page involved in the write */
+ uint32_t last_page = (dest + len - 1) & ~(SAMD_PAGE_SIZE - 1);
+ uint32_t next_page;
+ uint32_t length;
+
+ for (uint32_t page = first_page; page <= last_page; page += SAMD_PAGE_SIZE) {
+ next_page = page + SAMD_PAGE_SIZE;
+ length = MIN(end + 4, next_page) - addr;
+
+ /* Write within a single page. This may be part or all of the page */
+ target_mem_write(t, addr, &data[i], length);
+ addr += length; i += (length >> 2);
+
+ /* If MANW=0 (default) we may have triggered an automatic
+ * write. Ignore this */
+
+ /* Unlock */
+ samd_unlock_current_address(t);
+
+ /* Issue the write page command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_WRITEPAGE);
+
+ /* Poll for NVM Ready */
+ while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
+ if (target_check_error(t))
+ return -1;
+
+ /* Lock */
+ samd_lock_current_address(t);
+ }
+
+ return 0;
+}
+
+/**
+ * Uses the Device Service Unit to erase the entire flash
+ */
+static bool samd_cmd_erase_all(target *t)
+{
+ /* Clear the DSU status bits */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT,
+ SAMD_STATUSA_DONE | SAMD_STATUSA_PERR |
+ SAMD_STATUSA_FAIL);
+
+ /* Erase all */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_CTRL_CHIP_ERASE);
+
+ /* Poll for DSU Ready */
+ uint32_t status;
+ while (((status = target_mem_read32(t, SAMD_DSU_CTRLSTAT)) &
+ (SAMD_STATUSA_DONE | SAMD_STATUSA_PERR | SAMD_STATUSA_FAIL)) == 0)
+ if (target_check_error(t))
+ return false;
+
+ /* Test the protection error bit in Status A */
+ if (status & SAMD_STATUSA_PERR) {
+ gdb_outf("Erase failed due to a protection error.\n");
+ return true;
+ }
+
+ /* Test the fail bit in Status A */
+ if (status & SAMD_STATUSA_FAIL) {
+ gdb_outf("Erase failed.\n");
+ return true;
+ }
+
+ gdb_outf("Erase successful!\n");
+
+ return true;
+}
+
+/**
+ * Sets the NVM region lock bits in the User Row. This value is read
+ * at startup as the default value for the lock bits, and hence does
+ * not take effect until a reset.
+ *
+ * 0x0000 = Lock, 0xFFFF = Unlock (default)
+ */
+static bool samd_set_flashlock(target *t, uint16_t value)
+{
+ uint32_t high = target_mem_read32(t, SAMD_NVM_USER_ROW_HIGH);
+ uint32_t low = target_mem_read32(t, SAMD_NVM_USER_ROW_LOW);
+
+ /* Write address of a word in the row to erase it */
+ /* Must be shifted right for 16-bit address, see Datasheet §20.8.8 Address */
+ target_mem_write32(t, SAMD_NVMC_ADDRESS, SAMD_NVM_USER_ROW_LOW >> 1);
+
+ /* Issue the erase command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_ERASEAUXROW);
+
+ /* Poll for NVM Ready */
+ while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
+ if (target_check_error(t))
+ return -1;
+
+ /* Modify the high byte of the user row */
+ high = (high & 0x0000FFFF) | ((value << 16) & 0xFFFF0000);
+
+ /* Write back */
+ target_mem_write32(t, SAMD_NVM_USER_ROW_LOW, low);
+ target_mem_write32(t, SAMD_NVM_USER_ROW_HIGH, high);
+
+ /* Issue the page write command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_WRITEAUXPAGE);
+
+ return true;
+}
+
+static bool samd_cmd_lock_flash(target *t)
+{
+ return samd_set_flashlock(t, 0x0000);
+}
+
+static bool samd_cmd_unlock_flash(target *t)
+{
+ return samd_set_flashlock(t, 0xFFFF);
+}
+
+static bool samd_cmd_read_userrow(target *t)
+{
+ gdb_outf("User Row: 0x%08x%08x\n",
+ target_mem_read32(t, SAMD_NVM_USER_ROW_HIGH),
+ target_mem_read32(t, SAMD_NVM_USER_ROW_LOW));
+
+ return true;
+}
+
+/**
+ * Reads the 128-bit serial number from the NVM
+ */
+static bool samd_cmd_serial(target *t)
+{
+ gdb_outf("Serial Number: 0x");
+
+ for (uint32_t i = 0; i < 4; i++) {
+ gdb_outf("%08x", target_mem_read32(t, SAMD_NVM_SERIAL(i)));
+ }
+
+ gdb_outf("\n");
+
+ return true;
+}
+
+/**
+ * Returns the size (in bytes) of the current SAM D20's flash memory.
+ */
+static uint32_t samd_flash_size(target *t)
+{
+ /* Read the Device ID */
+ uint32_t did = target_mem_read32(t, SAMD_DSU_DID);
+
+ /* Mask off the device select bits */
+ uint8_t devsel = did & SAMD_DID_DEVSEL_MASK;
+
+ /* Shift the maximum flash size (256KB) down as appropriate */
+ return (0x40000 >> (devsel % 5));
+}
+
+/**
+ * Runs the Memory Built In Self Test (MBIST)
+ */
+static bool samd_cmd_mbist(target *t)
+{
+ /* Write the memory parameters to the DSU */
+ target_mem_write32(t, SAMD_DSU_ADDRESS, 0);
+ target_mem_write32(t, SAMD_DSU_LENGTH, samd_flash_size(t));
+
+ /* Clear the fail bit */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_STATUSA_FAIL);
+
+ /* Write the MBIST command */
+ target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_CTRL_MBIST);
+
+ /* Poll for DSU Ready */
+ uint32_t status;
+ while (((status = target_mem_read32(t, SAMD_DSU_CTRLSTAT)) &
+ (SAMD_STATUSA_DONE | SAMD_STATUSA_PERR | SAMD_STATUSA_FAIL)) == 0)
+ if (target_check_error(t))
+ return false;
+
+ /* Test the protection error bit in Status A */
+ if (status & SAMD_STATUSA_PERR) {
+ gdb_outf("MBIST not run due to protection error.\n");
+ return true;
+ }
+
+ /* Test the fail bit in Status A */
+ if (status & SAMD_STATUSA_FAIL) {
+ gdb_outf("MBIST Fail @ 0x%08x\n",
+ target_mem_read32(t, SAMD_DSU_ADDRESS));
+ } else {
+ gdb_outf("MBIST Passed!\n");
+ }
+
+ return true;
+}
+/**
+ * Sets the security bit
+ */
+static bool samd_cmd_ssb(target *t)
+{
+ /* Issue the ssb command */
+ target_mem_write32(t, SAMD_NVMC_CTRLA,
+ SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_SSB);
+
+ /* Poll for NVM Ready */
+ while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
+ if (target_check_error(t))
+ return -1;
+
+ gdb_outf("Set the security bit! "
+ "You will need to issue 'monitor erase_mass' to clear this.\n");
+
+ return true;
+}
+
diff --git a/src/stm32f1.c b/src/stm32f1.c
index 4cc7b2a..459c159 100644
--- a/src/stm32f1.c
+++ b/src/stm32f1.c
@@ -29,12 +29,10 @@
* Programming manual - STM32F10xxx Flash memory microcontrollers
*/
-#include <stdlib.h>
-#include <string.h>
-
#include "general.h"
#include "adiv5.h"
#include "target.h"
+#include "cortexm.h"
#include "command.h"
#include "gdb_packet.h"
@@ -48,17 +46,21 @@ const struct command_s stm32f1_cmd_list[] = {
};
-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 stm32f1_flash_erase(struct target_s *target, uint32_t addr, int len,
+static int stm32md_flash_erase(target *t, uint32_t addr, size_t len);
+static int stm32hd_flash_erase(target *t, uint32_t addr, size_t len);
+static int stm32f1_flash_erase(target *t, uint32_t addr, size_t len,
uint32_t pagesize);
-static int stm32f1_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
+static int stm32f1_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
static const char stm32f1_driver_str[] = "STM32, Medium density.";
static const char stm32hd_driver_str[] = "STM32, High density.";
static const char stm32f3_driver_str[] = "STM32F3xx";
-static const char stm32f0_driver_str[] = "STM32F0xx";
+static const char stm32f03_driver_str[] = "STM32F03x";
+static const char stm32f04_driver_str[] = "STM32F04x";
+static const char stm32f05_driver_str[] = "STM32F05x";
+static const char stm32f07_driver_str[] = "STM32F07x";
+static const char stm32f09_driver_str[] = "STM32F09x";
static const char stm32f1_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
@@ -101,9 +103,11 @@ static const char stm32hd_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define FLASH_CR_MER (1 << 2)
#define FLASH_CR_PER (1 << 1)
+#define FLASH_OBR_RDPRT (1 << 1)
+
#define FLASH_SR_BSY (1 << 0)
-#define FLASH_OBP_RDP 0x1FFFF800
+#define FLASH_OBP_RDP 0x1FFFF800
#define FLASH_OBP_RDP_KEY 0x5aa5
#define FLASH_OBP_RDP_KEY_F3 0x55AA
@@ -116,117 +120,106 @@ static const char stm32hd_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define DBGMCU_IDCODE 0xE0042000
#define DBGMCU_IDCODE_F0 0x40015800
-uint16_t stm32f1_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:
- 0x2b00, // cmp r3, #0
- 0xd00a, // beq _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
- 0xe7f2, // b _next
-// _done:
- 0xbe00, // bkpt
-// .org 0x28
-// _flashbase:
- 0x2000, 0x4002, // .word 0x40022000 (FPEC_BASE)
-// _addr:
-// 0x0000, 0x0000,
-// _size:
-// 0x0000, 0x0000,
-// _data:
-// ...
+static const uint16_t stm32f1_flash_write_stub[] = {
+#include "../flashstub/stm32f1.stub"
};
-bool stm32f1_probe(struct target_s *target)
-{
+#define SRAM_BASE 0x20000000
+#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(stm32f1_flash_write_stub), 4)
- target->idcode = adiv5_ap_mem_read(adiv5_target_ap(target), DBGMCU_IDCODE) & 0xfff;
- switch(target->idcode) {
+bool stm32f1_probe(target *t)
+{
+ t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff;
+ switch(t->idcode) {
case 0x410: /* Medium density */
case 0x412: /* Low denisty */
case 0x420: /* Value Line, Low-/Medium density */
- target->driver = stm32f1_driver_str;
- target->xml_mem_map = stm32f1_xml_memory_map;
- target->flash_erase = stm32md_flash_erase;
- target->flash_write = stm32f1_flash_write;
- target_add_commands(target, stm32f1_cmd_list, "STM32 LD/MD");
+ t->driver = stm32f1_driver_str;
+ t->xml_mem_map = stm32f1_xml_memory_map;
+ t->flash_erase = stm32md_flash_erase;
+ t->flash_write = stm32f1_flash_write;
+ target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD");
return true;
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 = stm32f1_flash_write;
- target_add_commands(target, stm32f1_cmd_list, "STM32 HD/CL");
+ t->driver = stm32hd_driver_str;
+ t->xml_mem_map = stm32hd_xml_memory_map;
+ t->flash_erase = stm32hd_flash_erase;
+ t->flash_write = stm32f1_flash_write;
+ target_add_commands(t, stm32f1_cmd_list, "STM32 HD/CL");
return true;
case 0x422: /* STM32F30x */
case 0x432: /* STM32F37x */
- target->driver = stm32f3_driver_str;
- target->xml_mem_map = stm32hd_xml_memory_map;
- target->flash_erase = stm32hd_flash_erase;
- target->flash_write = stm32f1_flash_write;
- target_add_commands(target, stm32f1_cmd_list, "STM32F3");
+ t->driver = stm32f3_driver_str;
+ t->xml_mem_map = stm32hd_xml_memory_map;
+ t->flash_erase = stm32hd_flash_erase;
+ t->flash_write = stm32f1_flash_write;
+ target_add_commands(t, stm32f1_cmd_list, "STM32F3");
return true;
}
- target->idcode = adiv5_ap_mem_read(adiv5_target_ap(target), DBGMCU_IDCODE_F0) & 0xfff;
- switch(target->idcode) {
- case 0x440: /* STM32F0 */
- target->driver = stm32f0_driver_str;
- target->xml_mem_map = stm32f1_xml_memory_map;
- target->flash_erase = stm32md_flash_erase;
- target->flash_write = stm32f1_flash_write;
- target_add_commands(target, stm32f1_cmd_list, "STM32F0");
+ t->idcode = target_mem_read32(t, DBGMCU_IDCODE_F0) & 0xfff;
+ switch(t->idcode) {
+ case 0x444: /* STM32F03 RM0091 Rev.7 */
+ case 0x445: /* STM32F04 RM0091 Rev.7 */
+ case 0x440: /* STM32F05 RM0091 Rev.7 */
+ case 0x448: /* STM32F07 RM0091 Rev.7 */
+ case 0x442: /* STM32F09 RM0091 Rev.7 */
+ switch(t->idcode) {
+ case 0x444: /* STM32F03 */
+ t->driver = stm32f03_driver_str;
+ break;
+ case 0x445: /* STM32F04 */
+ t->driver = stm32f04_driver_str;
+ break;
+ case 0x440: /* STM32F05 */
+ t->driver = stm32f05_driver_str;
+ break;
+ case 0x448: /* STM32F07 */
+ t->driver = stm32f07_driver_str;
+ break;
+ case 0x442: /* STM32F09 */
+ t->driver = stm32f09_driver_str;
+ break;
+ }
+ t->xml_mem_map = stm32f1_xml_memory_map;
+ t->flash_erase = stm32md_flash_erase;
+ t->flash_write = stm32f1_flash_write;
+ target_add_commands(t, stm32f1_cmd_list, "STM32F0");
return true;
}
return false;
}
-static void stm32f1_flash_unlock(ADIv5_AP_t *ap)
+static void stm32f1_flash_unlock(target *t)
{
- adiv5_ap_mem_write(ap, FLASH_KEYR, KEY1);
- adiv5_ap_mem_write(ap, FLASH_KEYR, KEY2);
+ target_mem_write32(t, FLASH_KEYR, KEY1);
+ target_mem_write32(t, FLASH_KEYR, KEY2);
}
-static int stm32f1_flash_erase(struct target_s *target, uint32_t addr, int len, uint32_t pagesize)
+static int stm32f1_flash_erase(target *t, uint32_t addr,
+ size_t len, uint32_t pagesize)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
uint16_t sr;
addr &= ~(pagesize - 1);
- len &= ~(pagesize - 1);
+ len = (len + pagesize - 1) & ~(pagesize - 1);
- stm32f1_flash_unlock(ap);
+ stm32f1_flash_unlock(t);
while(len) {
/* Flash page erase instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_PER);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_PER);
/* write address to FMA */
- adiv5_ap_mem_write(ap, FLASH_AR, addr);
+ target_mem_write32(t, FLASH_AR, addr);
/* Flash page erase start instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_STRT | FLASH_CR_PER);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_PER);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
- if(target_check_error(target))
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
+ if(target_check_error(t))
return -1;
len -= pagesize;
@@ -234,73 +227,57 @@ static int stm32f1_flash_erase(struct target_s *target, uint32_t addr, int len,
}
/* Check for error */
- sr = adiv5_ap_mem_read(ap, FLASH_SR);
+ sr = target_mem_read32(t, 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)
+static int stm32hd_flash_erase(target *t, uint32_t addr, size_t len)
{
- return stm32f1_flash_erase(target, addr, len, 0x800);
+ return stm32f1_flash_erase(t, addr, len, 0x800);
}
-static int stm32md_flash_erase(struct target_s *target, uint32_t addr, int len)
+static int stm32md_flash_erase(target *t, uint32_t addr, size_t len)
{
- return stm32f1_flash_erase(target, addr, len, 0x400);
+ return stm32f1_flash_erase(t, addr, len, 0x400);
}
-static int stm32f1_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len)
+static int stm32f1_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
uint32_t offset = dest % 4;
- uint32_t words = (offset + len + 3) / 4;
- uint32_t data[2 + words];
+ uint8_t data[ALIGN(offset + len, 4)];
/* Construct data buffer used by stub */
- data[0] = dest - offset;
- data[1] = words * 4; /* length must always be a multiple of 4 */
- data[2] = 0xFFFFFFFF; /* pad partial words with all 1s to avoid */
- data[words + 1] = 0xFFFFFFFF; /* damaging overlapping areas */
- memcpy((uint8_t *)&data[2] + offset, src, len);
+ /* pad partial words with all 1s to avoid damaging overlapping areas */
+ memset(data, 0xff, sizeof(data));
+ memcpy((uint8_t *)data + offset, src, len);
/* Write stub and data to target ram and set PC */
- target_mem_write_words(target, 0x20000000, (void*)stm32f1_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(ap, FLASH_SR) & SR_ERROR_MASK)
- return -1;
-
- return 0;
+ target_mem_write(t, SRAM_BASE, stm32f1_flash_write_stub,
+ sizeof(stm32f1_flash_write_stub));
+ target_mem_write(t, STUB_BUFFER_BASE, data, sizeof(data));
+ return cortexm_run_stub(t, SRAM_BASE, dest - offset,
+ STUB_BUFFER_BASE, sizeof(data), 0);
}
static bool stm32f1_cmd_erase_mass(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
- stm32f1_flash_unlock(ap);
+ stm32f1_flash_unlock(t);
/* Flash mass erase start instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_MER);
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_MER);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
if(target_check_error(t))
return false;
/* Check for error */
- uint16_t sr = adiv5_ap_mem_read(ap, FLASH_SR);
+ uint16_t sr = target_mem_read32(t, FLASH_SR);
if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP))
return false;
@@ -309,38 +286,65 @@ static bool stm32f1_cmd_erase_mass(target *t)
static bool stm32f1_option_erase(target *t)
{
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
/* Erase option bytes instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_OPTER | FLASH_CR_OPTWRE);
- adiv5_ap_mem_write(ap, FLASH_CR,
- FLASH_CR_STRT | FLASH_CR_OPTER | FLASH_CR_OPTWRE);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_OPTER | FLASH_CR_OPTWRE);
+ target_mem_write32(t, FLASH_CR,
+ FLASH_CR_STRT | FLASH_CR_OPTER | FLASH_CR_OPTWRE);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
if(target_check_error(t))
return false;
return true;
}
-static bool stm32f1_option_write(target *t, uint32_t addr, uint16_t value)
+static bool stm32f1_option_write_erased(target *t, uint32_t addr, uint16_t value)
{
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
+ if (value == 0xffff)
+ return true;
/* Erase option bytes instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, FLASH_CR_OPTPG | FLASH_CR_OPTWRE);
- adiv5_ap_mem_write_halfword(ap, addr, value);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_OPTPG | FLASH_CR_OPTWRE);
+ target_mem_write16(t, addr, value);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
if(target_check_error(t))
return false;
return true;
}
+static bool stm32f1_option_write(target *t, uint32_t addr, uint16_t value)
+{
+ uint16_t opt_val[8];
+ int i, index;
+
+ index = (addr - FLASH_OBP_RDP) / 2;
+ if ((index < 0) || (index > 7))
+ return false;
+ /* Retrieve old values */
+ for (i = 0; i < 16; i = i +4) {
+ uint32_t val = target_mem_read32(t, FLASH_OBP_RDP + i);
+ opt_val[i/2] = val & 0xffff;
+ opt_val[i/2 +1] = val >> 16;
+ }
+ if (opt_val[index] == value)
+ return true;
+ /* Check for erased value */
+ if (opt_val[index] != 0xffff)
+ if (!(stm32f1_option_erase(t)))
+ return false;
+ opt_val[index] = value;
+ /* Write changed values*/
+ for (i = 0; i < 8; i++)
+ if (!(stm32f1_option_write_erased
+ (t, FLASH_OBP_RDP + i*2,opt_val[i])))
+ return false;
+ return true;
+}
+
static bool stm32f1_cmd_option(target *t, int argc, char *argv[])
{
uint32_t addr, val;
uint32_t flash_obp_rdp_key;
- ADIv5_AP_t *ap = adiv5_target_ap(t);
+ uint32_t rdprt;
switch(t->idcode) {
case 0x422: /* STM32F30x */
@@ -350,13 +354,18 @@ static bool stm32f1_cmd_option(target *t, int argc, char *argv[])
break;
default: flash_obp_rdp_key = FLASH_OBP_RDP_KEY;
}
- stm32f1_flash_unlock(ap);
- adiv5_ap_mem_write(ap, FLASH_OPTKEYR, KEY1);
- adiv5_ap_mem_write(ap, FLASH_OPTKEYR, KEY2);
+ rdprt = target_mem_read32(t, FLASH_OBR) & FLASH_OBR_RDPRT;
+ stm32f1_flash_unlock(t);
+ target_mem_write32(t, FLASH_OPTKEYR, KEY1);
+ target_mem_write32(t, FLASH_OPTKEYR, KEY2);
if ((argc == 2) && !strcmp(argv[1], "erase")) {
stm32f1_option_erase(t);
- stm32f1_option_write(t, FLASH_OBP_RDP, flash_obp_rdp_key);
+ stm32f1_option_write_erased(t, FLASH_OBP_RDP, flash_obp_rdp_key);
+ } else if (rdprt) {
+ gdb_out("Device is Read Protected\n");
+ gdb_out("Use \"monitor option erase\" to unprotect, erasing device\n");
+ return true;
} else if (argc == 3) {
addr = strtol(argv[1], NULL, 0);
val = strtol(argv[2], NULL, 0);
@@ -368,7 +377,7 @@ static bool stm32f1_cmd_option(target *t, int argc, char *argv[])
if (0 && flash_obp_rdp_key == FLASH_OBP_RDP_KEY_F3) {
/* Reload option bytes on F0 and F3*/
- val = adiv5_ap_mem_read(ap, FLASH_CR);
+ val = target_mem_read32(t, FLASH_CR);
val |= FLASH_CR_OBL_LAUNCH;
stm32f1_option_write(t, FLASH_CR, val);
val &= ~FLASH_CR_OBL_LAUNCH;
@@ -377,7 +386,7 @@ static bool stm32f1_cmd_option(target *t, int argc, char *argv[])
for (int i = 0; i < 0xf; i += 4) {
addr = 0x1ffff800 + i;
- val = adiv5_ap_mem_read(ap, addr);
+ val = target_mem_read32(t, addr);
gdb_outf("0x%08X: 0x%04X\n", addr, val & 0xFFFF);
gdb_outf("0x%08X: 0x%04X\n", addr + 2, val >> 16);
}
diff --git a/src/stm32f4.c b/src/stm32f4.c
index 364fef8..03a33f0 100644
--- a/src/stm32f4.c
+++ b/src/stm32f4.c
@@ -30,25 +30,26 @@
* manual
*/
-#include <stdlib.h>
-#include <string.h>
-
#include "general.h"
#include "adiv5.h"
#include "target.h"
+#include "cortexm.h"
#include "command.h"
+#include "gdb_packet.h"
+static bool stm32f4_cmd_erase_mass(target *t);
static bool stm32f4_cmd_option(target *t, int argc, char *argv[]);
const struct command_s stm32f4_cmd_list[] = {
+ {"erase_mass", (cmd_handler)stm32f4_cmd_erase_mass, "Erase entire flash memory"},
{"option", (cmd_handler)stm32f4_cmd_option, "Manipulate option bytes"},
{NULL, NULL, NULL}
};
-static int stm32f4_flash_erase(struct target_s *target, uint32_t addr, int len);
-static int stm32f4_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
+static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len);
+static int stm32f4_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len);
static const char stm32f4_driver_str[] = "STM32F4xx";
@@ -66,11 +67,19 @@ static const char stm32f4_xml_memory_map[] = "<?xml version=\"1.0\"?>"
" <memory type=\"flash\" start=\"0x8020000\" length=\"0xE0000\">"
" <property name=\"blocksize\">0x20000</property>"
" </memory>"
+ " <memory type=\"flash\" start=\"0x8100000\" length=\"0x10000\">"
+ " <property name=\"blocksize\">0x4000</property>"
+ " </memory>"
+ " <memory type=\"flash\" start=\"0x8110000\" length=\"0x10000\">"
+ " <property name=\"blocksize\">0x10000</property>"
+ " </memory>"
+ " <memory type=\"flash\" start=\"0x8120000\" length=\"0xE0000\">"
+ " <property name=\"blocksize\">0x20000</property>"
+ " </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x30000\"/>"
" <memory type=\"ram\" start=\"0x10000000\" length=\"0x10000\"/>"
"</memory-map>";
-
/* Flash Program ad Erase Controller Register Map */
#define FPEC_BASE 0x40023C00
#define FLASH_ACR (FPEC_BASE+0x00)
@@ -91,6 +100,7 @@ static const char stm32f4_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define FLASH_CR_EOPIE (1 << 24)
#define FLASH_CR_ERRIE (1 << 25)
#define FLASH_CR_STRT (1 << 16)
+#define FLASH_CR_LOCK (1 << 31)
#define FLASH_SR_BSY (1 << 16)
@@ -110,76 +120,54 @@ static const char stm32f4_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define DBGMCU_IDCODE 0xE0042000
/* This routine is uses word access. Only usable on target voltage >2.7V */
-uint16_t stm32f4_flash_write_stub[] = {
-// _start:
- 0x480a, // ldr r0, [pc, #40] // _flashbase
- 0x490b, // ldr r1, [pc, #44] // _addr
- 0x467a, // mov r2, pc
- 0x3230, // adds r2, #48
- 0x4b0a, // ldr r3, [pc, #36] // _size
- 0x4d07, // ldr r5, [pc, #28] // _cr
-// _next:
- 0xb153, // cbz r3, _done
- 0x6105, // str r5, [r0, #16]
- 0x6814, // ldr r4, [r2]
- 0x600c, // str r4, [r1]
-// _wait:
- 0x89c4, // ldrb r4, [r0, #14]
- 0x2601, // movs r6, #1
- 0x4234, // tst r4, r6
- 0xd1fb, // bne _wait
-
- 0x3b04, // subs r3, #4
- 0x3104, // adds r1, #4
- 0x3204, // adds r2, #4
- 0xe7f3, // b _next
-// _done:
- 0xbe00, // bkpt
- 0x0000,
-// .org 0x28
-//_cr:
- 0x0201, 0x0000, //.word 0x00000201 (Value to write to FLASH_CR) */
-// _flashbase:
- 0x3c00, 0x4002, // .word 0x40023c00 (FPEC_BASE)
-// _addr:
-// 0x0000, 0x0000,
-// _size:
-// 0x0000, 0x0000,
-// _data:
-// ...
+static const uint16_t stm32f4_flash_write_stub[] = {
+#include "../flashstub/stm32f4.stub"
};
-bool stm32f4_probe(struct target_s *target)
+#define SRAM_BASE 0x20000000
+#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(stm32f4_flash_write_stub), 4)
+
+bool stm32f4_probe(target *t)
{
uint32_t idcode;
- idcode = adiv5_ap_mem_read(adiv5_target_ap(target), DBGMCU_IDCODE);
+ idcode = target_mem_read32(t, DBGMCU_IDCODE);
switch(idcode & 0xFFF) {
case 0x411: /* Documented to be 0x413! This is what I read... */
- case 0x413:
- target->driver = stm32f4_driver_str;
- target->xml_mem_map = stm32f4_xml_memory_map;
- target->flash_erase = stm32f4_flash_erase;
- target->flash_write = stm32f4_flash_write;
- target_add_commands(target, stm32f4_cmd_list, "STM32F4");
+ case 0x413: /* F407VGT6 */
+ case 0x419: /* 427/437 */
+ case 0x423: /* F401 B/C RM0368 Rev.3 */
+ case 0x431: /* F411 RM0383 Rev.4 */
+ case 0x433: /* F401 D/E RM0368 Rev.3 */
+ t->xml_mem_map = stm32f4_xml_memory_map;
+ t->driver = stm32f4_driver_str;
+ t->flash_erase = stm32f4_flash_erase;
+ t->flash_write = stm32f4_flash_write;
+ target_add_commands(t, stm32f4_cmd_list, "STM32F4");
return true;
}
return false;
}
+static void stm32f4_flash_unlock(target *t)
+{
+ if (target_mem_read32(t, FLASH_CR) & FLASH_CR_LOCK) {
+ /* Enable FPEC controller access */
+ target_mem_write32(t, FLASH_KEYR, KEY1);
+ target_mem_write32(t, FLASH_KEYR, KEY2);
+ }
+}
-static int stm32f4_flash_erase(struct target_s *target, uint32_t addr, int len)
+static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
uint16_t sr;
uint32_t cr;
uint32_t pagesize;
addr &= 0x07FFC000;
- /* Enable FPEC controller access */
- adiv5_ap_mem_write(ap, FLASH_KEYR, KEY1);
- adiv5_ap_mem_write(ap, FLASH_KEYR, KEY2);
+ stm32f4_flash_unlock(t);
+
while(len) {
if (addr < 0x10000) { /* Sector 0..3 */
cr = (addr >> 11);
@@ -195,13 +183,13 @@ static int stm32f4_flash_erase(struct target_s *target, uint32_t addr, int len)
}
cr |= FLASH_CR_EOPIE | FLASH_CR_ERRIE | FLASH_CR_SER;
/* Flash page erase instruction */
- adiv5_ap_mem_write(ap, FLASH_CR, cr);
+ target_mem_write32(t, FLASH_CR, cr);
/* write address to FMA */
- adiv5_ap_mem_write(ap, FLASH_CR, cr | FLASH_CR_STRT);
+ target_mem_write32(t, FLASH_CR, cr | FLASH_CR_STRT);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
- if(target_check_error(target))
+ while(target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
+ if(target_check_error(t))
return -1;
len -= pagesize;
@@ -209,67 +197,79 @@ static int stm32f4_flash_erase(struct target_s *target, uint32_t addr, int len)
}
/* Check for error */
- sr = adiv5_ap_mem_read(ap, FLASH_SR);
+ sr = target_mem_read32(t, FLASH_SR);
if(sr & SR_ERROR_MASK)
return -1;
return 0;
}
-static int stm32f4_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len)
+static int stm32f4_flash_write(target *t, uint32_t dest,
+ const uint8_t *src, size_t len)
{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
uint32_t offset = dest % 4;
- uint32_t words = (offset + len + 3) / 4;
- uint32_t data[2 + words];
- uint16_t sr;
+ uint8_t data[ALIGN(offset + len, 4)];
/* Construct data buffer used by stub */
- data[0] = dest - offset;
- data[1] = words * 4; /* length must always be a multiple of 4 */
- data[2] = 0xFFFFFFFF; /* pad partial words with all 1s to avoid */
- data[words + 1] = 0xFFFFFFFF; /* damaging overlapping areas */
- memcpy((uint8_t *)&data[2] + offset, src, len);
-
- /* Write stub and data to target ram and set PC */
- target_mem_write_words(target, 0x20000000, (void*)stm32f4_flash_write_stub, 0x30);
- target_mem_write_words(target, 0x20000030, data, sizeof(data));
- target_pc_write(target, 0x20000000);
- if(target_check_error(target))
- return -1;
+ /* pad partial words with all 1s to avoid damaging overlapping areas */
+ memset(data, 0xff, sizeof(data));
+ memcpy((uint8_t *)data + offset, src, len);
+
+ /* Write buffer to target ram call stub */
+ target_mem_write(t, SRAM_BASE, stm32f4_flash_write_stub,
+ sizeof(stm32f4_flash_write_stub));
+ target_mem_write(t, STUB_BUFFER_BASE, data, sizeof(data));
+ return cortexm_run_stub(t, SRAM_BASE, dest - offset,
+ STUB_BUFFER_BASE, sizeof(data), 0);
+}
- /* Execute the stub */
- target_halt_resume(target, 0);
- while(!target_halt_wait(target));
+static bool stm32f4_cmd_erase_mass(target *t)
+{
+ const char spinner[] = "|/-\\";
+ int spinindex = 0;
+
+ gdb_out("Erasing flash... This may take a few seconds. ");
+ stm32f4_flash_unlock(t);
+
+ /* Flash mass erase start instruction */
+ target_mem_write32(t, FLASH_CR, FLASH_CR_MER);
+ target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER);
+
+ /* Read FLASH_SR to poll for BSY bit */
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) {
+ gdb_outf("\b%c", spinner[spinindex++ % 4]);
+ if(target_check_error(t)) {
+ gdb_out("\n");
+ return false;
+ }
+ }
+ gdb_out("\n");
/* Check for error */
- sr = adiv5_ap_mem_read(ap, FLASH_SR);
- if(sr & SR_ERROR_MASK)
- return -1;
+ uint16_t sr = target_mem_read32(t, FLASH_SR);
+ if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP))
+ return false;
- return 0;
+ return true;
}
static bool stm32f4_option_write(target *t, uint32_t value)
{
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
- adiv5_ap_mem_write(ap, FLASH_OPTKEYR, OPTKEY1);
- adiv5_ap_mem_write(ap, FLASH_OPTKEYR, OPTKEY2);
+ target_mem_write32(t, FLASH_OPTKEYR, OPTKEY1);
+ target_mem_write32(t, FLASH_OPTKEYR, OPTKEY2);
value &= ~FLASH_OPTCR_RESERVED;
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
+ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
if(target_check_error(t))
return -1;
/* WRITE option bytes instruction */
- adiv5_ap_mem_write(ap, FLASH_OPTCR, value);
- adiv5_ap_mem_write(ap, FLASH_OPTCR, value | FLASH_OPTCR_OPTSTRT);
+ target_mem_write32(t, FLASH_OPTCR, value);
+ target_mem_write32(t, FLASH_OPTCR, value | FLASH_OPTCR_OPTSTRT);
/* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
+ while(target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY)
if(target_check_error(t))
return false;
- adiv5_ap_mem_write(ap, FLASH_OPTCR, value | FLASH_OPTCR_OPTLOCK);
+ target_mem_write32(t, FLASH_OPTCR, value | FLASH_OPTCR_OPTLOCK);
return true;
}
@@ -277,18 +277,20 @@ static bool stm32f4_cmd_option(target *t, int argc, char *argv[])
{
uint32_t addr, val;
- ADIv5_AP_t *ap = adiv5_target_ap(t);
-
- if ((argc == 3) && !strcmp(argv[1], "write")) {
+ if ((argc == 2) && !strcmp(argv[1], "erase")) {
+ stm32f4_option_write(t, 0x0fffaaed);
+ }
+ else if ((argc == 3) && !strcmp(argv[1], "write")) {
val = strtoul(argv[2], NULL, 0);
stm32f4_option_write(t, val);
} else {
+ gdb_out("usage: monitor option erase\n");
gdb_out("usage: monitor option write <value>\n");
}
for (int i = 0; i < 0xf; i += 8) {
addr = 0x1fffC000 + i;
- val = adiv5_ap_mem_read(ap, addr);
+ val = target_mem_read32(t, addr);
gdb_outf("0x%08X: 0x%04X\n", addr, val & 0xFFFF);
}
return true;
diff --git a/src/stm32l0.c b/src/stm32l0.c
new file mode 100644
index 0000000..8193b12
--- /dev/null
+++ b/src/stm32l0.c
@@ -0,0 +1,1019 @@
+/*
+ * This file is part of the Black Magic Debug project.
+ *
+ * Copyright (C) 2014,2015 Marc Singer <elf@woollysoft.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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/>.
+ */
+
+/* Description
+ -----------
+
+ This is an implementation of the target-specific functions for the
+ STM32L0x[1] and STM32L1x[2] families of ST Microelectronics MCUs,
+ Cortex M0+ SOCs. The NVM interface is substantially similar to the
+ STM32L1x parts. This module is written to better generalize the
+ NVM interface and to provide more features.
+
+ [1] ST Microelectronics Document RM0377 (DocID025942), "Reference
+ manual for Ultra-low-power STM32L0x1 advanced ARM-based 32-bit
+ MCUs," April 2014.
+
+ [2] ST Microelectronics Document RM0038 (DocID15965, "..."Reference
+ manual for STM32L100xx, STM32L151xx, STM32L152xx and STM32L162xx
+ advanced ARM®-based 32-bit MCUs, " July 2014
+
+
+ NOTES
+ =====
+
+ o Stubbed and non-stubbed NVM operation functions. The STM32L0xx
+ appears to behave differently from other STM32 cores. When it
+ enters a fault state it will not exit this state without a
+ reset. However, the reset will immediately enter a fault state
+ if program flash is erased. When in this state, it will not
+ permit execution of code in RAM in the way that other cores
+ will. Changing the PC to the start of RAM and single stepping
+ will immediately HardFault.
+
+ The stub functions can be both faster and simpler because they
+ have direct access to the MCU. So, to permit stub operation in
+ the best circumstances, the NVM operation functions will check
+ the MCU state and either execute the stub or non-stub version
+ accordingly. The user can override stubs as well with a command
+ in case the detection feature fails...which it seems to do in
+ most cases.
+
+ o Erase would be more efficient if we checked for non-blank-ness
+ before initiating an erase. This would have to be done in a stub
+ for efficiency.
+
+ o Mass erase unimplemented. The method for performing a mass erase
+ is to set the options for read protection, reload the option
+ bytes, set options for no protection, and then reload the option
+ bytes again. The command fails because we lose contact with the
+ target when we perform the option byte reload. For the time
+ being, the command is disabled.
+
+ The body of the function was the following. It is left here for
+ reference in case someone either discovers what is wrong with
+ these lines, or a change is made to the emulator that allows it
+ to regain control of the target after the option byte reload.
+
+ stm32l0_option_write(t, 0x1ff80000, 0xffff0000);
+ target_mem_write32(target, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
+ stm32l0_option_write(t, 0x1ff80000, 0xff5500aa);
+ target_mem_write32(target, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
+
+ uint32_t sr;
+ do {
+ sr = target_mem_read32(target, STM32L0_NVM_SR);
+ } while (sr & STM32L0_NVM_SR_BSY);
+
+ o Errors. We probably should clear SR errors immediately after
+ detecting them. If we don't then we always must wait for the NVM
+ module to complete the last operation before we can start another.
+
+ o There are minor inconsistencies between the stm32l0 and the
+ stm32l1 in when handling NVM operations.
+
+ o When we erase or write individual words (not half-pages) on the
+ stm32l0, we set the PROG bit. On the stm32l1 the PROG bit is
+ only set when erasing. This is not documented in the register
+ summaries, but in the functional quick reference. Argh.
+
+ o On the STM32L1xx, PECR can only be changed when the NVM
+ hardware is idle. The STM32L0xx allows the PECR to be updated
+ while an operation is in progress.
+
+ o Performance. The throughput for writing is not high. We
+ suspected it may be possible to improve throughput significantly
+ by increasing the MCU clock. The code, as is, offers a
+ simplicity without undue knowledge of the inner workings of the
+ MCUs. Increasing clock frequencies would require substantial
+ knowledge of the clock tree.
+
+ FWIW, this was tried. We verified that the system clocks were
+ changed, but the flash write was no faster. It looks like this
+ is due to the fact that the emulator performs a target reset
+ before executing the flash operations, bringing the system back
+ to the reset state clocking.
+
+*/
+
+#include "general.h"
+#include "adiv5.h"
+#include "target.h"
+#include "command.h"
+#include "gdb_packet.h"
+#include "cortexm.h"
+
+#include "stm32lx-nvm.h"
+
+static int inhibit_stubs; /* Local option to force non-stub flash IO */
+
+static int stm32lx_nvm_erase(target *t, uint32_t addr, size_t len);
+static int stm32lx_nvm_write(target *t, uint32_t dest, const uint8_t* src,
+ size_t size);
+
+static int stm32lx_nvm_prog_erase(target *t, uint32_t addr, size_t len);
+static int stm32lx_nvm_prog_write(target *t, uint32_t dest, const uint8_t* src,
+ size_t size);
+
+static int stm32lx_nvm_prog_erase_stubbed(target *t, uint32_t addr, size_t len);
+static int stm32lx_nvm_prog_write_stubbed(target *t, uint32_t dest,
+ const uint8_t* src, size_t size);
+
+static int stm32lx_nvm_data_erase(target *t, uint32_t addr, size_t len);
+static int stm32lx_nvm_data_write(target *t, uint32_t dest,
+ const uint8_t* src, size_t size);
+
+static bool stm32lx_cmd_option(target* t, int argc, char** argv);
+static bool stm32lx_cmd_eeprom(target* t, int argc, char** argv);
+static bool stm32lx_cmd_stubs(target* t, int argc, char** argv);
+
+static const struct command_s stm32lx_cmd_list[] = {
+ { "stubs", (cmd_handler) stm32lx_cmd_stubs,
+ "Enable/disable NVM operation stubs" },
+ { "option", (cmd_handler) stm32lx_cmd_option,
+ "Manipulate option bytes"},
+ { "eeprom", (cmd_handler) stm32lx_cmd_eeprom,
+ "Manipulate EEPROM(NVM data) memory"},
+ { 0 },
+};
+
+enum {
+ STM32L0_DBGMCU_IDCODE_PHYS = 0x40015800,
+ STM32L1_DBGMCU_IDCODE_PHYS = 0xe0042000,
+};
+
+static const char stm32l0_driver_str[] = "STM32L0xx";
+
+static const char stm32l0_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>"
+ /* Program flash; ranges up to 64KiB(0x10000). */
+ " <memory type=\"flash\" start=\"0x08000000\" length=\"0x10000\">"
+ " <property name=\"blocksize\">0x80</property>"
+ " </memory>"
+ /* Data(EEPROM) NVRAM; ranges up to 2KiB(0x800). */
+ " <memory type=\"flash\" start=\"0x08080000\" length=\"0x800\">"
+ " <property name=\"blocksize\">0x4</property>"
+ " </memory>"
+ /* SRAM; ranges up to 8KiB(0x2000). */
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x2000\"/>"
+ "</memory-map>";
+
+static const char stm32l1_driver_str[] = "STM32L1xx";
+
+static const char stm32l1_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>"
+ /* Program flash; ranges from 32KiB to 512KiB(0x80000). */
+ " <memory type=\"flash\" start=\"0x08000000\" length=\"0x80000\">"
+ " <property name=\"blocksize\">0x100</property>"
+ " </memory>"
+ /* Data(EEPROM) NVRAM; ranges from 2K to 16KiB(0x4000). */
+ " <memory type=\"flash\" start=\"0x08080000\" length=\"0x4000\">"
+ " <property name=\"blocksize\">0x4</property>"
+ " </memory>"
+ /* SRAM; ranges from 4KiB to 80KiB(0x14000). */
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x14000\"/>"
+ "</memory-map>";
+
+static const uint16_t stm32l0_nvm_prog_write_stub [] = {
+#include "../flashstub/stm32l05x-nvm-prog-write.stub"
+};
+
+static const uint16_t stm32l0_nvm_prog_erase_stub [] = {
+#include "../flashstub/stm32l05x-nvm-prog-erase.stub"
+};
+
+static uint32_t stm32lx_nvm_prog_page_size(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return STM32L0_NVM_PROG_PAGE_SIZE;
+ default: /* STM32L1xx */
+ return STM32L1_NVM_PROG_PAGE_SIZE;
+ }
+}
+
+static bool stm32lx_is_stm32l1(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return false;
+ default: /* STM32L1xx */
+ return true;
+ }
+}
+
+static uint32_t stm32lx_nvm_eeprom_size(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return STM32L0_NVM_EEPROM_SIZE;
+ default: /* STM32L1xx */
+ return STM32L1_NVM_EEPROM_SIZE;
+ }
+}
+
+static uint32_t stm32lx_nvm_phys(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return STM32L0_NVM_PHYS;
+ default: /* STM32L1xx */
+ return STM32L1_NVM_PHYS;
+ }
+}
+
+static uint32_t stm32lx_nvm_data_page_size(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return STM32L0_NVM_DATA_PAGE_SIZE;
+ default: /* STM32L1xx */
+ return STM32L1_NVM_DATA_PAGE_SIZE;
+ }
+}
+
+static uint32_t stm32lx_nvm_option_size(target *t)
+{
+ switch (t->idcode) {
+ case 0x417: /* STM32L0xx */
+ return STM32L0_NVM_OPT_SIZE;
+ default: /* STM32L1xx */
+ return STM32L1_NVM_OPT_SIZE;
+ }
+}
+
+/** Query MCU memory for an indication as to whether or not the
+ currently attached target is served by this module. We detect the
+ STM32L0xx parts as well as the STM32L1xx's. */
+bool stm32l0_probe(target *t)
+{
+ uint32_t idcode;
+
+ idcode = target_mem_read32(t, STM32L1_DBGMCU_IDCODE_PHYS) & 0xfff;
+ switch (idcode) {
+ case 0x416: /* CAT. 1 device */
+ case 0x429: /* CAT. 2 device */
+ case 0x427: /* CAT. 3 device */
+ case 0x436: /* CAT. 4 device */
+ case 0x437: /* CAT. 5 device */
+ t->idcode = idcode;
+ t->driver = stm32l1_driver_str;
+ t->xml_mem_map = stm32l1_xml_memory_map;
+ t->flash_erase = stm32lx_nvm_erase;
+ t->flash_write = stm32lx_nvm_write;
+ target_add_commands(t, stm32lx_cmd_list, "STM32L1x");
+ return true;
+ }
+
+ idcode = target_mem_read32(t, STM32L0_DBGMCU_IDCODE_PHYS) & 0xfff;
+ switch (idcode) {
+ default:
+ break;
+
+ case 0x417: /* STM32L0x[123] & probably others */
+ t->idcode = idcode;
+ t->driver = stm32l0_driver_str;
+ t->xml_mem_map = stm32l0_xml_memory_map;
+ t->flash_erase = stm32lx_nvm_erase;
+ t->flash_write = stm32lx_nvm_write;
+ target_add_commands(t, stm32lx_cmd_list, "STM32L0x");
+ return true;
+ }
+
+ return false;
+}
+
+
+/** Lock the NVM control registers preventing writes or erases. */
+static void stm32lx_nvm_lock(target *t, uint32_t nvm)
+{
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_PELOCK);
+}
+
+
+/** Unlock the NVM control registers for modifying program or
+ data flash. Returns true if the unlock succeeds. */
+static bool stm32lx_nvm_prog_data_unlock(target* t, uint32_t nvm)
+{
+ /* Always lock first because that's the only way to know that the
+ unlock can succeed on the STM32L0's. */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_PELOCK);
+ target_mem_write32(t, STM32Lx_NVM_PEKEYR(nvm), STM32Lx_NVM_PEKEY1);
+ target_mem_write32(t, STM32Lx_NVM_PEKEYR(nvm), STM32Lx_NVM_PEKEY2);
+ target_mem_write32(t, STM32Lx_NVM_PRGKEYR(nvm), STM32Lx_NVM_PRGKEY1);
+ target_mem_write32(t, STM32Lx_NVM_PRGKEYR(nvm), STM32Lx_NVM_PRGKEY2);
+
+ return !(target_mem_read32(t, STM32Lx_NVM_PECR(nvm))
+ & STM32Lx_NVM_PECR_PRGLOCK);
+}
+
+
+/** Unlock the NVM control registers for modifying option bytes.
+ Returns true if the unlock succeeds. */
+static bool stm32lx_nvm_opt_unlock(target *t, uint32_t nvm)
+{
+ /* Always lock first because that's the only way to know that the
+ unlock can succeed on the STM32L0's. */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_PELOCK);
+ target_mem_write32(t, STM32Lx_NVM_PEKEYR(nvm), STM32Lx_NVM_PEKEY1);
+ target_mem_write32(t, STM32Lx_NVM_PEKEYR(nvm), STM32Lx_NVM_PEKEY2);
+ target_mem_write32(t, STM32Lx_NVM_OPTKEYR(nvm), STM32Lx_NVM_OPTKEY1);
+ target_mem_write32(t, STM32Lx_NVM_OPTKEYR(nvm), STM32Lx_NVM_OPTKEY2);
+
+ return !(target_mem_read32(t, STM32Lx_NVM_PECR(nvm))
+ & STM32Lx_NVM_PECR_OPTLOCK);
+}
+
+
+/** Erase a region of flash using a stub function. This only works
+ when the MCU hasn't entered a fault state(see NOTES). The flash
+ array is erased for all pages from addr to addr+len inclusive. */
+static int stm32lx_nvm_prog_erase_stubbed(target *t,
+ uint32_t addr, size_t size)
+{
+ struct stm32lx_nvm_stub_info info;
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+
+ info.nvm = nvm;
+ info.page_size = stm32lx_nvm_prog_page_size(t);
+
+ /* Load the stub */
+ target_mem_write(t, STM32Lx_STUB_PHYS,
+ &stm32l0_nvm_prog_erase_stub[0],
+ sizeof(stm32l0_nvm_prog_erase_stub));
+
+ /* Setup parameters */
+ info.destination = addr;
+ info.size = size;
+
+ /* Copy parameters */
+ target_mem_write(t, STM32Lx_STUB_INFO_PHYS, &info, sizeof(info));
+
+ /* Execute stub */
+ cortexm_run_stub(t, STM32Lx_STUB_PHYS, 0, 0, 0, 0);
+
+ if (target_mem_read32(t, STM32Lx_NVM_SR(nvm))
+ & STM32Lx_NVM_SR_ERR_M)
+ return -1;
+
+
+ return 0;
+}
+
+
+/** Write to program flash using a stub function. This only works
+ when the MCU hasn't entered a fault state. Once the MCU faults,
+ this function will not succeed because the MCU will fault before
+ executing a single instruction in the stub. */
+static int stm32lx_nvm_prog_write_stubbed(target *t,
+ uint32_t destination,
+ const uint8_t* source,
+ size_t size)
+{
+ struct stm32lx_nvm_stub_info info;
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+ const size_t page_size = stm32lx_nvm_prog_page_size(t);
+
+ /* We can only handle word aligned writes and even
+ word-multiple ranges. The stm32lx's cannot perform
+ anything smaller than a word write due to the ECC bits.
+ So, the caller must do the fixup. */
+ if ((destination & 3) || (size & 3))
+ return -1;
+
+ info.nvm = nvm;
+ info.page_size = page_size;
+
+ /* Load the stub */
+ target_mem_write(t, STM32Lx_STUB_PHYS,
+ &stm32l0_nvm_prog_write_stub[0],
+ sizeof(stm32l0_nvm_prog_write_stub));
+
+ while (size > 0) {
+
+ /* Max transfer size is adjusted in the event that the
+ destination isn't half-page aligned. This allows
+ the stub to write the first partial half-page and
+ then as many half-pages as will fit in the
+ buffer. */
+ size_t max = STM32Lx_STUB_DATA_MAX
+ - (destination - (destination
+ & ~(info.page_size/2 - 1)));
+ size_t cb = size;
+ if (cb > max)
+ cb = max;
+
+ /* Setup parameters */
+ info.source = STM32Lx_STUB_DATA_PHYS;
+ info.destination = destination;
+ info.size = cb;
+
+ /* Copy data to write to flash */
+ target_mem_write(t, info.source, source, info.size);
+
+ /* Move pointers early */
+ destination += cb;
+ source += cb;
+ size -= cb;
+
+ /* Copy parameters */
+ target_mem_write(t, STM32Lx_STUB_INFO_PHYS,
+ &info, sizeof(info));
+
+ /* Execute stub */
+ cortexm_run_stub(t, STM32Lx_STUB_PHYS, 0, 0, 0, 0);
+
+ if (target_mem_read32(t, STM32Lx_NVM_SR(nvm))
+ & STM32Lx_NVM_SR_ERR_M)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/** Erase a region of NVM for STM32Lx. This is the lead function and
+ it will invoke an implementation, stubbed or not depending on the
+ options and the range of addresses. */
+static int stm32lx_nvm_erase(target *t, uint32_t addr, size_t size)
+{
+ if (addr >= STM32Lx_NVM_EEPROM_PHYS)
+ return stm32lx_nvm_data_erase(t, addr, size);
+
+ /* Use stub if not inhibited, the MCU is in a non-exceptonal state
+ and there is stub. */
+ volatile uint32_t regs[20];
+ target_regs_read(t, &regs);
+ if (inhibit_stubs || (regs[16] & 0xf))
+ return stm32lx_nvm_prog_erase(t, addr, size);
+
+ return stm32lx_nvm_prog_erase_stubbed(t, addr, size);
+}
+
+
+/** Write to a region on NVM for STM32Lxxx. This is the lead function
+ and it will invoke an implementation, stubbed or not depending on
+ the options and the range of addresses. Data (EEPROM) writes
+ don't have to care about alignment, but the program flash does.
+ There is a fixup for unaligned program flash writes. */
+static int stm32lx_nvm_write(target *t,
+ uint32_t destination,
+ const uint8_t* source,
+ size_t size)
+{
+ if (destination >= STM32Lx_NVM_EEPROM_PHYS)
+ return stm32lx_nvm_data_write(t, destination, source,
+ size);
+
+ /* Unaligned destinations. To make this feature simple to
+ implement, we do a fixup on the source data as well as the
+ adjusting the write parameters if the caller has asked for
+ an unaligned operation. Padding of this data is zeros
+ because the STM32L's are built that way. */
+ if ((destination & 3) || (size & 3)) {
+ size_t size_aligned = size
+ + (destination & 3)
+ + (((size + 3) & ~3) - size);
+ uint8_t* source_aligned = alloca (size_aligned);
+ memset (source_aligned, 0, size_aligned);
+ memcpy (source_aligned + (destination & 3), source, size);
+ source = source_aligned;
+ destination &= ~3;
+ size = size_aligned;
+ }
+
+ /* Skip stub if the MCU is in a questionable state, or if the
+ user asks us to avoid stubs. */
+ volatile uint32_t regs[20];
+ target_regs_read(t, &regs);
+ if (inhibit_stubs || (regs[16] & 0xf))
+ return stm32lx_nvm_prog_write(t, destination, source,
+ size);
+
+ return stm32lx_nvm_prog_write_stubbed(t, destination, source,
+ size);
+}
+
+
+/** Erase a region of program flash using operations through the debug
+ interface. This is slower than stubbed versions(see NOTES). The
+ flash array is erased for all pages from addr to addr+len
+ inclusive. NVM register file address chosen from target. */
+static int stm32lx_nvm_prog_erase(target *t, uint32_t addr, size_t len)
+{
+ const size_t page_size = stm32lx_nvm_prog_page_size(t);
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+
+ /* Word align */
+ len += (addr & 3);
+ addr &= ~3;
+
+ if (!stm32lx_nvm_prog_data_unlock(t, nvm))
+ return -1;
+
+ /* Flash page erase instruction */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_PROG);
+
+ uint32_t pecr = target_mem_read32(t, STM32Lx_NVM_PECR(nvm));
+ if ((pecr & (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE))
+ != (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE))
+ return -1;
+
+ /* Clear errors. Note that this only works when we wait for the NVM
+ block to complete the last operation. */
+ target_mem_write32(t, STM32Lx_NVM_SR(nvm), STM32Lx_NVM_SR_ERR_M);
+
+ while (len > 0) {
+ /* Write first word of page to 0 */
+ target_mem_write32(t, addr, 0);
+
+ len -= page_size;
+ addr += page_size;
+ }
+
+ /* Disable further programming by locking PECR */
+ stm32lx_nvm_lock(t, nvm);
+
+ /* Wait for completion or an error */
+ while (1) {
+ uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ if (target_check_error(t))
+ return -1;
+ if (sr & STM32Lx_NVM_SR_BSY)
+ continue;
+ if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+
+/** Write to program flash using operations through the debug
+ interface. This is slower than the stubbed write(see NOTES).
+ NVM register file address chosen from target. */
+static int stm32lx_nvm_prog_write(target *t,
+ uint32_t destination,
+ const uint8_t* source_8,
+ size_t size)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+ const bool is_stm32l1 = stm32lx_is_stm32l1(t);
+
+ /* We can only handle word aligned writes and even
+ word-multiple ranges. The stm32lx's cannot perform
+ anything smaller than a word write due to the ECC bits.
+ So, the caller must do the fixup. */
+ if ((destination & 3) || (size & 3))
+ return -1;
+
+ if (!stm32lx_nvm_prog_data_unlock(t, nvm))
+ return -1;
+
+ const size_t half_page_size = stm32lx_nvm_prog_page_size(t)/2;
+ uint32_t* source = (uint32_t*) source_8;
+
+ while (size > 0) {
+
+ /* Wait for BSY to clear because we cannot write the PECR until
+ the previous operation completes on STM32Lxxx. */
+ while (target_mem_read32(t, STM32Lx_NVM_SR(nvm))
+ & STM32Lx_NVM_SR_BSY)
+ if (target_check_error(t)) {
+ return -1;
+ }
+
+ // Either we're not half-page aligned or we have less
+ // than a half page to write
+ if (size < half_page_size
+ || (destination & (half_page_size - 1))) {
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ is_stm32l1
+ ? 0
+ : STM32Lx_NVM_PECR_PROG);
+ size_t c = half_page_size - (destination
+ & (half_page_size - 1));
+
+ if (c > size)
+ c = size;
+ size -= c;
+
+ target_mem_write(t, destination, source, c);
+ source += c/4;
+ destination += c;
+ }
+ // Or we are writing a half-page(s)
+ else {
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ STM32Lx_NVM_PECR_PROG
+ | STM32Lx_NVM_PECR_FPRG);
+
+ size_t c = size & ~(half_page_size - 1);
+ size -= c;
+ target_mem_write(t, destination, source, c);
+ source += c/4;
+ destination += c;
+ }
+ }
+
+ /* Disable further programming by locking PECR */
+ stm32lx_nvm_lock(t, nvm);
+
+ /* Wait for completion or an error */
+ while (1) {
+ uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ if (target_check_error(t)) {
+ return -1;
+ }
+ if (sr & STM32Lx_NVM_SR_BSY)
+ continue;
+ if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP)) {
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+/** Erase a region of data flash using operations through the debug
+ interface . The flash is erased for all pages from addr to
+ addr+len, inclusive, on a word boundary. NVM register file
+ address chosen from target. */
+static int stm32lx_nvm_data_erase(target *t,
+ uint32_t addr, size_t len)
+{
+ const size_t page_size = stm32lx_nvm_data_page_size(t);
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+
+ /* Word align */
+ len += (addr & 3);
+ addr &= ~3;
+
+ if (!stm32lx_nvm_prog_data_unlock(t, nvm))
+ return -1;
+
+ /* Flash data erase instruction */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA);
+
+ uint32_t pecr = target_mem_read32(t, STM32Lx_NVM_PECR(nvm));
+ if ((pecr & (STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA))
+ != (STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA))
+ return -1;
+
+ while (len > 0) {
+ /* Write first word of page to 0 */
+ target_mem_write32(t, addr, 0);
+
+ len -= page_size;
+ addr += page_size;
+ }
+
+ /* Disable further programming by locking PECR */
+ stm32lx_nvm_lock(t, nvm);
+
+ /* Wait for completion or an error */
+ while (1) {
+ uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ if (target_check_error(t))
+ return -1;
+ if (sr & STM32Lx_NVM_SR_BSY)
+ continue;
+ if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+
+/** Write to data flash using operations through the debug interface.
+ NVM register file address chosen from target. Unaligned
+ destination writes are supported (though unaligned sources are
+ not). */
+static int stm32lx_nvm_data_write(target *t,
+ uint32_t destination,
+ const uint8_t* source_8,
+ size_t size)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+ const bool is_stm32l1 = stm32lx_is_stm32l1(t);
+ uint32_t* source = (uint32_t*) source_8;
+
+ if (!stm32lx_nvm_prog_data_unlock(t, nvm))
+ return -1;
+
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ is_stm32l1 ? 0 : STM32Lx_NVM_PECR_DATA);
+
+ while (size) {
+ size -= 4;
+ uint32_t v = *source++;
+ target_mem_write32(t, destination, v);
+ destination += 4;
+
+ if (target_check_error(t))
+ return -1;
+ }
+
+ /* Disable further programming by locking PECR */
+ stm32lx_nvm_lock(t, nvm);
+
+ /* Wait for completion or an error */
+ while (1) {
+ uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ if (target_check_error(t))
+ return -1;
+ if (sr & STM32Lx_NVM_SR_BSY)
+ continue;
+ if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+
+/** Write one option word. The address is the physical address of the
+ word and the value is a complete word value. The caller is
+ responsible for making sure that the value satisfies the proper
+ format where the upper 16 bits are the 1s complement of the lower
+ 16 bits. The funtion returns when the operation is complete.
+ The return value is true if the write succeeded. */
+static bool stm32lx_option_write(target *t, uint32_t address, uint32_t value)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+
+ /* Erase and program option in one go. */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_FIX);
+ target_mem_write32(t, address, value);
+
+ uint32_t sr;
+ do {
+ sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ } while (sr & STM32Lx_NVM_SR_BSY);
+
+ return !(sr & STM32Lx_NVM_SR_ERR_M);
+}
+
+
+/** Write one eeprom value. This version is more flexible than that
+ bulk version used for writing data from the executable file. The
+ address is the physical address of the word and the value is a
+ complete word value. The funtion returns when the operation is
+ complete. The return value is true if the write succeeded.
+ FWIW, byte writing isn't supported because the adiv5 layer
+ doesn't support byte-level operations. */
+static bool stm32lx_eeprom_write(target *t, uint32_t address,
+ size_t cb, uint32_t value)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+ const bool is_stm32l1 = stm32lx_is_stm32l1(t);
+
+ /* Clear errors. */
+ target_mem_write32(t, STM32Lx_NVM_SR(nvm), STM32Lx_NVM_SR_ERR_M);
+
+ /* Erase and program option in one go. */
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ (is_stm32l1 ? 0 : STM32Lx_NVM_PECR_DATA)
+ | STM32Lx_NVM_PECR_FIX);
+ if (cb == 4)
+ target_mem_write32(t, address, value);
+ else if (cb == 2)
+ target_mem_write16(t, address, value);
+ else if (cb == 1)
+ target_mem_write8(t, address, value);
+ else
+ return false;
+
+ uint32_t sr;
+ do {
+ sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm));
+ } while (sr & STM32Lx_NVM_SR_BSY);
+
+ return !(sr & STM32Lx_NVM_SR_ERR_M);
+}
+
+static bool stm32lx_cmd_stubs(target* t,
+ int argc, char** argv)
+{
+ (void) t;
+ if (argc == 1) {
+ gdb_out("usage: mon stubs [enable/disable]\n");
+ }
+ else if (argc == 2) {
+ size_t cb = strlen(argv[1]);
+ if (!strncasecmp(argv[1], "enable", cb))
+ inhibit_stubs = 0;
+ if (!strncasecmp(argv[1], "disable", cb))
+ inhibit_stubs = 1;
+ }
+ gdb_outf("stubs: %sabled\n", inhibit_stubs ? "dis" : "en");
+
+ return true;
+}
+
+static bool stm32lx_cmd_option(target* t, int argc, char** argv)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+ const size_t opt_size = stm32lx_nvm_option_size(t);
+
+ if (!stm32lx_nvm_opt_unlock(t, nvm)) {
+ gdb_out("unable to unlock NVM option bytes\n");
+ return true;
+ }
+
+ size_t cb = strlen(argv[1]);
+
+ if (argc == 2 && !strncasecmp(argv[1], "obl_launch", cb)) {
+ target_mem_write32(t, STM32Lx_NVM_PECR(nvm),
+ STM32Lx_NVM_PECR_OBL_LAUNCH);
+ }
+ else if (argc == 4 && !strncasecmp(argv[1], "raw", cb)) {
+ uint32_t addr = strtoul(argv[2], NULL, 0);
+ uint32_t val = strtoul(argv[3], NULL, 0);
+ gdb_outf("raw %08x <- %08x\n", addr, val);
+ if ( addr < STM32Lx_NVM_OPT_PHYS
+ || addr >= STM32Lx_NVM_OPT_PHYS + opt_size
+ || (addr & 3))
+ goto usage;
+ if (!stm32lx_option_write(t, addr, val))
+ gdb_out("option write failed\n");
+ }
+ else if (argc == 4 && !strncasecmp(argv[1], "write", cb)) {
+ uint32_t addr = strtoul(argv[2], NULL, 0);
+ uint32_t val = strtoul(argv[3], NULL, 0);
+ val = (val & 0xffff) | ((~val & 0xffff) << 16);
+ gdb_outf("write %08x <- %08x\n", addr, val);
+ if ( addr < STM32Lx_NVM_OPT_PHYS
+ || addr >= STM32Lx_NVM_OPT_PHYS + opt_size
+ || (addr & 3))
+ goto usage;
+ if (!stm32lx_option_write(t, addr, val))
+ gdb_out("option write failed\n");
+ }
+ else if (argc == 2 && !strncasecmp(argv[1], "show", cb))
+ ;
+ else
+ goto usage;
+
+ /* Report the current option values */
+ for(unsigned i = 0; i < opt_size; i += sizeof(uint32_t)) {
+ uint32_t addr = STM32Lx_NVM_OPT_PHYS + i;
+ uint32_t val = target_mem_read32(t, addr);
+ gdb_outf("0x%08x: 0x%04x 0x%04x %s\n",
+ addr, val & 0xffff, (val >> 16) & 0xffff,
+ ((val & 0xffff) == ((~val >> 16) & 0xffff))
+ ? "OK" : "ERR");
+ }
+
+ if (stm32lx_is_stm32l1(t)) {
+ uint32_t optr = target_mem_read32(t, STM32Lx_NVM_OPTR(nvm));
+ uint8_t rdprot = (optr >> STM32L1_NVM_OPTR_RDPROT_S)
+ & STM32L1_NVM_OPTR_RDPROT_M;
+ if (rdprot == STM32L1_NVM_OPTR_RDPROT_0)
+ rdprot = 0;
+ else if (rdprot == STM32L1_NVM_OPTR_RDPROT_2)
+ rdprot = 2;
+ else
+ rdprot = 1;
+ gdb_outf("OPTR: 0x%08x, RDPRT %d, SPRMD %d, "
+ "BOR %d, WDG_SW %d, nRST_STP %d, nRST_STBY %d, "
+ "nBFB2 %d\n",
+ optr, rdprot,
+ (optr & STM32L1_NVM_OPTR_SPRMOD) ? 1 : 0,
+ (optr >> STM32L1_NVM_OPTR_BOR_LEV_S)
+ & STM32L1_NVM_OPTR_BOR_LEV_M,
+ (optr & STM32L1_NVM_OPTR_WDG_SW) ? 1 : 0,
+ (optr & STM32L1_NVM_OPTR_nRST_STOP) ? 1 : 0,
+ (optr & STM32L1_NVM_OPTR_nRST_STDBY) ? 1 : 0,
+ (optr & STM32L1_NVM_OPTR_nBFB2) ? 1 : 0);
+ }
+ else {
+ uint32_t optr = target_mem_read32(t, STM32Lx_NVM_OPTR(nvm));
+ uint8_t rdprot = (optr >> STM32L0_NVM_OPTR_RDPROT_S)
+ & STM32L0_NVM_OPTR_RDPROT_M;
+ if (rdprot == STM32L0_NVM_OPTR_RDPROT_0)
+ rdprot = 0;
+ else if (rdprot == STM32L0_NVM_OPTR_RDPROT_2)
+ rdprot = 2;
+ else
+ rdprot = 1;
+ gdb_outf("OPTR: 0x%08x, RDPROT %d, WPRMOD %d, WDG_SW %d, "
+ "BOOT1 %d\n",
+ optr, rdprot,
+ (optr & STM32L0_NVM_OPTR_WPRMOD) ? 1 : 0,
+ (optr & STM32L0_NVM_OPTR_WDG_SW) ? 1 : 0,
+ (optr & STM32L0_NVM_OPTR_BOOT1) ? 1 : 0);
+ }
+
+ goto done;
+
+usage:
+ gdb_out("usage: monitor option [ARGS]\n");
+ gdb_out(" show - Show options in NVM and as"
+ " loaded\n");
+ gdb_out(" obl_launch - Reload options from NVM\n");
+ gdb_out(" write <addr> <value16> - Set option half-word; "
+ "complement computed\n");
+ gdb_out(" raw <addr> <value32> - Set option word\n");
+ gdb_outf("The value of <addr> must be word aligned and from 0x%08x "
+ "to +0x%x\n",
+ STM32Lx_NVM_OPT_PHYS,
+ STM32Lx_NVM_OPT_PHYS + opt_size - sizeof(uint32_t));
+
+done:
+ stm32lx_nvm_lock(t, nvm);
+ return true;
+}
+
+
+static bool stm32lx_cmd_eeprom(target* t, int argc, char** argv)
+{
+ const uint32_t nvm = stm32lx_nvm_phys(t);
+
+ if (!stm32lx_nvm_prog_data_unlock(t, nvm)) {
+ gdb_out("unable to unlock EEPROM\n");
+ return true;
+ }
+
+ size_t cb = strlen(argv[1]);
+
+ if (argc == 4) {
+ uint32_t addr = strtoul(argv[2], NULL, 0);
+ uint32_t val = strtoul(argv[3], NULL, 0);
+
+ if ( addr < STM32Lx_NVM_EEPROM_PHYS
+ || addr >= STM32Lx_NVM_EEPROM_PHYS
+ + stm32lx_nvm_eeprom_size(t))
+ goto usage;
+
+ if (!strncasecmp(argv[1], "byte", cb)) {
+ gdb_outf("write byte 0x%08x <- 0x%08x\n", addr, val);
+ if (!stm32lx_eeprom_write(t, addr, 1, val))
+ gdb_out("eeprom write failed\n");
+ } else if (!strncasecmp(argv[1], "halfword", cb)) {
+ val &= 0xffff;
+ gdb_outf("write halfword 0x%08x <- 0x%04x\n",
+ addr, val);
+ if (addr & 1)
+ goto usage;
+ if (!stm32lx_eeprom_write(t, addr, 2, val))
+ gdb_out("eeprom write failed\n");
+ } else if (!strncasecmp(argv[1], "word", cb)) {
+ gdb_outf("write word 0x%08x <- 0x%08x\n", addr, val);
+ if (addr & 3)
+ goto usage;
+ if (!stm32lx_eeprom_write(t, addr, 4, val))
+ gdb_out("eeprom write failed\n");
+ }
+ else
+ goto usage;
+ }
+ else
+ goto usage;
+
+ goto done;
+
+usage:
+ gdb_out("usage: monitor eeprom [ARGS]\n");
+ gdb_out(" byte <addr> <value8> - Write a byte\n");
+ gdb_out(" halfword <addr> <value16> - Write a half-word\n");
+ gdb_out(" word <addr> <value32> - Write a word\n");
+ gdb_outf("The value of <addr> must in the interval [0x%08x, 0x%x)\n",
+ STM32Lx_NVM_EEPROM_PHYS,
+ STM32Lx_NVM_EEPROM_PHYS
+ + stm32lx_nvm_eeprom_size(t));
+
+done:
+ stm32lx_nvm_lock(t, nvm);
+ return true;
+}
diff --git a/src/stm32l1.c b/src/stm32l1.c
deleted file mode 100644
index 8fd25f7..0000000
--- a/src/stm32l1.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * This file is part of the Black Magic Debug project.
- *
- * Copyright (C) 2012 Vegard Storheil Eriksen <zyp@jvnv.net>
- *
- * 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 STM32L1 target specific functions for detecting
- * the device, providing the XML memory map and Flash memory programming.
- *
- * Refereces:
- * ST doc - RM0038
- * Reference manual - STM32L151xx, STM32L152xx and STM32L162xx
- * advanced ARM-based 32-bit MCUs
- * ST doc - PM0062
- * Programming manual - STM32L151xx, STM32L152xx and STM32L162xx
- * Flash and EEPROM programming
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "general.h"
-#include "adiv5.h"
-#include "target.h"
-#include "command.h"
-#include "gdb_packet.h"
-
-static int stm32l1_flash_erase(struct target_s *target, uint32_t addr, int len);
-static int stm32l1_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len);
-
-static const char stm32l1_driver_str[] = "STM32L1xx";
-
-static const char stm32l1_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\">0x100</property>"
- " </memory>"
- " <memory type=\"ram\" start=\"0x20000000\" length=\"0x5000\"/>"
- "</memory-map>";
-
-/* Flash Controller Register Map */
-#define FLASH_BASE 0x40023C00
-#define FLASH_ACR (FLASH_BASE+0x00)
-#define FLASH_PECR (FLASH_BASE+0x04)
-#define FLASH_PDKEYR (FLASH_BASE+0x08)
-#define FLASH_PEKEYR (FLASH_BASE+0x0C)
-#define FLASH_PRGKEYR (FLASH_BASE+0x10)
-#define FLASH_OPTKEYR (FLASH_BASE+0x14)
-#define FLASH_SR (FLASH_BASE+0x18)
-#define FLASH_OBR (FLASH_BASE+0x1C)
-#define FLASH_WRPR1 (FLASH_BASE+0x20)
-#define FLASH_WRPR2 (FLASH_BASE+0x80)
-#define FLASH_WRPR3 (FLASH_BASE+0x84)
-
-#define FLASH_PECR_FPRG (1 << 10)
-#define FLASH_PECR_ERASE (1 << 9)
-#define FLASH_PECR_PROG (1 << 3)
-
-#define FLASH_SR_BSY (1 << 0)
-#define FLASH_SR_EOP (1 << 1)
-
-#define FLASH_SR_ERROR_MASK (0x1f << 8)
-
-#define PEKEY1 0x89ABCDEF
-#define PEKEY2 0x02030405
-#define PRGKEY1 0x8C9DAEBF
-#define PRGKEY2 0x13141516
-
-#define DBGMCU_IDCODE 0xE0042000
-
-bool stm32l1_probe(struct target_s *target)
-{
- uint32_t idcode;
-
- idcode = adiv5_ap_mem_read(adiv5_target_ap(target), DBGMCU_IDCODE);
- switch(idcode & 0xFFF) {
- case 0x416: /* Medium density */
- case 0x436: /* High density */
- target->driver = stm32l1_driver_str;
- target->xml_mem_map = stm32l1_xml_memory_map;
- target->flash_erase = stm32l1_flash_erase;
- target->flash_write = stm32l1_flash_write;
- return true;
- }
-
- return false;
-}
-
-static void stm32l1_flash_unlock(ADIv5_AP_t *ap)
-{
- adiv5_ap_mem_write(ap, FLASH_PEKEYR, PEKEY1);
- adiv5_ap_mem_write(ap, FLASH_PEKEYR, PEKEY2);
- adiv5_ap_mem_write(ap, FLASH_PRGKEYR, PRGKEY1);
- adiv5_ap_mem_write(ap, FLASH_PRGKEYR, PRGKEY2);
-}
-
-static int stm32l1_flash_erase(struct target_s *target, uint32_t addr, int len)
-{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint16_t sr;
-
- addr &= ~255;
- len &= ~255;
-
- stm32l1_flash_unlock(ap);
-
- /* Flash page erase instruction */
- adiv5_ap_mem_write(ap, FLASH_PECR, FLASH_PECR_ERASE | FLASH_PECR_PROG);
-
- /* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
- if(target_check_error(target))
- return -1;
-
- while(len) {
- /* Write first word of page to 0 */
- adiv5_ap_mem_write(ap, addr, 0);
-
- len -= 256;
- addr += 256;
- }
-
- /* Disable programming mode */
- adiv5_ap_mem_write(ap, FLASH_PECR, 0);
-
- /* Check for error */
- sr = adiv5_ap_mem_read(ap, FLASH_SR);
- if ((sr & FLASH_SR_ERROR_MASK) || !(sr & FLASH_SR_EOP))
- return -1;
-
- return 0;
-}
-
-static int stm32l1_flash_write(struct target_s *target, uint32_t dest,
- const uint8_t *src, int len)
-{
- ADIv5_AP_t *ap = adiv5_target_ap(target);
- uint16_t sr;
-
- /* Handle non word-aligned start */
- if(dest & 3) {
- uint32_t data = 0;
- uint32_t wlen = 4 - (dest & 3);
- if(wlen > len)
- wlen = len;
-
- memcpy((uint8_t *)&data + (dest & 3), src, wlen);
- adiv5_ap_mem_write(ap, dest & ~3, data);
- src += wlen;
- dest += wlen;
- len -= wlen;
- }
-
- /* Handle non half-page-aligned start */
- if(dest & 127 && len >= 4) {
- uint32_t xlen = 128 - (dest & 127);
- if(xlen > len)
- xlen = len & ~3;
-
- target_mem_write_words(target, dest, (uint32_t*)src, xlen);
- src += xlen;
- dest += xlen;
- len -= xlen;
- }
-
- /* Write half-pages */
- if(len > 128) {
- /* Enable half page mode */
- adiv5_ap_mem_write(ap, FLASH_PECR, FLASH_PECR_FPRG | FLASH_PECR_PROG);
-
- /* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
- if(target_check_error(target))
- return -1;
-
- target_mem_write_words(target, dest, (uint32_t*)src, len & ~127);
- src += len & ~127;
- dest += len & ~127;
- len -= len & ~127;
-
- /* Disable half page mode */
- adiv5_ap_mem_write(ap, FLASH_PECR, 0);
-
- /* Read FLASH_SR to poll for BSY bit */
- while(adiv5_ap_mem_read(ap, FLASH_SR) & FLASH_SR_BSY)
- if(target_check_error(target))
- return -1;
- }
-
- /* Handle non-full page at the end */
- if(len >= 4) {
- target_mem_write_words(target, dest, (uint32_t*)src, len & ~3);
- src += len & ~3;
- dest += len & ~3;
- len -= len & ~3;
- }
-
- /* Handle non-full word at the end */
- if(len) {
- uint32_t data = 0;
-
- memcpy((uint8_t *)&data, src, len);
- adiv5_ap_mem_write(ap, dest, data);
- }
-
- /* Check for error */
- sr = adiv5_ap_mem_read(ap, FLASH_SR);
- if ((sr & FLASH_SR_ERROR_MASK) || !(sr & FLASH_SR_EOP))
- return -1;
-
- return 0;
-}
diff --git a/src/target.c b/src/target.c
index b46f9e3..4299a45 100644
--- a/src/target.c
+++ b/src/target.c
@@ -21,9 +21,8 @@
#include "general.h"
#include "target.h"
-#include <stdlib.h>
-
target *target_list = NULL;
+bool connect_assert_srst;
target *target_new(unsigned size)
{