1453 lines
45 KiB
C
1453 lines
45 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/***************************************************************************
|
|
* Copyright (C) 2011 by Julius Baxter *
|
|
* julius@opencores.org *
|
|
* *
|
|
* Copyright (C) 2013 by Marek Czerski *
|
|
* ma.czerski@gmail.com *
|
|
* *
|
|
* Copyright (C) 2013 by Franck Jullien *
|
|
* elec4fun@gmail.com *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <jtag/jtag.h>
|
|
#include <target/register.h>
|
|
#include <target/target.h>
|
|
#include <target/breakpoints.h>
|
|
#include <target/target_type.h>
|
|
#include <helper/time_support.h>
|
|
#include <helper/fileio.h>
|
|
#include "or1k_tap.h"
|
|
#include "or1k.h"
|
|
#include "or1k_du.h"
|
|
|
|
OOCD_LIST_HEAD(tap_list);
|
|
OOCD_LIST_HEAD(du_list);
|
|
|
|
static int or1k_remove_breakpoint(struct target *target,
|
|
struct breakpoint *breakpoint);
|
|
|
|
static int or1k_read_core_reg(struct target *target, int num);
|
|
static int or1k_write_core_reg(struct target *target, int num);
|
|
|
|
static struct or1k_core_reg *or1k_core_reg_list_arch_info;
|
|
|
|
static const struct or1k_core_reg_init or1k_init_reg_list[] = {
|
|
{"r0", GROUP0 + 1024, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r1", GROUP0 + 1025, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r2", GROUP0 + 1026, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r3", GROUP0 + 1027, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r4", GROUP0 + 1028, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r5", GROUP0 + 1029, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r6", GROUP0 + 1030, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r7", GROUP0 + 1031, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r8", GROUP0 + 1032, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r9", GROUP0 + 1033, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r10", GROUP0 + 1034, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r11", GROUP0 + 1035, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r12", GROUP0 + 1036, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r13", GROUP0 + 1037, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r14", GROUP0 + 1038, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r15", GROUP0 + 1039, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r16", GROUP0 + 1040, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r17", GROUP0 + 1041, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r18", GROUP0 + 1042, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r19", GROUP0 + 1043, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r20", GROUP0 + 1044, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r21", GROUP0 + 1045, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r22", GROUP0 + 1046, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r23", GROUP0 + 1047, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r24", GROUP0 + 1048, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r25", GROUP0 + 1049, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r26", GROUP0 + 1050, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r27", GROUP0 + 1051, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r28", GROUP0 + 1052, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r29", GROUP0 + 1053, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r30", GROUP0 + 1054, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"r31", GROUP0 + 1055, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"ppc", GROUP0 + 18, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"npc", GROUP0 + 16, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"sr", GROUP0 + 17, "org.gnu.gdb.or1k.group0", NULL},
|
|
{"vr", GROUP0 + 0, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"upr", GROUP0 + 1, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"cpucfgr", GROUP0 + 2, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"dmmucfgr", GROUP0 + 3, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"immucfgr", GROUP0 + 4, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"dccfgr", GROUP0 + 5, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"iccfgr", GROUP0 + 6, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"dcfgr", GROUP0 + 7, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"pccfgr", GROUP0 + 8, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"fpcsr", GROUP0 + 20, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr0", GROUP0 + 32, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr1", GROUP0 + 33, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr2", GROUP0 + 34, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr3", GROUP0 + 35, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr4", GROUP0 + 36, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr5", GROUP0 + 37, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr6", GROUP0 + 38, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr7", GROUP0 + 39, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr8", GROUP0 + 40, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr9", GROUP0 + 41, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr10", GROUP0 + 42, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr11", GROUP0 + 43, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr12", GROUP0 + 44, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr13", GROUP0 + 45, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr14", GROUP0 + 46, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"epcr15", GROUP0 + 47, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear0", GROUP0 + 48, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear1", GROUP0 + 49, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear2", GROUP0 + 50, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear3", GROUP0 + 51, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear4", GROUP0 + 52, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear5", GROUP0 + 53, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear6", GROUP0 + 54, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear7", GROUP0 + 55, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear8", GROUP0 + 56, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear9", GROUP0 + 57, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear10", GROUP0 + 58, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear11", GROUP0 + 59, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear12", GROUP0 + 60, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear13", GROUP0 + 61, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear14", GROUP0 + 62, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"eear15", GROUP0 + 63, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr0", GROUP0 + 64, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr1", GROUP0 + 65, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr2", GROUP0 + 66, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr3", GROUP0 + 67, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr4", GROUP0 + 68, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr5", GROUP0 + 69, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr6", GROUP0 + 70, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr7", GROUP0 + 71, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr8", GROUP0 + 72, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr9", GROUP0 + 73, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr10", GROUP0 + 74, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr11", GROUP0 + 75, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr12", GROUP0 + 76, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr13", GROUP0 + 77, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr14", GROUP0 + 78, "org.gnu.gdb.or1k.group0", "system"},
|
|
{"esr15", GROUP0 + 79, "org.gnu.gdb.or1k.group0", "system"},
|
|
|
|
{"dmmuucr", GROUP1 + 0, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"dmmuupr", GROUP1 + 1, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"dtlbeir", GROUP1 + 2, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbmr0", GROUP1 + 4, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbmr1", GROUP1 + 5, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbmr2", GROUP1 + 6, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbmr3", GROUP1 + 7, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbtr0", GROUP1 + 8, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbtr1", GROUP1 + 9, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbtr2", GROUP1 + 10, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
{"datbtr3", GROUP1 + 11, "org.gnu.gdb.or1k.group1", "dmmu"},
|
|
|
|
{"immucr", GROUP2 + 0, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"immupr", GROUP2 + 1, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"itlbeir", GROUP2 + 2, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbmr0", GROUP2 + 4, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbmr1", GROUP2 + 5, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbmr2", GROUP2 + 6, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbmr3", GROUP2 + 7, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbtr0", GROUP2 + 8, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbtr1", GROUP2 + 9, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbtr2", GROUP2 + 10, "org.gnu.gdb.or1k.group2", "immu"},
|
|
{"iatbtr3", GROUP2 + 11, "org.gnu.gdb.or1k.group2", "immu"},
|
|
|
|
{"dccr", GROUP3 + 0, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
{"dcbpr", GROUP3 + 1, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
{"dcbfr", GROUP3 + 2, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
{"dcbir", GROUP3 + 3, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
{"dcbwr", GROUP3 + 4, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
{"dcblr", GROUP3 + 5, "org.gnu.gdb.or1k.group3", "dcache"},
|
|
|
|
{"iccr", GROUP4 + 0, "org.gnu.gdb.or1k.group4", "icache"},
|
|
{"icbpr", GROUP4 + 1, "org.gnu.gdb.or1k.group4", "icache"},
|
|
{"icbir", GROUP4 + 2, "org.gnu.gdb.or1k.group4", "icache"},
|
|
{"icblr", GROUP4 + 3, "org.gnu.gdb.or1k.group4", "icache"},
|
|
|
|
{"maclo", GROUP5 + 0, "org.gnu.gdb.or1k.group5", "mac"},
|
|
{"machi", GROUP5 + 1, "org.gnu.gdb.or1k.group5", "mac"},
|
|
|
|
{"dvr0", GROUP6 + 0, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr1", GROUP6 + 1, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr2", GROUP6 + 2, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr3", GROUP6 + 3, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr4", GROUP6 + 4, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr5", GROUP6 + 5, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr6", GROUP6 + 6, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dvr7", GROUP6 + 7, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr0", GROUP6 + 8, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr1", GROUP6 + 9, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr2", GROUP6 + 10, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr3", GROUP6 + 11, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr4", GROUP6 + 12, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr5", GROUP6 + 13, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr6", GROUP6 + 14, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcr7", GROUP6 + 15, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dmr1", GROUP6 + 16, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dmr2", GROUP6 + 17, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcwr0", GROUP6 + 18, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dcwr1", GROUP6 + 19, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"dsr", GROUP6 + 20, "org.gnu.gdb.or1k.group6", "debug"},
|
|
{"drr", GROUP6 + 21, "org.gnu.gdb.or1k.group6", "debug"},
|
|
|
|
{"pccr0", GROUP7 + 0, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr1", GROUP7 + 1, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr2", GROUP7 + 2, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr3", GROUP7 + 3, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr4", GROUP7 + 4, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr5", GROUP7 + 5, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr6", GROUP7 + 6, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pccr7", GROUP7 + 7, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr0", GROUP7 + 8, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr1", GROUP7 + 9, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr2", GROUP7 + 10, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr3", GROUP7 + 11, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr4", GROUP7 + 12, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr5", GROUP7 + 13, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr6", GROUP7 + 14, "org.gnu.gdb.or1k.group7", "perf"},
|
|
{"pcmr7", GROUP7 + 15, "org.gnu.gdb.or1k.group7", "perf"},
|
|
|
|
{"pmr", GROUP8 + 0, "org.gnu.gdb.or1k.group8", "power"},
|
|
|
|
{"picmr", GROUP9 + 0, "org.gnu.gdb.or1k.group9", "pic"},
|
|
{"picsr", GROUP9 + 2, "org.gnu.gdb.or1k.group9", "pic"},
|
|
|
|
{"ttmr", GROUP10 + 0, "org.gnu.gdb.or1k.group10", "timer"},
|
|
{"ttcr", GROUP10 + 1, "org.gnu.gdb.or1k.group10", "timer"},
|
|
};
|
|
|
|
static int or1k_add_reg(struct target *target, struct or1k_core_reg *new_reg)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
int reg_list_size = or1k->nb_regs * sizeof(struct or1k_core_reg);
|
|
|
|
or1k_core_reg_list_arch_info = realloc(or1k_core_reg_list_arch_info,
|
|
reg_list_size + sizeof(struct or1k_core_reg));
|
|
|
|
memcpy(&or1k_core_reg_list_arch_info[or1k->nb_regs], new_reg,
|
|
sizeof(struct or1k_core_reg));
|
|
|
|
or1k_core_reg_list_arch_info[or1k->nb_regs].list_num = or1k->nb_regs;
|
|
|
|
or1k->nb_regs++;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_create_reg_list(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
or1k_core_reg_list_arch_info = malloc(ARRAY_SIZE(or1k_init_reg_list) *
|
|
sizeof(struct or1k_core_reg));
|
|
|
|
for (int i = 0; i < (int)ARRAY_SIZE(or1k_init_reg_list); i++) {
|
|
or1k_core_reg_list_arch_info[i].name = or1k_init_reg_list[i].name;
|
|
or1k_core_reg_list_arch_info[i].spr_num = or1k_init_reg_list[i].spr_num;
|
|
or1k_core_reg_list_arch_info[i].group = or1k_init_reg_list[i].group;
|
|
or1k_core_reg_list_arch_info[i].feature = or1k_init_reg_list[i].feature;
|
|
or1k_core_reg_list_arch_info[i].list_num = i;
|
|
or1k_core_reg_list_arch_info[i].target = NULL;
|
|
or1k_core_reg_list_arch_info[i].or1k_common = NULL;
|
|
}
|
|
|
|
or1k->nb_regs = ARRAY_SIZE(or1k_init_reg_list);
|
|
|
|
struct or1k_core_reg new_reg;
|
|
new_reg.target = NULL;
|
|
new_reg.or1k_common = NULL;
|
|
|
|
char name[32];
|
|
for (int way = 0; way < 4; way++) {
|
|
for (int i = 0; i < 128; i++) {
|
|
|
|
sprintf(name, "dtlbw%dmr%d", way, i);
|
|
new_reg.name = strdup(name);
|
|
new_reg.spr_num = GROUP1 + 512 + i + (way * 256);
|
|
new_reg.feature = "org.gnu.gdb.or1k.group1";
|
|
new_reg.group = "dmmu";
|
|
or1k_add_reg(target, &new_reg);
|
|
|
|
sprintf(name, "dtlbw%dtr%d", way, i);
|
|
new_reg.name = strdup(name);
|
|
new_reg.spr_num = GROUP1 + 640 + i + (way * 256);
|
|
new_reg.feature = "org.gnu.gdb.or1k.group1";
|
|
new_reg.group = "dmmu";
|
|
or1k_add_reg(target, &new_reg);
|
|
|
|
|
|
sprintf(name, "itlbw%dmr%d", way, i);
|
|
new_reg.name = strdup(name);
|
|
new_reg.spr_num = GROUP2 + 512 + i + (way * 256);
|
|
new_reg.feature = "org.gnu.gdb.or1k.group2";
|
|
new_reg.group = "immu";
|
|
or1k_add_reg(target, &new_reg);
|
|
|
|
|
|
sprintf(name, "itlbw%dtr%d", way, i);
|
|
new_reg.name = strdup(name);
|
|
new_reg.spr_num = GROUP2 + 640 + i + (way * 256);
|
|
new_reg.feature = "org.gnu.gdb.or1k.group2";
|
|
new_reg.group = "immu";
|
|
or1k_add_reg(target, &new_reg);
|
|
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_jtag_read_regs(struct or1k_common *or1k, uint32_t *regs)
|
|
{
|
|
struct or1k_du *du_core = or1k_jtag_to_du(&or1k->jtag);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
return du_core->or1k_jtag_read_cpu(&or1k->jtag,
|
|
or1k->arch_info[OR1K_REG_R0].spr_num, OR1K_REG_R31 + 1,
|
|
regs + OR1K_REG_R0);
|
|
}
|
|
|
|
static int or1k_jtag_write_regs(struct or1k_common *or1k, uint32_t *regs)
|
|
{
|
|
struct or1k_du *du_core = or1k_jtag_to_du(&or1k->jtag);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
return du_core->or1k_jtag_write_cpu(&or1k->jtag,
|
|
or1k->arch_info[OR1K_REG_R0].spr_num, OR1K_REG_R31 + 1,
|
|
®s[OR1K_REG_R0]);
|
|
}
|
|
|
|
static int or1k_save_context(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
int regs_read = 0;
|
|
int retval;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
for (int i = 0; i < OR1KNUMCOREREGS; i++) {
|
|
if (!or1k->core_cache->reg_list[i].valid) {
|
|
if (i == OR1K_REG_PPC || i == OR1K_REG_NPC || i == OR1K_REG_SR) {
|
|
retval = du_core->or1k_jtag_read_cpu(&or1k->jtag,
|
|
or1k->arch_info[i].spr_num, 1,
|
|
&or1k->core_regs[i]);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
} else if (!regs_read) {
|
|
/* read gpr registers at once (but only one time in this loop) */
|
|
retval = or1k_jtag_read_regs(or1k, or1k->core_regs);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
/* prevent next reads in this loop */
|
|
regs_read = 1;
|
|
}
|
|
/* We've just updated the core_reg[i], now update
|
|
the core cache */
|
|
or1k_read_core_reg(target, i);
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_restore_context(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
int reg_write = 0;
|
|
int retval;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
for (int i = 0; i < OR1KNUMCOREREGS; i++) {
|
|
if (or1k->core_cache->reg_list[i].dirty) {
|
|
or1k_write_core_reg(target, i);
|
|
|
|
if (i == OR1K_REG_PPC || i == OR1K_REG_NPC || i == OR1K_REG_SR) {
|
|
retval = du_core->or1k_jtag_write_cpu(&or1k->jtag,
|
|
or1k->arch_info[i].spr_num, 1,
|
|
&or1k->core_regs[i]);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while restoring context");
|
|
return retval;
|
|
}
|
|
} else
|
|
reg_write = 1;
|
|
}
|
|
}
|
|
|
|
if (reg_write) {
|
|
/* read gpr registers at once (but only one time in this loop) */
|
|
retval = or1k_jtag_write_regs(or1k, or1k->core_regs);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while restoring context");
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_read_core_reg(struct target *target, int num)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
uint32_t reg_value;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
if ((num < 0) || (num >= or1k->nb_regs))
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
if ((num >= 0) && (num < OR1KNUMCOREREGS)) {
|
|
reg_value = or1k->core_regs[num];
|
|
buf_set_u32(or1k->core_cache->reg_list[num].value, 0, 32, reg_value);
|
|
LOG_DEBUG("Read core reg %i value 0x%08" PRIx32, num, reg_value);
|
|
or1k->core_cache->reg_list[num].valid = true;
|
|
or1k->core_cache->reg_list[num].dirty = false;
|
|
} else {
|
|
/* This is an spr, always read value from HW */
|
|
int retval = du_core->or1k_jtag_read_cpu(&or1k->jtag,
|
|
or1k->arch_info[num].spr_num, 1, ®_value);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while reading spr 0x%08" PRIx32, or1k->arch_info[num].spr_num);
|
|
return retval;
|
|
}
|
|
buf_set_u32(or1k->core_cache->reg_list[num].value, 0, 32, reg_value);
|
|
LOG_DEBUG("Read spr reg %i value 0x%08" PRIx32, num, reg_value);
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_write_core_reg(struct target *target, int num)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
if ((num < 0) || (num >= OR1KNUMCOREREGS))
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
uint32_t reg_value = buf_get_u32(or1k->core_cache->reg_list[num].value, 0, 32);
|
|
or1k->core_regs[num] = reg_value;
|
|
LOG_DEBUG("Write core reg %i value 0x%08" PRIx32, num, reg_value);
|
|
or1k->core_cache->reg_list[num].valid = true;
|
|
or1k->core_cache->reg_list[num].dirty = false;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_get_core_reg(struct reg *reg)
|
|
{
|
|
struct or1k_core_reg *or1k_reg = reg->arch_info;
|
|
struct target *target = or1k_reg->target;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
if (target->state != TARGET_HALTED)
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
|
|
return or1k_read_core_reg(target, or1k_reg->list_num);
|
|
}
|
|
|
|
static int or1k_set_core_reg(struct reg *reg, uint8_t *buf)
|
|
{
|
|
struct or1k_core_reg *or1k_reg = reg->arch_info;
|
|
struct target *target = or1k_reg->target;
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
uint32_t value = buf_get_u32(buf, 0, 32);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
if (target->state != TARGET_HALTED)
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
|
|
if (or1k_reg->list_num < OR1KNUMCOREREGS) {
|
|
buf_set_u32(reg->value, 0, 32, value);
|
|
reg->dirty = true;
|
|
reg->valid = true;
|
|
} else {
|
|
/* This is an spr, write it to the HW */
|
|
int retval = du_core->or1k_jtag_write_cpu(&or1k->jtag,
|
|
or1k_reg->spr_num, 1, &value);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while writing spr 0x%08" PRIx32, or1k_reg->spr_num);
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static const struct reg_arch_type or1k_reg_type = {
|
|
.get = or1k_get_core_reg,
|
|
.set = or1k_set_core_reg,
|
|
};
|
|
|
|
static struct reg_cache *or1k_build_reg_cache(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
|
|
struct reg_cache *cache = malloc(sizeof(struct reg_cache));
|
|
struct reg *reg_list = calloc(or1k->nb_regs, sizeof(struct reg));
|
|
struct or1k_core_reg *arch_info =
|
|
malloc((or1k->nb_regs) * sizeof(struct or1k_core_reg));
|
|
struct reg_feature *feature;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
/* Build the process context cache */
|
|
cache->name = "OpenRISC 1000 registers";
|
|
cache->next = NULL;
|
|
cache->reg_list = reg_list;
|
|
cache->num_regs = or1k->nb_regs;
|
|
(*cache_p) = cache;
|
|
or1k->core_cache = cache;
|
|
or1k->arch_info = arch_info;
|
|
|
|
for (int i = 0; i < or1k->nb_regs; i++) {
|
|
arch_info[i] = or1k_core_reg_list_arch_info[i];
|
|
arch_info[i].target = target;
|
|
arch_info[i].or1k_common = or1k;
|
|
reg_list[i].name = or1k_core_reg_list_arch_info[i].name;
|
|
|
|
feature = malloc(sizeof(struct reg_feature));
|
|
feature->name = or1k_core_reg_list_arch_info[i].feature;
|
|
reg_list[i].feature = feature;
|
|
|
|
reg_list[i].group = or1k_core_reg_list_arch_info[i].group;
|
|
reg_list[i].size = 32;
|
|
reg_list[i].value = calloc(1, 4);
|
|
reg_list[i].dirty = false;
|
|
reg_list[i].valid = false;
|
|
reg_list[i].type = &or1k_reg_type;
|
|
reg_list[i].arch_info = &arch_info[i];
|
|
reg_list[i].number = i;
|
|
reg_list[i].exist = true;
|
|
}
|
|
|
|
return cache;
|
|
}
|
|
|
|
static int or1k_debug_entry(struct target *target)
|
|
{
|
|
LOG_DEBUG("-");
|
|
|
|
int retval = or1k_save_context(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_save_context");
|
|
return retval;
|
|
}
|
|
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
uint32_t addr = or1k->core_regs[OR1K_REG_NPC];
|
|
|
|
if (breakpoint_find(target, addr))
|
|
/* Halted on a breakpoint, step back to permit executing the instruction there */
|
|
retval = or1k_set_core_reg(&or1k->core_cache->reg_list[OR1K_REG_NPC],
|
|
(uint8_t *)&addr);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int or1k_halt(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("target->state: %s",
|
|
target_state_name(target));
|
|
|
|
if (target->state == TARGET_HALTED) {
|
|
LOG_DEBUG("Target was already halted");
|
|
return ERROR_OK;
|
|
}
|
|
|
|
if (target->state == TARGET_UNKNOWN)
|
|
LOG_WARNING("Target was in unknown state when halt was requested");
|
|
|
|
if (target->state == TARGET_RESET) {
|
|
if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) &&
|
|
jtag_get_srst()) {
|
|
LOG_ERROR("Can't request a halt while in reset if nSRST pulls nTRST");
|
|
return ERROR_TARGET_FAILURE;
|
|
} else {
|
|
target->debug_reason = DBG_REASON_DBGRQ;
|
|
return ERROR_OK;
|
|
}
|
|
}
|
|
|
|
int retval = du_core->or1k_cpu_stall(&or1k->jtag, CPU_STALL);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Impossible to stall the CPU");
|
|
return retval;
|
|
}
|
|
|
|
target->debug_reason = DBG_REASON_DBGRQ;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_is_cpu_running(struct target *target, int *running)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
int retval;
|
|
int tries = 0;
|
|
const int RETRIES_MAX = 5;
|
|
|
|
/* Have a retry loop to determine of the CPU is running.
|
|
If target has been hard reset for any reason, it might take a couple
|
|
of goes before it's ready again.
|
|
*/
|
|
while (tries < RETRIES_MAX) {
|
|
|
|
tries++;
|
|
|
|
retval = du_core->or1k_is_cpu_running(&or1k->jtag, running);
|
|
if (retval != ERROR_OK) {
|
|
LOG_WARNING("Debug IF CPU control reg read failure.");
|
|
/* Try once to restart the JTAG infrastructure -
|
|
quite possibly the board has just been reset. */
|
|
LOG_WARNING("Resetting JTAG TAP state and reconnecting to debug IF.");
|
|
du_core->or1k_jtag_init(&or1k->jtag);
|
|
|
|
LOG_WARNING("...attempt %d of %d", tries, RETRIES_MAX);
|
|
|
|
alive_sleep(2);
|
|
|
|
continue;
|
|
} else
|
|
return ERROR_OK;
|
|
}
|
|
|
|
LOG_ERROR("Could not re-establish communication with target");
|
|
return retval;
|
|
}
|
|
|
|
static int or1k_poll(struct target *target)
|
|
{
|
|
int retval;
|
|
int running;
|
|
|
|
retval = or1k_is_cpu_running(target, &running);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_is_cpu_running");
|
|
return retval;
|
|
}
|
|
|
|
/* check for processor halted */
|
|
if (!running) {
|
|
/* It's actually stalled, so update our software's state */
|
|
if ((target->state == TARGET_RUNNING) ||
|
|
(target->state == TARGET_RESET)) {
|
|
|
|
target->state = TARGET_HALTED;
|
|
|
|
retval = or1k_debug_entry(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_debug_entry");
|
|
return retval;
|
|
}
|
|
|
|
target_call_event_callbacks(target,
|
|
TARGET_EVENT_HALTED);
|
|
} else if (target->state == TARGET_DEBUG_RUNNING) {
|
|
target->state = TARGET_HALTED;
|
|
|
|
retval = or1k_debug_entry(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_debug_entry");
|
|
return retval;
|
|
}
|
|
|
|
target_call_event_callbacks(target,
|
|
TARGET_EVENT_DEBUG_HALTED);
|
|
}
|
|
} else { /* ... target is running */
|
|
|
|
/* If target was supposed to be stalled, stall it again */
|
|
if (target->state == TARGET_HALTED) {
|
|
|
|
target->state = TARGET_RUNNING;
|
|
|
|
retval = or1k_halt(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_halt");
|
|
return retval;
|
|
}
|
|
|
|
retval = or1k_debug_entry(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_debug_entry");
|
|
return retval;
|
|
}
|
|
|
|
target_call_event_callbacks(target,
|
|
TARGET_EVENT_DEBUG_HALTED);
|
|
}
|
|
|
|
target->state = TARGET_RUNNING;
|
|
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_assert_reset(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
int retval = du_core->or1k_cpu_reset(&or1k->jtag, CPU_RESET);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while asserting RESET");
|
|
return retval;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_deassert_reset(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
int retval = du_core->or1k_cpu_reset(&or1k->jtag, CPU_NOT_RESET);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while deasserting RESET");
|
|
return retval;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_soft_reset_halt(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
int retval = du_core->or1k_cpu_stall(&or1k->jtag, CPU_STALL);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while stalling the CPU");
|
|
return retval;
|
|
}
|
|
|
|
retval = or1k_assert_reset(target);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
retval = or1k_deassert_reset(target);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static bool is_any_soft_breakpoint(struct target *target)
|
|
{
|
|
struct breakpoint *breakpoint = target->breakpoints;
|
|
|
|
LOG_DEBUG("-");
|
|
|
|
while (breakpoint)
|
|
if (breakpoint->type == BKPT_SOFT)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int or1k_resume_or_step(struct target *target, int current,
|
|
uint32_t address, int handle_breakpoints,
|
|
int debug_execution, int step)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
struct breakpoint *breakpoint = NULL;
|
|
uint32_t resume_pc;
|
|
uint32_t debug_reg_list[OR1K_DEBUG_REG_NUM];
|
|
|
|
LOG_DEBUG("Addr: 0x%" PRIx32 ", stepping: %s, handle breakpoints %s\n",
|
|
address, step ? "yes" : "no", handle_breakpoints ? "yes" : "no");
|
|
|
|
if (target->state != TARGET_HALTED) {
|
|
LOG_TARGET_ERROR(target, "not halted");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
if (!debug_execution)
|
|
target_free_all_working_areas(target);
|
|
|
|
/* current ? continue on current pc : continue at <address> */
|
|
if (!current)
|
|
buf_set_u32(or1k->core_cache->reg_list[OR1K_REG_NPC].value, 0,
|
|
32, address);
|
|
|
|
int retval = or1k_restore_context(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_restore_context");
|
|
return retval;
|
|
}
|
|
|
|
/* read debug registers (starting from DMR1 register) */
|
|
retval = du_core->or1k_jtag_read_cpu(&or1k->jtag, OR1K_DMR1_CPU_REG_ADD,
|
|
OR1K_DEBUG_REG_NUM, debug_reg_list);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while reading debug registers");
|
|
return retval;
|
|
}
|
|
|
|
/* Clear Debug Reason Register (DRR) */
|
|
debug_reg_list[OR1K_DEBUG_REG_DRR] = 0;
|
|
|
|
/* Clear watchpoint break generation in Debug Mode Register 2 (DMR2) */
|
|
debug_reg_list[OR1K_DEBUG_REG_DMR2] &= ~OR1K_DMR2_WGB;
|
|
if (step)
|
|
/* Set the single step trigger in Debug Mode Register 1 (DMR1) */
|
|
debug_reg_list[OR1K_DEBUG_REG_DMR1] |= OR1K_DMR1_ST | OR1K_DMR1_BT;
|
|
else
|
|
/* Clear the single step trigger in Debug Mode Register 1 (DMR1) */
|
|
debug_reg_list[OR1K_DEBUG_REG_DMR1] &= ~(OR1K_DMR1_ST | OR1K_DMR1_BT);
|
|
|
|
/* Set traps to be handled by the debug unit in the Debug Stop
|
|
Register (DSR). Check if we have any software breakpoints in
|
|
place before setting this value - the kernel, for instance,
|
|
relies on l.trap instructions not stalling the processor ! */
|
|
if (is_any_soft_breakpoint(target) == true)
|
|
debug_reg_list[OR1K_DEBUG_REG_DSR] |= OR1K_DSR_TE;
|
|
|
|
/* Write debug registers (starting from DMR1 register) */
|
|
retval = du_core->or1k_jtag_write_cpu(&or1k->jtag, OR1K_DMR1_CPU_REG_ADD,
|
|
OR1K_DEBUG_REG_NUM, debug_reg_list);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while writing back debug registers");
|
|
return retval;
|
|
}
|
|
|
|
resume_pc = buf_get_u32(or1k->core_cache->reg_list[OR1K_REG_NPC].value,
|
|
0, 32);
|
|
|
|
/* The front-end may request us not to handle breakpoints */
|
|
if (handle_breakpoints) {
|
|
/* Single step past breakpoint at current address */
|
|
breakpoint = breakpoint_find(target, resume_pc);
|
|
if (breakpoint) {
|
|
LOG_DEBUG("Unset breakpoint at 0x%08" TARGET_PRIxADDR, breakpoint->address);
|
|
retval = or1k_remove_breakpoint(target, breakpoint);
|
|
if (retval != ERROR_OK)
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
/* Unstall time */
|
|
retval = du_core->or1k_cpu_stall(&or1k->jtag, CPU_UNSTALL);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while unstalling the CPU");
|
|
return retval;
|
|
}
|
|
|
|
if (step)
|
|
target->debug_reason = DBG_REASON_SINGLESTEP;
|
|
else
|
|
target->debug_reason = DBG_REASON_NOTHALTED;
|
|
|
|
/* Registers are now invalid */
|
|
register_cache_invalidate(or1k->core_cache);
|
|
|
|
if (!debug_execution) {
|
|
target->state = TARGET_RUNNING;
|
|
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
|
|
LOG_DEBUG("Target resumed at 0x%08" PRIx32, resume_pc);
|
|
} else {
|
|
target->state = TARGET_DEBUG_RUNNING;
|
|
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
|
|
LOG_DEBUG("Target debug resumed at 0x%08" PRIx32, resume_pc);
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_resume(struct target *target, int current,
|
|
target_addr_t address, int handle_breakpoints,
|
|
int debug_execution)
|
|
{
|
|
return or1k_resume_or_step(target, current, address,
|
|
handle_breakpoints,
|
|
debug_execution,
|
|
NO_SINGLE_STEP);
|
|
}
|
|
|
|
static int or1k_step(struct target *target, int current,
|
|
target_addr_t address, int handle_breakpoints)
|
|
{
|
|
return or1k_resume_or_step(target, current, address,
|
|
handle_breakpoints,
|
|
0,
|
|
SINGLE_STEP);
|
|
|
|
}
|
|
|
|
static int or1k_add_breakpoint(struct target *target,
|
|
struct breakpoint *breakpoint)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
uint8_t data;
|
|
|
|
LOG_DEBUG("Adding breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, id: %" PRIu32,
|
|
breakpoint->address, breakpoint->length, breakpoint->type,
|
|
breakpoint->unique_id);
|
|
|
|
/* Only support SW breakpoints for now. */
|
|
if (breakpoint->type == BKPT_HARD)
|
|
LOG_ERROR("HW breakpoints not supported for now. Doing SW breakpoint.");
|
|
|
|
/* Read and save the instruction */
|
|
int retval = du_core->or1k_jtag_read_memory(&or1k->jtag,
|
|
breakpoint->address,
|
|
4,
|
|
1,
|
|
&data);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while reading the instruction at 0x%08" TARGET_PRIxADDR,
|
|
breakpoint->address);
|
|
return retval;
|
|
}
|
|
|
|
free(breakpoint->orig_instr);
|
|
|
|
breakpoint->orig_instr = malloc(breakpoint->length);
|
|
memcpy(breakpoint->orig_instr, &data, breakpoint->length);
|
|
|
|
/* Sub in the OR1K trap instruction */
|
|
uint8_t or1k_trap_insn[4];
|
|
target_buffer_set_u32(target, or1k_trap_insn, OR1K_TRAP_INSTR);
|
|
retval = du_core->or1k_jtag_write_memory(&or1k->jtag,
|
|
breakpoint->address,
|
|
4,
|
|
1,
|
|
or1k_trap_insn);
|
|
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while writing OR1K_TRAP_INSTR at 0x%08" TARGET_PRIxADDR,
|
|
breakpoint->address);
|
|
return retval;
|
|
}
|
|
|
|
/* invalidate instruction cache */
|
|
uint32_t addr = breakpoint->address;
|
|
retval = du_core->or1k_jtag_write_cpu(&or1k->jtag,
|
|
OR1K_ICBIR_CPU_REG_ADD, 1, &addr);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while invalidating the ICACHE");
|
|
return retval;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_remove_breakpoint(struct target *target,
|
|
struct breakpoint *breakpoint)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("Removing breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, id: %" PRIu32,
|
|
breakpoint->address, breakpoint->length, breakpoint->type,
|
|
breakpoint->unique_id);
|
|
|
|
/* Only support SW breakpoints for now. */
|
|
if (breakpoint->type == BKPT_HARD)
|
|
LOG_ERROR("HW breakpoints not supported for now. Doing SW breakpoint.");
|
|
|
|
/* Replace the removed instruction */
|
|
int retval = du_core->or1k_jtag_write_memory(&or1k->jtag,
|
|
breakpoint->address,
|
|
4,
|
|
1,
|
|
breakpoint->orig_instr);
|
|
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while writing back the instruction at 0x%08" TARGET_PRIxADDR,
|
|
breakpoint->address);
|
|
return retval;
|
|
}
|
|
|
|
/* invalidate instruction cache */
|
|
uint32_t addr = breakpoint->address;
|
|
retval = du_core->or1k_jtag_write_cpu(&or1k->jtag,
|
|
OR1K_ICBIR_CPU_REG_ADD, 1, &addr);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while invalidating the ICACHE");
|
|
return retval;
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_add_watchpoint(struct target *target,
|
|
struct watchpoint *watchpoint)
|
|
{
|
|
LOG_ERROR("%s: implement me", __func__);
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_remove_watchpoint(struct target *target,
|
|
struct watchpoint *watchpoint)
|
|
{
|
|
LOG_ERROR("%s: implement me", __func__);
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_read_memory(struct target *target, target_addr_t address,
|
|
uint32_t size, uint32_t count, uint8_t *buffer)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("Read memory at 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: 0x%08" PRIx32, address, size, count);
|
|
|
|
if (target->state != TARGET_HALTED) {
|
|
LOG_TARGET_ERROR(target, "not halted");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
/* Sanitize arguments */
|
|
if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !buffer) {
|
|
LOG_ERROR("Bad arguments");
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
}
|
|
|
|
if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) {
|
|
LOG_ERROR("Can't handle unaligned memory access");
|
|
return ERROR_TARGET_UNALIGNED_ACCESS;
|
|
}
|
|
|
|
return du_core->or1k_jtag_read_memory(&or1k->jtag, address, size, count, buffer);
|
|
}
|
|
|
|
static int or1k_write_memory(struct target *target, target_addr_t address,
|
|
uint32_t size, uint32_t count, const uint8_t *buffer)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
LOG_DEBUG("Write memory at 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: 0x%08" PRIx32, address, size, count);
|
|
|
|
if (target->state != TARGET_HALTED) {
|
|
LOG_TARGET_ERROR(target, "not halted");
|
|
return ERROR_TARGET_NOT_HALTED;
|
|
}
|
|
|
|
/* Sanitize arguments */
|
|
if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !buffer) {
|
|
LOG_ERROR("Bad arguments");
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
}
|
|
|
|
if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) {
|
|
LOG_ERROR("Can't handle unaligned memory access");
|
|
return ERROR_TARGET_UNALIGNED_ACCESS;
|
|
}
|
|
|
|
return du_core->or1k_jtag_write_memory(&or1k->jtag, address, size, count, buffer);
|
|
}
|
|
|
|
static int or1k_init_target(struct command_context *cmd_ctx,
|
|
struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
struct or1k_jtag *jtag = &or1k->jtag;
|
|
|
|
if (!du_core) {
|
|
LOG_ERROR("No debug unit selected");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (!jtag->tap_ip) {
|
|
LOG_ERROR("No tap selected");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
or1k->jtag.tap = target->tap;
|
|
or1k->jtag.or1k_jtag_inited = 0;
|
|
or1k->jtag.or1k_jtag_module_selected = -1;
|
|
or1k->jtag.target = target;
|
|
|
|
or1k_build_reg_cache(target);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_target_create(struct target *target, Jim_Interp *interp)
|
|
{
|
|
if (!target->tap)
|
|
return ERROR_FAIL;
|
|
|
|
struct or1k_common *or1k = calloc(1, sizeof(struct or1k_common));
|
|
|
|
target->arch_info = or1k;
|
|
|
|
or1k_create_reg_list(target);
|
|
|
|
or1k_tap_vjtag_register();
|
|
or1k_tap_xilinx_bscan_register();
|
|
or1k_tap_mohor_register();
|
|
|
|
or1k_du_adv_register();
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_examine(struct target *target)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
|
|
if (!target_was_examined(target)) {
|
|
|
|
target_set_examined(target);
|
|
|
|
int running;
|
|
|
|
int retval = du_core->or1k_is_cpu_running(&or1k->jtag, &running);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Couldn't read the CPU state");
|
|
return retval;
|
|
} else {
|
|
if (running)
|
|
target->state = TARGET_RUNNING;
|
|
else {
|
|
LOG_DEBUG("Target is halted");
|
|
|
|
/* This is the first time we examine the target,
|
|
* it is stalled and we don't know why. Let's
|
|
* assume this is because of a debug reason.
|
|
*/
|
|
if (target->state == TARGET_UNKNOWN)
|
|
target->debug_reason = DBG_REASON_DBGRQ;
|
|
|
|
target->state = TARGET_HALTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_arch_state(struct target *target)
|
|
{
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static int or1k_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
|
|
int *reg_list_size, enum target_register_class reg_class)
|
|
{
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
|
|
if (reg_class == REG_CLASS_GENERAL) {
|
|
/* We will have this called whenever GDB connects. */
|
|
int retval = or1k_save_context(target);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while calling or1k_save_context");
|
|
return retval;
|
|
}
|
|
*reg_list_size = OR1KNUMCOREREGS;
|
|
/* this is free()'d back in gdb_server.c's gdb_get_register_packet() */
|
|
*reg_list = malloc((*reg_list_size) * sizeof(struct reg *));
|
|
|
|
for (int i = 0; i < OR1KNUMCOREREGS; i++)
|
|
(*reg_list)[i] = &or1k->core_cache->reg_list[i];
|
|
} else {
|
|
*reg_list_size = or1k->nb_regs;
|
|
*reg_list = malloc((*reg_list_size) * sizeof(struct reg *));
|
|
|
|
for (int i = 0; i < or1k->nb_regs; i++)
|
|
(*reg_list)[i] = &or1k->core_cache->reg_list[i];
|
|
}
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
static int or1k_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
|
|
{
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
static int or1k_checksum_memory(struct target *target, target_addr_t address,
|
|
uint32_t count, uint32_t *checksum)
|
|
{
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
static int or1k_profiling(struct target *target, uint32_t *samples,
|
|
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
|
|
{
|
|
struct timeval timeout, now;
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_du *du_core = or1k_to_du(or1k);
|
|
int retval = ERROR_OK;
|
|
|
|
gettimeofday(&timeout, NULL);
|
|
timeval_add_time(&timeout, seconds, 0);
|
|
|
|
LOG_INFO("Starting or1k profiling. Sampling npc as fast as we can...");
|
|
|
|
/* Make sure the target is running */
|
|
target_poll(target);
|
|
if (target->state == TARGET_HALTED)
|
|
retval = target_resume(target, 1, 0, 0, 0);
|
|
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while resuming target");
|
|
return retval;
|
|
}
|
|
|
|
uint32_t sample_count = 0;
|
|
|
|
for (;;) {
|
|
uint32_t reg_value;
|
|
retval = du_core->or1k_jtag_read_cpu(&or1k->jtag, GROUP0 + 16 /* NPC */, 1, ®_value);
|
|
if (retval != ERROR_OK) {
|
|
LOG_ERROR("Error while reading NPC");
|
|
return retval;
|
|
}
|
|
|
|
samples[sample_count++] = reg_value;
|
|
|
|
gettimeofday(&now, NULL);
|
|
if ((sample_count >= max_num_samples) || timeval_compare(&now, &timeout) > 0) {
|
|
LOG_INFO("Profiling completed. %" PRIu32 " samples.", sample_count);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*num_samples = sample_count;
|
|
return retval;
|
|
}
|
|
|
|
COMMAND_HANDLER(or1k_tap_select_command_handler)
|
|
{
|
|
struct target *target = get_current_target(CMD_CTX);
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_jtag *jtag = &or1k->jtag;
|
|
struct or1k_tap_ip *or1k_tap;
|
|
|
|
if (CMD_ARGC != 1)
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
list_for_each_entry(or1k_tap, &tap_list, list) {
|
|
if (or1k_tap->name) {
|
|
if (!strcmp(CMD_ARGV[0], or1k_tap->name)) {
|
|
jtag->tap_ip = or1k_tap;
|
|
LOG_INFO("%s tap selected", or1k_tap->name);
|
|
return ERROR_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_ERROR("%s unknown, no tap selected", CMD_ARGV[0]);
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
}
|
|
|
|
COMMAND_HANDLER(or1k_tap_list_command_handler)
|
|
{
|
|
struct or1k_tap_ip *or1k_tap;
|
|
|
|
if (CMD_ARGC != 0)
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
list_for_each_entry(or1k_tap, &tap_list, list) {
|
|
if (or1k_tap->name)
|
|
command_print(CMD, "%s", or1k_tap->name);
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
COMMAND_HANDLER(or1k_du_select_command_handler)
|
|
{
|
|
struct target *target = get_current_target(CMD_CTX);
|
|
struct or1k_common *or1k = target_to_or1k(target);
|
|
struct or1k_jtag *jtag = &or1k->jtag;
|
|
struct or1k_du *or1k_du;
|
|
|
|
if (CMD_ARGC > 2)
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
list_for_each_entry(or1k_du, &du_list, list) {
|
|
if (or1k_du->name) {
|
|
if (!strcmp(CMD_ARGV[0], or1k_du->name)) {
|
|
jtag->du_core = or1k_du;
|
|
LOG_INFO("%s debug unit selected", or1k_du->name);
|
|
|
|
if (CMD_ARGC == 2) {
|
|
int options;
|
|
COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], options);
|
|
or1k_du->options = options;
|
|
LOG_INFO("Option %x is passed to %s debug unit"
|
|
, options, or1k_du->name);
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_ERROR("%s unknown, no debug unit selected", CMD_ARGV[0]);
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
}
|
|
|
|
COMMAND_HANDLER(or1k_du_list_command_handler)
|
|
{
|
|
struct or1k_du *or1k_du;
|
|
|
|
if (CMD_ARGC != 0)
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
list_for_each_entry(or1k_du, &du_list, list) {
|
|
if (or1k_du->name)
|
|
command_print(CMD, "%s", or1k_du->name);
|
|
}
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
COMMAND_HANDLER(or1k_addreg_command_handler)
|
|
{
|
|
struct target *target = get_current_target(CMD_CTX);
|
|
struct or1k_core_reg new_reg;
|
|
|
|
if (CMD_ARGC != 4)
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
new_reg.target = NULL;
|
|
new_reg.or1k_common = NULL;
|
|
|
|
uint32_t addr;
|
|
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], addr);
|
|
|
|
new_reg.name = strdup(CMD_ARGV[0]);
|
|
new_reg.spr_num = addr;
|
|
new_reg.feature = strdup(CMD_ARGV[2]);
|
|
new_reg.group = strdup(CMD_ARGV[3]);
|
|
|
|
or1k_add_reg(target, &new_reg);
|
|
|
|
LOG_DEBUG("Add reg \"%s\" @ 0x%08" PRIx32 ", group \"%s\", feature \"%s\"",
|
|
new_reg.name, addr, new_reg.group, new_reg.feature);
|
|
|
|
return ERROR_OK;
|
|
}
|
|
|
|
static const struct command_registration or1k_hw_ip_command_handlers[] = {
|
|
{
|
|
.name = "tap_select",
|
|
.handler = or1k_tap_select_command_handler,
|
|
.mode = COMMAND_ANY,
|
|
.usage = "name",
|
|
.help = "Select the TAP core to use",
|
|
},
|
|
{
|
|
.name = "tap_list",
|
|
.handler = or1k_tap_list_command_handler,
|
|
.mode = COMMAND_ANY,
|
|
.usage = "",
|
|
.help = "Display available TAP core",
|
|
},
|
|
{
|
|
.name = "du_select",
|
|
.handler = or1k_du_select_command_handler,
|
|
.mode = COMMAND_ANY,
|
|
.usage = "name",
|
|
.help = "Select the Debug Unit core to use",
|
|
},
|
|
{
|
|
.name = "du_list",
|
|
.handler = or1k_du_list_command_handler,
|
|
.mode = COMMAND_ANY,
|
|
.usage = "select_tap name",
|
|
.help = "Display available Debug Unit core",
|
|
},
|
|
COMMAND_REGISTRATION_DONE
|
|
};
|
|
|
|
static const struct command_registration or1k_reg_command_handlers[] = {
|
|
{
|
|
.name = "addreg",
|
|
.handler = or1k_addreg_command_handler,
|
|
.mode = COMMAND_ANY,
|
|
.usage = "name addr feature group",
|
|
.help = "Add a register to the register list",
|
|
},
|
|
COMMAND_REGISTRATION_DONE
|
|
};
|
|
|
|
static const struct command_registration or1k_command_handlers[] = {
|
|
{
|
|
.chain = or1k_reg_command_handlers,
|
|
},
|
|
{
|
|
.chain = or1k_hw_ip_command_handlers,
|
|
},
|
|
COMMAND_REGISTRATION_DONE
|
|
};
|
|
|
|
|
|
struct target_type or1k_target = {
|
|
.name = "or1k",
|
|
|
|
.poll = or1k_poll,
|
|
.arch_state = or1k_arch_state,
|
|
|
|
.target_request_data = NULL,
|
|
|
|
.halt = or1k_halt,
|
|
.resume = or1k_resume,
|
|
.step = or1k_step,
|
|
|
|
.assert_reset = or1k_assert_reset,
|
|
.deassert_reset = or1k_deassert_reset,
|
|
.soft_reset_halt = or1k_soft_reset_halt,
|
|
|
|
.get_gdb_reg_list = or1k_get_gdb_reg_list,
|
|
|
|
.read_memory = or1k_read_memory,
|
|
.write_memory = or1k_write_memory,
|
|
.checksum_memory = or1k_checksum_memory,
|
|
|
|
.commands = or1k_command_handlers,
|
|
.add_breakpoint = or1k_add_breakpoint,
|
|
.remove_breakpoint = or1k_remove_breakpoint,
|
|
.add_watchpoint = or1k_add_watchpoint,
|
|
.remove_watchpoint = or1k_remove_watchpoint,
|
|
|
|
.target_create = or1k_target_create,
|
|
.init_target = or1k_init_target,
|
|
.examine = or1k_examine,
|
|
|
|
.get_gdb_fileio_info = or1k_get_gdb_fileio_info,
|
|
|
|
.profiling = or1k_profiling,
|
|
};
|