blackmagic/src/cortexm3.c

395 lines
9.5 KiB
C

/*
* 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;
}