Import of working source tree.

This commit is contained in:
Gareth McMullin 2011-02-04 20:23:52 +13:00
commit 406617a2a4
37 changed files with 4577 additions and 0 deletions

41
src/Makefile Normal file
View File

@ -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)

268
src/adiv5.c Normal file
View File

@ -0,0 +1,268 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the transport generic functions of the
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
*
* Issues:
* Currently doesn't use ROM table for introspection, just assumes
* the device is Cortex-M3.
*/
#include <stdio.h>
#include <stdlib.h>
#include "general.h"
#include "jtag_scan.h"
#include "gdb_packet.h"
#include "adiv5.h"
#include "target.h"
#include "cortexm3.h"
#ifndef DO_RESET_SEQ
#define DO_RESET_SEQ 0
#endif
/* Bits in the DP_CTRLSTAT register */
#define CSYSPWRUPACK 0x80000000L
#define CSYSPWRUPREQ 0x40000000L
#define CDBGPWRUPACK 0x20000000L
#define CDBGPWRUPREQ 0x10000000L
#define CDBGRSTACK 0x08000000L
#define CDBGRSTREQ 0x04000000L
/* This belongs elsewhere... */
target *target_list = NULL;
target *cur_target = NULL;
target *last_target = NULL;
static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP";
ADIv5_DP_t *adiv5_dp_list;
/*
ADIv5_DP_t adiv5_dps[5];
int adiv5_dp_count;
*/
ADIv5_AP_t adiv5_aps[5];
int adiv5_ap_count;
static int ap_check_error(struct target_s *target);
static int ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len);
static int ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len);
static int ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len);
static int ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len);
void adiv5_free_all(void)
{
ADIv5_DP_t *dp;
while(adiv5_dp_list) {
dp = adiv5_dp_list->next;
free(adiv5_dp_list);
adiv5_dp_list = dp;
}
adiv5_ap_count = 0;
}
void adiv5_dp_init(ADIv5_DP_t *dp)
{
uint32_t ctrlstat;
dp->next = adiv5_dp_list;
adiv5_dp_list = dp;
ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT);
/* Write request for system and debug power up */
adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat |= CSYSPWRUPREQ | CDBGPWRUPREQ);
/* Wait for acknowledge */
while(((ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT)) &
(CSYSPWRUPACK | CDBGPWRUPACK)) != (CSYSPWRUPACK | CDBGPWRUPACK));
if(DO_RESET_SEQ) {
/* This AP reset logic is described in ADIv5, but fails to work
* correctly on STM32. CDBGRSTACK is never asserted, and we
* just wait forever.
*/
/* Write request for debug reset */
adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat |= CDBGRSTREQ);
/* Wait for acknowledge */
while(!((ctrlstat = adiv5_dp_read(dp, DP_CTRLSTAT)) & CDBGRSTACK));
/* Write request for debug reset release */
adiv5_dp_write(dp, DP_CTRLSTAT, ctrlstat &= ~CDBGRSTREQ);
/* Wait for acknowledge */
while(adiv5_dp_read(dp, DP_CTRLSTAT) & CDBGRSTACK);
}
/* Probe for APs on this DP */
for(int i = 0; i < 256; i++) {
uint32_t idr;
adiv5_dp_write(dp, DP_SELECT, ((uint32_t)i << 24) | 0xF0);
idr = adiv5_dp_read_ap(dp, 0x0C); /* attempt to read IDR */
if(idr) { /* We have a valid AP, adding to list */
target *t;
adiv5_aps[adiv5_ap_count].dp = dp;
adiv5_aps[adiv5_ap_count].apsel = i;
adiv5_aps[adiv5_ap_count].idr = idr;
adiv5_aps[adiv5_ap_count].cfg = adiv5_dp_read_ap(dp, 0x04);
adiv5_aps[adiv5_ap_count].base = adiv5_dp_read_ap(dp, 0x08);
/* Should probe further here... */
/* Prepend to target list... */
t = target_list;
target_list = (void*)calloc(1, sizeof(struct target_ap_s));
target_list->next = t;
((struct target_ap_s *)target_list)->ap = &adiv5_aps[adiv5_ap_count];
target_list->driver = adiv5_driver_str;
target_list->check_error = ap_check_error;
target_list->mem_read_words = ap_mem_read_words;
target_list->mem_write_words = ap_mem_write_words;
target_list->mem_read_bytes = ap_mem_read_bytes;
target_list->mem_write_bytes = ap_mem_write_bytes;
/* The rest sould only be added after checking ROM table */
cm3_probe((void*)target_list);
adiv5_ap_count++;
} else break;
}
}
static int
ap_check_error(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
return adiv5_dp_error(t->ap->dp);
}
static int
ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len)
{
struct target_ap_s *t = (void *)target;
len >>= 2;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, src);
adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
while(--len)
*dest++ = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
*dest++ = adiv5_dp_low_access(t->ap->dp, 0, 1, DP_RDBUFF, 0);
return 0;
}
static int
ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len)
{
struct target_ap_s *t = (void *)target;
uint32_t tmp = src;
adiv5_ap_write(t->ap, 0x00, 0xA2000050);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, src);
adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
while(--len) {
tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x0C, 0);
*dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
}
tmp = adiv5_dp_low_access(t->ap->dp, 0, 1, DP_RDBUFF, 0);
*dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
return 0;
}
static int
ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len)
{
struct target_ap_s *t = (void *)target;
len >>= 2;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, dest);
while(len--)
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x0C, *src++);
return 0;
}
static int
ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
{
struct target_ap_s *t = (void *)target;
uint32_t tmp;
adiv5_ap_write(t->ap, 0x00, 0xA2000050);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, dest);
while(len--) {
tmp = (uint32_t)*src++ << ((dest++ & 3) << 3);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x0C, tmp);
}
return 0;
}
uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr)
{
adiv5_ap_write(ap, 0x00, 0xA2000052);
adiv5_ap_write(ap, 0x04, addr);
return adiv5_ap_read(ap, 0x0C);
}
void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value)
{
adiv5_ap_write(ap, 0x00, 0xA2000052);
adiv5_ap_write(ap, 0x04, addr);
adiv5_ap_write(ap, 0x0C, value);
}
void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value)
{
adiv5_dp_write(ap->dp, DP_SELECT,
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
adiv5_dp_write_ap(ap->dp, addr, value);
}
uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr)
{
uint32_t ret;
adiv5_dp_write(ap->dp, DP_SELECT,
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
ret = adiv5_dp_read_ap(ap->dp, addr);
return ret;
}

123
src/adiv5_jtagdp.c Normal file
View File

@ -0,0 +1,123 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the JTAG-DP specific functions of the
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
*/
#include "general.h"
#include "platform.h"
#include "adiv5.h"
#include "jtag_scan.h"
#include "jtagtap.h"
#include <stdlib.h>
#define JTAGDP_ACK_OK 0x02
#define JTAGDP_ACK_WAIT 0x01
/* 35-bit registers that control the ADIv5 DP */
#define IR_ABORT 0x8
#define IR_DPACC 0xA
#define IR_APACC 0xB
static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr);
static void adiv5_jtagdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
static uint32_t adiv5_jtagdp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp);
static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
uint8_t addr, uint32_t value);
void adiv5_jtag_dp_handler(jtag_dev_t *dev)
{
ADIv5_DP_t *dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
dp->dev = dev;
dp->idcode = dev->idcode;
dp->dp_write = adiv5_jtagdp_write;
dp->dp_read = adiv5_jtagdp_read;
dp->ap_write = adiv5_jtagdp_write_ap;
dp->ap_read = adiv5_jtagdp_read_ap;
dp->error = adiv5_jtagdp_error;
dp->low_access = adiv5_jtagdp_low_access;
adiv5_dp_init(dp);
}
static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
adiv5_jtagdp_low_access(dp, 0, 0, addr, value);
}
static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr)
{
adiv5_jtagdp_low_access(dp, 0, 1, addr, 0);
return adiv5_jtagdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
}
static void adiv5_jtagdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
adiv5_jtagdp_low_access(dp, 1, 0, addr, value);
}
static uint32_t adiv5_jtagdp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
{
adiv5_jtagdp_low_access(dp, 1, 1, addr, 0);
return adiv5_jtagdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
}
static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp)
{
adiv5_jtagdp_low_access(dp, 0, 1, DP_CTRLSTAT, 0);
return adiv5_jtagdp_low_access(dp, 0, 0, DP_CTRLSTAT, 0xF0000032) & 0x32;
}
static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
uint8_t addr, uint32_t value)
{
uint64_t request, response;
uint8_t ack;
request = ((uint64_t)value << 3) | ((addr >> 1) & 0x06) | (RnW?1:0);
jtag_dev_write_ir(dp->dev, APnDP?IR_APACC:IR_DPACC);
do {
jtag_dev_shift_dr(dp->dev, (uint8_t*)&response, (uint8_t*)&request, 35);
ack = response & 0x07;
} while(ack == JTAGDP_ACK_WAIT);
if((ack != JTAGDP_ACK_OK)) {
/* Fatal error if invalid ACK response */
PLATFORM_FATAL_ERROR(1);
}
return (uint32_t)(response >> 3);
}

171
src/adiv5_swdp.c Normal file
View File

@ -0,0 +1,171 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the SW-DP specific functions of the
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
*/
#include "general.h"
#include "platform.h"
#include "adiv5.h"
#include "swdptap.h"
#include "command.h"
#include <stdlib.h>
#define SWDP_ACK_OK 0x01
#define SWDP_ACK_WAIT 0x02
#define SWDP_ACK_FAULT 0x04
static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr);
static void adiv5_swdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
static uint32_t adiv5_swdp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp);
static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
uint8_t addr, uint32_t value);
int adiv5_swdp_scan(void)
{
ADIv5_DP_t *dp;
uint8_t ack;
TARGET_LIST_FREE();
dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
swdptap_init();
/* Read the SW-DP IDCODE register to syncronise */
/* This could be done with adiv_swdp_low_access(), but this doesn't
* allow the ack to be checked here. */
swdptap_seq_out(0b10100101, 8);
ack = swdptap_seq_in(3);
if((ack != SWDP_ACK_OK) || swdptap_seq_in_parity(&dp->idcode, 32)) {
morse("NO TARGETS.", 1);
free(dp);
return -1;
}
dp->dp_write = adiv5_swdp_write;
dp->dp_read = adiv5_swdp_read;
dp->ap_write = adiv5_swdp_write_ap;
dp->ap_read = adiv5_swdp_read_ap;
dp->error = adiv5_swdp_error;
dp->low_access = adiv5_swdp_low_access;
adiv5_dp_init(dp);
if(!target_list) morse("NO TARGETS.", 1);
else morse(NULL, 0);
return target_list?1:0;
}
static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
adiv5_swdp_low_access(dp, 0, 0, addr, value);
}
static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr)
{
return adiv5_swdp_low_access(dp, 0, 1, addr, 0);
}
static void adiv5_swdp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
adiv5_swdp_low_access(dp, 1, 0, addr, value);
}
static uint32_t adiv5_swdp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
{
uint32_t ret;
adiv5_swdp_low_access(dp, 1, 1, addr, 0);
ret = adiv5_swdp_low_access(dp, 0, 1, DP_RDBUFF, 0);
return ret;
}
static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp)
{
uint32_t err, clr = 0;
err = adiv5_swdp_read(dp, DP_CTRLSTAT) & 0x32;
if(err & 0x02) clr |= 0x10; /* STICKORUN */
if(err & 0x10) clr |= 0x02; /* STICKCMP */
if(err & 0x20) clr |= 0x04; /* STICKERR */
adiv5_swdp_write(dp, DP_ABORT, clr);
dp->fault = 0;
return err;
}
static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
uint8_t addr, uint32_t value)
{
uint8_t request = 0b10000001;
uint32_t response;
uint8_t ack;
if(APnDP && dp->fault) return 0;
if(APnDP) request ^= 0b100010;
if(RnW) request ^= 0b100100;
addr &= 0xC;
request |= (addr << 1) & 0b11000;
if((addr == 4) || (addr == 8))
request ^= 0b100000;
do {
swdptap_seq_out(request, 8);
ack = swdptap_seq_in(3);
} while(ack == SWDP_ACK_WAIT);
if(ack == SWDP_ACK_FAULT) {
dp->fault = 1;
return 0;
}
if(ack != SWDP_ACK_OK) {
/* Fatal error if invalid ACK response */
PLATFORM_FATAL_ERROR(1);
}
if(RnW) {
if(swdptap_seq_in_parity(&response, 32)) /* Give up on parity error */
PLATFORM_FATAL_ERROR(1);
} else {
swdptap_seq_out_parity(value, 32);
}
/* REMOVE THIS */
swdptap_seq_out(0, 8);
return response;
}

159
src/command.c Normal file
View File

@ -0,0 +1,159 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements a basic command interpreter for GDB 'monitor'
* commands.
*
* TODO: Add a mechanism for target driver so register new commands.
*/
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include "general.h"
#include "command.h"
#include "gdb_packet.h"
#include "jtag_scan.h"
#include "target.h"
#include "adiv5.h"
void cmd_version(void);
void cmd_help(void);
void cmd_jtag_scan(void);
void cmd_swdp_scan(void);
void cmd_targets(void);
void cmd_morse(void);
const struct command_s cmd_list[] = {
{"version", (cmd_handler)cmd_version, "Display firmware version info"},
{"help", (cmd_handler)cmd_help, "Display help for monitor commands"},
{"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" },
{"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
{"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
{"morse", (cmd_handler)cmd_morse, "Display morse error message" },
{NULL, NULL, NULL}
};
int command_process(char *cmd)
{
const struct command_s *c;
int argc = 0;
const char **argv;
/* Initial estimate for argc */
for(char *s = cmd; *s; s++)
if((*s == ' ') || (*s == '\t')) argc++;
argv = alloca(sizeof(const char *) * argc);
/* Tokenize cmd to find argv */
for(argc = 0, argv[argc] = strtok(cmd, " \t");
argv[argc]; argv[++argc] = strtok(NULL, " \t"));
/* Look for match and call handler */
for(c = cmd_list; c->cmd; c++) {
if(!strcmp(argv[0], c->cmd)) {
c->handler(argc, argv);
return 0;
}
}
return -1;
}
void cmd_version(void)
{
gdb_out("Black Magic Probe (Firmware 1.5, build " BUILDDATE ")\n");
gdb_out("Copyright (C) 2010 Black Sphere Technologies\n");
gdb_out("All rights reserved.\n\n");
}
void cmd_help(void)
{
const struct command_s *c;
for(c = cmd_list; c->cmd; c++)
gdb_outf("%s -- %s\n", c->cmd, c->help);
}
void cmd_jtag_scan(void)
{
int devs = jtag_scan();
if(devs < 0) {
gdb_out("JTAG device scan failed!\n");
return;
}
if(devs == 0) {
gdb_out("JTAG scan found no devices!\n");
return;
}
gdb_outf("Device IR Len IDCODE Description\n");
for(int i = 0; i < jtag_dev_count; i++)
gdb_outf("%d\t%d\t0x%08lX %s\n", i,
jtag_devs[i].ir_len, jtag_devs[i].idcode,
jtag_devs[i].descr);
gdb_out("\n");
cmd_targets();
}
void cmd_swdp_scan(void)
{
if(adiv5_swdp_scan() < 0) {
gdb_out("SW-DP scan failed!\n");
return;
}
gdb_outf("SW-DP detected IDCODE: 0x%08X\n", adiv5_dp_list->idcode);
cmd_targets();
}
void cmd_targets(void)
{
struct target_s *t;
int i;
if(!target_list) {
gdb_out("No usable targets found.\n");
return;
}
gdb_out("Available Targets:\n");
gdb_out("No. Att Driver\n");
for(t = target_list, i = 1; t; t = t->next, i++)
gdb_outf("%2d %c %s\n", i, t==cur_target?'*':' ',
t->driver);
}
void cmd_morse(void)
{
if(morse_msg)
gdb_outf("%s\n", morse_msg);
}

394
src/cortexm3.c Normal file
View File

@ -0,0 +1,394 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements debugging functionality specific to ARM
* the Cortex-M3 core. This should be generic to ARMv7-M as it is
* implemented according to the "ARMv7-M Architectue Reference Manual",
* ARM doc DDI0403C.
*
* Issues:
* There are way too many magic numbers used here.
*/
#include <stdio.h>
#include "general.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "adiv5.h"
#include "target.h"
#include "cortexm3.h"
#include "lmi.h"
#include "stm32_tgt.h"
static char cm3_driver_str[] = "ARM Cortex-M3";
static void cm3_attach(struct target_s *target);
static void cm3_detach(struct target_s *target);
static int ap_regs_read(struct target_s *target, void *data);
static int ap_regs_write(struct target_s *target, const void *data);
static int ap_pc_write(struct target_s *target, const uint32_t val);
static void cm3_reset(struct target_s *target);
static void ap_halt_resume(struct target_s *target, uint8_t step);
static int ap_halt_wait(struct target_s *target);
static void ap_halt_request(struct target_s *target);
static int cm3_set_hw_bp(struct target_s *target, uint32_t addr);
static int cm3_clear_hw_bp(struct target_s *target, uint32_t addr);
static int cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
static int cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
static int cm3_check_hw_wp(struct target_s *target, uint32_t *addr);
/* Watchpoint unit status */
static struct wp_unit_s {
uint32_t addr;
uint8_t type;
uint8_t size;
} hw_watchpoint[4];
/* Breakpoint unit status */
static uint32_t hw_breakpoint[6];
int
cm3_probe(struct target_s *target)
{
target->driver = cm3_driver_str;
target->attach = cm3_attach;
target->detach = cm3_detach;
/* Should probe here to make sure it's Cortex-M3 */
target->regs_read = ap_regs_read;
target->regs_write = ap_regs_write;
// target->pc_read = ap_pc_read;
target->pc_write = ap_pc_write;
target->reset = cm3_reset;
target->halt_request = ap_halt_request;
target->halt_wait = ap_halt_wait;
target->halt_resume = ap_halt_resume;
target->regs_size = 16<<2;
/* if not STM32 try LMI */
if(stm32_probe(target) != 0)
lmi_probe(target);
return 0;
}
static void
cm3_attach(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
int i;
target_halt_request(target);
while(!target_halt_wait(target));
/* Request halt on reset */
/* TRCENA | VC_CORERESET */
adiv5_ap_mem_write(t->ap, 0xE000EDFC, 0x01000401);
/* Clear any stale breakpoints */
for(i = 0; i < 6; i++) {
adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
hw_breakpoint[i] = 0;
}
/* Clear any stale watchpoints */
for(i = 0; i < 4; i++) {
adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
hw_watchpoint[i].type = 0;
}
/* Flash Patch Control Register: set ENABLE */
adiv5_ap_mem_write(t->ap, 0xE0002000, 3);
target->set_hw_bp = cm3_set_hw_bp;
target->clear_hw_bp = cm3_clear_hw_bp;
/* Data Watchpoint and Trace */
target->set_hw_wp = cm3_set_hw_wp;
target->clear_hw_wp = cm3_clear_hw_wp;
target->check_hw_wp = cm3_check_hw_wp;
}
static void
cm3_detach(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
int i;
/* Clear any stale breakpoints */
for(i = 0; i < 6; i++)
adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
/* Clear any stale watchpoints */
for(i = 0; i < 4; i++)
adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
/* Disable debug */
adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, 0xA05F0000UL);
}
static int
ap_regs_read(struct target_s *target, void *data)
{
struct target_ap_s *t = (void *)target;
uint32_t *regs = data;
int i;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x14, 0); /* Required to switch banks */
*regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
for(i = 1; i < 16; i++) {
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i);
*regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
}
return 0;
}
static int
ap_regs_write(struct target_s *target, const void *data)
{
struct target_ap_s *t = (void *)target;
const uint32_t *regs = data;
int i;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x18, *regs++); /* Required to switch banks */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x10000);
for(i = 1; i < 16; i++) {
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x18, *regs++);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i | 0x10000);
}
return 0;
}
static int
ap_pc_write(struct target_s *target, const uint32_t val)
{
struct target_ap_s *t = (void *)target;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x18, val); /* Required to switch banks */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x1000F);
return 0;
}
/* The following three routines implement target halt/resume
* using the core debug registers in the NVIC. */
static void
cm3_reset(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
jtagtap_srst();
/* Request system reset from NVIC: SRST doesn't work correctly */
/* This could be VECTRESET: 0x05FA0001 (reset only core)
* or SYSRESETREQ: 0x05FA0004 (system reset)
*/
adiv5_ap_mem_write(t->ap, 0xE000ED0C, 0x05FA0004);
adiv5_ap_mem_write(t->ap, 0xE000ED0C, 0x05FA0001);
}
static void
ap_halt_request(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, 0xA05F0003UL);
}
static int
ap_halt_wait(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
return adiv5_ap_mem_read(t->ap, 0xE000EDF0UL) & 0x20000;
}
static void
ap_halt_resume(struct target_s *target, uint8_t step)
{
struct target_ap_s *t = (void *)target;
static uint8_t old_step = 0;
/* Disable interrupts while single stepping... */
if(step != old_step) {
adiv5_ap_mem_write(t->ap, 0xE000EDF0UL,
step?0xA05F000FUL:0xA05F0003UL);
old_step = step;
}
adiv5_ap_mem_write(t->ap, 0xE000EDF0UL, step?0xA05F000DUL:0xA05F0001UL);
}
/* The following routines implement hardware breakpoints.
* The Flash Patch and Breakpoint (FPB) system is used. */
static int
cm3_set_hw_bp(struct target_s *target, uint32_t addr)
{
struct target_ap_s *t = (void *)target;
uint32_t val = addr & 0x1FFFFFFC;
int i;
val |= (addr & 2)?0x80000000:0x40000000;
val |= 1;
for(i = 0; i < 6; i++)
if((hw_breakpoint[i] & 1) == 0) break;
if(i == 6) return -1;
hw_breakpoint[i] = addr | 1;
adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, val);
return 0;
}
static int
cm3_clear_hw_bp(struct target_s *target, uint32_t addr)
{
struct target_ap_s *t = (void *)target;
int i;
for(i = 0; i < 6; i++)
if((hw_breakpoint[i] & ~1) == addr) break;
if(i == 6) return -1;
hw_breakpoint[i] = 0;
adiv5_ap_mem_write(t->ap, 0xE0002008 + i*4, 0);
return 0;
}
/* The following routines implement hardware watchpoints.
* The Data Watch and Trace (DWT) system is used. */
static int
cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
{
struct target_ap_s *t = (void *)target;
int i;
switch(len) { /* Convert bytes size to mask size */
case 1: len = 0; break;
case 2: len = 1; break;
case 4: len = 2; break;
default:
return -1;
}
switch(type) { /* Convert gdb type to function type */
case 2: type = 6; break;
case 3: type = 5; break;
case 4: type = 7; break;
default:
return -1;
}
for(i = 0; i < 4; i++)
if((hw_watchpoint[i].type) == 0) break;
if(i == 4) return -2;
hw_watchpoint[i].type = type;
hw_watchpoint[i].addr = addr;
hw_watchpoint[i].size = len;
adiv5_ap_mem_write(t->ap, 0xE0001020 + i*0x10, addr);
adiv5_ap_mem_write(t->ap, 0xE0001024 + i*0x10, len);
adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0x800 | type);
return 0;
}
static int
cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
{
struct target_ap_s *t = (void *)target;
int i;
switch(len) {
case 1: len = 0; break;
case 2: len = 1; break;
case 4: len = 2; break;
default:
return -1;
}
switch(type) {
case 2: type = 6; break;
case 3: type = 5; break;
case 4: type = 7; break;
default:
return -1;
}
for(i = 0; i < 4; i++)
if((hw_watchpoint[i].addr == addr) &&
(hw_watchpoint[i].type == type) &&
(hw_watchpoint[i].size == len)) break;
if(i == 4) return -2;
hw_watchpoint[i].type = 0;
adiv5_ap_mem_write(t->ap, 0xE0001028 + i*0x10, 0);
return 0;
}
static int
cm3_check_hw_wp(struct target_s *target, uint32_t *addr)
{
struct target_ap_s *t = (void *)target;
int i;
for(i = 0; i < 4; i++)
/* if SET and MATCHED then break */
if(hw_watchpoint[i].type &&
(adiv5_ap_mem_read(t->ap, 0xE0001028 + i*0x10) & 0x01000000))
break;
if(i == 4) return 0;
*addr = hw_watchpoint[i].addr;
return 1;
}

408
src/gdb_main.c Normal file
View File

@ -0,0 +1,408 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the GDB Remote Serial Debugging protocol as
* described in "Debugging with GDB" build from GDB source.
*
* Originally written for GDB 6.8, updated and tested with GDB 7.2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <assert.h>
#include "platform.h"
#include "general.h"
#include "hex_utils.h"
#include "gdb_if.h"
#include "gdb_packet.h"
#include "gdb_main.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "adiv5.h"
#include "target.h"
#include "command.h"
#define BUF_SIZE 1024
#define ERROR_IF_NO_TARGET() \
if(!cur_target) { gdb_putpacketz("EFF"); break; }
static unsigned char pbuf[BUF_SIZE];
static void handle_q_packet(char *packet, int len);
static void handle_v_packet(char *packet, int len);
uint32_t arm_regs[16];
void
gdb_main(void)
{
int size;
static uint8_t single_step = 0;
DEBUG("Entring GDB protocol main loop\n");
/* GDB protocol main loop */
while(1) {
SET_IDLE_STATE(1);
size = gdb_getpacket(pbuf, BUF_SIZE);
SET_IDLE_STATE(0);
DEBUG("%s\n", pbuf);
switch(pbuf[0]) {
/* Implementation of these is mandatory! */
case 'g': { /* 'g': Read general registers */
ERROR_IF_NO_TARGET();
target_regs_read(cur_target, (void*)arm_regs);
gdb_putpacket(hexify(pbuf, (void*)arm_regs, sizeof(arm_regs)), sizeof(arm_regs)*2);
break;
}
case 'm': { /* 'm addr,len': Read len bytes from addr */
uint32_t addr, len;
char *mem;
ERROR_IF_NO_TARGET();
siscanf(pbuf, "m%08lX,%08lX", &addr, &len);
DEBUG("m packet: addr = %08lX, len = %08lX\n", addr, len);
mem = malloc(len);
if(!mem) break;
if(((addr & 3) == 0) && ((len & 3) == 0))
target_mem_read_words(cur_target, (void*)mem, addr, len);
else
target_mem_read_bytes(cur_target, (void*)mem, addr, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
else
gdb_putpacket(hexify(pbuf, mem, len), len*2);
free(mem);
break;
}
case 'G': /* 'G XX': Write general registers */
ERROR_IF_NO_TARGET();
unhexify((void*)arm_regs, &pbuf[1], cur_target->regs_size);
target_regs_write(cur_target, arm_regs);
gdb_putpacket("OK", 2);
break;
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
uint32_t addr, len;
int hex;
char *mem;
ERROR_IF_NO_TARGET();
siscanf(pbuf, "M%08lX,%08lX:%n", &addr, &len, &hex);
DEBUG("M packet: addr = %08lX, len = %08lX\n", addr, len);
mem = malloc(len);
unhexify(mem, pbuf + hex, len);
if(((addr & 3) == 0) && ((len & 3) == 0))
target_mem_write_words(cur_target, addr, (void*)mem, len);
else
target_mem_write_bytes(cur_target, addr, (void*)mem, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
else
gdb_putpacket("OK", 2);
free(mem);
break;
}
case 's': /* 's [addr]': Single step [start at addr] */
single_step = 1;
// Fall through to resume target
case 'c': /* 'c [addr]': Continue [at addr] */
if(!cur_target) {
gdb_putpacketz("X1D");
break;
}
target_halt_resume(cur_target, single_step);
SET_RUN_STATE(1);
single_step = 0;
// Fall through to wait for target halt
case '?': { /* '?': Request reason for target halt */
/* This packet isn't documented as being mandatory,
* but GDB doesn't work without it. */
int sent_int = 0;
uint32_t watch_addr;
if(!cur_target) {
/* Report "target exited" if no target */
gdb_putpacketz("W00");
break;
}
/* Wait for target halt */
while(!target_halt_wait(cur_target))
if(gdb_if_getchar_to(0) == '\x03') {
target_halt_request(cur_target);
sent_int = 1;
}
SET_RUN_STATE(0);
/* Report reason for halt */
if(target_check_hw_wp(cur_target, &watch_addr))
/* Watchpoint hit */
gdb_putpacket_f("T05watch:%08X;", watch_addr);
else if(sent_int)
/* Target interrupted */
gdb_putpacketz("T02");
else
gdb_putpacketz("T05");
break;
}
/* Optional GDB packet support */
case '!': /* Enable Extended GDB Protocol. */
/* This doesn't do anything, we support the extended
* protocol anyway, but GDB will never send us a 'R'
* packet unless we answer 'OK' here.
*/
gdb_putpacket("OK", 2);
break;
case 0x04:
case 'D': /* GDB 'detach' command. */
if(cur_target) target_detach(cur_target);
last_target = cur_target;
cur_target = NULL;
gdb_putpacket("OK", 2);
break;
case 'k': /* Kill the target */
if(cur_target) {
target_reset(cur_target);
target_detach(cur_target);
last_target = cur_target;
cur_target = NULL;
}
break;
case 'r': /* Reset the target system */
case 'R': /* Restart the target program */
if(cur_target)
target_reset(cur_target);
else if(last_target) {
cur_target = last_target;
target_attach(cur_target);
target_reset(cur_target);
}
break;
case 'X': { /* 'X addr,len:XX': Write binary data to addr */
uint32_t addr, len;
int bin;
ERROR_IF_NO_TARGET();
siscanf(pbuf, "X%08lX,%08lX:%n", &addr, &len, &bin);
DEBUG("X packet: addr = %08lX, len = %08lX\n", addr, len);
if(((addr & 3) == 0) && ((len & 3) == 0))
target_mem_write_words(cur_target, addr, (void*)pbuf+bin, len);
else
target_mem_write_bytes(cur_target, addr, (void*)pbuf+bin, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
else
gdb_putpacket("OK", 2);
break;
}
case 'q': /* General query packet */
handle_q_packet(pbuf, size);
break;
case 'v': /* General query packet */
handle_v_packet(pbuf, size);
break;
/* These packet implement hardware break-/watchpoints */
case 'Z': /* Z type,addr,len: Set breakpoint packet */
case 'z': { /* z type,addr,len: Clear breakpoint packet */
uint8_t set = (pbuf[0]=='Z')?1:0;
int type, len;
uint32_t addr;
int ret;
ERROR_IF_NO_TARGET();
/* I have no idea why this doesn't work. Seems to work
* with real sscanf() though... */
//siscanf(pbuf, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
type = pbuf[1] - '0';
siscanf(pbuf + 2, ",%08lX,%d", &addr, &len);
switch(type) {
case 1: /* Hardware breakpoint */
if(!cur_target->set_hw_bp) { /* Not supported */
gdb_putpacket("", 0);
break;
}
if(set) ret = target_set_hw_bp(cur_target, addr);
else ret = target_clear_hw_bp(cur_target, addr);
if(!ret) gdb_putpacket("OK", 2);
else gdb_putpacket("E01", 3);
break;
case 2:
case 3:
case 4:
if(!cur_target->set_hw_wp) { /* Not supported */
gdb_putpacket("", 0);
break;
}
if(set) ret = target_set_hw_wp(cur_target, type, addr, len);
else ret = target_clear_hw_wp(cur_target, type, addr, len);
if(!ret) gdb_putpacket("OK", 2);
else gdb_putpacket("E01", 3);
break;
default:
gdb_putpacket("", 0);
}
break;
}
default: /* Packet not implemented */
DEBUG("Unsupported packet: %s\n", pbuf);
gdb_putpacket("", 0);
}
}
}
static void
handle_q_packet(char *packet, int len)
{
/* These 'monitor' commands only available on the real deal */
if(!strncmp(packet, "qRcmd,", 6)) {
unsigned char *data;
int datalen;
/* calculate size and allocate buffer for command */
datalen = (len - 6) / 2;
data = alloca(datalen+1);
/* dehexify command */
unhexify(data, packet+6, datalen);
data[datalen] = 0; /* add terminating null */
if(command_process(data) < 0)
gdb_putpacket("", 0);
else gdb_putpacket("OK", 2);
} else if (!strncmp (packet, "qSupported", 10)) {
/* Query supported protocol features */
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+", BUF_SIZE);
} else if (strncmp (packet, "qXfer:memory-map:read::", 23) == 0) {
/* Read target XML memory map */
uint32_t addr, len;
siscanf(packet+23, "%08lX,%08lX", &addr, &len);
if((!cur_target) && last_target) {
/* Attach to last target if detached. */
cur_target = last_target;
target_attach(cur_target);
}
if((!cur_target) || (!cur_target->xml_mem_map)) {
gdb_putpacketz("E01");
return;
}
if (addr < strlen (cur_target->xml_mem_map)) {
uint8_t reply[len+2];
reply[0] = 'm';
strncpy (reply + 1, &cur_target->xml_mem_map[addr], len);
if(len > strlen(&cur_target->xml_mem_map[addr]))
len = strlen(&cur_target->xml_mem_map[addr]);
gdb_putpacket(reply, len + 1);
} else if (addr == strlen (cur_target->xml_mem_map)) {
gdb_putpacketz("l");
} else
gdb_putpacketz("E01");
} else gdb_putpacket("", 0);
}
static void
handle_v_packet(char *packet, int plen)
{
uint32_t addr, len;
int bin;
static uint8_t flash_mode = 0;
if (siscanf(packet, "vAttach;%08lX", &addr) == 1) {
/* Attach to remote target processor */
target *t;
uint32_t i;
for(t = target_list, i = 1; t; t = t->next, i++)
if(i == addr) {
cur_target = t;
target_attach(t);
gdb_putpacketz("T05");
break;
}
if(!cur_target) /* Failed to attach */
gdb_putpacketz("E01");
} else if (!strcmp(packet, "vRun;")) {
/* Run target program. For us (embedded) this means reset. */
if(cur_target) {
target_reset(cur_target);
gdb_putpacketz("T05");
} else if(last_target) {
cur_target = last_target;
target_attach(cur_target);
target_reset(cur_target);
gdb_putpacketz("T05");
} else gdb_putpacketz("E01");
} else if (siscanf(packet, "vFlashErase:%08lX,%08lX", &addr, &len) == 2) {
/* Erase Flash Memory */
DEBUG("Flash Erase %08lX %08lX\n", addr, len);
if(!cur_target) { gdb_putpacketz("EFF"); return; }
if(!flash_mode) {
/* Reset target if first flash command! */
/* This saves us if we're interrupted in IRQ context */
target_reset(cur_target);
flash_mode = 1;
}
if(target_flash_erase(cur_target, addr, len) == 0)
gdb_putpacketz("OK");
else
gdb_putpacketz("EFF");
} else if (siscanf(packet, "vFlashWrite:%08lX:%n", &addr, &bin) == 1) {
/* Write Flash Memory */
len = plen - bin;
DEBUG("Flash Write %08lX %08lX\n", addr, len);
if(cur_target && target_flash_write_words(cur_target, addr, (void*)packet + bin, len) == 0)
gdb_putpacketz("OK");
else
gdb_putpacketz("EFF");
} else if (!strcmp(packet, "vFlashDone")) {
/* Commit flash operations. */
gdb_putpacketz("OK");
flash_mode = 0;
} else
gdb_putpacket("", 0);
}

153
src/gdb_packet.c Normal file
View File

@ -0,0 +1,153 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the GDB Remote Serial Debugging protocol packet
* reception and transmission as well as some convenience functions.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <alloca.h>
#include "general.h"
#include "gdb_if.h"
#include "gdb_packet.h"
#include "hex_utils.h"
int
gdb_getpacket(unsigned char *packet, int size)
{
unsigned char c;
unsigned char csum;
char recv_csum[3];
int i;
while(1) {
/* Wait for packet start */
while((packet[0] = gdb_if_getchar()) != '$')
if(packet[0] == 0x04) return 1;
i = 0; csum = 0;
/* Capture packet data into buffer */
while((c = gdb_if_getchar()) != '#') {
if(i == size) break; /* Oh shit */
if(c == '$') { /* Restart capture */
i = 0;
csum = 0;
continue;
}
if(c == '}') { /* escaped char */
c = gdb_if_getchar();
csum += c + '}';
packet[i++] = c ^ 0x20;
continue;
}
csum += c;
packet[i++] = c;
}
recv_csum[0] = gdb_if_getchar();
recv_csum[1] = gdb_if_getchar();
recv_csum[2] = 0;
/* return packet if checksum matches */
if(csum == strtol(recv_csum, NULL, 16)) break;
/* get here if checksum fails */
gdb_if_putchar('-', 1); /* send nack */
}
gdb_if_putchar('+', 1); /* send ack */
packet[i] = 0;
return i;
}
void gdb_putpacket(unsigned char *packet, int size)
{
int i;
unsigned char csum;
unsigned char c;
char xmit_csum[3];
int tries = 0;
do {
csum = 0;
gdb_if_putchar('$', 0);
for(i = 0; i < size; i++) {
c = packet[i];
if((c == '$') || (c == '#') || (c == '}')) {
gdb_if_putchar('}', 0);
gdb_if_putchar(c ^ 0x20, 0);
csum += '}' + (c ^ 0x20);
} else {
gdb_if_putchar(c, 0);
csum += c;
}
}
gdb_if_putchar('#', 0);
siprintf(xmit_csum, "%02X", csum);
gdb_if_putchar(xmit_csum[0], 0);
gdb_if_putchar(xmit_csum[1], 1);
} while((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
}
void gdb_putpacket_f(const unsigned char *fmt, ...)
{
va_list ap;
char *buf;
int size;
va_start(ap, fmt);
size = vasiprintf(&buf, fmt, ap);
gdb_putpacket(buf, size);
free(buf);
va_end(ap);
}
void gdb_out(const char *buf)
{
char *hexdata;
int i;
hexdata = alloca((i = strlen(buf)*2 + 1) + 1);
hexdata[0] = 'O';
hexify(hexdata+1, buf, strlen(buf));
gdb_putpacket(hexdata, i);
}
void gdb_outf(const char *fmt, ...)
{
va_list ap;
char *buf;
va_start(ap, fmt);
vasiprintf(&buf, fmt, ap);
gdb_out(buf);
free(buf);
va_end(ap);
}

62
src/hex_utils.c Normal file
View File

@ -0,0 +1,62 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Convenience function to convert to/from ascii strings of hex digits.
*/
#include <stdio.h>
#include <stdint.h>
#include "hex_utils.h"
static char hexdigits[] = "0123456789abcdef";
char * hexify(char *hex, const unsigned char *buf, int size)
{
char *tmp = hex;
while(size--) {
*tmp++ = hexdigits[*buf >> 4];
*tmp++ = hexdigits[*buf++ & 0xF];
}
*tmp++ = 0;
return hex;
}
static uint8_t unhex_digit(char hex)
{
uint8_t tmp = hex - '0';
if(tmp > 9)
tmp -= 'A' - '0' - 10;
if(tmp > 16)
tmp -= 'a' - 'A';
return tmp;
}
char * unhexify(unsigned char *buf, const char *hex, int size)
{
while(size--) {
*buf = unhex_digit(*hex++) << 4;
*buf++ |= unhex_digit(*hex++);
}
return buf;
}

119
src/include/adiv5.h Normal file
View File

@ -0,0 +1,119 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ADIV5_H
#define __ADIV5_H
#include "general.h"
#include "jtag_scan.h"
#include "target.h"
#define DP_ABORT 0x0
#define DP_CTRLSTAT 0x4
#define DP_SELECT 0x8
#define DP_RDBUFF 0xC
/* Try to keep this somewhat absract for later adding SW-DP */
typedef struct ADIv5_DP_s {
struct ADIv5_DP_s *next;
uint32_t idcode;
void (*dp_write)(struct ADIv5_DP_s *dp, uint8_t addr, uint32_t value);
uint32_t (*dp_read)(struct ADIv5_DP_s *dp, uint8_t addr);
void (*ap_write)(struct ADIv5_DP_s *dp, uint8_t addr, uint32_t value);
uint32_t (*ap_read)(struct ADIv5_DP_s *dp, uint8_t addr);
uint32_t (*error)(struct ADIv5_DP_s *dp);
uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t APnDP, uint8_t RnW,
uint8_t addr, uint32_t value);
union {
jtag_dev_t *dev;
uint8_t fault;
};
} ADIv5_DP_t;
static inline void adiv5_dp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
dp->dp_write(dp, addr, value);
}
static inline uint32_t adiv5_dp_read(ADIv5_DP_t *dp, uint8_t addr)
{
return dp->dp_read(dp, addr);
}
static inline void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
{
dp->ap_write(dp, addr, value);
}
static inline uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
{
return dp->ap_read(dp, addr);
}
static inline uint32_t adiv5_dp_error(ADIv5_DP_t *dp)
{
return dp->error(dp);
}
static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t APnDP,
uint8_t RnW, uint8_t addr, uint32_t value)
{
return dp->low_access(dp, APnDP, RnW, addr, value);
}
extern ADIv5_DP_t *adiv5_dp_list;
typedef struct ADIv5_AP_s {
ADIv5_DP_t *dp;
uint8_t apsel;
uint32_t idr;
uint32_t cfg;
uint32_t base;
} ADIv5_AP_t;
struct target_ap_s {
target t;
ADIv5_AP_t *ap;
};
extern ADIv5_AP_t adiv5_aps[5];
extern int adiv5_ap_count;
void adiv5_free_all(void);
void adiv5_dp_init(ADIv5_DP_t *dp);
uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr);
void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value);
void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value);
uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr);
void adiv5_jtag_dp_handler(jtag_dev_t *dev);
int adiv5_swdp_scan(void);
#endif

37
src/include/command.h Normal file
View File

@ -0,0 +1,37 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __COMMAND_H
#define __COMMAND_H
#include "general.h"
int command_process(char *cmd);
typedef void (*cmd_handler)(int argc, const char **argv);
struct command_s {
const char *cmd;
cmd_handler handler;
const char *help;
};
#endif

29
src/include/cortexm3.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CORTEXM3_H
#define __CORTEXM3_H
#include "target.h"
int cm3_probe(struct target_s *target);
#endif

30
src/include/gdb_if.h Normal file
View File

@ -0,0 +1,30 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDB_IF_H
#define __GDB_IF_H
int gdb_if_init(void);
unsigned char gdb_if_getchar(void);
unsigned char gdb_if_getchar_to(int timeout);
void gdb_if_putchar(unsigned char c, int flush);
#endif

27
src/include/gdb_main.h Normal file
View File

@ -0,0 +1,27 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDB_MAIN_H
#define __GDB_MAIN_H
void gdb_main(void);
#endif

36
src/include/gdb_packet.h Normal file
View File

@ -0,0 +1,36 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDB_PACKET_H
#define __GDB_PACKET_H
#include <string.h>
int gdb_getpacket(unsigned char *packet, int size);
void gdb_putpacket(unsigned char *packet, int size);
#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
void gdb_putpacket_f(const unsigned char *packet, ...);
void gdb_out(const char *buf);
void gdb_outf(const char *fmt, ...);
#endif

34
src/include/general.h Normal file
View File

@ -0,0 +1,34 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GENERAL_H
#define __GENERAL_H
#include "platform.h"
#ifndef DEBUG
#include <stdio.h>
#define DEBUG printf
#endif
#include <stdint.h>
#endif

29
src/include/hex_utils.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __HEX_UTILS_H
#define __HEX_UTILS_H
char * hexify(char *hex, const unsigned char *buf, int size);
char * unhexify(unsigned char *buf, const char *hex, int size);
#endif

56
src/include/jtag_scan.h Normal file
View File

@ -0,0 +1,56 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __JTAG_SCAN_H
#define __JTAG_SCAN_H
#include "general.h"
#define JTAG_MAX_DEVS 5
#define JTAG_MAX_IR_LEN 16
typedef struct jtag_dev_s {
union {
uint8_t dev;
uint8_t dr_prescan;
};
uint8_t dr_postscan;
uint8_t ir_len;
uint8_t ir_prescan;
uint8_t ir_postscan;
uint32_t idcode;
char *descr;
uint32_t current_ir;
} jtag_dev_t;
extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
extern int jtag_dev_count;
int jtag_scan(void);
void jtag_dev_write_ir(jtag_dev_t *dev, uint32_t ir);
void jtag_dev_shift_dr(jtag_dev_t *dev, uint8_t *dout, const uint8_t *din, int ticks);
#endif

69
src/include/jtagtap.h Normal file
View File

@ -0,0 +1,69 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __JTAGTAP_H
#define __JTAGTAP_H
#include "general.h"
/* Note: Signal names are as for the device under test. */
int jtagtap_init(void);
void jtagtap_reset(void);
void jtagtap_srst(void);
uint8_t jtagtap_next(const uint8_t TMS, const uint8_t TDI);
/* tap_next executes one state transision in the JTAG TAP state machine:
* - Ensure TCK is low
* - Assert the values of TMS and TDI
* - Assert TCK (TMS and TDO are latched on rising edge
* - Caputure the value on TDO
* - Release TCK.
*/
void jtagtap_tms_seq(uint32_t MS, int ticks);
void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks);
void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks);
/* Shift out a sequence on MS and DI, capture data to DO.
* - This is not endian safe: First byte will always be first shifted out.
* - DO may be NULL to ignore captured data.
* - DO may be point to the same address as DI.
*/
/* generic soft reset: 1, 1, 1, 1, 1, 0 */
#define jtagtap_soft_reset() \
jtagtap_tms_seq(0x1F, 6)
/* Goto Shift-IR: 1, 1, 0, 0 */
#define jtagtap_shift_ir() \
jtagtap_tms_seq(0x03, 4)
/* Goto Shift-DR: 1, 0, 0 */
#define jtagtap_shift_dr() \
jtagtap_tms_seq(0x01, 3)
/* Goto Run-test/Idle: 1, 1, 0 */
#define jtagtap_return_idle() \
jtagtap_tms_seq(0x01, 2)
#endif

29
src/include/lmi.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LMI_H
#define __LMI_H
#include "target.h"
int lmi_probe(struct target_s *target);
#endif

29
src/include/stm32_tgt.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STM32_TGT_H
#define __STM32_TGT_H
#include "target.h"
int stm32_probe(struct target_s *target);
#endif

39
src/include/swdptap.h Normal file
View File

@ -0,0 +1,39 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SWDPTAP_H
#define __SWDPTAP_H
#include "general.h"
int swdptap_init(void);
void swdptap_reset(void);
void swdptap_turnaround(uint8_t dir);
uint8_t swdptap_bit_in(void);
void swdptap_bit_out(uint8_t val);
uint32_t swdptap_seq_in(int ticks);
uint8_t swdptap_seq_in_parity(uint32_t *data, int ticks);
void swdptap_seq_out(uint32_t MS, int ticks);
void swdptap_seq_out_parity(uint32_t MS, int ticks);
#endif

173
src/include/target.h Normal file
View File

@ -0,0 +1,173 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Provides an abstract 'target object', the 'methods' of which must be
* implemented by a target driver when a supported device is detected.
*/
#ifndef __TARGET_H
#define __TARGET_H
/* Halt/resume functions */
#define target_attach(target) \
(target)->attach(target)
#define target_detach(target) \
(target)->detach(target)
#define target_check_error(target) \
(target)->check_error(target)
/* Memory access functions */
#define target_mem_read_words(target, dest, src, len) \
(target)->mem_read_words((target), (dest), (src), (len))
#define target_mem_write_words(target, dest, src, len) \
(target)->mem_write_words((target), (dest), (src), (len))
#define target_mem_read_bytes(target, dest, src, len) \
(target)->mem_read_bytes((target), (dest), (src), (len))
#define target_mem_write_bytes(target, dest, src, len) \
(target)->mem_write_bytes((target), (dest), (src), (len))
/* Register access functions */
#define target_regs_read(target, data) \
(target)->regs_read((target), (data))
#define target_regs_write(target, data) \
(target)->regs_write((target), (data))
#define target_pc_read(target) \
(target)->pc_read((target))
#define target_pc_write(target, val) \
(target)->pc_write((target), (val))
/* Halt/resume functions */
#define target_reset(target) \
(target)->reset(target)
#define target_halt_request(target) \
(target)->halt_request(target)
#define target_halt_wait(target) \
(target)->halt_wait(target)
#define target_halt_resume(target, step) \
(target)->halt_resume((target), (step))
/* Break-/watchpoint functions */
#define target_set_hw_bp(target, addr) \
(target)->set_hw_bp((target), (addr))
#define target_clear_hw_bp(target, addr) \
(target)->clear_hw_bp((target), (addr))
#define target_set_hw_wp(target, type, addr, len) \
(target)->set_hw_wp((target), (type), (addr), (len))
#define target_clear_hw_wp(target, type, addr, len) \
(target)->clear_hw_wp((target), (type), (addr), (len))
#define target_check_hw_wp(target, addr) \
(target)->check_hw_wp((target), (addr))
/* Flash memory access functions */
#define target_flash_erase(target, addr, len) \
(target)->flash_erase((target), (addr), (len))
#define target_flash_write_words(target, dest, src, len) \
(target)->flash_write_words((target), (dest), (src), (len))
#define TARGET_LIST_FREE() { \
while(target_list) { \
target *t = target_list->next; \
free(target_list); \
target_list = t; \
} \
last_target = cur_target = NULL; \
}
typedef struct target_s {
/* Attach/Detach funcitons */
void (*attach)(struct target_s *target);
void (*detach)(struct target_s *target);
int (*check_error)(struct target_s *target);
/* Memory access functions */
int (*mem_read_words)(struct target_s *target, uint32_t *dest, uint32_t src,
int len);
int (*mem_write_words)(struct target_s *target, uint32_t dest,
const uint32_t *src, int len);
int (*mem_read_bytes)(struct target_s *target, uint8_t *dest, uint32_t src,
int len);
int (*mem_write_bytes)(struct target_s *target, uint32_t dest,
const uint8_t *src, int len);
/* Register access functions */
int regs_size;
int (*regs_read)(struct target_s *target, void *data);
int (*regs_write)(struct target_s *target, const void *data);
uint32_t (*pc_read)(struct target_s *target);
int (*pc_write)(struct target_s *target, const uint32_t val);
/* Halt/resume functions */
void (*reset)(struct target_s *target);
void (*halt_request)(struct target_s *target);
int (*halt_wait)(struct target_s *target);
void (*halt_resume)(struct target_s *target, uint8_t step);
/* Break-/watchpoint functions */
int (*set_hw_bp)(struct target_s *target, uint32_t addr);
int (*clear_hw_bp)(struct target_s *target, uint32_t addr);
int (*set_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
int (*clear_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
int (*check_hw_wp)(struct target_s *target, uint32_t *addr);
/* Flash memory access functions */
const char *xml_mem_map;
int (*flash_erase)(struct target_s *target, uint32_t addr, int len);
int (*flash_write_words)(struct target_s *target, uint32_t dest,
const uint32_t *src, int len);
const char *driver;
int size;
struct target_s *next;
} target;
extern target *target_list, *cur_target, *last_target;
#endif

232
src/jtag_scan.c Normal file
View File

@ -0,0 +1,232 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements JTAG protocol support. Provides functionality
* to detect devices on the scan chain and read their IDCODEs.
* It depends on the low-level function provided by the platform's jtagtap.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <alloca.h>
#include "general.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "gdb_packet.h"
#include "adiv5.h"
struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
int jtag_dev_count;
static struct jtag_dev_descr_s {
uint32_t idcode;
uint32_t idmask;
char *descr;
void (*handler)(jtag_dev_t *dev);
} dev_descr[] = {
{.idcode = 0x0BA00477, .idmask = 0x0FFFFFFF,
.descr = "ARM Limited: ADIv5 JTAG-DP port.",
.handler = adiv5_jtag_dp_handler},
{.idcode = 0x06410041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Medium density."},
{.idcode = 0x06412041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Low density."},
{.idcode = 0x06414041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, High density."},
{.idcode = 0x06418041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Connectivity Line."},
{.idcode = 0x06420041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Value Line."},
{.idcode = 0x06428041, .idmask = 0x0FFFFFFF,
.descr = "ST Microelectronics: STM32, Value Line, High density."},
/* Just for fun, unsupported */
{.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."},
{.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."},
{.idcode = 0x20270013, .idmask = 0xFFFFFFFF, .descr = "Intel: i80386ex."},
{.idcode = 0, .idmask = 0, .descr = "Unknown"},
};
/* bucket of ones for don't care TDI */
static const char ones[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
/* Scan JTAG chain for devices, store IR length and IDCODE (if present).
* Reset TAP state machine.
* Select Shift-IR state.
* Each device is assumed to shift out IR at 0x01. (this may not always be true)
* Shift in ones until we read two consecutive ones, then we have shifted out the
* IRs of all devices.
*
* After this process all the IRs are loaded with the BYPASS command.
* Select Shift-DR state.
* Shift in ones and count zeros shifted out. Should be one for each device.
* Check this against device count obtained by IR scan above.
*
* Reset the TAP state machine again. This should load all IRs with IDCODE.
* For each device, shift out one bit. If this is zero IDCODE isn't present,
* continue to next device. If this is one shift out the remaining 31 bits
* of the IDCODE register.
*/
int jtag_scan(void)
{
int i;
uint32_t j;
TARGET_LIST_FREE();
jtag_dev_count = 0;
memset(&jtag_devs, 0, sizeof(jtag_devs));
#warning "These should be elsewhere!"
adiv5_free_all();
/* Run throught the SWD to JTAG sequence for the case where an attached SWJ-DP is
* in SW-DP mode.
*/
DEBUG("Resetting TAP\n");
jtagtap_init();
jtagtap_reset();
DEBUG("Change state to Shift-IR\n");
jtagtap_shift_ir();
DEBUG("Scanning out IRs\n");
if(!jtagtap_next(0, 1)) {
DEBUG("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n");
jtag_dev_count = -1;
return -1; /* must be 1 */
}
jtag_devs[0].ir_len = 1; j = 1;
while((jtag_dev_count <= JTAG_MAX_DEVS) &&
(jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) {
if(jtagtap_next(0, 1)) {
if(jtag_devs[jtag_dev_count].ir_len == 1) break;
jtag_devs[++jtag_dev_count].ir_len = 1;
jtag_devs[jtag_dev_count].ir_prescan = j;
jtag_devs[jtag_dev_count].dev = jtag_dev_count;
} else jtag_devs[jtag_dev_count].ir_len++;
j++;
}
if(jtag_dev_count > JTAG_MAX_DEVS) {
DEBUG("jtag_scan: Maximum device count exceeded\n");
jtag_dev_count = -1;
return -1;
}
if(jtag_devs[jtag_dev_count].ir_len > JTAG_MAX_IR_LEN) {
DEBUG("jtag_scan: Maximum IR length exceeded\n");
jtag_dev_count = -1;
return -1;
}
DEBUG("Return to Run-Test/Idle\n");
jtagtap_next(1, 1);
jtagtap_return_idle();
/* All devices should be in BYPASS now */
/* Count device on chain */
DEBUG("Change state to Shift-DR\n");
jtagtap_shift_dr();
for(i = 0; (jtagtap_next(0, 1) == 0) && (i <= jtag_dev_count); i++)
jtag_devs[i].dr_postscan = jtag_dev_count - i - 1;
if(i != jtag_dev_count) {
DEBUG("jtag_scan: Sanity check failed: "
"BYPASS dev count doesn't match IR scan\n");
jtag_dev_count = -1;
return -1;
}
DEBUG("Return to Run-Test/Idle\n");
jtagtap_next(1, 1);
jtagtap_return_idle();
if(!jtag_dev_count) {
morse("NO TARGETS.", 1);
return 0;
}
/* Fill in the ir_postscan fields */
for(i = jtag_dev_count - 1; i; i--)
jtag_devs[i-1].ir_postscan = jtag_devs[i].ir_postscan +
jtag_devs[i].ir_len;
/* Reset jtagtap: should take all devs to IDCODE */
jtagtap_reset();
jtagtap_shift_dr();
for(i = 0; i < jtag_dev_count; i++) {
if(!jtagtap_next(0, 1)) continue;
jtag_devs[i].idcode = 1;
for(j = 2; j; j <<= 1)
if(jtagtap_next(0, 1)) jtag_devs[i].idcode |= j;
}
DEBUG("Return to Run-Test/Idle\n");
jtagtap_next(1, 1);
jtagtap_return_idle();
/* Check for known devices and handle accordingly */
for(i = 0; i < jtag_dev_count; i++)
for(j = 0; dev_descr[j].idcode; j++)
if((jtag_devs[i].idcode & dev_descr[j].idmask) ==
dev_descr[j].idcode) {
jtag_devs[i].current_ir = -1;
/* Save description in table */
jtag_devs[i].descr = dev_descr[j].descr;
/* Call handler to initialise/probe device further */
if(dev_descr[j].handler)
dev_descr[j].handler(&jtag_devs[i]);
break;
}
if(!target_list) morse("NO TARGETS.", 1);
else morse(NULL, 0);
return jtag_dev_count;
}
void jtag_dev_write_ir(jtag_dev_t *d, uint32_t ir)
{
if(ir == d->current_ir) return;
d->current_ir = ir;
jtagtap_shift_ir();
jtagtap_tdi_seq(0, ones, d->ir_prescan);
jtagtap_tdi_seq(d->ir_postscan?0:1, (void*)&ir, d->ir_len);
jtagtap_tdi_seq(1, ones, d->ir_postscan);
jtagtap_return_idle();
}
void jtag_dev_shift_dr(jtag_dev_t *d, uint8_t *dout, const uint8_t *din, int ticks)
{
jtagtap_shift_dr();
jtagtap_tdi_seq(0, ones, d->dr_prescan);
if(dout)
jtagtap_tdi_tdo_seq((void*)dout, d->dr_postscan?0:1, (void*)din, ticks);
else
jtagtap_tdi_seq(d->dr_postscan?0:1, (void*)din, ticks);
jtagtap_tdi_seq(1, ones, d->dr_postscan);
jtagtap_return_idle();
}

72
src/jtagtap_generic.c Normal file
View File

@ -0,0 +1,72 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file provides generic forms of the low-level jtagtap functions
* for platforms that don't require optimised forms.
*/
#ifdef PROVIDE_GENERIC_JTAGTAP_TMS_SEQ
void
jtagtap_tms_seq(uint32_t MS, int ticks)
{
while(ticks--) {
jtagtap_next(MS & 1, 1);
MS >>= 1;
}
}
#endif
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
void
jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
{
uint8_t index = 1;
while(ticks--) {
if(jtagtap_next(ticks?0:final_tms, *DI & index)) {
*DO |= index;
} else {
*DO &= ~index;
}
if(!(index <<= 1)) {
index = 1;
DI++; DO++;
}
}
}
#endif
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
void
jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
{
uint8_t index = 1;
while(ticks--) {
jtagtap_next(ticks?0:final_tms, *DI & index);
if(!(index <<= 1)) {
index = 1;
DI++;
}
}
}
#endif

183
src/lmi.c Normal file
View File

@ -0,0 +1,183 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements TI/LMI LM3S target specific functions providing
* the XML memory map and Flash memory programming.
*
* Issues:
* No detection of the target device.
* Add reference to documentation.
* Flash erase is very slow.
*/
#include <stdlib.h>
#include <string.h>
#include "general.h"
#include "adiv5.h"
#include "target.h"
static int lmi_flash_erase(struct target_s *target, uint32_t addr, int len);
static int lmi_flash_write_words(struct target_s *target, uint32_t dest,
const uint32_t *src, int len);
static const char lmi_driver_str[] = "LuminaryMicro Stellaris";
static const char lmi_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
"<memory-map>"
" <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
"</memory-map>";
uint16_t lmi_flash_write_stub[] = {
// _start:
0x4809, // ldr r0, [pc, #36] // _flashbase
0x490b, // ldr r1, [pc, #44] // _addr
0x467a, // mov r2, pc
0x3230, // adds r2, #48
0x4b0a, // ldr r3, [pc, #40] // _size
0x4d08, // ldr r5, [pc, #32] // _flash_write_cmd
// _next:
0xb15b, // cbz r3, _done
0x6001, // str r1, [r0, #0]
0x6814, // ldr r4, [r2]
0x6044, // str r4, [r0, #4]
0x6085, // str r5, [r0, #8]
// _wait:
0x6884, // ldr r4, [r0, #8]
0x2601, // movs r6, #1
0x4234, // tst r4, r6
0xd1fb, // bne _wait
0x3b01, // subs r3, #1
0x3104, // adds r1, #4
0x3204, // adds r2, #4
0xe7f2, // b _next
// _done:
0xbe00, // bkpt
// _flashbase:
0xd000, 0x400f, // .word 0x400fd000
// _flash_write_cmd:
0x0001, 0xa442, // .word 0xa4420001
// _addr:
// 0x0000, 0x0000,
// _size:
// 0x0000, 0x0000,
// _data:
// ...
};
int lmi_probe(struct target_s *target)
{
/* How do we really probe the LMI device??? */
target->driver = lmi_driver_str;
target->xml_mem_map = lmi_xml_memory_map;
target->flash_erase = lmi_flash_erase;
target->flash_write_words = lmi_flash_write_words;
return 0;
}
int lmi_flash_erase(struct target_s *target, uint32_t addr, int len)
{
struct target_ap_s *t = (void *)target;
uint32_t tmp;
addr &= 0xFFFFFC00;
len &= 0xFFFFFC00;
/* setup word access */
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
/* select Flash Control */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0x400FD000);
while(len) {
/* write address to FMA */
adiv5_ap_write(t->ap, 0x10, addr); /* Required to switch banks */
/* set ERASE bit in FMC */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x08, 0xA4420002);
/* Read FMC to poll for ERASE bit */
adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
do {
tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
} while (tmp & 2);
len -= 0x400;
addr += 0x400;
}
return 0;
}
int lmi_flash_write_words(struct target_s *target, uint32_t dest,
const uint32_t *src, int len)
{
#if 0
struct target_ap_s *t = (void *)target;
uint32_t tmp;
dest &= 0xFFFFFFFC;
len &= 0xFFFFFFFC;
/* setup word access */
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
/* select Flash Control */
while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x04, 0x400FD000) != 2);
while(len) {
/* write address to FMA */
adiv5_ap_write(t->ap, 0x10, dest); /* Required to switch banks */
/* Write data in FMD */
while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x04, *src++) != 2);
/* set ERASE bit in FMC */
while(adiv5_dp_access(t->ap->dp, NULL, 0, 0x08, 0xA4420001) != 2);
/* Read FMC to poll for ERASE bit */
while(adiv5_dp_access(t->ap->dp, NULL, 1, 0x08, 0) != 2);
do {
while(adiv5_dp_access(t->ap->dp, &tmp, 1, 0x08, 0) != 2);
} while (tmp & 1);
len -= 0x4;
dest += 0x4;
}
#else
uint32_t data[(len>>2)+2];
data[0] = dest;
data[1] = len >> 2;
memcpy(&data[2], src, len);
DEBUG("Sending stub\n");
target_mem_write_words(target, 0x20000000, (void*)lmi_flash_write_stub, 0x30);
DEBUG("Sending data\n");
target_mem_write_words(target, 0x20000030, data, len + 8);
DEBUG("Running stub\n");
target_pc_write(target, 0x20000000);
target_halt_resume(target, 0);
DEBUG("Waiting for halt\n");
while(!target_halt_wait(target));
#endif
return 0;
}

53
src/main.c Normal file
View File

@ -0,0 +1,53 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Provides main entry point. Initialise subsystems and enter GDB
* protocol loop.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "gdb_if.h"
#include "gdb_main.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "target.h"
int
main(void)
{
assert(platform_init() == 0);
assert(gdb_if_init() == 0);
assert(jtagtap_init() == 0);
jtag_scan();
// adiv5_swdp_scan();
PLATFORM_SET_FATAL_ERROR_RECOVERY();
gdb_main();
/* Should never get here */
return 0;
}

25
src/stm32/Makefile.inc Normal file
View File

@ -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

29
src/stm32/blackmagic.ld Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
/* Include the common ld script from libopenstm32. */
INCLUDE libopencm3_stm32.ld

304
src/stm32/cdcacm.c Normal file
View File

@ -0,0 +1,304 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements a the USB Communications Device Class - Abstract
* Control Model (CDC-ACM) as defined in CDC PSTN subclass 1.2.
* A Device Firmware Upgrade (DFU 1.1) class interface is provided for
* field firmware upgrade.
*
* The device's unique id is used as the USB serial number string.
*/
#include <libopencm3/stm32/nvic.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
#include <libopencm3/stm32/scb.h>
#include <libopencm3/usb/dfu.h>
#include <stdlib.h>
#include "platform.h"
static char *get_dev_unique_id(char *serial_no);
static int configured;
static const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = USB_CLASS_CDC,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0x5740,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
/* This notification endpoint isn't implemented. According to CDC spec its
* optional, but its absence causes a NULL pointer dereference in Linux cdc_acm
* driver. */
static const struct usb_endpoint_descriptor comm_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x83,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 16,
.bInterval = 255,
}};
static const struct usb_endpoint_descriptor data_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
}, {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x82,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct {
struct usb_cdc_header_descriptor header;
struct usb_cdc_call_management_descriptor call_mgmt;
struct usb_cdc_acm_descriptor acm;
struct usb_cdc_union_descriptor cdc_union;
} __attribute__((packed)) cdcacm_functional_descriptors = {
.header = {
.bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_HEADER,
.bcdCDC = 0x0110,
},
.call_mgmt = {
.bFunctionLength =
sizeof(struct usb_cdc_call_management_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
.bmCapabilities = 0,
.bDataInterface = 1,
},
.acm = {
.bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_ACM,
.bmCapabilities = 0,
},
.cdc_union = {
.bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_UNION,
.bControlInterface = 0,
.bSubordinateInterface0 = 1,
}
};
static const struct usb_interface_descriptor comm_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
.iInterface = 0,
.endpoint = comm_endp,
.extra = &cdcacm_functional_descriptors,
.extralen = sizeof(cdcacm_functional_descriptors)
}};
static const struct usb_interface_descriptor data_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = data_endp,
}};
const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = DFU_FUNCTIONAL,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeout = 255,
.wTransferSize = 1024,
.bcdDFUVersion = 0x011A,
};
const struct usb_interface_descriptor dfu_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 2,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xFE,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 1,
.iInterface = 0,
.extra = &dfu_function,
.extralen = sizeof(dfu_function),
};
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = comm_iface,
}, {
.num_altsetting = 1,
.altsetting = data_iface,
}, {
.num_altsetting = 1,
.altsetting = &dfu_iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 3,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
.interface = ifaces,
};
static char serial_no[25];
static const char *usb_strings[] = {
"x",
"Black Sphere Technologies",
"Black Magic Probe",
serial_no,
};
static void dfu_detach_complete(struct usb_setup_data *req)
{
(void)req;
/* Disconnect USB cable */
gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, 0, USB_PU_PIN);
/* Assert boot-request pin */
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
gpio_clear(GPIOB, GPIO12);
/* Reset core to enter bootloader */
scb_reset_core();
}
static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len, void (**complete)(struct usb_setup_data *req))
{
(void)complete;
(void)buf;
(void)len;
switch(req->bRequest) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
/* This Linux cdc_acm driver requires this to be implemented
* even though it's optional in the CDC spec, and we don't
* advertise it in the ACM functional descriptor. */
return 1;
case DFU_DETACH:
if(req->wIndex == 2) {
*complete = dfu_detach_complete;
return 1;
}
return 0;
}
return 0;
}
int cdcacm_get_config(void)
{
return configured;
}
static void cdcacm_set_config(u16 wValue)
{
configured = wValue;
usbd_ep_setup(0x01, USB_ENDPOINT_ATTR_BULK, 64, NULL);
usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
usbd_register_control_callback(
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
cdcacm_control_request);
}
void cdcacm_init(void)
{
get_dev_unique_id(serial_no);
usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings);
usbd_register_set_config_callback(cdcacm_set_config);
nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
gpio_set(USB_PU_PORT, USB_PU_PIN);
gpio_set_mode(USB_PU_PORT, GPIO_MODE_OUTPUT_10_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, USB_PU_PIN);
}
void usb_lp_can_rx0_isr(void)
{
usbd_poll();
}
static char *get_dev_unique_id(char *s)
{
volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFFF7E8;
int i;
/* Fetch serial number from chip's unique ID */
for(i = 0; i < 24; i+=2) {
s[i] = ((*unique_id >> 4) & 0xF) + '0';
s[i+1] = (*unique_id++ & 0xF) + '0';
}
for(i = 0; i < 24; i++)
if(s[i] > '9')
s[i] += 'A' - '9' - 1;
return s;
}

81
src/stm32/gdb_if.c Normal file
View File

@ -0,0 +1,81 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements a transparent channel over which the GDB Remote
* Serial Debugging protocol is implemented. This implementation for STM32
* uses the USB CDC-ACM device bulk endpoints to implement the channel.
*/
#include "platform.h"
#include <usbd.h>
#include "gdb_if.h"
#define VIRTUAL_COM_PORT_DATA_SIZE 64
static uint32_t count_out;
static uint32_t count_in;
static uint32_t out_ptr;
static uint8_t buffer_out[VIRTUAL_COM_PORT_DATA_SIZE];
static uint8_t buffer_in[VIRTUAL_COM_PORT_DATA_SIZE];
int gdb_if_init(void)
{
cdcacm_init();
return 0;
}
void gdb_if_putchar(unsigned char c, int flush)
{
buffer_in[count_in++] = c;
if(flush || (count_in == VIRTUAL_COM_PORT_DATA_SIZE)) {
while(usbd_ep_write_packet(2, buffer_in, count_in) <= 0);
count_in = 0;
}
}
unsigned char gdb_if_getchar(void)
{
while(!(out_ptr < count_out)) {
while(cdcacm_get_config() != 1);
count_out = usbd_ep_read_packet(1, buffer_out,
VIRTUAL_COM_PORT_DATA_SIZE);
out_ptr = 0;
}
return buffer_out[out_ptr++];
}
unsigned char gdb_if_getchar_to(int timeout)
{
timeout_counter = timeout/100;
if(!(out_ptr < count_out)) do {
count_out = usbd_ep_read_packet(1, buffer_out,
VIRTUAL_COM_PORT_DATA_SIZE);
out_ptr = 0;
} while(timeout_counter && !(out_ptr < count_out));
if(out_ptr < count_out)
return gdb_if_getchar();
return -1;
}

90
src/stm32/jtagtap.c Normal file
View File

@ -0,0 +1,90 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the low-level JTAG TAP interface. */
#include <libopencm3/stm32/gpio.h>
#include <stdio.h>
#include "general.h"
#include "jtagtap.h"
int jtagtap_init(void)
{
/* This needs some fixing... */
/* Toggle required to sort out line drivers... */
gpio_port_write(GPIOA, 0x8100);
gpio_port_write(GPIOB, 0x0000);
gpio_port_write(GPIOA, 0x8180);
gpio_port_write(GPIOB, 0x0002);
gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN);
/* Go to JTAG mode for SWJ-DP */
for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-DP */
jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */
jtagtap_soft_reset();
return 0;
}
void jtagtap_reset(void)
{
volatile int i;
gpio_clear(GPIOB, GPIO1);
for(i = 0; i < 10000; i++) asm("nop");
gpio_set(GPIOB, GPIO1);
jtagtap_soft_reset();
}
void jtagtap_srst(void)
{
volatile int i;
gpio_set(GPIOA, GPIO2);
for(i = 0; i < 10000; i++) asm("nop");
gpio_clear(GPIOA, GPIO2);
}
inline uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO)
{
uint8_t ret;
gpio_set_val(JTAG_PORT, TMS_PIN, dTMS);
gpio_set_val(JTAG_PORT, TDI_PIN, dTDO);
gpio_set(JTAG_PORT, TCK_PIN);
ret = gpio_get(JTAG_PORT, TDO_PIN);
gpio_clear(JTAG_PORT, TCK_PIN);
DEBUG("jtagtap_next(TMS = %d, TDO = %d) = %d\n", dTMS, dTDO, ret);
return ret;
}
#define PROVIDE_GENERIC_JTAGTAP_TMS_SEQ
#define PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
#define PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
#include "jtagtap_generic.c"

179
src/stm32/platform.c Normal file
View File

@ -0,0 +1,179 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the platform specific functions for the STM32
* implementation.
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/systick.h>
#include <libopencm3/stm32/scb.h>
#include <libopencm3/stm32/nvic.h>
#include "platform.h"
#include <ctype.h>
uint8_t running_status;
volatile uint32_t timeout_counter;
jmp_buf fatal_error_jmpbuf;
void morse(const char *msg, char repeat);
static void morse_update(void);
int
platform_init(void)
{
#ifndef LIGHT
rcc_clock_setup_in_hse_8mhz_out_72mhz();
#else
rcc_clock_setup_in_hsi_out_48mhz();
#endif
/* Enable peripherals */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPDEN);
/* Setup GPIO ports */
#ifdef LIGHT
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;
#endif
gpio_clear(USB_PU_PORT, USB_PU_PIN);
gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT,
USB_PU_PIN);
gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_10_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
TMS_PIN | TCK_PIN | TDO_PIN);
gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
LED_RUN | LED_IDLE | LED_ERROR);
/* Setup heartbeat timer */
systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
systick_set_reload(900000); /* Interrupt us at 10 Hz */
systick_interrupt_enable();
systick_counter_enable();
#ifndef LIGHT
SCB_VTOR = 0x2000; // Relocate interrupt vector table here
#endif
/* Enable IRQs */
nvic_enable_irq(NVIC_TIM2_IRQ);
return 0;
}
void sys_tick_handler(void)
{
if(running_status)
gpio_toggle(LED_PORT, LED_RUN);
else
gpio_clear(LED_PORT, LED_RUN);
if(timeout_counter)
timeout_counter--;
morse_update();
}
/* Morse code patterns and lengths */
static const struct {
uint16_t code;
uint8_t bits;
} morse_letter[] = {
{ 0b00011101, 8}, // 'A' .-
{ 0b000101010111, 12}, // 'B' -...
{ 0b00010111010111, 14}, // 'C' -.-.
{ 0b0001010111, 10}, // 'D' -..
{ 0b0001, 4}, // 'E' .
{ 0b000101110101, 12}, // 'F' ..-.
{ 0b000101110111, 12}, // 'G' --.
{ 0b0001010101, 10}, // 'H' ....
{ 0b000101, 6}, // 'I' ..
{0b0001110111011101, 16}, // 'J' .---
{ 0b000111010111, 12}, // 'K' -.-
{ 0b000101011101, 12}, // 'L' .-..
{ 0b0001110111, 10}, // 'M' --
{ 0b00010111, 8}, // 'N' -.
{ 0b00011101110111, 14}, // 'O' ---
{ 0b00010111011101, 14}, // 'P' .--.
{0b0001110101110111, 16}, // 'Q' --.-
{ 0b0001011101, 10}, // 'R' .-.
{ 0b00010101, 8}, // 'S' ...
{ 0b000111, 6}, // 'T' -
{ 0b0001110101, 10}, // 'U' ..-
{ 0b000111010101, 12}, // 'V' ...-
{ 0b000111011101, 12}, // 'W' .--
{ 0b00011101010111, 14}, // 'X' -..-
{0b0001110111010111, 16}, // 'Y' -.--
{ 0b00010101110111, 14}, // 'Z' --..
};
const char *morse_msg;
static const char * volatile morse_ptr;
static char morse_repeat;
void morse(const char *msg, char repeat)
{
morse_msg = morse_ptr = msg;
morse_repeat = repeat;
SET_ERROR_STATE(0);
}
static void morse_update(void)
{
static uint16_t code;
static uint8_t bits;
if(!morse_ptr) return;
if(!bits) {
char c = *morse_ptr++;
if(!c) {
if(morse_repeat) {
morse_ptr = morse_msg;
c = *morse_ptr++;
} else {
morse_ptr = 0;
return;
}
}
if((c >= 'A') && (c <= 'Z')) {
c -= 'A';
code = morse_letter[c].code;
bits = morse_letter[c].bits;
} else {
code = 0; bits = 4;
}
}
SET_ERROR_STATE(code & 1);
code >>= 1; bits--;
}

121
src/stm32/platform.h Normal file
View File

@ -0,0 +1,121 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the platform specific functions for the STM32
* implementation.
*/
#ifndef __PLATFORM_H
#define __PLATFORM_H
#include <libopencm3/stm32/gpio.h>
#include <setjmp.h>
#include "gdb_packet.h"
/* Important pin mappings for STM32 implementation:
*
* LED0 = PB2 (Yellow LED : Running)
* LED1 = PB10 (Yellow LED : Idle)
* LED2 = PB11 (Red LED : Error)
*
* TPWR = RB0 (input)
* nTRST = PB1
* SRST_OUT = PA2
* TDI = PA3
* TMS = PA4 (input for SWDP)
* TCK = PA5
* TDO = PA6 (input)
* nSRST = PA7 (input)
*
* USB cable pull-up: PA8 // was PA10 on prototype
* Force DFU mode button: PB12
*/
/* Hardware definitions... */
#ifndef LIGHT
# define JTAG_PORT GPIOA
# define TDI_PIN GPIO3
# define TMS_PIN GPIO4
# define TCK_PIN GPIO5
# define TDO_PIN GPIO6
# define SWDP_PORT JTAG_PORT
# define SWDIO_PIN TMS_PIN
# define SWCLK_PIN TCK_PIN
# define USB_PU_PORT GPIOA
# define USB_PU_PIN GPIO8
# define LED_PORT GPIOB
# define LED_RUN GPIO2
# define LED_IDLE GPIO10
# define LED_ERROR GPIO11
#else
# define JTAG_PORT GPIOA
# define TDI_PIN GPIO3
# define TMS_PIN GPIO2
# define TCK_PIN GPIO7
# define TDO_PIN GPIO6
# define SWDP_PORT JTAG_PORT
# define SWDIO_PIN TMS_PIN
# define SWCLK_PIN TCK_PIN
# define USB_PU_PORT GPIOA
# define USB_PU_PIN GPIO15
#endif
#define DEBUG(...)
extern uint8_t running_status;
extern volatile uint32_t timeout_counter;
extern jmp_buf fatal_error_jmpbuf;
extern const char *morse_msg;
#define gpio_set_val(port, pin, val) do { \
if(val) \
gpio_set((port), (pin)); \
else \
gpio_clear((port), (pin)); \
} while(0)
#define SET_RUN_STATE(state) {running_status = (state);}
#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE, state);}
#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);}
#define PLATFORM_SET_FATAL_ERROR_RECOVERY() {setjmp(fatal_error_jmpbuf);}
#define PLATFORM_FATAL_ERROR(error) { \
if(running_status) gdb_putpacketz("X1D"); \
else gdb_putpacketz("EFF"); \
running_status = 0; \
TARGET_LIST_FREE(); \
cur_target = last_target = NULL; \
morse("TARGET LOST.", 1); \
longjmp(fatal_error_jmpbuf, (error)); \
}
int platform_init(void);
void morse(const char *msg, char repeat);
#endif

156
src/stm32/swdptap.c Normal file
View File

@ -0,0 +1,156 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements the low-level SW-DP interface. */
#include <libopencm3/stm32/gpio.h>
#include <stdio.h>
#include "general.h"
#include "swdptap.h"
#include "gdb_packet.h"
void swdptap_turnaround(uint8_t dir)
{
static uint8_t olddir = 0;
DEBUG("%s", dir ? "\n-> ":"\n<- ");
/* Don't turnaround if direction not changing */
if(dir == olddir) return;
olddir = dir;
if(dir)
gpio_set_mode(SWDP_PORT, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, SWDIO_PIN);
gpio_set(SWDP_PORT, SWCLK_PIN);
gpio_clear(SWDP_PORT, SWCLK_PIN);
if(!dir)
gpio_set_mode(SWDP_PORT, GPIO_MODE_OUTPUT_10_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN);
}
uint8_t swdptap_bit_in(void)
{
uint8_t ret;
ret = gpio_get(SWDP_PORT, SWDIO_PIN);
gpio_set(SWDP_PORT, SWCLK_PIN);
gpio_clear(SWDP_PORT, SWCLK_PIN);
DEBUG("%d", ret?1:0);
return ret;
}
void swdptap_bit_out(uint8_t val)
{
DEBUG("%d", val);
gpio_set_val(SWDP_PORT, SWDIO_PIN, val);
gpio_set(SWDP_PORT, SWCLK_PIN);
gpio_clear(SWDP_PORT, SWCLK_PIN);
}
int swdptap_init(void)
{
/* This must be investigated in more detail.
* As described in STM32 Reference Manual... */
swdptap_reset();
swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */
swdptap_reset();
swdptap_seq_out(0, 16);
return 0;
}
void swdptap_reset(void)
{
swdptap_turnaround(0);
/* 50 clocks with TMS high */
for(int i = 0; i < 50; i++) swdptap_bit_out(1);
}
uint32_t swdptap_seq_in(int ticks)
{
uint32_t index = 1;
uint32_t ret = 0;
swdptap_turnaround(1);
while(ticks--) {
if(swdptap_bit_in()) ret |= index;
index <<= 1;
}
return ret;
}
uint8_t swdptap_seq_in_parity(uint32_t *ret, int ticks)
{
uint32_t index = 1;
uint8_t parity = 0;
*ret = 0;
swdptap_turnaround(1);
while(ticks--) {
if(swdptap_bit_in()) {
*ret |= index;
parity ^= 1;
}
index <<= 1;
}
if(swdptap_bit_in()) parity ^= 1;
return parity;
}
void swdptap_seq_out(uint32_t MS, int ticks)
{
swdptap_turnaround(0);
while(ticks--) {
swdptap_bit_out(MS & 1);
MS >>= 1;
}
}
void swdptap_seq_out_parity(uint32_t MS, int ticks)
{
uint8_t parity = 0;
swdptap_turnaround(0);
while(ticks--) {
swdptap_bit_out(MS & 1);
parity ^= MS;
MS >>= 1;
}
swdptap_bit_out(parity & 1);
}

308
src/stm32/usbdfu.c Normal file
View File

@ -0,0 +1,308 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <libopencm3/stm32/systick.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/flash.h>
#include <libopencm3/stm32/scb.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/dfu.h>
#define APP_ADDRESS 0x08002000
/* Commands sent with wBlockNum == 0 as per ST implementation. */
#define CMD_SETADDR 0x21
#define CMD_ERASE 0x41
/* We need a special large control buffer for this device: */
u8 usbd_control_buffer[1024];
static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
static char *get_dev_unique_id(char *serial_no);
static struct {
u8 buf[sizeof(usbd_control_buffer)];
u16 len;
u32 addr;
u16 blocknum;
} prog;
const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0xDF11,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = DFU_FUNCTIONAL,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeout = 255,
.wTransferSize = 1024,
.bcdDFUVersion = 0x011A,
};
const struct usb_interface_descriptor iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 2,
/* The ST Microelectronics DfuSe application needs this string.
* The format isn't documented... */
.iInterface = 4,
.extra = &dfu_function,
.extralen = sizeof(dfu_function),
};
const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = &iface,
}};
const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xC0,
.bMaxPower = 0x32,
.interface = ifaces,
};
static char serial_no[25];
static const char *usb_strings[] = {
"x",
"Black Sphere Technologies",
"Black Magic Probe (Upgrade)",
serial_no,
/* This string is used by ST Microelectronics' DfuSe utility */
"@Internal Flash /0x08000000/8*001Ka,120*001Kg"
};
static u8 usbdfu_getstatus(u32 *bwPollTimeout)
{
switch(usbdfu_state) {
case STATE_DFU_DNLOAD_SYNC:
usbdfu_state = STATE_DFU_DNBUSY;
*bwPollTimeout = 100;
return DFU_STATUS_OK;
case STATE_DFU_MANIFEST_SYNC:
/* Device will reset when read is complete */
usbdfu_state = STATE_DFU_MANIFEST;
return DFU_STATUS_OK;
default:
return DFU_STATUS_OK;
}
}
static void usbdfu_getstatus_complete(struct usb_setup_data *req)
{
int i;
(void)req;
switch(usbdfu_state) {
case STATE_DFU_DNBUSY:
flash_unlock();
if(prog.blocknum == 0) {
switch(prog.buf[0]) {
case CMD_ERASE:
flash_erase_page(*(u32*)(prog.buf+1));
case CMD_SETADDR:
prog.addr = *(u32*)(prog.buf+1);
}
} else {
u32 baseaddr = prog.addr +
((prog.blocknum - 2) *
dfu_function.wTransferSize);
for(i = 0; i < prog.len; i += 2)
flash_program_half_word(baseaddr + i,
*(u16*)(prog.buf+i));
}
flash_lock();
/* We jump straight to dfuDNLOAD-IDLE,
* skipping dfuDNLOAD-SYNC
*/
usbdfu_state = STATE_DFU_DNLOAD_IDLE;
return;
case STATE_DFU_MANIFEST:
/* USB device must detach, we just reset... */
scb_reset_system();
return; /* Will never return */
default:
return;
}
}
static int usbdfu_control_request(struct usb_setup_data *req, u8 **buf,
u16 *len, void (**complete)(struct usb_setup_data *req))
{
if((req->bmRequestType & 0x7F) != 0x21)
return 0; /* Only accept class request */
switch(req->bRequest) {
case DFU_DNLOAD:
if((len == NULL) || (*len == 0)) {
usbdfu_state = STATE_DFU_MANIFEST_SYNC;
return 1;
} else {
/* Copy download data for use on GET_STATUS */
prog.blocknum = req->wValue;
prog.len = *len;
memcpy(prog.buf, *buf, *len);
usbdfu_state = STATE_DFU_DNLOAD_SYNC;
return 1;
}
case DFU_CLRSTATUS:
/* Clear error and return to dfuIDLE */
if(usbdfu_state == STATE_DFU_ERROR)
usbdfu_state = STATE_DFU_IDLE;
return 1;
case DFU_ABORT:
/* Abort returns to dfuIDLE state */
usbdfu_state = STATE_DFU_IDLE;
return 1;
case DFU_UPLOAD:
/* Upload not supported for now */
return 0;
case DFU_GETSTATUS: {
u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
(*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
(*buf)[1] = bwPollTimeout & 0xFF;
(*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
(*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
(*buf)[4] = usbdfu_state;
(*buf)[5] = 0; /* iString not used here */
*len = 6;
*complete = usbdfu_getstatus_complete;
return 1;
}
case DFU_GETSTATE:
/* Return state with no state transision */
*buf[0] = usbdfu_state;
*len = 1;
return 1;
}
return 0;
}
int main(void)
{
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN);
if(gpio_get(GPIOB, GPIO12)) {
/* Boot the application if it's valid */
if((*(volatile u32*)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
/* Set vector table base address */
SCB_VTOR = APP_ADDRESS & 0xFFFF;
/* Initialise master stack pointer */
asm volatile ("msr msp, %0"::"g"
(*(volatile u32*)APP_ADDRESS));
/* Jump to application */
(*(void(**)())(APP_ADDRESS + 4))();
}
}
rcc_clock_setup_in_hse_8mhz_out_72mhz();
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO8);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO11);
systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
systick_set_reload(900000);
systick_interrupt_enable();
systick_counter_enable();
gpio_set_mode(GPIOB, GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, GPIO2 | GPIO10);
get_dev_unique_id(serial_no);
usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings);
usbd_set_control_buffer_size(sizeof(usbd_control_buffer));
usbd_register_control_callback(
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
usbdfu_control_request);
gpio_set(GPIOA, GPIO8);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
while (1)
usbd_poll();
}
static char *get_dev_unique_id(char *s)
{
volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFFF7E8;
int i;
/* Fetch serial number from chip's unique ID */
for(i = 0; i < 24; i+=2) {
s[i] = ((*unique_id >> 4) & 0xF) + '0';
s[i+1] = (*unique_id++ & 0xF) + '0';
}
for(i = 0; i < 24; i++)
if(s[i] > '9')
s[i] += 'A' - '9' - 1;
return s;
}
void sys_tick_handler()
{
gpio_toggle(GPIOB, GPIO11); /* LED2 on/off */
}

229
src/stm32_tgt.c Normal file
View File

@ -0,0 +1,229 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements STM32 target specific functions for detecting
* the device, providing the XML memory map and Flash memory programming.
*
* Refereces:
* ST doc - RM0008
* Reference manual - STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx
* and STM32F107xx advanced ARM-based 32-bit MCUs
* ST doc - PM0075
* Programming manual - STM32F10xxx Flash memory microcontrollers
*
* Issues:
* On some devices this fails occasionally. A retry suceeds. Maybe
* a timing issue.
*/
#include <stdlib.h>
#include <string.h>
#include "general.h"
#include "adiv5.h"
#include "target.h"
#include "stm32_tgt.h"
static int stm32md_flash_erase(struct target_s *target, uint32_t addr, int len);
static int stm32hd_flash_erase(struct target_s *target, uint32_t addr, int len);
static int stm32_flash_erase(struct target_s *target, uint32_t addr, int len,
uint32_t pagesize);
static int stm32_flash_write_words(struct target_s *target, uint32_t dest, const uint32_t *src,
int len);
static const char stm32_driver_str[] = "STM32, Medium density.";
static const char stm32hd_driver_str[] = "STM32, High density.";
static const char stm32_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
"<memory-map>"
" <memory type=\"flash\" start=\"0x8000000\" length=\"0x20000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x5000\"/>"
"</memory-map>";
static const char stm32hd_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
"<memory-map>"
" <memory type=\"flash\" start=\"0x8000000\" length=\"0x80000\">"
" <property name=\"blocksize\">0x800</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
"</memory-map>";
/* Flash Program ad Erase Controller Register Map */
#define FPEC_BASE 0x40022000
#define FLASH_ACR (FPEC_BASE+0x00)
#define FLASH_KEYR (FPEC_BASE+0x04)
#define FLASH_OPTKEYR (FPEC_BASE+0x08)
#define FLASH_SR (FPEC_BASE+0x0C)
#define FLASH_CR (FPEC_BASE+0x10)
#define FLASH_AR (FPEC_BASE+0x14)
#define FLASH_OBR (FPEC_BASE+0x1C)
#define FLASH_WRPR (FPEC_BASE+0x20)
#define KEY1 0x45670123
#define KEY2 0xCDEF89AB
#define SR_ERROR_MASK 0x14
#define SR_EOP 0x20
uint16_t stm32_flash_write_stub[] = {
// _start:
0x4809, // ldr r0, [pc, #36] // _flashbase
0x490a, // ldr r1, [pc, #40] // _addr
0x467a, // mov r2, pc
0x322c, // adds r2, #44
0x4b09, // ldr r3, [pc, #36] // _size
0x2501, // movs r5, #1
// _next:
0xb153, // cbz r3, _done
0x6105, // str r5, [r0, #16]
0x8814, // ldrh r4, [r2]
0x800c, // strh r4, [r1]
// _wait:
0x68c4, // ldr r4, [r0, #12]
0x2601, // movs r6, #1
0x4234, // tst r4, r6
0xd1fb, // bne _wait
0x3b02, // subs r3, #2
0x3102, // adds r1, #2
0x3202, // adds r2, #2
0xe7f3, // b _next
// _done:
0xbe00, // bkpt
0x0000,
// .org 0x28
// _flashbase:
0x2000, 0x4002, // .word 0x40022000 (FPEC_BASE)
// _addr:
// 0x0000, 0x0000,
// _size:
// 0x0000, 0x0000,
// _data:
// ...
};
int stm32_probe(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
uint32_t idcode;
idcode = adiv5_ap_mem_read(t->ap, 0xE0042000);
switch(idcode & 0xFFF) {
case 0x410: /* Medium density */
case 0x412: /* Low denisty */
case 0x420: /* Value Line, Low-/Medium density */
target->driver = stm32_driver_str;
target->xml_mem_map = stm32_xml_memory_map;
target->flash_erase = stm32md_flash_erase;
target->flash_write_words = stm32_flash_write_words;
return 0;
case 0x414: /* High density */
case 0x418: /* Connectivity Line */
case 0x428: /* Value Line, High Density */
target->driver = stm32hd_driver_str;
target->xml_mem_map = stm32hd_xml_memory_map;
target->flash_erase = stm32hd_flash_erase;
target->flash_write_words = stm32_flash_write_words;
return 0;
default:
return -1;
}
}
static int stm32_flash_erase(struct target_s *target, uint32_t addr, int len, uint32_t pagesize)
{
struct target_ap_s *t = (void *)target;
uint16_t sr;
addr &= ~(pagesize - 1);
len &= ~(pagesize - 1);
/* Enable FPEC controller access */
adiv5_ap_mem_write(t->ap, FLASH_KEYR, KEY1);
adiv5_ap_mem_write(t->ap, FLASH_KEYR, KEY2);
while(len) {
/* Flash page erase instruction */
adiv5_ap_mem_write(t->ap, FLASH_CR, 2);
/* write address to FMA */
adiv5_ap_mem_write(t->ap, FLASH_AR, addr);
/* Flash page erase start instruction */
adiv5_ap_mem_write(t->ap, FLASH_CR, 0x42);
/* Read FLASH_SR to poll for BSY bit */
while(adiv5_ap_mem_read(t->ap, FLASH_SR) & 1)
if(target_check_error(target)) return -1;
len -= pagesize;
addr += pagesize;
}
/* Check for error */
sr = adiv5_ap_mem_read(t->ap, FLASH_SR);
if((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) return -1;
return 0;
}
static int stm32hd_flash_erase(struct target_s *target, uint32_t addr, int len)
{
return stm32_flash_erase(target, addr, len, 0x800);
}
static int stm32md_flash_erase(struct target_s *target, uint32_t addr, int len)
{
return stm32_flash_erase(target, addr, len, 0x400);
}
static int stm32_flash_write_words(struct target_s *target, uint32_t dest,
const uint32_t *src, int len)
{
struct target_ap_s *t = (void *)target;
uint32_t data[(len>>2)+2];
/* Construct data buffer used by stub */
data[0] = dest & 0xFFFFFFFE;
data[1] = len & 0xFFFFFFFE;
memcpy(&data[2], src, len);
/* Write stub and data to target ram and set PC */
target_mem_write_words(target, 0x20000000, (void*)stm32_flash_write_stub, 0x2C);
target_mem_write_words(target, 0x2000002C, data, len + 8);
target_pc_write(target, 0x20000000);
if(target_check_error(target)) return -1;
/* Execute the stub */
target_halt_resume(target, 0);
while(!target_halt_wait(target));
/* Check for error */
if(adiv5_ap_mem_read(t->ap, FLASH_SR) & SR_ERROR_MASK) return -1;
return 0;
}