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