Import of working source tree.
This commit is contained in:
commit
406617a2a4
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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--;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue