diff options
author | Franck Jullien <franck.jullien@gmail.com> | 2013-08-08 23:45:47 +0200 |
---|---|---|
committer | Spencer Oliver <spen@spen-soft.co.uk> | 2013-09-26 09:52:56 +0000 |
commit | 4e79b48e2c7e535ef21178a69788c15b571c72ff (patch) | |
tree | a3f340d856d4272e3545158ecdc3b32c9a910c73 /src/target/openrisc | |
parent | d19fafc8bdb30974e70bfc5a6ce63e7578b6e3b2 (diff) | |
download | riscv-openocd-4e79b48e2c7e535ef21178a69788c15b571c72ff.zip riscv-openocd-4e79b48e2c7e535ef21178a69788c15b571c72ff.tar.gz riscv-openocd-4e79b48e2c7e535ef21178a69788c15b571c72ff.tar.bz2 |
Add new target type: OpenRISC
Add support for OpenRISC target. This implementation
supports the adv_debug_sys debug unit core. The mohor
dbg_if is not supported. Support for mohor TAP core
and Altera Virtual JTAG core are also provided.
Change-Id: I3b1cfab1bbb28e497c4fca6ed1bd3a4362609b72
Signed-off-by: Franck Jullien <franck.jullien@gmail.com>
Reviewed-on: http://openocd.zylin.com/1547
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
Diffstat (limited to 'src/target/openrisc')
-rw-r--r-- | src/target/openrisc/Makefile.am | 15 | ||||
-rw-r--r-- | src/target/openrisc/or1k.c | 1472 | ||||
-rw-r--r-- | src/target/openrisc/or1k.h | 159 | ||||
-rw-r--r-- | src/target/openrisc/or1k_du.h | 77 | ||||
-rw-r--r-- | src/target/openrisc/or1k_du_adv.c | 900 | ||||
-rw-r--r-- | src/target/openrisc/or1k_tap.h | 43 | ||||
-rw-r--r-- | src/target/openrisc/or1k_tap_mohor.c | 65 | ||||
-rw-r--r-- | src/target/openrisc/or1k_tap_vjtag.c | 310 |
8 files changed, 3041 insertions, 0 deletions
diff --git a/src/target/openrisc/Makefile.am b/src/target/openrisc/Makefile.am new file mode 100644 index 0000000..61e4742 --- /dev/null +++ b/src/target/openrisc/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/common.mk + +noinst_LTLIBRARIES = libopenrisc.la +libopenrisc_la_SOURCES = $(OPENRISC_SRC) + +OPENRISC_SRC = \ + or1k.c \ + or1k_du_adv.c \ + or1k_tap_mohor.c \ + or1k_tap_vjtag.c + +noinst_HEADERS = \ + or1k.h \ + or1k_du.h \ + or1k_tap.h diff --git a/src/target/openrisc/or1k.c b/src/target/openrisc/or1k.c new file mode 100644 index 0000000..8d8ad40 --- /dev/null +++ b/src/target/openrisc/or1k.c @@ -0,0 +1,1472 @@ +/*************************************************************************** + * 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 * + * * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#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/fileio.h> +#include "or1k_tap.h" +#include "or1k.h" +#include "or1k_du.h" + +LIST_HEAD(tap_list); +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; + +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%08x", num , reg_value); + or1k->core_cache->reg_list[num].valid = 1; + or1k->core_cache->reg_list[num].dirty = 0; + } 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%08x", 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%08x", 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%08x", num , reg_value); + or1k->core_cache->reg_list[num].valid = 1; + or1k->core_cache->reg_list[num].dirty = 0; + + 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 = 1; + reg->valid = 1; + } 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%08x", 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 = malloc((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 = 0; + reg_list[i].valid = 0; + 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 reconnectiong 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 desasserting 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%x, stepping: %s, handle breakpoints %s\n", + address, step ? "yes" : "no", handle_breakpoints ? "yes" : "no"); + + if (target->state != TARGET_HALTED) { + LOG_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%08x", 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%08x", resume_pc); + } else { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + LOG_DEBUG("Target debug resumed at 0x%08x", resume_pc); + } + + return ERROR_OK; +} + +static int or1k_resume(struct target *target, int current, + uint32_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, + uint32_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%08x, len %d, type %d, set: %d, id: %d", + breakpoint->address, breakpoint->length, breakpoint->type, + breakpoint->set, 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%08x", + breakpoint->address); + return retval; + } + + if (breakpoint->orig_instr != NULL) + free(breakpoint->orig_instr); + + breakpoint->orig_instr = malloc(breakpoint->length); + memcpy(breakpoint->orig_instr, &data, breakpoint->length); + + /* Sub in the OR1K trap instruction */ + uint32_t or1k_trap_insn = OR1K_TRAP_INSTR; + retval = du_core->or1k_jtag_write_memory(&or1k->jtag, + breakpoint->address, + 4, + 1, + (uint8_t *)&or1k_trap_insn); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while writing OR1K_TRAP_INSTR at 0x%08x", + breakpoint->address); + return retval; + } + + /* invalidate instruction cache */ + retval = du_core->or1k_jtag_write_cpu(&or1k->jtag, + OR1K_ICBIR_CPU_REG_ADD, 1, &breakpoint->address); + 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%08x, len %d, type %d, set: %d, id: %d", + breakpoint->address, breakpoint->length, breakpoint->type, + breakpoint->set, 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%08x", + breakpoint->address); + return retval; + } + + /* invalidate instruction cache */ + retval = du_core->or1k_jtag_write_cpu(&or1k->jtag, + OR1K_ICBIR_CPU_REG_ADD, 1, &breakpoint->address); + 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, uint32_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%08x, size: %d, count: 0x%08x", address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_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; + } + + /* or1k_read_memory with size 4/2 returns uint32_t/uint16_t in host */ + /* endianness, but byte array should represent target endianness */ + + void *t = NULL; + if (size > 1) { + t = malloc(count * size * sizeof(uint8_t)); + if (t == NULL) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + } else + t = buffer; + + + int retval = du_core->or1k_jtag_read_memory(&or1k->jtag, address, + size, count, t); + + if (retval == ERROR_OK) { + switch (size) { + case 4: + target_buffer_set_u32_array(target, buffer, count, t); + break; + case 2: + target_buffer_set_u16_array(target, buffer, count, t); + break; + } + } + + if ((size > 1) && (t != NULL)) + free(t); + + return ERROR_OK; +} + +static int or1k_write_memory(struct target *target, uint32_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%08x, size: %d, count: 0x%08x", address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_WARNING("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; + } + + /* or1k_write_memory with size 4/2 requires uint32_t/uint16_t in host */ + /* endianness, but byte array represents target endianness */ + + void *t = NULL; + if (size > 1) { + t = malloc(count * size * sizeof(uint8_t)); + if (t == NULL) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + switch (size) { + case 4: + target_buffer_get_u32_array(target, buffer, count, (uint32_t *)t); + break; + case 2: + target_buffer_get_u16_array(target, buffer, count, (uint16_t *)t); + break; + } + buffer = t; + } + + int retval = du_core->or1k_jtag_write_memory(&or1k->jtag, address, size, count, buffer); + + if (t != NULL) + free(t); + + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +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 == NULL) { + LOG_ERROR("No debug unit selected"); + return ERROR_FAIL; + } + + if (jtag->tap_ip == NULL) { + 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_build_reg_cache(target); + + return ERROR_OK; +} + +static int or1k_target_create(struct target *target, Jim_Interp *interp) +{ + struct or1k_common *or1k = calloc(1, sizeof(struct or1k_common)); + + if (target->tap == NULL) + return ERROR_FAIL; + + target->arch_info = or1k; + + or1k_create_reg_list(target); + + or1k_tap_vjtag_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; + +} + +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, uint32_t address, + uint32_t count, uint32_t *checksum) { + + return ERROR_FAIL; +} + +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_CTX, "%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_CTX, "%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%08x, 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[] = { + { + "tap_select", + .handler = or1k_tap_select_command_handler, + .mode = COMMAND_ANY, + .usage = "tap_select name", + .help = "Select the TAP core to use", + }, + { + "tap_list", + .handler = or1k_tap_list_command_handler, + .mode = COMMAND_ANY, + .usage = "tap_list", + .help = "Display available TAP core", + }, + { + "du_select", + .handler = or1k_du_select_command_handler, + .mode = COMMAND_ANY, + .usage = "du_select name", + .help = "Select the Debug Unit core to use", + }, + { + "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[] = { + { + "addreg", + .handler = or1k_addreg_command_handler, + .mode = COMMAND_ANY, + .usage = "addreg 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, +}; diff --git a/src/target/openrisc/or1k.h b/src/target/openrisc/or1k.h new file mode 100644 index 0000000..5610e01 --- /dev/null +++ b/src/target/openrisc/or1k.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * 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 * + * * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef OR1K_H +#define OR1K_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <target/target.h> + +/* SPR groups start address */ +#define GROUP0 (0 << 11) +#define GROUP1 (1 << 11) +#define GROUP2 (2 << 11) +#define GROUP3 (3 << 11) +#define GROUP4 (4 << 11) +#define GROUP5 (5 << 11) +#define GROUP6 (6 << 11) +#define GROUP7 (7 << 11) +#define GROUP8 (8 << 11) +#define GROUP9 (9 << 11) +#define GROUP10 (10 << 11) + +/* OR1K registers */ +enum or1k_reg_nums { + OR1K_REG_R0 = 0, + OR1K_REG_R1, + OR1K_REG_R2, + OR1K_REG_R3, + OR1K_REG_R4, + OR1K_REG_R5, + OR1K_REG_R6, + OR1K_REG_R7, + OR1K_REG_R8, + OR1K_REG_R9, + OR1K_REG_R10, + OR1K_REG_R11, + OR1K_REG_R12, + OR1K_REG_R13, + OR1K_REG_R14, + OR1K_REG_R15, + OR1K_REG_R16, + OR1K_REG_R17, + OR1K_REG_R18, + OR1K_REG_R19, + OR1K_REG_R20, + OR1K_REG_R21, + OR1K_REG_R22, + OR1K_REG_R23, + OR1K_REG_R24, + OR1K_REG_R25, + OR1K_REG_R26, + OR1K_REG_R27, + OR1K_REG_R28, + OR1K_REG_R29, + OR1K_REG_R30, + OR1K_REG_R31, + OR1K_REG_PPC, + OR1K_REG_NPC, + OR1K_REG_SR, + OR1KNUMCOREREGS +}; + +struct or1k_jtag { + struct jtag_tap *tap; + int or1k_jtag_inited; + int or1k_jtag_module_selected; + uint8_t *current_reg_idx; + struct or1k_tap_ip *tap_ip; + struct or1k_du *du_core; +}; + +struct or1k_common { + struct or1k_jtag jtag; + struct reg_cache *core_cache; + uint32_t core_regs[OR1KNUMCOREREGS]; + int nb_regs; + struct or1k_core_reg *arch_info; +}; + +static inline struct or1k_common * +target_to_or1k(struct target *target) +{ + return (struct or1k_common *)target->arch_info; +} + +struct or1k_core_reg { + const char *name; + uint32_t list_num; /* Index in register cache */ + uint32_t spr_num; /* Number in architecture's SPR space */ + struct target *target; + struct or1k_common *or1k_common; + const char *feature; /* feature name in XML tdesc file */ + const char *group; /* register group in XML tdesc file */ +}; + +struct or1k_core_reg_init { + const char *name; + uint32_t spr_num; /* Number in architecture's SPR space */ + const char *feature; /* feature name in XML tdesc file */ + const char *group; /* register group in XML tdesc file */ +}; + +/* ORBIS32 Trap instruction */ +#define OR1K_TRAP_INSTR 0x21000001 + +enum or1k_debug_reg_nums { + OR1K_DEBUG_REG_DMR1 = 0, + OR1K_DEBUG_REG_DMR2, + OR1K_DEBUG_REG_DCWR0, + OR1K_DEBUG_REG_DCWR1, + OR1K_DEBUG_REG_DSR, + OR1K_DEBUG_REG_DRR, + OR1K_DEBUG_REG_NUM +}; + +#define NO_SINGLE_STEP 0 +#define SINGLE_STEP 1 + +/* OR1K Debug registers and bits needed for resuming */ +#define OR1K_DEBUG_REG_BASE GROUP6 /* Debug registers Base address */ +#define OR1K_DMR1_CPU_REG_ADD (OR1K_DEBUG_REG_BASE + 16) /* Debug Mode Register 1 0x3010 */ +#define OR1K_DMR1_ST 0x00400000 /* Single-step trace */ +#define OR1K_DMR1_BT 0x00800000 /* Branch trace */ +#define OR1K_DMR2_WGB 0x003ff000 /* Watchpoints generating breakpoint */ +#define OR1K_DSR_TE 0x00002000 /* Trap exception */ + +/* OR1K Instruction cache registers needed for invalidating instruction + * memory during adding and removing breakpoints. + */ +#define OR1K_ICBIR_CPU_REG_ADD ((4 << 11) + 2) /* IC Block Invalidate Register 0x2002 */ + +#endif diff --git a/src/target/openrisc/or1k_du.h b/src/target/openrisc/or1k_du.h new file mode 100644 index 0000000..564241d --- /dev/null +++ b/src/target/openrisc/or1k_du.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2013 Franck Jullien * + * elec4fun@gmail.com * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef OR1K_DU +#define OR1K_DU + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define CPU_STALL 0 +#define CPU_UNSTALL 1 + +#define CPU_RESET 0 +#define CPU_NOT_RESET 1 + +int or1k_du_adv_register(void); + +/* Linear list over all available or1k debug unit */ +extern struct list_head du_list; + +struct or1k_du { + const char *name; + struct list_head list; + int options; + + int (*or1k_jtag_init)(struct or1k_jtag *jtag_info); + + int (*or1k_is_cpu_running)(struct or1k_jtag *jtag_info, int *running); + + int (*or1k_cpu_stall)(struct or1k_jtag *jtag_info, int action); + + int (*or1k_cpu_reset)(struct or1k_jtag *jtag_info, int action); + + int (*or1k_jtag_read_cpu)(struct or1k_jtag *jtag_info, + uint32_t addr, int count, uint32_t *value); + + int (*or1k_jtag_write_cpu)(struct or1k_jtag *jtag_info, + uint32_t addr, int count, const uint32_t *value); + + int (*or1k_jtag_read_memory)(struct or1k_jtag *jtag_info, uint32_t addr, uint32_t size, + int count, uint8_t *buffer); + + int (*or1k_jtag_write_memory)(struct or1k_jtag *jtag_info, uint32_t addr, uint32_t size, + int count, const uint8_t *buffer); +}; + +static inline struct or1k_du *or1k_jtag_to_du(struct or1k_jtag *jtag_info) +{ + return (struct or1k_du *)jtag_info->du_core; +} + +static inline struct or1k_du *or1k_to_du(struct or1k_common *or1k) +{ + struct or1k_jtag *jtag = &or1k->jtag; + return (struct or1k_du *)jtag->du_core; +} + +#endif + diff --git a/src/target/openrisc/or1k_du_adv.c b/src/target/openrisc/or1k_du_adv.c new file mode 100644 index 0000000..6d449e8 --- /dev/null +++ b/src/target/openrisc/or1k_du_adv.c @@ -0,0 +1,900 @@ +/*************************************************************************** + * Copyright (C) 2013 by Franck Jullien * + * elec4fun@gmail.com * + * * + * Inspired from adv_jtag_bridge which is: * + * Copyright (C) 2008-2010 Nathan Yawn * + * nyawn@opencores.net * + * * + * And the Mohor interface version of this file which is: * + * Copyright (C) 2011 by Julius Baxter * + * julius@opencores.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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "or1k_tap.h" +#include "or1k.h" +#include "or1k_du.h" + +#include <target/target.h> +#include <jtag/jtag.h> + +/* This an option to the adv debug unit. + * If this is defined, status bits will be skipped on burst + * reads and writes to improve download speeds. + * This option must match the RTL configured option. + */ +#define ADBG_USE_HISPEED 1 + +/* Definitions for the top-level debug unit. This really just consists + * of a single register, used to select the active debug module ("chain"). + */ +#define DBG_MODULE_SELECT_REG_SIZE 2 +#define DBG_MAX_MODULES 4 + +#define DC_WISHBONE 0 +#define DC_CPU0 1 +#define DC_CPU1 2 +#define DC_JSP 3 + +/* CPU control register bits mask */ +#define DBG_CPU_CR_STALL 0x01 +#define DBG_CPU_CR_RESET 0x02 + +/* Polynomial for the CRC calculation + * Yes, it's backwards. Yes, this is on purpose. + * The hardware is designed this way to save on logic and routing, + * and it's really all the same to us here. + */ +#define ADBG_CRC_POLY 0xedb88320 + +/* These are for the internal registers in the Wishbone module + * The first is the length of the index register, + * the indexes of the various registers are defined after that. + */ +#define DBG_WB_REG_SEL_LEN 1 +#define DBG_WB_REG_ERROR 0 + +/* Opcode definitions for the Wishbone module. */ +#define DBG_WB_OPCODE_LEN 4 +#define DBG_WB_CMD_NOP 0x0 +#define DBG_WB_CMD_BWRITE8 0x1 +#define DBG_WB_CMD_BWRITE16 0x2 +#define DBG_WB_CMD_BWRITE32 0x3 +#define DBG_WB_CMD_BREAD8 0x5 +#define DBG_WB_CMD_BREAD16 0x6 +#define DBG_WB_CMD_BREAD32 0x7 +#define DBG_WB_CMD_IREG_WR 0x9 +#define DBG_WB_CMD_IREG_SEL 0xd + +/* Internal register definitions for the CPU0 module. */ +#define DBG_CPU0_REG_SEL_LEN 1 +#define DBG_CPU0_REG_STATUS 0 + +/* Opcode definitions for the first CPU module. */ +#define DBG_CPU0_OPCODE_LEN 4 +#define DBG_CPU0_CMD_NOP 0x0 +#define DBG_CPU0_CMD_BWRITE32 0x3 +#define DBG_CPU0_CMD_BREAD32 0x7 +#define DBG_CPU0_CMD_IREG_WR 0x9 +#define DBG_CPU0_CMD_IREG_SEL 0xd + +/* Internal register definitions for the CPU1 module. */ +#define DBG_CPU1_REG_SEL_LEN 1 +#define DBG_CPU1_REG_STATUS 0 + +/* Opcode definitions for the second CPU module. */ +#define DBG_CPU1_OPCODE_LEN 4 +#define DBG_CPU1_CMD_NOP 0x0 +#define DBG_CPU1_CMD_BWRITE32 0x3 +#define DBG_CPU1_CMD_BREAD32 0x7 +#define DBG_CPU1_CMD_IREG_WR 0x9 +#define DBG_CPU1_CMD_IREG_SEL 0xd + +#define MAX_READ_STATUS_WAIT 10 +#define MAX_READ_BUSY_RETRY 2 +#define MAX_READ_CRC_RETRY 2 +#define MAX_WRITE_CRC_RETRY 2 +#define BURST_READ_READY 1 +#define MAX_BUS_ERRORS 2 + +#define MAX_BURST_SIZE (4 * 1024) + +#define STATUS_BYTES 1 +#define CRC_LEN 4 + +static struct or1k_du or1k_du_adv; + +static const char * const chain_name[] = {"WISHBONE", "CPU0", "CPU1", "JSP"}; + +static uint32_t adbg_compute_crc(uint32_t crc, uint32_t data_in, + int length_bits) +{ + for (int i = 0; i < length_bits; i++) { + uint32_t d, c; + d = ((data_in >> i) & 0x1) ? 0xffffffff : 0; + c = (crc & 0x1) ? 0xffffffff : 0; + crc = crc >> 1; + crc = crc ^ ((d ^ c) & ADBG_CRC_POLY); + } + + return crc; +} + +static int find_status_bit(void *_buf, int len) +{ + int i = 0; + int count = 0; + int ret = -1; + uint8_t *buf = _buf; + + while (!(buf[i] & (1 << count++)) && (i < len)) { + if (count == 8) { + count = 0; + i++; + } + } + + if (i < len) + ret = (i * 8) + count; + + return ret; +} + +static int or1k_adv_jtag_init(struct or1k_jtag *jtag_info) +{ + struct or1k_tap_ip *tap_ip = jtag_info->tap_ip; + + int retval = tap_ip->init(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("TAP initialization failed"); + return retval; + } + + /* TAP is now configured to communicate with debug interface */ + jtag_info->or1k_jtag_inited = 1; + + /* TAP reset - not sure what state debug module chain is in now */ + jtag_info->or1k_jtag_module_selected = -1; + + jtag_info->current_reg_idx = malloc(DBG_MAX_MODULES * sizeof(uint8_t)); + memset(jtag_info->current_reg_idx, 0, DBG_MAX_MODULES * sizeof(uint8_t)); + + if (or1k_du_adv.options & ADBG_USE_HISPEED) + LOG_INFO("adv debug unit is configured with option ADBG_USE_HISPEED"); + + LOG_DEBUG("Init done"); + + return ERROR_OK; + +} + +/* Selects one of the modules in the debug unit + * (e.g. wishbone unit, CPU0, etc.) + */ +static int adbg_select_module(struct or1k_jtag *jtag_info, int chain) +{ + if (jtag_info->or1k_jtag_module_selected == chain) + return ERROR_OK; + + /* MSB of the data out must be set to 1, indicating a module + * select command + */ + uint8_t data = chain | (1 << DBG_MODULE_SELECT_REG_SIZE); + + LOG_DEBUG("Select module: %s", chain_name[chain]); + + struct scan_field field; + + field.num_bits = (DBG_MODULE_SELECT_REG_SIZE + 1); + field.out_value = &data; + field.in_value = NULL; + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + jtag_info->or1k_jtag_module_selected = chain; + + return ERROR_OK; +} + +/* Set the index of the desired register in the currently selected module + * 1 bit module select command + * 4 bits opcode + * n bits index + */ +static int adbg_select_ctrl_reg(struct or1k_jtag *jtag_info, uint8_t regidx) +{ + int index_len; + uint32_t opcode; + uint32_t opcode_len; + + /* If this reg is already selected, don't do a JTAG transaction */ + if (jtag_info->current_reg_idx[jtag_info->or1k_jtag_module_selected] == regidx) + return ERROR_OK; + + switch (jtag_info->or1k_jtag_module_selected) { + case DC_WISHBONE: + index_len = DBG_WB_REG_SEL_LEN; + opcode = DBG_WB_CMD_IREG_SEL; + opcode_len = DBG_WB_OPCODE_LEN; + break; + case DC_CPU0: + index_len = DBG_CPU0_REG_SEL_LEN; + opcode = DBG_CPU0_CMD_IREG_SEL; + opcode_len = DBG_CPU0_OPCODE_LEN; + break; + case DC_CPU1: + index_len = DBG_CPU1_REG_SEL_LEN; + opcode = DBG_CPU1_CMD_IREG_SEL; + opcode_len = DBG_CPU1_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while selecting control register", + jtag_info->or1k_jtag_module_selected); + return ERROR_FAIL; + } + + /* MSB must be 0 to access modules */ + uint32_t data = (opcode & ~(1 << opcode_len)) << index_len; + data |= regidx; + + struct scan_field field; + + field.num_bits = (opcode_len + 1) + index_len; + field.out_value = (uint8_t *)&data; + field.in_value = NULL; + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + jtag_info->current_reg_idx[jtag_info->or1k_jtag_module_selected] = regidx; + + return ERROR_OK; +} + +/* Write control register (internal to the debug unit) */ +static int adbg_ctrl_write(struct or1k_jtag *jtag_info, uint8_t regidx, + uint32_t *cmd_data, int length_bits) +{ + int index_len; + uint32_t opcode; + uint32_t opcode_len; + + LOG_DEBUG("Write control register %d: 0x%08x", regidx, cmd_data[0]); + + int retval = adbg_select_ctrl_reg(jtag_info, regidx); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling adbg_select_ctrl_reg"); + return retval; + } + + switch (jtag_info->or1k_jtag_module_selected) { + case DC_WISHBONE: + index_len = DBG_WB_REG_SEL_LEN; + opcode = DBG_WB_CMD_IREG_WR; + opcode_len = DBG_WB_OPCODE_LEN; + break; + case DC_CPU0: + index_len = DBG_CPU0_REG_SEL_LEN; + opcode = DBG_CPU0_CMD_IREG_WR; + opcode_len = DBG_CPU0_OPCODE_LEN; + break; + case DC_CPU1: + index_len = DBG_CPU1_REG_SEL_LEN; + opcode = DBG_CPU1_CMD_IREG_WR; + opcode_len = DBG_CPU1_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing control write", + jtag_info->or1k_jtag_module_selected); + return ERROR_FAIL; + } + + struct scan_field field[2]; + + /* MSB must be 0 to access modules */ + uint32_t data = (opcode & ~(1 << opcode_len)) << index_len; + data |= regidx; + + field[0].num_bits = length_bits; + field[0].out_value = (uint8_t *)cmd_data; + field[0].in_value = NULL; + + field[1].num_bits = (opcode_len + 1) + index_len; + field[1].out_value = (uint8_t *)&data; + field[1].in_value = NULL; + + jtag_add_dr_scan(jtag_info->tap, 2, field, TAP_IDLE); + + return jtag_execute_queue(); +} + +/* Reads control register (internal to the debug unit) */ +static int adbg_ctrl_read(struct or1k_jtag *jtag_info, uint32_t regidx, + uint32_t *data, int length_bits) +{ + + int retval = adbg_select_ctrl_reg(jtag_info, regidx); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling adbg_select_ctrl_reg"); + return retval; + } + + int opcode_len; + uint32_t opcode; + + /* There is no 'read' command, We write a NOP to read */ + switch (jtag_info->or1k_jtag_module_selected) { + case DC_WISHBONE: + opcode = DBG_WB_CMD_NOP; + opcode_len = DBG_WB_OPCODE_LEN; + break; + case DC_CPU0: + opcode = DBG_CPU0_CMD_NOP; + opcode_len = DBG_CPU0_OPCODE_LEN; + break; + case DC_CPU1: + opcode = DBG_CPU1_CMD_NOP; + opcode_len = DBG_CPU1_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing control read", + jtag_info->or1k_jtag_module_selected); + return ERROR_FAIL; + } + + /* Zero MSB = op for module, not top-level debug unit */ + uint32_t outdata = opcode & ~(0x1 << opcode_len); + + struct scan_field field[2]; + + field[0].num_bits = length_bits; + field[0].out_value = NULL; + field[0].in_value = (uint8_t *)data; + + field[1].num_bits = opcode_len + 1; + field[1].out_value = (uint8_t *)&outdata; + field[1].in_value = NULL; + + jtag_add_dr_scan(jtag_info->tap, 2, field, TAP_IDLE); + + return jtag_execute_queue(); +} + +/* sends out a burst command to the selected module in the debug unit (MSB to LSB): + * 1-bit module command + * 4-bit opcode + * 32-bit address + * 16-bit length (of the burst, in words) + */ +static int adbg_burst_command(struct or1k_jtag *jtag_info, uint32_t opcode, + uint32_t address, uint16_t length_words) +{ + uint32_t data[2]; + + /* Set up the data */ + data[0] = length_words | (address << 16); + /* MSB must be 0 to access modules */ + data[1] = ((address >> 16) | ((opcode & 0xf) << 16)) & ~(0x1 << 20); + + struct scan_field field; + + field.num_bits = 53; + field.out_value = (uint8_t *)&data[0]; + field.in_value = NULL; + + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + return jtag_execute_queue(); +} + +static int adbg_wb_burst_read(struct or1k_jtag *jtag_info, int size, + int count, uint32_t start_address, uint8_t *data) +{ + int retry_full_crc = 0; + int retry_full_busy = 0; + int retval; + uint8_t opcode; + + LOG_DEBUG("Doing burst read, word size %d, word count %d, start address 0x%08x", + size, count, start_address); + + /* Select the appropriate opcode */ + switch (jtag_info->or1k_jtag_module_selected) { + case DC_WISHBONE: + if (size == 1) + opcode = DBG_WB_CMD_BREAD8; + else if (size == 2) + opcode = DBG_WB_CMD_BREAD16; + else if (size == 4) + opcode = DBG_WB_CMD_BREAD32; + else { + LOG_WARNING("Tried burst read with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_WB_CMD_BREAD32; + } + break; + case DC_CPU0: + if (size == 4) + opcode = DBG_CPU0_CMD_BREAD32; + else { + LOG_WARNING("Tried burst read with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU0_CMD_BREAD32; + } + break; + case DC_CPU1: + if (size == 4) + opcode = DBG_CPU1_CMD_BREAD32; + else { + LOG_WARNING("Tried burst read with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU0_CMD_BREAD32; + } + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing burst read", + jtag_info->or1k_jtag_module_selected); + return ERROR_FAIL; + } + + int total_size_bytes = count * size; + struct scan_field field; + uint8_t *in_buffer = malloc(total_size_bytes + CRC_LEN + STATUS_BYTES); + +retry_read_full: + + /* Send the BURST READ command, returns TAP to idle state */ + retval = adbg_burst_command(jtag_info, opcode, start_address, count); + if (retval != ERROR_OK) + goto out; + + field.num_bits = (total_size_bytes + CRC_LEN + STATUS_BYTES) * 8; + field.out_value = NULL; + field.in_value = in_buffer; + + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + goto out; + + /* Look for the start bit in the first (STATUS_BYTES * 8) bits */ + int shift = find_status_bit(in_buffer, STATUS_BYTES); + + /* We expect the status bit to be in the first byte */ + if (shift < 0) { + if (retry_full_busy++ < MAX_READ_BUSY_RETRY) { + LOG_WARNING("Burst read timed out"); + goto retry_read_full; + } else { + LOG_ERROR("Burst read failed"); + retval = ERROR_FAIL; + goto out; + } + } + + buffer_shr(in_buffer, total_size_bytes + CRC_LEN + STATUS_BYTES, shift); + + uint32_t crc_read; + memcpy(data, in_buffer, total_size_bytes); + memcpy(&crc_read, &in_buffer[total_size_bytes], 4); + + uint32_t crc_calc = 0xffffffff; + for (int i = 0; i < total_size_bytes; i++) + crc_calc = adbg_compute_crc(crc_calc, data[i], 8); + + if (crc_calc != crc_read) { + LOG_WARNING("CRC ERROR! Computed 0x%08x, read CRC 0x%08x", crc_calc, crc_read); + if (retry_full_crc++ < MAX_READ_CRC_RETRY) + goto retry_read_full; + else { + LOG_ERROR("Burst read failed"); + retval = ERROR_FAIL; + goto out; + } + } else + LOG_DEBUG("CRC OK!"); + + /* Now, read the error register, and retry/recompute as necessary */ + if (jtag_info->or1k_jtag_module_selected == DC_WISHBONE && + !(or1k_du_adv.options & ADBG_USE_HISPEED)) { + + uint32_t err_data[2] = {0, 0}; + uint32_t addr; + int bus_error_retries = 0; + + /* First, just get 1 bit...read address only if necessary */ + retval = adbg_ctrl_read(jtag_info, DBG_WB_REG_ERROR, err_data, 1); + if (retval != ERROR_OK) + goto out; + + /* Then we have a problem */ + if (err_data[0] & 0x1) { + + retval = adbg_ctrl_read(jtag_info, DBG_WB_REG_ERROR, err_data, 33); + if (retval != ERROR_OK) + goto out; + + addr = (err_data[0] >> 1) | (err_data[1] << 31); + LOG_WARNING("WB bus error during burst read, address 0x%08x, retrying!", addr); + + bus_error_retries++; + if (bus_error_retries > MAX_BUS_ERRORS) { + LOG_ERROR("Max WB bus errors reached during burst read"); + retval = ERROR_FAIL; + goto out; + } + + /* Don't call retry_do(), a JTAG reset won't help a WB bus error */ + /* Write 1 bit, to reset the error register */ + err_data[0] = 1; + retval = adbg_ctrl_write(jtag_info, DBG_WB_REG_ERROR, err_data, 1); + if (retval != ERROR_OK) + goto out; + + goto retry_read_full; + } + } + +out: + free(in_buffer); + + return retval; +} + +/* Set up and execute a burst write to a contiguous set of addresses */ +static int adbg_wb_burst_write(struct or1k_jtag *jtag_info, const uint8_t *data, int size, + int count, unsigned long start_address) +{ + int retry_full_crc = 0; + int retval; + uint8_t opcode; + + LOG_DEBUG("Doing burst write, word size %d, word count %d," + "start address 0x%08lx", size, count, start_address); + + /* Select the appropriate opcode */ + switch (jtag_info->or1k_jtag_module_selected) { + case DC_WISHBONE: + if (size == 1) + opcode = DBG_WB_CMD_BWRITE8; + else if (size == 2) + opcode = DBG_WB_CMD_BWRITE16; + else if (size == 4) + opcode = DBG_WB_CMD_BWRITE32; + else { + LOG_DEBUG("Tried WB burst write with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_WB_CMD_BWRITE32; + } + break; + case DC_CPU0: + if (size == 4) + opcode = DBG_CPU0_CMD_BWRITE32; + else { + LOG_DEBUG("Tried CPU0 burst write with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU0_CMD_BWRITE32; + } + break; + case DC_CPU1: + if (size == 4) + opcode = DBG_CPU1_CMD_BWRITE32; + else { + LOG_DEBUG("Tried CPU1 burst write with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU0_CMD_BWRITE32; + } + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing burst write", + jtag_info->or1k_jtag_module_selected); + return ERROR_FAIL; + } + +retry_full_write: + + /* Send the BURST WRITE command, returns TAP to idle state */ + retval = adbg_burst_command(jtag_info, opcode, start_address, count); + if (retval != ERROR_OK) + return retval; + + struct scan_field field[3]; + + /* Write a start bit so it knows when to start counting */ + uint8_t value = 1; + field[0].num_bits = 1; + field[0].out_value = &value; + field[0].in_value = NULL; + + uint32_t crc_calc = 0xffffffff; + for (int i = 0; i < (count * size); i++) + crc_calc = adbg_compute_crc(crc_calc, data[i], 8); + + field[1].num_bits = count * size * 8; + field[1].out_value = data; + field[1].in_value = NULL; + + field[2].num_bits = 32; + field[2].out_value = (uint8_t *)&crc_calc; + field[2].in_value = NULL; + + jtag_add_dr_scan(jtag_info->tap, 3, field, TAP_DRSHIFT); + + /* Read the 'CRC match' bit, and go to idle */ + field[0].num_bits = 1; + field[0].out_value = NULL; + field[0].in_value = &value; + jtag_add_dr_scan(jtag_info->tap, 1, field, TAP_IDLE); + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + if (!value) { + LOG_WARNING("CRC ERROR! match bit after write is %i (computed CRC 0x%08x)", value, crc_calc); + if (retry_full_crc++ < MAX_WRITE_CRC_RETRY) + goto retry_full_write; + else + return ERROR_FAIL; + } else + LOG_DEBUG("CRC OK!\n"); + + /* Now, read the error register, and retry/recompute as necessary */ + if (jtag_info->or1k_jtag_module_selected == DC_WISHBONE && + !(or1k_du_adv.options & ADBG_USE_HISPEED)) { + uint32_t addr; + int bus_error_retries = 0; + uint32_t err_data[2] = {0, 0}; + + /* First, just get 1 bit...read address only if necessary */ + retval = adbg_ctrl_read(jtag_info, DBG_WB_REG_ERROR, err_data, 1); + if (retval != ERROR_OK) + return retval; + + /* Then we have a problem */ + if (err_data[0] & 0x1) { + + retval = adbg_ctrl_read(jtag_info, DBG_WB_REG_ERROR, err_data, 33); + if (retval != ERROR_OK) + return retval; + + addr = (err_data[0] >> 1) | (err_data[1] << 31); + LOG_WARNING("WB bus error during burst write, address 0x%08x, retrying!", addr); + + bus_error_retries++; + if (bus_error_retries > MAX_BUS_ERRORS) { + LOG_ERROR("Max WB bus errors reached during burst read"); + retval = ERROR_FAIL; + return retval; + } + + /* Don't call retry_do(), a JTAG reset won't help a WB bus error */ + /* Write 1 bit, to reset the error register */ + err_data[0] = 1; + retval = adbg_ctrl_write(jtag_info, DBG_WB_REG_ERROR, err_data, 1); + if (retval != ERROR_OK) + return retval; + + goto retry_full_write; + } + } + + return ERROR_OK; +} + +/* Currently hard set in functions to 32-bits */ +static int or1k_adv_jtag_read_cpu(struct or1k_jtag *jtag_info, + uint32_t addr, int count, uint32_t *value) +{ + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + return adbg_wb_burst_read(jtag_info, 4, count, addr, (uint8_t *)value); +} + +static int or1k_adv_jtag_write_cpu(struct or1k_jtag *jtag_info, + uint32_t addr, int count, const uint32_t *value) +{ + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + return adbg_wb_burst_write(jtag_info, (uint8_t *)value, 4, count, addr); +} + +static int or1k_adv_cpu_stall(struct or1k_jtag *jtag_info, int action) +{ + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + uint32_t cpu_cr; + retval = adbg_ctrl_read(jtag_info, DBG_CPU0_REG_STATUS, &cpu_cr, 2); + if (retval != ERROR_OK) + return retval; + + if (action == CPU_STALL) + cpu_cr |= DBG_CPU_CR_STALL; + else + cpu_cr &= ~DBG_CPU_CR_STALL; + + retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + return adbg_ctrl_write(jtag_info, DBG_CPU0_REG_STATUS, &cpu_cr, 2); +} + +static int or1k_adv_is_cpu_running(struct or1k_jtag *jtag_info, int *running) +{ + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + uint32_t cpu_cr = 0; + retval = adbg_ctrl_read(jtag_info, DBG_CPU0_REG_STATUS, &cpu_cr, 2); + if (retval != ERROR_OK) + return retval; + + if (cpu_cr & DBG_CPU_CR_STALL) + *running = 0; + else + *running = 1; + + return ERROR_OK; +} + +static int or1k_adv_cpu_reset(struct or1k_jtag *jtag_info, int action) +{ + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + uint32_t cpu_cr; + retval = adbg_ctrl_read(jtag_info, DBG_CPU0_REG_STATUS, &cpu_cr, 2); + if (retval != ERROR_OK) + return retval; + + if (action == CPU_RESET) + cpu_cr |= DBG_CPU_CR_RESET; + else + cpu_cr &= ~DBG_CPU_CR_RESET; + + retval = adbg_select_module(jtag_info, DC_CPU0); + if (retval != ERROR_OK) + return retval; + + return adbg_ctrl_write(jtag_info, DBG_CPU0_REG_STATUS, &cpu_cr, 2); +} + +static int or1k_adv_jtag_read_memory(struct or1k_jtag *jtag_info, + uint32_t addr, uint32_t size, int count, uint8_t *buffer) +{ + LOG_DEBUG("Reading WB%d at 0x%08x", size * 8, addr); + + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_WISHBONE); + if (retval != ERROR_OK) + return retval; + + int block_count_left = count; + uint32_t block_count_address = addr; + uint8_t *block_count_buffer = (uint8_t *)buffer; + + while (block_count_left) { + + int blocks_this_round = (block_count_left > MAX_BURST_SIZE) ? + MAX_BURST_SIZE : block_count_left; + + retval = adbg_wb_burst_read(jtag_info, size, blocks_this_round, + block_count_address, block_count_buffer); + if (retval != ERROR_OK) + return retval; + + block_count_left -= blocks_this_round; + block_count_address += size * MAX_BURST_SIZE; + block_count_buffer += size * MAX_BURST_SIZE; + } + + return ERROR_OK; +} + +static int or1k_adv_jtag_write_memory(struct or1k_jtag *jtag_info, + uint32_t addr, uint32_t size, int count, const uint8_t *buffer) +{ + LOG_DEBUG("Writing WB%d at 0x%08x", size * 8, addr); + + if (!jtag_info->or1k_jtag_inited) + or1k_adv_jtag_init(jtag_info); + + int retval = adbg_select_module(jtag_info, DC_WISHBONE); + if (retval != ERROR_OK) + return retval; + + int block_count_left = count; + uint32_t block_count_address = addr; + uint8_t *block_count_buffer = (uint8_t *)buffer; + + while (block_count_left) { + + int blocks_this_round = (block_count_left > MAX_BURST_SIZE) ? + MAX_BURST_SIZE : block_count_left; + + retval = adbg_wb_burst_write(jtag_info, block_count_buffer, + size, blocks_this_round, + block_count_address); + if (retval != ERROR_OK) + return retval; + + block_count_left -= blocks_this_round; + block_count_address += size * MAX_BURST_SIZE; + block_count_buffer += size * MAX_BURST_SIZE; + } + + return ERROR_OK; +} + +static struct or1k_du or1k_du_adv = { + .name = "adv", + .options = ADBG_USE_HISPEED, + .or1k_jtag_init = or1k_adv_jtag_init, + + .or1k_is_cpu_running = or1k_adv_is_cpu_running, + .or1k_cpu_stall = or1k_adv_cpu_stall, + .or1k_cpu_reset = or1k_adv_cpu_reset, + + .or1k_jtag_read_cpu = or1k_adv_jtag_read_cpu, + .or1k_jtag_write_cpu = or1k_adv_jtag_write_cpu, + + .or1k_jtag_read_memory = or1k_adv_jtag_read_memory, + .or1k_jtag_write_memory = or1k_adv_jtag_write_memory +}; + +int or1k_du_adv_register(void) +{ + list_add_tail(&or1k_du_adv.list, &du_list); + return 0; +} diff --git a/src/target/openrisc/or1k_tap.h b/src/target/openrisc/or1k_tap.h new file mode 100644 index 0000000..64bdef2 --- /dev/null +++ b/src/target/openrisc/or1k_tap.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2012 by Franck Jullien * + * elec4fun@gmail.com * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _OR1K_TAP_H_ +#define _OR1K_TAP_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/list.h> +#include "or1k.h" + +int or1k_tap_vjtag_register(void); +int or1k_tap_mohor_register(void); + +/* Linear list over all available or1k taps */ +extern struct list_head tap_list; + +struct or1k_tap_ip { + struct list_head list; + int (*init)(struct or1k_jtag *jtag_info); + const char *name; +}; + +#endif diff --git a/src/target/openrisc/or1k_tap_mohor.c b/src/target/openrisc/or1k_tap_mohor.c new file mode 100644 index 0000000..7c7f437 --- /dev/null +++ b/src/target/openrisc/or1k_tap_mohor.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by Franck Jullien * + * elec4fun@gmail.com * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "or1k_tap.h" +#include "or1k.h" + +#include <jtag/jtag.h> + +#define OR1K_TAP_INST_DEBUG 0x8 + +static int or1k_tap_mohor_init(struct or1k_jtag *jtag_info) +{ + LOG_DEBUG("Initialising OpenCores JTAG TAP"); + + /* Put TAP into state where it can talk to the debug interface + * by shifting in correct value to IR. + */ + + /* Ensure TAP is reset - maybe not necessary*/ + jtag_add_tlr(); + + struct jtag_tap *tap = jtag_info->tap; + struct scan_field field; + uint8_t ir_value = OR1K_TAP_INST_DEBUG; + + field.num_bits = tap->ir_length; + field.out_value = &ir_value; + field.in_value = NULL; + + jtag_add_ir_scan(tap, &field, TAP_IDLE); + + return jtag_execute_queue(); +} + +static struct or1k_tap_ip mohor_tap = { + .name = "mohor", + .init = or1k_tap_mohor_init, +}; + +int or1k_tap_mohor_register(void) +{ + list_add_tail(&mohor_tap.list, &tap_list); + return 0; +} diff --git a/src/target/openrisc/or1k_tap_vjtag.c b/src/target/openrisc/or1k_tap_vjtag.c new file mode 100644 index 0000000..ae729a0 --- /dev/null +++ b/src/target/openrisc/or1k_tap_vjtag.c @@ -0,0 +1,310 @@ +/*************************************************************************** + * Copyright (C) 2013 by Franck Jullien * + * elec4fun@gmail.com * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "or1k_tap.h" +#include "or1k.h" + +#include <jtag/jtag.h> + +/* Contains constants relevant to the Altera Virtual JTAG + * device, which are not included in the BSDL. + * As of this writing, these are constant across every + * device which supports virtual JTAG. + */ + +/* These are commands for the FPGA's IR. */ +#define ALTERA_CYCLONE_CMD_USER1 0x0E +#define ALTERA_CYCLONE_CMD_USER0 0x0C + +/* These defines are for the virtual IR (not the FPGA's) + * The virtual TAP was defined in hardware to match the OpenCores native + * TAP in both IR size and DEBUG command. + */ +#define ALT_VJTAG_IR_SIZE 4 +#define ALT_VJTAG_CMD_DEBUG 0x8 + +/* SLD node ID. */ +#define JTAG_TO_AVALON_NODE_ID 0x84 +#define VJTAG_NODE_ID 0x08 +#define SIGNAL_TAP_NODE_ID 0x00 +#define SERIAL_FLASH_LOADER_NODE_ID 0x04 + +#define VER(x) ((x >> 27) & 0x1f) +#define NB_NODES(x) ((x >> 19) & 0xff) +#define ID(x) ((x >> 19) & 0xff) +#define MANUF(x) ((x >> 8) & 0x7ff) +#define M_WIDTH(x) ((x >> 0) & 0xff) +#define INST_ID(x) ((x >> 0) & 0xff) + +/* tap instructions - Mohor JTAG TAP */ +#define OR1K_TAP_INST_IDCODE 0x2 +#define OR1K_TAP_INST_DEBUG 0x8 + +static char *id_to_string(unsigned char id) +{ + switch (id) { + case VJTAG_NODE_ID: + return "Virtual JTAG"; + case JTAG_TO_AVALON_NODE_ID: + return "JTAG to avalon bridge"; + case SIGNAL_TAP_NODE_ID: + return "Signal TAP"; + case SERIAL_FLASH_LOADER_NODE_ID: + return "Serial Flash Loader"; + } + return "unknown"; +} + +static unsigned char guess_addr_width(unsigned char number_of_nodes) +{ + unsigned char width = 0; + + while (number_of_nodes) { + number_of_nodes >>= 1; + width++; + } + + return width; +} + +static int or1k_tap_vjtag_init(struct or1k_jtag *jtag_info) +{ + LOG_DEBUG("Initialising Altera Virtual JTAG TAP"); + + /* Put TAP into state where it can talk to the debug interface + * by shifting in correct value to IR. + */ + + /* Ensure TAP is reset - maybe not necessary*/ + jtag_add_tlr(); + + /* You can use a custom JTAG controller to discover transactions + * necessary to enumerate all Virtual JTAG megafunction instances + * from your design atruntime. All SLD nodes and the virtual JTAG + * registers that they contain are targeted by two Instruction Register + * values, USER0 and USER1. + * + * The USER1 instruction targets the virtual IR of either the sld_hub + * or a SLD node. That is,when the USER1 instruction is issued to + * the device, the subsequent DR scans target a specific virtual + * IR chain based on an address field contained within the DR scan. + * The table below shows how the virtual IR, the DR target of the + * USER1 instruction is interpreted. + * + * The VIR_VALUE in the table below is the virtual IR value for the + * target SLD node. The width of this field is m bits in length, + * where m is the length of the largest VIR for all of the SLD nodes + * in the design. All SLD nodes with VIR lengths of fewer than m + * bits must pad VIR_VALUE with zeros up to a length of m. + * + * -------------------------------+------------------------------- + * m + n - 1 m | m -1 0 + * -------------------------------+------------------------------- + * ADDR [(n – 1)..0] | VIR_VALUE [(m – 1)..0] + * -------------------------------+------------------------------- + * + * The ADDR bits act as address values to signal the active SLD node + * that the virtual IR shift targets. ADDR is n bits in length, where + * n bits must be long enough to encode all SLD nodes within the design, + * as shown below. + * + * n = CEIL(log2(Number of SLD_nodes +1)) + * + * The SLD hub is always 0 in the address map. + * + * Discovery and enumeration of the SLD instances within a design + * requires interrogation of the sld_hub to determine the dimensions + * of the USER1 DR (m and n) and associating each SLD instance, specifically + * the Virtual JTAG megafunction instances, with an address value + * contained within the ADDR bits of the USER1 DR. + * + * The SLD hub contains the HUB IP Configuration Register and SLD_NODE_INFO + * register for each SLD node in the design. The HUB IP configuration register provides + * information needed to determine the dimensions of the USER1 DR chain. The + * SLD_NODE_INFO register is used to determine the address mapping for Virtual + * JTAG instance in your design. This register set is shifted out by issuing the + * HUB_INFO instruction. Both the ADDR bits for the SLD hub and the HUB_INFO + * instruction is 0 × 0. + * Because m and n are unknown at this point, the DR register + * (ADDR bits + VIR_VALUE) must be filled with zeros. Shifting a sequence of 64 zeroes + * into the USER1 DR is sufficient to cover the most conservative case for m and n. + */ + + uint8_t t[4]; + struct scan_field field; + struct jtag_tap *tap = jtag_info->tap; + + /* Select VIR */ + buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER1); + field.num_bits = tap->ir_length; + field.out_value = t; + field.in_value = NULL; + jtag_add_ir_scan(tap, &field, TAP_IDLE); + + /* Select the SLD Hub */ + field.num_bits = 64; + field.out_value = NULL; + field.in_value = NULL; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + + /* HUB IP Configuration Register + * + * When the USER1 and HUB_INFO instruction sequence is issued, the + * USER0 instruction must be applied to enable the target register + * of the HUB_INFO instruction. The HUB IP configuration register + * is shifted out using eight four-bit nibble scans of the DR register. + * Each four-bit scan must pass through the UPDATE_DR state before + * the next four-bit scan. The 8 scans are assembled into a 32-bit + * value with the definitions shown in the table below. + * + * -------------------------------------------------------------------------------- + * NIBBLE7 | NIBBLE6 | NIBBLE5 | NIBBLE4 | NIBBLE3 | NIBBLE2 | NIBBLE1 | NIBBLE0 + * ----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----- + * | | | | | | | | | | | | | | | + * ----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----- + * HUB IP version| N | ALTERA_MFG_ID (0x06E) | SUM (m, n) + * --------------+-------------------+------------------------+-------------------- + */ + + /* Select VDR */ + buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER0); + field.num_bits = tap->ir_length; + field.out_value = t; + field.in_value = NULL; + jtag_add_ir_scan(tap, &field, TAP_IDLE); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + uint8_t nibble; + uint32_t hub_info = 0; + + for (int i = 0; i < 8; i++) { + field.num_bits = 4; + field.out_value = NULL; + field.in_value = &nibble; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + hub_info = ((hub_info >> 4) | ((nibble & 0xf) << 28)); + } + + int nb_nodes = NB_NODES(hub_info); + int m_width = M_WIDTH(hub_info); + + LOG_DEBUG("SLD HUB Configuration register"); + LOG_DEBUG("------------------------------"); + LOG_DEBUG("m_width = %d", m_width); + LOG_DEBUG("manufacturer_id = 0x%02x", MANUF(hub_info)); + LOG_DEBUG("nb_of_node = %d", nb_nodes); + LOG_DEBUG("version = %d", VER(hub_info)); + LOG_DEBUG("VIR length = %d", guess_addr_width(nb_nodes) + m_width); + + /* Because the number of SLD nodes is now known, the Nodes on the hub can be + * enumerated by repeating the 8 four-bit nibble scans, once for each Node, + * to yield the SLD_NODE_INFO register of each Node. The DR nibble shifts + * are a continuation of the HUB_INFO DR shift used to shift out the Hub IP + * Configuration register. + * + * The order of the Nodes as they are shifted out determines the ADDR + * values for the Nodes, beginning with, for the first Node SLD_NODE_INFO + * shifted out, up to and including, for the last node on the hub. The + * tables below show the SLD_NODE_INFO register and a their functional descriptions. + * + * --------------+-----------+---------------+---------------- + * 31 27 | 26 19 | 18 8 | 7 0 + * --------------+-----------+---------------+---------------- + * Node Version | NODE ID | NODE MFG_ID | NODE INST ID + * + */ + + int vjtag_node_address = -1; + int node_index; + uint32_t node_info = 0; + for (node_index = 0; node_index < nb_nodes; node_index++) { + + for (int i = 0; i < 8; i++) { + field.num_bits = 4; + field.out_value = NULL; + field.in_value = &nibble; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + node_info = ((node_info >> 4) | ((nibble & 0xf) << 28)); + } + + LOG_DEBUG("Node info register"); + LOG_DEBUG("--------------------"); + LOG_DEBUG("instance_id = %d", ID(node_info)); + LOG_DEBUG("manufacturer_id = 0x%02x", MANUF(node_info)); + LOG_DEBUG("node_id = %d (%s)", ID(node_info), + id_to_string(ID(node_info))); + LOG_DEBUG("version = %d", VER(node_info)); + + if (ID(node_info) == VJTAG_NODE_ID) + vjtag_node_address = node_index + 1; + } + + if (vjtag_node_address < 0) { + LOG_ERROR("No VJTAG TAP instance found !"); + return ERROR_FAIL; + } + + /* Select VIR */ + t[0] = ALTERA_CYCLONE_CMD_USER1; + field.num_bits = tap->ir_length; + field.out_value = t; + field.in_value = NULL; + jtag_add_ir_scan(tap, &field, TAP_IDLE); + + /* Send the DEBUG command to the VJTAG IR */ + buf_set_u32(t, 0, field.num_bits, (vjtag_node_address << m_width) | ALT_VJTAG_CMD_DEBUG); + field.num_bits = guess_addr_width(nb_nodes) + m_width; + field.out_value = t; + field.in_value = NULL; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + + /* Select the VJTAG DR */ + t[0] = ALTERA_CYCLONE_CMD_USER0; + field.num_bits = tap->ir_length; + field.out_value = t; + field.in_value = NULL; + jtag_add_ir_scan(tap, &field, TAP_IDLE); + + return jtag_execute_queue(); +} + +static struct or1k_tap_ip vjtag_tap = { + .name = "vjtag", + .init = or1k_tap_vjtag_init, +}; + +int or1k_tap_vjtag_register(void) +{ + list_add_tail(&vjtag_tap.list, &tap_list); + return 0; +} |