This is an automated email from Gerrit. "Bjorn Schouteten <b_schoute...@hotmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7151
-- gerrit commit 7364f3e47f20de84ee9fb64897e329fc72cb87ba Author: Bjorn Schouteten <b_schoute...@hotmail.com> Date: Wed Aug 31 21:38:52 2022 +0200 target/roalogic: Add roalogic RV12 support Added support for the RV12 processor of RoaLogic. The RV12 is a highly configurable single-issue, single-core RV32I, RV64I compliant RISC CPU intended for the embedded market. The debugging interface is based on advance debug interface from OpenCores. Signed-off-by: Bjorn Schouteten <b_schoute...@hotmail.com> Change-Id: Ia9a2474c06c095cde41f3dad641671ec0509ea9b diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 46870922af..3f566466e0 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -3,7 +3,8 @@ %C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \ %D%/riscv/libriscv.la \ %D%/xtensa/libxtensa.la \ - %D%/espressif/libespressif.la + %D%/espressif/libespressif.la \ + %D%/roalogic/libroalogic.la %C%_libtarget_la_CPPFLAGS = $(AM_CPPFLAGS) @@ -265,4 +266,5 @@ ARC_SRC = \ include %D%/openrisc/Makefile.am include %D%/riscv/Makefile.am include %D%/xtensa/Makefile.am -include %D%/espressif/Makefile.am \ No newline at end of file +include %D%/espressif/Makefile.am +include %D%/roalogic/Makefile.am \ No newline at end of file diff --git a/src/target/roalogic/Makefile.am b/src/target/roalogic/Makefile.am new file mode 100644 index 0000000000..c12137c336 --- /dev/null +++ b/src/target/roalogic/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES += %D%/libroalogic.la +%C%_libroalogic_la_SOURCES = \ + %D%/rv12.c \ + %D%/rl_dbg_adv.c \ + %D%/rl_universal_tap.c \ + %D%/jsp_server.c \ + %D%/rv12.h \ + %D%/rl_dbg_adv.h \ + %D%/jsp_server.h diff --git a/src/target/roalogic/jsp_server.c b/src/target/roalogic/jsp_server.c new file mode 100644 index 0000000000..7803ca4a3a --- /dev/null +++ b/src/target/roalogic/jsp_server.c @@ -0,0 +1,247 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.com * + * * + * Based on OR1K version * + * Based on ./src/server/telnet_server.c * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <server/telnet_server.h> + +#include "rl_tap.h" +#include "rl_dbg_adv.h" +#include "jsp_server.h" + +static char *jsp_port; + +/**A skim of the relevant RFCs suggests that if my application simply sent the + * characters IAC DONT LINEMODE (\377\376\042) as soon as the client connects, + * the client should be forced into character mode. However it doesn't make any difference. + */ + +static const char * const negotiate = + "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */ + "\xFF\xFB\x01" /* IAC WILL Echo */ + "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */ + "\xFF\xFE\x01"; /* IAC DON'T Echo */ + +/* The only way we can detect that the socket is closed is the first time + * we write to it, we will fail. Subsequent write operations will + * succeed. Shudder! + */ +static int telnet_write(struct connection *connection, const void *data, int len) +{ + struct telnet_connection *t_con = connection->priv; + if (t_con->closed) + return ERROR_SERVER_REMOTE_CLOSED; + + if (connection_write(connection, data, len) == len) + return ERROR_OK; + t_con->closed = 1; + return ERROR_SERVER_REMOTE_CLOSED; +} + +static int jsp_poll_read(void *priv) +{ + struct jsp_service *jsp_service = (struct jsp_service *)priv; + unsigned char out_buffer[10]; + unsigned char in_buffer[10]; + int out_len = 0; + int in_len; + + if (!jsp_service->connection) + return ERROR_FAIL; + + memset(out_buffer, 0, 10); + + rl_adv_jtag_jsp_xfer(jsp_service->jtag_info, &out_len, out_buffer, &in_len, in_buffer); + if (in_len) + telnet_write(jsp_service->connection, in_buffer, in_len); + + return ERROR_OK; +} + +static int jsp_new_connection(struct connection *connection) +{ + struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection)); + struct jsp_service *jsp_service = connection->service->priv; + + connection->priv = telnet_connection; + + /* initialize telnet connection information */ + telnet_connection->closed = 0; + telnet_connection->line_size = 0; + telnet_connection->line_cursor = 0; + telnet_connection->state = TELNET_STATE_DATA; + + /* negotiate telnet options */ + telnet_write(connection, negotiate, strlen(negotiate)); + + /* print connection banner */ + if (jsp_service->banner) { + telnet_write(connection, jsp_service->banner, strlen(jsp_service->banner)); + telnet_write(connection, "\r\n", 2); + } + + jsp_service->connection = connection; + + int retval = target_register_timer_callback(&jsp_poll_read, 1, + TARGET_TIMER_TYPE_PERIODIC, jsp_service); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int jsp_input(struct connection *connection) +{ + int bytes_read; + unsigned char buffer[TELNET_BUFFER_SIZE]; + unsigned char *buf_p; + struct telnet_connection *t_con = connection->priv; + struct jsp_service *jsp_service = connection->service->priv; + + bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE); + + if (bytes_read == 0) + return ERROR_SERVER_REMOTE_CLOSED; + else if (bytes_read == -1) { + LOG_ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + + buf_p = buffer; + while (bytes_read) { + switch (t_con->state) { + case TELNET_STATE_DATA: + if (*buf_p == 0xff) + t_con->state = TELNET_STATE_IAC; + else { + int out_len = 1; + int in_len; + unsigned char in_buffer[10]; + rl_adv_jtag_jsp_xfer(jsp_service->jtag_info, + &out_len, buf_p, &in_len, + in_buffer); + if (in_len) + telnet_write(connection, + in_buffer, in_len); + } + break; + case TELNET_STATE_IAC: + switch (*buf_p) { + case 0xfe: + t_con->state = TELNET_STATE_DONT; + break; + case 0xfd: + t_con->state = TELNET_STATE_DO; + break; + case 0xfc: + t_con->state = TELNET_STATE_WONT; + break; + case 0xfb: + t_con->state = TELNET_STATE_WILL; + break; + } + break; + case TELNET_STATE_SB: + break; + case TELNET_STATE_SE: + break; + case TELNET_STATE_WILL: + case TELNET_STATE_WONT: + case TELNET_STATE_DO: + case TELNET_STATE_DONT: + t_con->state = TELNET_STATE_DATA; + break; + default: + LOG_ERROR("unknown telnet state"); + exit(-1); + } + + bytes_read--; + buf_p++; + } + + return ERROR_OK; +} + +static int jsp_connection_closed(struct connection *connection) +{ + struct jsp_service *jsp_service = connection->service->priv; + + int retval = target_unregister_timer_callback(&jsp_poll_read, jsp_service); + if (retval != ERROR_OK) + return retval; + + free(connection->priv); + connection->priv = NULL; + return ERROR_OK; +} + +static const struct service_driver jsp_service_driver = { + .name = "jsp", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = jsp_new_connection, + .input_handler = jsp_input, + .connection_closed_handler = jsp_connection_closed, + .keep_client_alive_handler = NULL, +}; + + +int jsp_init(struct rl_jtag *jtag_info, char *banner) +{ + struct jsp_service *jsp_service = malloc(sizeof(struct jsp_service)); + jsp_service->banner = banner; + jsp_service->jtag_info = jtag_info; + + return add_service(&jsp_service_driver, jsp_port, 1, jsp_service); +} + +COMMAND_HANDLER(handle_jsp_port_command) +{ + return CALL_COMMAND_HANDLER(server_pipe_command, &jsp_port); +} + +static const struct command_registration jsp_command_handlers[] = { + { + .name = "jsp_port", + .handler = handle_jsp_port_command, + .mode = COMMAND_ANY, + .help = "Specify port on which to listen " + "for incoming JSP telnet connections.", + .usage = "[port_num]", + }, + COMMAND_REGISTRATION_DONE +}; + +int jsp_register_commands(struct command_context *cmd_ctx) +{ + jsp_port = strdup("7777"); + return register_commands(cmd_ctx, NULL, jsp_command_handlers); +} + +void jsp_service_free(void) +{ + free(jsp_port); +} diff --git a/src/target/roalogic/jsp_server.h b/src/target/roalogic/jsp_server.h new file mode 100644 index 0000000000..d412ff8bac --- /dev/null +++ b/src/target/roalogic/jsp_server.h @@ -0,0 +1,18 @@ +#ifndef OPENOCD_TARGET_ROALOGIC_JSP_SERVER_H +#define OPENOCD_TARGET_ROALOGIC_JSP_SERVER_H + +#include "rl_tap.h" +#include "rv12.h" +#include "rl_dbg_adv.h" + +struct jsp_service { + char *banner; + struct rl_jtag *jtag_info; + struct connection *connection; +}; + +int jsp_init(struct rl_jtag *jtag_info, char *banner); +int jsp_register_commands(struct command_context *cmd_ctx); +void jsp_service_free(void); + +#endif /* OPENOCD_TARGET_ROALOGIC_JSP_SERVER_H */ diff --git a/src/target/roalogic/rl_dbg_adv.c b/src/target/roalogic/rl_dbg_adv.c new file mode 100644 index 0000000000..07354436c9 --- /dev/null +++ b/src/target/roalogic/rl_dbg_adv.c @@ -0,0 +1,1154 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.com * + * * + * Based on OR1K version * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rl_tap.h" +#include "rv12.h" +#include "rl_dbg_adv.h" +#include "jsp_server.h" + +#include <target/target.h> +#include <jtag/jtag.h> + +#define JSP_BANNER "\n\r" \ + "******************************\n\r" \ + "** JTAG Serial Port **\n\r" \ + "******************************\n\r" \ + "\n\r" + +#define NO_OPTION 0 + +/* 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 USE_HISPEED 1 + + +/* This an option to the adv debug unit. + * If this is defined, the JTAG Serial Port Server is started. + * This option must match the RTL configured option. + */ +#define ENABLE_JSP_SERVER 2 + + +/* Define this if you intend to use the JSP in a system with multiple + * devices on the JTAG chain + */ +#define ENABLE_JSP_MULTI 4 + + +/* 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_NONE -1 +#define DC_SYSBUS 0 +#define DC_CPU 1 +#define DC_JSP 2 + + +/* 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 SystemBus module + * The first is the length of the index register, + * the indexes of the various registers are defined after that. + */ +#define DBG_SYSBUS_REG_SEL_LEN 1 +#define DBG_SYSBUS_REG_ERROR 0 + + +/* Opcode definitions for the SystemBus module. */ +#define DBG_SYSBUS_OPCODE_LEN 4 +#define DBG_SYSBUS_CMD_NOP 0x0 +#define DBG_SYSBUS_CMD_BWRITE8 0x1 +#define DBG_SYSBUS_CMD_BWRITE16 0x2 +#define DBG_SYSBUS_CMD_BWRITE32 0x3 +#define DBG_SYSBUS_CMD_BWRITE64 0x4 +#define DBG_SYSBUS_CMD_BREAD8 0x5 +#define DBG_SYSBUS_CMD_BREAD16 0x6 +#define DBG_SYSBUS_CMD_BREAD32 0x7 +#define DBG_SYSBUS_CMD_BREAD64 0x8 +#define DBG_SYSBUS_CMD_IREG_WR 0x9 +#define DBG_SYSBUS_CMD_IREG_SEL 0xd + + +/* Internal register definitions for the CP module. */ +#define DBG_CPU_REG_SEL_LEN 1 +#define DBG_CPU_REG_STATUS 0 + + +/* CPU Select */ +#define DBG_CPU_CPUSEL_LEN 4 + + +/* Opcode definitions for the CPU module. */ +#define DBG_CPU_OPCODE_LEN 4 +#define DBG_CPU_CMD_NOP 0x0 +#define DBG_CPU_CMD_BWRITE32 0x3 +#define DBG_CPU_CMD_BREAD32 0x7 +#define DBG_CPU_CMD_IREG_WR 0x9 +#define DBG_CPU_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 rl_du rl_dbg_adv; + +static const char * const chain_name[] = {"SYSBUS", "CPU", "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 rl_adv_jtag_init(struct rl_jtag *jtag_info) +{ + struct rl_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->rl_jtag_inited = 1; + + /* TODO hardcoded HW parameters */ + jtag_info->rl_jtag_address_size = 32; +// jtag_info->cpu_size = 4; + jtag_info->rl_jtag_cpu_selected = 0; + + /* TAP reset - not sure what state debug module chain is in now */ + jtag_info->rl_jtag_module_selected = DC_NONE; + + 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 (rl_dbg_adv.options & USE_HISPEED) + LOG_INFO("Roa Logic adv debug unit is configured with option USE_HISPEED"); + + if (rl_dbg_adv.options & ENABLE_JSP_SERVER) { + if (rl_dbg_adv.options & ENABLE_JSP_MULTI) + LOG_INFO("Roa Logic adv debug unit is configured with option ENABLE_JSP_MULTI"); + LOG_INFO("Roa Logic adv debug unit is configured with option ENABLE_JSP_SERVER"); + retval = jsp_init(jtag_info, JSP_BANNER); + if (retval != ERROR_OK) { + LOG_ERROR("Couldn't start the JSP server"); + return retval; + } + } + + LOG_DEBUG("Init done"); + + return ERROR_OK; + +} + +/* Selects one of the modules in the debug unit + * (e.g. SYSBUS, CPU, JSP) + */ +static int adbg_select_module(struct rl_jtag *jtag_info, int chain) +{ + if (jtag_info->rl_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->rl_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 rl_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->rl_jtag_module_selected] == regidx) + return ERROR_OK; + + switch (jtag_info->rl_jtag_module_selected) { + case DC_SYSBUS: + index_len = DBG_SYSBUS_REG_SEL_LEN; + opcode = DBG_SYSBUS_CMD_IREG_SEL; + opcode_len = DBG_SYSBUS_OPCODE_LEN; + break; + case DC_CPU: + index_len = DBG_CPU_REG_SEL_LEN; + opcode = DBG_CPU_CMD_IREG_SEL; + opcode_len = DBG_CPU_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while selecting control register", + jtag_info->rl_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->rl_jtag_module_selected] = regidx; + + return ERROR_OK; +} + +/* Write control register (internal to the debug unit) */ +static int adbg_ctrl_write(struct rl_jtag *jtag_info, uint8_t regidx, + uint32_t *cmd_data, int length_bits) +{ + int index_len; + int cpusel_len; + int cpusel; + uint32_t opcode; + uint32_t opcode_len; + + LOG_DEBUG("Write control register %" PRId8 ": 0x%08" PRIx32, 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->rl_jtag_module_selected) { + case DC_SYSBUS: + index_len = DBG_SYSBUS_REG_SEL_LEN; + cpusel = 0; + cpusel_len = 0; + opcode = DBG_SYSBUS_CMD_IREG_WR; + opcode_len = DBG_SYSBUS_OPCODE_LEN; + break; + case DC_CPU: + index_len = DBG_CPU_REG_SEL_LEN; + cpusel = jtag_info->rl_jtag_cpu_selected; + cpusel_len = DBG_CPU_CPUSEL_LEN; + opcode = DBG_CPU_CMD_IREG_WR; + opcode_len = DBG_CPU_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing control write", + jtag_info->rl_jtag_module_selected); + return ERROR_FAIL; + } + + struct scan_field field[2]; + + /* MSB must be 0 to access modules */ + uint32_t data = ~(1 << (opcode_len + cpusel_len + index_len)); + data &= (((opcode << cpusel_len) | cpusel ) << 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 = 1+ opcode_len + cpusel_len + 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 rl_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 cpusel = 0; + int cpusel_len; + int opcode_len; + uint32_t opcode; + + /* There is no 'read' command, We write a NOP to read */ + switch (jtag_info->rl_jtag_module_selected) { + case DC_SYSBUS: + cpusel_len = 0; + opcode = DBG_SYSBUS_CMD_NOP; + opcode_len = DBG_SYSBUS_OPCODE_LEN; + break; + case DC_CPU: + cpusel = jtag_info->rl_jtag_cpu_selected; + cpusel_len = DBG_CPU_CPUSEL_LEN; + opcode = DBG_CPU_CMD_NOP; + opcode_len = DBG_CPU_OPCODE_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing control read", + jtag_info->rl_jtag_module_selected); + return ERROR_FAIL; + } + + /* Zero MSB = op for module, not top-level debug unit */ + uint32_t outdata = ~(1 << (opcode_len + cpusel_len)); + outdata &= (opcode << cpusel_len) | cpusel; + + 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 + cpusel_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 + * 4-bit CPU (optional) + * 32/64-bit address + * 16-bit length (of the burst, in words) + */ +static int adbg_burst_command(struct rl_jtag *jtag_info, + uint32_t opcode, uint8_t opcode_len, + uint64_t address, uint8_t address_len, + uint32_t cpusel, uint8_t cpusel_len, + uint16_t length_words) +{ + uint32_t bitcount; + uint32_t data[3]; + + /* Total bitcount */ + bitcount = 1; //1bit module command + bitcount += opcode_len; //4bit opcode + bitcount += cpusel_len; //4bit CPU select + bitcount += address_len; //Address + bitcount += 16; //Burst Length + + /* Set up the data */ + switch (address_len) { + case 32: + data[0] = (address << 16) | length_words; + data[1] = opcode; + if (cpusel_len) { + data[1] = (data[1] << cpusel_len) | cpusel; + } + data[1] = (data[1] << 16) | (address >> 16); + + /* MSB must be 0 to access modules */ + data[1] &= ~(1 << (16 + cpusel_len + opcode_len)); + break; + case 64: + data[0] = (address << 16) | length_words; + data[1] = address >> 16; + data[2] = opcode; + if (cpusel_len) { + data[2] = (data[2] << cpusel_len) | cpusel; + } + data[2] = (data[2] << 16) | (address >> 48); + + data[2] &= ~(1 << (16 + cpusel_len + opcode_len)); + break; + default: + LOG_ERROR("Unsupported address-length (%i)", address_len); + return ERROR_FAIL; + } + + struct scan_field field; + + field.num_bits = bitcount; + 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_sysbus_burst_read(struct rl_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; + uint8_t opcode_len; + uint8_t cpusel = 0; + uint8_t cpusel_len; + uint8_t address_len; + + LOG_DEBUG("Doing burst read, word size %d, word count %d, start address 0x%08" PRIx32, + size, count, start_address); + + /* Select the appropriate opcode */ + switch (jtag_info->rl_jtag_module_selected) { + case DC_SYSBUS: + if (size == 1) + opcode = DBG_SYSBUS_CMD_BREAD8; + else if (size == 2) + opcode = DBG_SYSBUS_CMD_BREAD16; + else if (size == 4) + opcode = DBG_SYSBUS_CMD_BREAD32; + else if (size == 8) + opcode = DBG_SYSBUS_CMD_BREAD64; + else { + LOG_WARNING("Tried burst read with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_SYSBUS_CMD_BREAD32; + } + opcode_len = DBG_SYSBUS_OPCODE_LEN; + cpusel_len = 0; + break; + case DC_CPU: + if (size == 4) + opcode = DBG_CPU_CMD_BREAD32; + else { + LOG_WARNING("Tried burst read with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU_CMD_BREAD32; + } + opcode_len = DBG_CPU_OPCODE_LEN; + cpusel = jtag_info->rl_jtag_cpu_selected; + cpusel_len = DBG_CPU_CPUSEL_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing burst read", + jtag_info->rl_jtag_module_selected); + return ERROR_FAIL; + } + address_len = jtag_info->rl_jtag_address_size; + + + 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, opcode_len, + start_address, address_len, cpusel, cpusel_len, 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%08" PRIx32 ", read CRC 0x%08" PRIx32, 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->rl_jtag_module_selected == DC_SYSBUS && + !(rl_dbg_adv.options & 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_SYSBUS_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_SYSBUS_REG_ERROR, err_data, 33); + if (retval != ERROR_OK) + goto out; + + addr = (err_data[0] >> 1) | (err_data[1] << 31); + LOG_WARNING("SYSBUS bus error during burst read, address 0x%08" PRIx32 ", retrying!", addr); + + bus_error_retries++; + if (bus_error_retries > MAX_BUS_ERRORS) { + LOG_ERROR("Max SYSBUS bus errors reached during burst read"); + retval = ERROR_FAIL; + goto out; + } + + /* Don't call retry_do(), a JTAG reset won't help a SYSBUS bus error */ + /* Write 1 bit, to reset the error register */ + err_data[0] = 1; + retval = adbg_ctrl_write(jtag_info, DBG_SYSBUS_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_sysbus_burst_write(struct rl_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; + uint8_t opcode_len; + uint8_t cpusel = 0; + uint8_t cpusel_len; + uint8_t address_len; + + 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->rl_jtag_module_selected) { + case DC_SYSBUS: + if (size == 1) + opcode = DBG_SYSBUS_CMD_BWRITE8; + else if (size == 2) + opcode = DBG_SYSBUS_CMD_BWRITE16; + else if (size == 4) + opcode = DBG_SYSBUS_CMD_BWRITE32; + else if (size == 8) + opcode = DBG_SYSBUS_CMD_BWRITE64; + else { + LOG_DEBUG("Tried SYSBUS burst write with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_SYSBUS_CMD_BWRITE32; + } + opcode_len = DBG_SYSBUS_OPCODE_LEN; + cpusel_len = 0; + break; + case DC_CPU: + if (size == 4) + opcode = DBG_CPU_CMD_BWRITE32; + else { + LOG_DEBUG("Tried CPU burst write with invalid word size (%d)," + "defaulting to 4-byte words", size); + opcode = DBG_CPU_CMD_BWRITE32; + } + opcode_len = DBG_CPU_OPCODE_LEN; + cpusel = jtag_info->rl_jtag_cpu_selected; + cpusel_len = DBG_CPU_CPUSEL_LEN; + break; + default: + LOG_ERROR("Illegal debug chain selected (%i) while doing burst write", + jtag_info->rl_jtag_module_selected); + return ERROR_FAIL; + } + address_len = jtag_info->rl_jtag_address_size; + + +retry_full_write: + + /* Send the BURST WRITE command, returns TAP to idle state */ + retval = adbg_burst_command(jtag_info, opcode, opcode_len, + start_address, address_len, cpusel, cpusel_len, 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 %" PRIi8 " (computed CRC 0x%08" PRIx32 ")", 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->rl_jtag_module_selected == DC_SYSBUS && + !(rl_dbg_adv.options & 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_SYSBUS_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_SYSBUS_REG_ERROR, err_data, 33); + if (retval != ERROR_OK) + return retval; + + addr = (err_data[0] >> 1) | (err_data[1] << 31); + LOG_WARNING("SYSBUS bus error during burst write, address 0x%08" PRIx32 ", retrying!", addr); + + bus_error_retries++; + if (bus_error_retries > MAX_BUS_ERRORS) { + LOG_ERROR("Max SYSBUS bus errors reached during burst read"); + retval = ERROR_FAIL; + return retval; + } + + /* Don't call retry_do(), a JTAG reset won't help a SYSBUS bus error */ + /* Write 1 bit, to reset the error register */ + err_data[0] = 1; + retval = adbg_ctrl_write(jtag_info, DBG_SYSBUS_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 rl_adv_jtag_read_cpu(struct rl_jtag *jtag_info, + uint32_t addr, int count, uint32_t *value) +{ + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_CPU); + if (retval != ERROR_OK) + return retval; + + return adbg_sysbus_burst_read(jtag_info, 4, count, addr, (uint8_t *)value); +} + +static int rl_adv_jtag_write_cpu(struct rl_jtag *jtag_info, + uint32_t addr, int count, const uint32_t *value) +{ + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_CPU); + if (retval != ERROR_OK) + return retval; + + return adbg_sysbus_burst_write(jtag_info, (uint8_t *)value, 4, count, addr); +} + +static int rl_adv_cpu_stall(struct rl_jtag *jtag_info, int action) +{ + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_CPU); + if (retval != ERROR_OK) + return retval; + + //TODO: the '2' length should be NumberOfCPU + uint32_t cpu_cr; + retval = adbg_ctrl_read(jtag_info, DBG_CPU_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_CPU); + if (retval != ERROR_OK) + return retval; + + //TODO: the '2' length should be number of CPUs + return adbg_ctrl_write(jtag_info, DBG_CPU_REG_STATUS, &cpu_cr, 2); +} + +static int rl_adv_is_cpu_running(struct rl_jtag *jtag_info, int *running) +{ + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + int current = jtag_info->rl_jtag_module_selected; + + retval = adbg_select_module(jtag_info, DC_CPU); + if (retval != ERROR_OK) + return retval; + + uint32_t cpu_cr = 0; + retval = adbg_ctrl_read(jtag_info, DBG_CPU_REG_STATUS, &cpu_cr, 2); + if (retval != ERROR_OK) + return retval; + + if (cpu_cr & DBG_CPU_CR_STALL) + *running = 0; + else + *running = 1; + + if (current != DC_NONE) { + retval = adbg_select_module(jtag_info, current); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int rl_adv_cpu_reset(struct rl_jtag *jtag_info, int action) +{ + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_CPU); + if (retval != ERROR_OK) + return retval; + + uint32_t cpu_cr; + retval = adbg_ctrl_read(jtag_info, DBG_CPU_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_CPU); + if (retval != ERROR_OK) + return retval; + + return adbg_ctrl_write(jtag_info, DBG_CPU_REG_STATUS, &cpu_cr, 2); +} + +static int rl_adv_jtag_read_memory(struct rl_jtag *jtag_info, + uint32_t addr, uint32_t size, int count, uint8_t *buffer) +{ + LOG_DEBUG("Reading SYSBUS%" PRIu32 " at 0x%08" PRIx32, size * 8, addr); + + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_SYSBUS); + if (retval != ERROR_OK) + return retval; + + int block_count_left = count; + uint32_t block_count_address = addr; + uint8_t *block_count_buffer = buffer; + + while (block_count_left) { + + int blocks_this_round = (block_count_left > MAX_BURST_SIZE) ? + MAX_BURST_SIZE : block_count_left; + + retval = adbg_sysbus_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; + } + + /* The adv_debug_if always return words and half words in + * little-endian order no matter what the target endian is. + * So if the target endian is big, change the order. + */ + + struct target *target = jtag_info->target; + if ((target->endianness == TARGET_BIG_ENDIAN) && (size != 1)) { + switch (size) { + case 4: + buf_bswap32(buffer, buffer, size * count); + break; + case 2: + buf_bswap16(buffer, buffer, size * count); + break; + } + } + + return ERROR_OK; +} + +static int rl_adv_jtag_write_memory(struct rl_jtag *jtag_info, + uint32_t addr, uint32_t size, int count, const uint8_t *buffer) +{ + LOG_DEBUG("Writing SYSBUS%" PRIu32 " at 0x%08" PRIx32, size * 8, addr); + + int retval; + if (!jtag_info->rl_jtag_inited) { + retval = rl_adv_jtag_init(jtag_info); + if (retval != ERROR_OK) + return retval; + } + + retval = adbg_select_module(jtag_info, DC_SYSBUS); + if (retval != ERROR_OK) + return retval; + + /* The adv_debug_if wants words and half words in little-endian + * order no matter what the target endian is. So if the target + * endian is big, change the order. + */ + + void *t = NULL; + struct target *target = jtag_info->target; + if ((target->endianness == TARGET_BIG_ENDIAN) && (size != 1)) { + t = malloc(count * size * sizeof(uint8_t)); + if (!t) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + switch (size) { + // case 8: + // bus_bswap64(t, buffer, size * count); + // break; + case 4: + buf_bswap32(t, buffer, size * count); + break; + case 2: + buf_bswap16(t, buffer, size * count); + break; + } + buffer = t; + } + + 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_sysbus_burst_write(jtag_info, block_count_buffer, + size, blocks_this_round, + block_count_address); + if (retval != ERROR_OK) { + free(t); + return retval; + } + + block_count_left -= blocks_this_round; + block_count_address += size * MAX_BURST_SIZE; + block_count_buffer += size * MAX_BURST_SIZE; + } + + free(t); + return ERROR_OK; +} + +int rl_adv_jtag_jsp_xfer(struct rl_jtag *jtag_info, + int *out_len, unsigned char *out_buffer, + int *in_len, unsigned char *in_buffer) +{ + LOG_DEBUG("JSP transfer"); + + int retval; + if (!jtag_info->rl_jtag_inited) + return ERROR_OK; + + retval = adbg_select_module(jtag_info, DC_JSP); + if (retval != ERROR_OK) + return retval; + + /* return nb char xmit */ + int xmitsize; + if (*out_len > 8) + xmitsize = 8; + else + xmitsize = *out_len; + + uint8_t out_data[10]; + uint8_t in_data[10]; + struct scan_field field; + int startbit, stopbit, wrapbit; + + memset(out_data, 0, 10); + + if (rl_dbg_adv.options & ENABLE_JSP_MULTI) { + + startbit = 1; + wrapbit = (xmitsize >> 3) & 0x1; + out_data[0] = (xmitsize << 5) | 0x1; /* set the start bit */ + + int i; + /* don't copy off the end of the input array */ + for (i = 0; i < xmitsize; i++) { + out_data[i + 1] = (out_buffer[i] << 1) | wrapbit; + wrapbit = (out_buffer[i] >> 7) & 0x1; + } + + if (i < 8) + out_data[i + 1] = wrapbit; + else + out_data[9] = wrapbit; + + /* If the last data bit is a '1', then we need to append a '0' so the top-level module + * won't treat the burst as a 'module select' command. + */ + stopbit = !!(out_data[9] & 0x01); + + } else { + startbit = 0; + /* First byte out has write count in upper nibble */ + out_data[0] = 0x0 | (xmitsize << 4); + if (xmitsize > 0) + memcpy(&out_data[1], out_buffer, xmitsize); + + /* If the last data bit is a '1', then we need to append a '0' so the top-level module + * won't treat the burst as a 'module select' command. + */ + stopbit = !!(out_data[8] & 0x80); + } + + field.num_bits = 72 + startbit + stopbit; + field.out_value = out_data; + field.in_value = in_data; + + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + /* bytes available is in the upper nibble */ + *in_len = (in_data[0] >> 4) & 0xF; + memcpy(in_buffer, &in_data[1], *in_len); + + int bytes_free = in_data[0] & 0x0F; + *out_len = (bytes_free < xmitsize) ? bytes_free : xmitsize; + + return ERROR_OK; +} + +static struct rl_du rl_dbg_adv = { + .name = "rl_dbg_adv", + .options = NO_OPTION, + .rl_jtag_init = rl_adv_jtag_init, + + .rl_is_cpu_running = rl_adv_is_cpu_running, + .rl_cpu_stall = rl_adv_cpu_stall, + .rl_cpu_reset = rl_adv_cpu_reset, + + .rl_jtag_read_cpu = rl_adv_jtag_read_cpu, + .rl_jtag_write_cpu = rl_adv_jtag_write_cpu, + + .rl_jtag_read_memory = rl_adv_jtag_read_memory, + .rl_jtag_write_memory = rl_adv_jtag_write_memory +}; + +int rl_dbg_adv_register(void) +{ + list_add_tail(&rl_dbg_adv.list, &rl_du_list); + return 0; +} diff --git a/src/target/roalogic/rl_dbg_adv.h b/src/target/roalogic/rl_dbg_adv.h new file mode 100644 index 0000000000..44a84ca090 --- /dev/null +++ b/src/target/roalogic/rl_dbg_adv.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2022 Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ROALOGIC_DBG_ADV_H +#define OPENOCD_TARGET_ROALOGIC_DBG_ADV_H + +#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 rl_dbg_adv_register(void); + +/* Linear list over all available or1k debug unit */ +extern struct list_head rl_du_list; + +struct rl_du { + const char *name; + struct list_head list; + int options; + + int (*rl_jtag_init)(struct rl_jtag *jtag_info); + + int (*rl_is_cpu_running)(struct rl_jtag *jtag_info, int *running); + + int (*rl_cpu_stall)(struct rl_jtag *jtag_info, int action); + + int (*rl_cpu_reset)(struct rl_jtag *jtag_info, int action); + + int (*rl_jtag_read_cpu)(struct rl_jtag *jtag_info, + uint32_t addr, int count, uint32_t *value); + + int (*rl_jtag_write_cpu)(struct rl_jtag *jtag_info, + uint32_t addr, int count, const uint32_t *value); + + int (*rl_jtag_read_memory)(struct rl_jtag *jtag_info, uint32_t addr, uint32_t size, + int count, uint8_t *buffer); + + int (*rl_jtag_write_memory)(struct rl_jtag *jtag_info, uint32_t addr, uint32_t size, + int count, const uint8_t *buffer); +}; + +static inline struct rl_du *rl_jtag_to_du(struct rl_jtag *jtag_info) +{ + return (struct rl_du *)jtag_info->du_core; +} + +static inline struct rl_du *rl_to_du(struct rv12_common *rvl) +{ + struct rl_jtag *jtag = &rvl->jtag; + return (struct rl_du *)jtag->du_core; +} + +int rl_adv_jtag_jsp_xfer(struct rl_jtag *jtag_info, + int *out_len, unsigned char *out_buffer, + int *in_len, unsigned char *in_buffer); + +#endif /* OPENOCD_TARGET_ROALOGIC_DBG_ADV_H */ diff --git a/src/target/roalogic/rl_tap.h b/src/target/roalogic/rl_tap.h new file mode 100644 index 0000000000..70baa213ea --- /dev/null +++ b/src/target/roalogic/rl_tap.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ROALOGIC_RL_TAP_H +#define OPENOCD_TARGET_ROALOGIC_RL_TAP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/list.h> +#include "rv12.h" + +int rl_tap_vjtag_register(void); +int rl_universal_tap_register(void); + +/* Linear list over all available Roa Logic TAPs */ +extern struct list_head rl_tap_list; + +struct rl_tap_ip { + struct list_head list; + int (*init)(struct rl_jtag *jtag_info); + const char *name; +}; + +#endif /* OPENOCD_TARGET_ROALOGIC_RL_TAP_H */ diff --git a/src/target/roalogic/rl_universal_tap.c b/src/target/roalogic/rl_universal_tap.c new file mode 100644 index 0000000000..3675ea93ae --- /dev/null +++ b/src/target/roalogic/rl_universal_tap.c @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.com * + * * + * * + * Based on OR1k Version * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rl_tap.h" +#include "rv12.h" + +#include <jtag/jtag.h> + +#define RL_TAP_INST_DEBUG 0x8 + +static int rl_universal_tap_init(struct rl_jtag *jtag_info) +{ + LOG_DEBUG("Initialising Roa Logic Universal 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 = RL_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 rl_tap_ip universal_tap = { + .name = "rl_universal_tap", + .init = rl_universal_tap_init, +}; + +int rl_universal_tap_register(void) +{ + list_add_tail(&universal_tap.list, &rl_tap_list); + return 0; +} diff --git a/src/target/roalogic/rv12.c b/src/target/roalogic/rv12.c new file mode 100644 index 0000000000..6393a755d9 --- /dev/null +++ b/src/target/roalogic/rv12.c @@ -0,0 +1,1884 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.com * + * * + * Based on OR1K version * + * * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <jtag/jtag.h> +#include <target/register.h> +#include <target/target.h> +#include <target/breakpoints.h> +#include <target/target_type.h> +#include <helper/time_support.h> +#include <helper/fileio.h> +#include "rl_tap.h" +#include "rv12.h" +#include "rl_dbg_adv.h" + +LIST_HEAD(rl_tap_list); +LIST_HEAD(rl_du_list); + +static int rv12_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint); + +static int rv12_read_core_reg(struct target *target, int num); +static int rv12_write_core_reg(struct target *target, int num); + +static struct rv12_core_reg *rv12_core_reg_list_arch_info; + +/* Combination of RISC-V and rv12 names + * until rv12 uses RISC-V debug spec */ +static const struct rv12_core_reg_init rv12_init_reg_list[] = { + /* Integer Register File */ + {"zero", GROUP_RF + 0, "org.gnu.gdb.riscv.cpu", NULL}, + {"ra", GROUP_RF + 1, "org.gnu.gdb.riscv.cpu", NULL}, + {"sp", GROUP_RF + 2, "org.gnu.gdb.riscv.cpu", NULL}, + {"gp", GROUP_RF + 3, "org.gnu.gdb.riscv.cpu", NULL}, + {"tp", GROUP_RF + 4, "org.gnu.gdb.riscv.cpu", NULL}, + {"t0", GROUP_RF + 5, "org.gnu.gdb.riscv.cpu", NULL}, + {"t1", GROUP_RF + 6, "org.gnu.gdb.riscv.cpu", NULL}, + {"t2", GROUP_RF + 7, "org.gnu.gdb.riscv.cpu", NULL}, + {"s0", GROUP_RF + 8, "org.gnu.gdb.riscv.cpu", NULL}, + {"s1", GROUP_RF + 9, "org.gnu.gdb.riscv.cpu", NULL}, + {"a0", GROUP_RF + 10, "org.gnu.gdb.riscv.cpu", NULL}, + {"a1", GROUP_RF + 11, "org.gnu.gdb.riscv.cpu", NULL}, + {"a2", GROUP_RF + 12, "org.gnu.gdb.riscv.cpu", NULL}, + {"a3", GROUP_RF + 13, "org.gnu.gdb.riscv.cpu", NULL}, + {"a4", GROUP_RF + 14, "org.gnu.gdb.riscv.cpu", NULL}, + {"a5", GROUP_RF + 15, "org.gnu.gdb.riscv.cpu", NULL}, + {"a6", GROUP_RF + 16, "org.gnu.gdb.riscv.cpu", NULL}, + {"a7", GROUP_RF + 17, "org.gnu.gdb.riscv.cpu", NULL}, + {"s2", GROUP_RF + 18, "org.gnu.gdb.riscv.cpu", NULL}, + {"s3", GROUP_RF + 19, "org.gnu.gdb.riscv.cpu", NULL}, + {"s4", GROUP_RF + 20, "org.gnu.gdb.riscv.cpu", NULL}, + {"s5", GROUP_RF + 21, "org.gnu.gdb.riscv.cpu", NULL}, + {"s6", GROUP_RF + 22, "org.gnu.gdb.riscv.cpu", NULL}, + {"s7", GROUP_RF + 23, "org.gnu.gdb.riscv.cpu", NULL}, + {"s8", GROUP_RF + 24, "org.gnu.gdb.riscv.cpu", NULL}, + {"s9", GROUP_RF + 25, "org.gnu.gdb.riscv.cpu", NULL}, + {"s10", GROUP_RF + 26, "org.gnu.gdb.riscv.cpu", NULL}, + {"s11", GROUP_RF + 27, "org.gnu.gdb.riscv.cpu", NULL}, + {"t3", GROUP_RF + 28, "org.gnu.gdb.riscv.cpu", NULL}, + {"t4", GROUP_RF + 29, "org.gnu.gdb.riscv.cpu", NULL}, + {"t5", GROUP_RF + 30, "org.gnu.gdb.riscv.cpu", NULL}, + {"t6", GROUP_RF + 31, "org.gnu.gdb.riscv.cpu", NULL}, + + + /* PC */ + {"pc", GROUP_GPRS + 0x201, "org.gnu.gdb.rv12.dbg", NULL}, + + + /* Floating Point Register File */ + + + /* CSRs */ + {"ustatus", GROUP_CSR + CSR_USTATUS, "org.gnu.gdb.riscv.csr", NULL}, + {"uie", GROUP_CSR + CSR_UIE, "org.gnu.gdb.riscv.csr", NULL}, + {"utvec", GROUP_CSR + CSR_UTVEC, "org.gnu.gdb.riscv.csr", NULL}, + {"uscratch", GROUP_CSR + CSR_USCRATCH, "org.gnu.gdb.riscv.csr", NULL}, + {"uepc", GROUP_CSR + CSR_UEPC, "org.gnu.gdb.riscv.csr", NULL}, + {"ucause", GROUP_CSR + CSR_UCAUSE, "org.gnu.gdb.riscv.csr", NULL}, + {"utval", GROUP_CSR + CSR_UTVAL, "org.gnu.gdb.riscv.csr", NULL}, + {"uip", GROUP_CSR + CSR_UIP, "org.gnu.gdb.riscv.csr", NULL}, + {"fflags", GROUP_CSR + CSR_FFLAGS, "org.gnu.gdb.riscv.csr", NULL}, + {"frm", GROUP_CSR + CSR_FRM, "org.gnu.gdb.riscv.csr", NULL}, + {"fcsr", GROUP_CSR + CSR_FCSR, "org.gnu.gdb.riscv.csr", NULL}, + {"cycle", GROUP_CSR + CSR_CYCLE, "org.gnu.gdb.riscv.csr", NULL}, + {"time", GROUP_CSR + CSR_TIME, "org.gnu.gdb.riscv.csr", NULL}, + {"instret", GROUP_CSR + CSR_INSTRET, "org.gnu.gdb.riscv.csr", NULL}, + {"cycleh", GROUP_CSR + CSR_CYCLEH, "org.gnu.gdb.riscv.csr", NULL}, + {"timeh", GROUP_CSR + CSR_TIMEH, "org.gnu.gdb.riscv.csr", NULL}, + {"instreth", GROUP_CSR + CSR_INSTRETH, "org.gnu.gdb.riscv.csr", NULL}, + + {"sstatus", GROUP_CSR + CSR_SSTATUS, "org.gnu.gdb.riscv.csr", NULL}, + {"sedeleg", GROUP_CSR + CSR_SEDELEG, "org.gnu.gdb.riscv.csr", NULL}, + {"sideleg", GROUP_CSR + CSR_SIDELEG, "org.gnu.gdb.riscv.csr", NULL}, + {"sie", GROUP_CSR + CSR_SIE, "org.gnu.gdb.riscv.csr", NULL}, + {"stvec", GROUP_CSR + CSR_STVEC, "org.gnu.gdb.riscv.csr", NULL}, + {"scounteren", GROUP_CSR + CSR_SCOUNTEREN, "org.gnu.gdb.riscv.csr", NULL}, + {"sscratch", GROUP_CSR + CSR_SSCRATCH, "org.gnu.gdb.riscv.csr", NULL}, + {"sepc", GROUP_CSR + CSR_SEPC, "org.gnu.gdb.riscv.csr", NULL}, + {"scause", GROUP_CSR + CSR_SCAUSE, "org.gnu.gdb.riscv.csr", NULL}, + {"stval", GROUP_CSR + CSR_STVAL, "org.gnu.gdb.riscv.csr", NULL}, + {"sip", GROUP_CSR + CSR_SIP, "org.gnu.gdb.riscv.csr", NULL}, + {"satp", GROUP_CSR + CSR_SATP, "org.gnu.gdb.riscv.csr", NULL}, + + {"mvendorid", GROUP_CSR + CSR_MVENDORID, "org.gnu.gdb.riscv.csr", NULL}, + {"marchid", GROUP_CSR + CSR_MARCHID, "org.gnu.gdb.riscv.csr", NULL}, + {"mimpid", GROUP_CSR + CSR_MIMPID, "org.gnu.gdb.riscv.csr", NULL}, + {"mhartid", GROUP_CSR + CSR_MHARTID, "org.gnu.gdb.riscv.csr", NULL}, + {"mstatus", GROUP_CSR + CSR_MSTATUS, "org.gnu.gdb.riscv.csr", NULL}, + {"misa", GROUP_CSR + CSR_MISA, "org.gnu.gdb.riscv.csr", NULL}, + {"medeleg", GROUP_CSR + CSR_MEDELEG, "org.gnu.gdb.riscv.csr", NULL}, + {"mideleg", GROUP_CSR + CSR_MIDELEG, "org.gnu.gdb.riscv.csr", NULL}, + {"mie", GROUP_CSR + CSR_MIE, "org.gnu.gdb.riscv.csr", NULL}, + {"mnmivec", GROUP_CSR + CSR_MNMIVEC, "org.gnu.gdb.riscv.csr", NULL}, + {"mtvec", GROUP_CSR + CSR_MTVEC, "org.gnu.gdb.riscv.csr", NULL}, + {"mcounteren", GROUP_CSR + CSR_MCOUNTEREN, "org.gnu.gdb.riscv.csr", NULL}, + {"mscratch", GROUP_CSR + CSR_MSCRATCH, "org.gnu.gdb.riscv.csr", NULL}, + {"mepc", GROUP_CSR + CSR_MEPC, "org.gnu.gdb.riscv.csr", NULL}, + {"mcause", GROUP_CSR + CSR_MCAUSE, "org.gnu.gdb.riscv.csr", NULL}, + {"mtval", GROUP_CSR + CSR_MTVAL, "org.gnu.gdb.riscv.csr", NULL}, + {"mip", GROUP_CSR + CSR_MIP, "org.gnu.gdb.riscv.csr", NULL}, + + {"pmpcfg0", GROUP_CSR + CSR_PMPCFG0, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpcfg1", GROUP_CSR + CSR_PMPCFG1, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpcfg2", GROUP_CSR + CSR_PMPCFG2, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpcfg3", GROUP_CSR + CSR_PMPCFG3, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr0", GROUP_CSR + CSR_PMPADDR0, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr1", GROUP_CSR + CSR_PMPADDR1, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr2", GROUP_CSR + CSR_PMPADDR2, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr3", GROUP_CSR + CSR_PMPADDR3, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr4", GROUP_CSR + CSR_PMPADDR4, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr5", GROUP_CSR + CSR_PMPADDR5, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr6", GROUP_CSR + CSR_PMPADDR6, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr7", GROUP_CSR + CSR_PMPADDR7, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr8", GROUP_CSR + CSR_PMPADDR8, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr9", GROUP_CSR + CSR_PMPADDR9, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr10", GROUP_CSR + CSR_PMPADDR10, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr11", GROUP_CSR + CSR_PMPADDR11, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr12", GROUP_CSR + CSR_PMPADDR12, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr13", GROUP_CSR + CSR_PMPADDR13, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr14", GROUP_CSR + CSR_PMPADDR14, "org.gnu.gdb.riscv.csr", NULL}, + {"pmpadr15", GROUP_CSR + CSR_PMPADDR15, "org.gnu.gdb.riscv.csr", NULL}, + + {"mcycle", GROUP_CSR + CSR_MCYCLE, "org.gnu.gdb.riscv.csr", NULL}, + {"minstret", GROUP_CSR + CSR_MINSTRET, "org.gnu.gdb.riscv.csr", NULL}, + {"mcycleh", GROUP_CSR + CSR_MCYCLEH, "org.gnu.gdb.riscv.csr", NULL}, + {"minstreth", GROUP_CSR + CSR_MINSTRETH, "org.gnu.gdb.riscv.csr", NULL}, + + + /* Debug Unit Internals */ + {"npc", GROUP_GPRS + 0x200, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbgctrl", GROUP_DBG + 0x00, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbghit", GROUP_DBG + 0x01, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbgie", GROUP_DBG + 0x02, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbgcause", GROUP_DBG + 0x03, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbg.bpctrl0", GROUP_DBG + 0x10, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbg.bpdata0", GROUP_DBG + 0x11, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbg.bpctrl1", GROUP_DBG + 0x12, "org.gnu.gbd.rv12.dbg", NULL}, + {"dbg.bpdata1", GROUP_DBG + 0x13, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbg.bpctrl2", GROUP_DBG + 0x14, "org.gnu.gdb.rv12.dbg", NULL}, + {"dbg.bpdata2", GROUP_DBG + 0x15, "org.gnu.gdb.rv12.dbg", NULL}, +}; + +static int rv12_add_reg(struct target *target, struct rv12_core_reg *new_reg) +{ + struct rv12_common *rv12 = target_to_rv12(target); + int reg_list_size = rv12->nb_regs * sizeof(struct rv12_core_reg); + + rv12_core_reg_list_arch_info = realloc(rv12_core_reg_list_arch_info, + reg_list_size + sizeof(struct rv12_core_reg)); + + memcpy(&rv12_core_reg_list_arch_info[rv12->nb_regs], new_reg, + sizeof(struct rv12_core_reg)); + + rv12_core_reg_list_arch_info[rv12->nb_regs].list_num = rv12->nb_regs; + + rv12->nb_regs++; + + return ERROR_OK; +} + +static int rv12_create_reg_list(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + + LOG_DEBUG("-"); + + rv12_core_reg_list_arch_info = malloc(ARRAY_SIZE(rv12_init_reg_list) * + sizeof(struct rv12_core_reg)); + + for (int i = 0; i < (int)ARRAY_SIZE(rv12_init_reg_list); i++) { + rv12_core_reg_list_arch_info[i].name = rv12_init_reg_list[i].name; + rv12_core_reg_list_arch_info[i].spr_num = rv12_init_reg_list[i].spr_num; + rv12_core_reg_list_arch_info[i].group = rv12_init_reg_list[i].group; + rv12_core_reg_list_arch_info[i].feature = rv12_init_reg_list[i].feature; + rv12_core_reg_list_arch_info[i].list_num = i; + rv12_core_reg_list_arch_info[i].target = NULL; + rv12_core_reg_list_arch_info[i].rv12_common = NULL; + } + + rv12->nb_regs = ARRAY_SIZE(rv12_init_reg_list); + + return ERROR_OK; +} + +static int rv12_jtag_read_regs(struct rv12_common *rv12, uint32_t *regs) +{ + struct rl_du *du_core = rl_jtag_to_du(&rv12->jtag); + + LOG_DEBUG("-"); + + return du_core->rl_jtag_read_cpu(&rv12->jtag, + rv12->arch_info[GDB_REGNO_ZERO].spr_num, GDB_REGNO_XPR31 + 1, + regs + GDB_REGNO_ZERO); +} + +static int rv12_jtag_write_regs(struct rv12_common *rv12, uint32_t *regs) +{ + struct rl_du *du_core = rl_jtag_to_du(&rv12->jtag); + + LOG_DEBUG("-"); + + return du_core->rl_jtag_write_cpu(&rv12->jtag, + rv12->arch_info[GDB_REGNO_ZERO].spr_num, GDB_REGNO_XPR31 + 1, + ®s[GDB_REGNO_ZERO]); +} + +static int rv12_save_context(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + int regs_read = 0; + int retval; + + LOG_DEBUG("-"); + + for (int i = 0; i < GDB_REGNO_FPR0; i++) + { + if (!rv12->core_cache->reg_list[i].valid) + { + // Read the PC for the PPC + if (i == GDB_REGNO_PC) + { + // Read the PPC register + retval = du_core->rl_jtag_read_cpu(&rv12->jtag, + (GROUP_GPRS + 0x201), 1, + &rv12->core_regs[i]); + + LOG_DEBUG("Read PC: Value: %08x\n", rv12->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 = rv12_jtag_read_regs(rv12, rv12->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 */ + rv12_read_core_reg(target, i); + } + } + + return ERROR_OK; +} + +static int rv12_restore_context(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + int reg_write = 0; + int retval; + + LOG_DEBUG("-"); + + for (int i = 0; i < GDB_REGNO_FPR0; i++) { + if (rv12->core_cache->reg_list[i].dirty) { + rv12_write_core_reg(target, i); + + if (i == GDB_REGNO_PC) { + retval = du_core->rl_jtag_write_cpu(&rv12->jtag, + // Write the PC to the NPC reg + (GROUP_GPRS + 0x200), 1, + &rv12->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 = rv12_jtag_write_regs(rv12, rv12->core_regs); + if (retval != ERROR_OK) { + LOG_ERROR("Error while restoring context"); + return retval; + } + } + + return ERROR_OK; +} + +static int rv12_read_core_reg(struct target *target, int num) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + uint32_t reg_value; + + LOG_DEBUG("-"); + + if ((num < 0) || (num >= rv12->nb_regs)) + return ERROR_COMMAND_SYNTAX_ERROR; + + if ((num >= 0) && (num < GDB_REGNO_COUNT)) { + reg_value = rv12->core_regs[num]; + buf_set_u32(rv12->core_cache->reg_list[num].value, 0, 32, reg_value); + LOG_DEBUG("Read core reg %i value 0x%08" PRIx32, num, reg_value); + rv12->core_cache->reg_list[num].valid = true; + rv12->core_cache->reg_list[num].dirty = false; + } else { + /* This is an spr, always read value from HW */ + int retval = du_core->rl_jtag_read_cpu(&rv12->jtag, + rv12->arch_info[num].spr_num, 1, ®_value); + if (retval != ERROR_OK) { + LOG_ERROR("Error while reading spr 0x%08" PRIx32, rv12->arch_info[num].spr_num); + return retval; + } + buf_set_u32(rv12->core_cache->reg_list[num].value, 0, 32, reg_value); + LOG_DEBUG("Read spr reg %i value 0x%08" PRIx32, num, reg_value); + } + + return ERROR_OK; +} + +static int rv12_write_core_reg(struct target *target, int num) +{ + struct rv12_common *rv12 = target_to_rv12(target); + + LOG_DEBUG("-"); + + if ((num < 0) || (num >= GDB_REGNO_COUNT)) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t reg_value = buf_get_u32(rv12->core_cache->reg_list[num].value, 0, 32); + rv12->core_regs[num] = reg_value; + LOG_DEBUG("Write core reg %i value 0x%08" PRIx32, num, reg_value); + rv12->core_cache->reg_list[num].valid = true; + rv12->core_cache->reg_list[num].dirty = false; + + return ERROR_OK; +} + +static int rv12_get_core_reg(struct reg *reg) +{ + struct rv12_core_reg *rv12_reg = reg->arch_info; + struct target *target = rv12_reg->target; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + // First get the register before returning the values + int retval = rv12_save_context(target); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_save_context"); + return retval; + } + + return rv12_read_core_reg(target, rv12_reg->list_num); +} + +static int rv12_set_core_reg(struct reg *reg, uint8_t *buf) +{ + struct rv12_core_reg *rv12_reg = reg->arch_info; + struct target *target = rv12_reg->target; + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + uint32_t value = buf_get_u32(buf, 0, 32); + + LOG_DEBUG("-"); + LOG_DEBUG("Set core reg: %u, value: %08x\n", rv12_reg->list_num, value); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + // If the register is lower then the Floating Point registers, write it to the cache + // At the moment a continue or a step is provoked, it shall be written to the CPU + if (rv12_reg->list_num < GDB_REGNO_FPR0) { + buf_set_u32(rv12->core_cache->reg_list[rv12_reg->list_num].value, 0, 32, value); + rv12->core_cache->reg_list[rv12_reg->list_num].dirty = true; + rv12->core_cache->reg_list[rv12_reg->list_num].valid = true; + } else { + /* Non cached register, so write to CPU immediately */ + int retval = du_core->rl_jtag_write_cpu(&rv12->jtag, rv12_reg->spr_num, 1, &value); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while writing spr 0x%08" PRIx32, rv12_reg->spr_num); + return retval; + } + } + + return ERROR_OK; +} + +static const struct reg_arch_type rv12_reg_type = { + .get = rv12_get_core_reg, + .set = rv12_set_core_reg, +}; + +static struct reg_cache *rv12_build_reg_cache(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache); + struct reg_cache *cache = malloc(sizeof(struct reg_cache)); + struct reg *reg_list = calloc(rv12->nb_regs, sizeof(struct reg)); + struct rv12_core_reg *arch_info = malloc((rv12->nb_regs) * sizeof(struct rv12_core_reg)); + struct reg_feature *feature; + + LOG_DEBUG("-"); + + /* Build the process context cache */ + cache->name = "Roa Logic RISC-V Registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = rv12->nb_regs; + (*cache_p) = cache; + rv12->core_cache = cache; + rv12->arch_info = arch_info; + + for (int i = 0; i < rv12->nb_regs; i++) { + arch_info[i] = rv12_core_reg_list_arch_info[i]; + arch_info[i].target = target; + arch_info[i].rv12_common = rv12; + reg_list[i].name = rv12_core_reg_list_arch_info[i].name; + + feature = malloc(sizeof(struct reg_feature)); + feature->name = rv12_core_reg_list_arch_info[i].feature; + reg_list[i].feature = feature; + + reg_list[i].group = rv12_core_reg_list_arch_info[i].group; + reg_list[i].size = 32; + reg_list[i].value = calloc(1, 4); + reg_list[i].dirty = false; + reg_list[i].valid = false; + reg_list[i].type = &rv12_reg_type; + reg_list[i].arch_info = &arch_info[i]; + reg_list[i].number = i; + reg_list[i].exist = true; + } + + return cache; +} + +static int rv12_debug_entry(struct target *target) +{ + LOG_DEBUG("-"); + uint32_t value = 0; + + int retval = rv12_save_context(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_save_context"); + return retval; + } + + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + uint32_t addr = rv12->core_regs[GDB_REGNO_PC]; + + //value |= DBG_IE_INST_MISALIGNED | DBG_IE_ILLEGAL | DBG_IE_BREAKPOINT | DBG_IE_LOAD_MISALIGNED | DBG_IE_AMO_MISALIGNED; + value |= DBG_IE_BREAKPOINT; + + // printf("Set DBG IE with value: %08x\n", value); + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_DBG + 0x02), 1, &value) != ERROR_OK) { + LOG_ERROR("Error: cannot set DBG IE reg\n"); + return ERROR_FAIL; + } + + if (breakpoint_find(target, addr)) { + buf_set_u32(rv12->core_cache->reg_list[GDB_REGNO_PC].value, 0, 32, addr); + rv12->core_cache->reg_list[GDB_REGNO_PC].dirty = true; + rv12->core_cache->reg_list[GDB_REGNO_PC].valid = true; + } + + return retval; +} + +static int rv12_halt(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + 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->rl_cpu_stall(&rv12->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 rl_is_cpu_running(struct target *target, int *running) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + 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->rl_is_cpu_running(&rv12->jtag, running); + if (retval != ERROR_OK) { + LOG_WARNING("Debug IF CPU control reg read failure."); + /* Try once to restart the JTAG infrastructure - + quite possibly the board has just been reset. */ + LOG_WARNING("Resetting JTAG TAP state and reconnecting to debug IF."); + du_core->rl_jtag_init(&rv12->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 rv12_poll(struct target *target) +{ + int retval; + int running; + + retval = rl_is_cpu_running(target, &running); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rl_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 = rv12_debug_entry(target); + if (retval != ERROR_OK) + { + LOG_ERROR("Error while calling rv12_debug_entry"); + return retval; + } + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } else if (target->state == TARGET_DEBUG_RUNNING) { + target->state = TARGET_HALTED; + + retval = rv12_debug_entry(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_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 = rv12_halt(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_halt"); + return retval; + } + + retval = rv12_debug_entry(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_debug_entry"); + return retval; + } + + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + } + + target->state = TARGET_RUNNING; + + } + + return ERROR_OK; +} + +static int rv12_assert_reset(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + LOG_DEBUG("Assert reset \n"); + + int retval = du_core->rl_cpu_reset(&rv12->jtag, CPU_RESET); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while asserting RESET"); + return retval; + } + + return ERROR_OK; +} + +static int rv12_deassert_reset(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + LOG_DEBUG("-"); + + int retval = du_core->rl_cpu_reset(&rv12->jtag, CPU_NOT_RESET); + if (retval != ERROR_OK) { + LOG_ERROR("Error while deasserting RESET"); + return retval; + } + + // Set the correpsponding debug flags again, since those are cleared + retval = rv12_debug_entry(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_debug_entry"); + return retval; + } + + return ERROR_OK; +} + +static int rv12_soft_reset_halt(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + LOG_DEBUG("Soft reset \n"); + + int retval = du_core->rl_cpu_stall(&rv12->jtag, CPU_STALL); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while stalling the CPU"); + return retval; + } + + retval = rv12_assert_reset(target); + if (retval != ERROR_OK) + return retval; + + retval = rv12_deassert_reset(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int rv12_resume_or_step(struct target *target, int current, + uint32_t address, int handle_breakpoints, + int debug_execution, int step) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + struct breakpoint *breakpoint = NULL; + uint32_t resume_pc = 0; + uint32_t tempReg = 0; + uint32_t tempMStatusReg = 0; + bool globalInterruptActive = false; + int retval; + + LOG_DEBUG("Addr: 0x%" PRIx32 ", stepping: %s, handle breakpoints %s\n", + address, step ? "yes" : "no", handle_breakpoints ? "yes" : "no"); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + // Free memory + if (!debug_execution) + target_free_all_working_areas(target); + + /* current ? continue on current pc : continue at <address> */ + if (!current) + buf_set_u32(rv12->core_cache->reg_list[GDB_REGNO_PC].value, 0, 32, address); + + retval = rv12_restore_context(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_restore_context"); + return retval; + } + + // Clear DBG HIT reg + tempReg = 0; + + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_DBG + 0x01), 1, &tempReg) != ERROR_OK) { + printf("Error: cannot clear HIT reg\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_DBG + 0x00), 1, &tempReg) != ERROR_OK) { + printf("Error: cannot read DBG ctrl reg\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_CSR + CSR_MSTATUS), 1, &tempMStatusReg) != ERROR_OK) { + printf("Error: cannot read MSTATUS reg\n"); + return ERROR_FAIL; + } + + if(tempMStatusReg & DBG_CSR_MSTASUS_MIE) { + globalInterruptActive = true; + } + + if (step) { + /* Set the single step trigger in Debug Mode Register 1 (DMR1) */ + tempReg |= DBG_CTRL_SINGLE_STEP_TRACE; + + if(globalInterruptActive) { + tempMStatusReg &= ~DBG_CSR_MSTASUS_MIE; + + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_CSR + CSR_MSTATUS), 1, &tempMStatusReg) != ERROR_OK) { + printf("Error: cannot read MSTATUS reg\n"); + return ERROR_FAIL; + } + } + } else { + /* Clear the single step trigger in Debug Mode Register 1 (DMR1) */ + tempReg &= ~DBG_CTRL_SINGLE_STEP_TRACE; + } + + + + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_DBG + 0x00), 1, &tempReg) != ERROR_OK) { + printf("Error: cannot write DBG ctrl reg\n"); + return ERROR_FAIL; + } + + resume_pc = buf_get_u32(rv12->core_cache->reg_list[GDB_REGNO_PC].value, 0, 32); + + /* The front-end may request us not to handle breakpoints */ + if (handle_breakpoints) { + /* Single step past breakpoint at current address */ + breakpoint = breakpoint_find(target, resume_pc); + if (breakpoint) { + LOG_DEBUG("Unset breakpoint at 0x%08" TARGET_PRIxADDR, breakpoint->address); + retval = rv12_remove_breakpoint(target, breakpoint); + if (retval != ERROR_OK) + return retval; + } + } + + /* Unstall time */ + retval = du_core->rl_cpu_stall(&rv12->jtag, CPU_UNSTALL); + if (retval != ERROR_OK) { + LOG_ERROR("Error while unstalling the CPU"); + return retval; + } + + if (step) { + target->debug_reason = DBG_REASON_SINGLESTEP; + + if(globalInterruptActive) { + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_CSR + CSR_MSTATUS), 1, &tempMStatusReg) != ERROR_OK) { + printf("Error: cannot read MSTATUS reg\n"); + return ERROR_FAIL; + } + tempMStatusReg |= DBG_CSR_MSTASUS_MIE; + + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_CSR + CSR_MSTATUS), 1, &tempMStatusReg) != ERROR_OK) { + printf("Error: cannot read MSTATUS reg\n"); + return ERROR_FAIL; + } + } + } else + target->debug_reason = DBG_REASON_NOTHALTED; + + // Invalidate current registers in local cache + // Nothing to do with CPU cache + register_cache_invalidate(rv12->core_cache); + + if (!debug_execution) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + LOG_DEBUG("Target resumed at 0x%08" PRIx32, resume_pc); + } else { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + LOG_DEBUG("Target debug resumed at 0x%08" PRIx32, resume_pc); + } + + return ERROR_OK; +} + +static int rv12_resume(struct target *target, int current, + target_addr_t address, int handle_breakpoints, + int debug_execution) +{ + return rv12_resume_or_step(target, current, address, + handle_breakpoints, + debug_execution, + NO_SINGLE_STEP); +} + +static int rv12_step(struct target *target, int current, + target_addr_t address, int handle_breakpoints) +{ + return rv12_resume_or_step(target, current, address, + handle_breakpoints, + 0, + SINGLE_STEP); + +} + +static int rv12_add_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + uint32_t data; + int retval; + + LOG_DEBUG("Adding breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, set: %d, id: %" PRIu32, + breakpoint->address, breakpoint->length, breakpoint->type, + breakpoint->is_set, breakpoint->unique_id); + + /* Only support SW breakpoints for now. */ + if(breakpoint->type != BKPT_SOFT) { + LOG_ERROR("HW breakpoints not supported for now\n"); + return ERROR_FAIL; + } + + if(breakpoint->length != 2 && breakpoint->length != 4) { + LOG_ERROR("Breakpoint unsupported length\n"); + return ERROR_FAIL; + } + + if(breakpoint->length == 2) { + /* Read and save the instruction */ + retval = du_core->rl_jtag_read_memory(&rv12->jtag, + breakpoint->address, + 2, + 1, + (uint8_t*)&data); + } else { + /* Read and save the instruction */ + retval = du_core->rl_jtag_read_memory(&rv12->jtag, + breakpoint->address, + 4, + 1, + (uint8_t*)&data); + } + + if (retval != ERROR_OK) { + LOG_ERROR("Error while reading the instruction at 0x%08" TARGET_PRIxADDR, + breakpoint->address); + return retval; + } + + free(breakpoint->orig_instr); + + breakpoint->orig_instr = malloc(breakpoint->length); + memcpy(breakpoint->orig_instr, &data, breakpoint->length); + + if(breakpoint->length == 2) { + uint16_t rv12_trap_insn16 = RV_EBREAK16_INSTR; + //target_buffer_set_u32(target, rv12_trap_insn, RV_EBREAK16_INSTR); + retval = du_core->rl_jtag_write_memory(&rv12->jtag, + breakpoint->address, + 2, + 1, + (uint8_t*)&rv12_trap_insn16); + } else { + uint32_t rv12_trap_insn = RV_EBREAK_INSTR; + //target_buffer_set_u32(target, rv12_trap_insn, RV_EBREAK_INSTR); + retval = du_core->rl_jtag_write_memory(&rv12->jtag, + breakpoint->address, + 4, + 1, + (uint8_t*)&rv12_trap_insn); + } + + return ERROR_OK; +} + +static int rv12_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + int retval; + + LOG_DEBUG("Removing breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, set: %d, id: %" PRIu32, + breakpoint->address, breakpoint->length, breakpoint->type, + breakpoint->is_set, breakpoint->unique_id); + + /* Only support SW breakpoints for now. */ + if(breakpoint->type != BKPT_SOFT) { + LOG_ERROR("HW breakpoints not supported for now\n"); + return ERROR_FAIL; + } + + if(breakpoint->length != 2 && breakpoint->length != 4) { + LOG_ERROR("Breakpoint unsupported length\n"); + return ERROR_FAIL; + } + + if(breakpoint->length == 2) { + /* Replace the removed instruction */ + retval = du_core->rl_jtag_write_memory(&rv12->jtag, + breakpoint->address, + 2, + 1, + breakpoint->orig_instr); + } else { + /* Replace the removed instruction */ + retval = du_core->rl_jtag_write_memory(&rv12->jtag, + breakpoint->address, + 4, + 1, + breakpoint->orig_instr); + } + + + if (retval != ERROR_OK) { + LOG_ERROR("Error while writing back the instruction at 0x%08" TARGET_PRIxADDR, + breakpoint->address); + return retval; + } + + return ERROR_OK; +} + +static int rv12_add_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + LOG_ERROR("%s: implement me", __func__); + return ERROR_OK; +} + +static int rv12_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + LOG_ERROR("%s: implement me", __func__); + return ERROR_OK; +} + +static int rv12_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + LOG_DEBUG("Read memory at 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: 0x%08" PRIx32, address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + + //TODO: 64bit accesses (for 64bit CPU) + /* 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("Unaligned memory access not supported"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + return du_core->rl_jtag_read_memory(&rv12->jtag, address, size, count, buffer); +} + +static int rv12_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + LOG_DEBUG("Write memory at 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: 0x%08" PRIx32, address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_WARNING("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + + //TODO: 64bit accesses for 64bit CPU + /* 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("Unaligned memory access not supported"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + return du_core->rl_jtag_write_memory(&rv12->jtag, address, size, count, buffer); +} + +static int rv12_init_target(struct command_context *cmd_ctx, + struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + struct rl_jtag *jtag = &rv12->jtag; + + if (!du_core) { + LOG_ERROR("No debug unit selected"); + return ERROR_FAIL; + } + + if (!jtag->tap_ip) { + LOG_ERROR("No tap selected"); + return ERROR_FAIL; + } + + rv12->jtag.tap = target->tap; + rv12->jtag.rl_jtag_inited = 0; + rv12->jtag.rl_jtag_module_selected = -1; + rv12->jtag.target = target; + + rv12_build_reg_cache(target); + + return ERROR_OK; +} + +static int rv12_target_create(struct target *target, Jim_Interp *interp) +{ + if (!target->tap) + return ERROR_FAIL; + + struct rv12_common *rv12 = calloc(1, sizeof(struct rv12_common)); + + target->arch_info = rv12; + + rv12_create_reg_list(target); + + rl_universal_tap_register(); + + rl_dbg_adv_register(); + + return ERROR_OK; +} + +static int rv12_examine(struct target *target) +{ + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + + if (!target_was_examined(target)) { + + target_set_examined(target); + + int running; + + int retval = du_core->rl_is_cpu_running(&rv12->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 rv12_arch_state(struct target *target) +{ + return ERROR_OK; +} + +static int rv12_get_gdb_reg_list(struct target *target, struct reg **reg_list[], + int *reg_list_size, enum target_register_class reg_class) +{ + struct rv12_common *rv12 = target_to_rv12(target); + + if (reg_class == REG_CLASS_GENERAL) { + /* We will have this called whenever GDB connects. */ + int retval = rv12_save_context(target); + if (retval != ERROR_OK) { + LOG_ERROR("Error while calling rv12_save_context"); + return retval; + } + + //TODO Load FPR when the CPU has a FPU + *reg_list_size = GDB_REGNO_FPR0; + /* 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 < GDB_REGNO_FPR0; i++) + (*reg_list)[i] = &rv12->core_cache->reg_list[i]; + } else { + *reg_list_size = rv12->nb_regs; + *reg_list = malloc((*reg_list_size) * sizeof(struct reg *)); + + for (int i = 0; i < rv12->nb_regs; i++) + (*reg_list)[i] = &rv12->core_cache->reg_list[i]; + } + + return ERROR_OK; + +} + +static int rv12_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info) +{ + return ERROR_FAIL; +} + +static int rv12_checksum_memory(struct target *target, target_addr_t address, + uint32_t count, uint32_t *checksum) +{ + return ERROR_FAIL; +} + +static int rv12_profiling(struct target *target, uint32_t *samples, + uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) +{ + struct timeval timeout, now; + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_to_du(rv12); + int retval = ERROR_OK; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, seconds, 0); + + LOG_INFO("Starting rv12 profiling. Sampling npc as fast as we can..."); + + /* Make sure the target is running */ + target_poll(target); + if (target->state == TARGET_HALTED) + retval = target_resume(target, 1, 0, 0, 0); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while resuming target"); + return retval; + } + + uint32_t sample_count = 0; + + for (;;) { + uint32_t reg_value; + retval = du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x201) /* PPC */, 1, ®_value); + if (retval != ERROR_OK) { + LOG_ERROR("Error while reading NPC"); + return retval; + } + + samples[sample_count++] = reg_value; + + gettimeofday(&now, NULL); + if ((sample_count >= max_num_samples) || timeval_compare(&now, &timeout) > 0) { + LOG_INFO("Profiling completed. %" PRIu32 " samples.", sample_count); + break; + } + } + + *num_samples = sample_count; + return retval; +} + +static int rv12_soc_test_sram(struct target *target) +{ + uint32_t baseAddress = 0x00010000; + uint32_t ins; + uint32_t insn[9]; + uint32_t address; + + insn[0] = 0x11112222; + insn[1] = 0x33334444; + insn[2] = 0x55556666; + insn[3] = 0x77778888; + insn[4] = 0x9999aaaa; + insn[5] = 0xbbbbcccc; + insn[6] = 0xddddeeee; + insn[7] = 0xffff0000; + insn[8] = 0xdedababa; + + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + return ERROR_FAIL; + } + + for(int i = 0; i < 9; i++) + { + address = baseAddress + (i * 4); + + printf("Write address: %08x, with: %08x\n",address, insn[i]); + if(rv12_write_memory(target, address, 4, 1, (uint8_t*)&insn[i]) !=ERROR_OK) + { + printf("Error: Cannot write memory\n"); + return ERROR_FAIL; + } + } + + for(int i = 0; i < 9; i++) + { + address = baseAddress + (i * 4); + + printf("Read address: %08x\n",address); + if(rv12_read_memory(target, address, 4, 1, (uint8_t*)&ins) == ERROR_OK) + { + if(ins != insn[i]) + { + printf("Error: Expected: %08x, received: %08x\n", insn[i], ins); + return ERROR_FAIL; + } + } + else + { + printf("Error: Reading memory\n"); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int rv12_soc_test_cpu(struct target *target) +{ + uint32_t baseAddress = 0x00010000; + uint32_t insn[11]; + uint32_t illIns = 0x00000000; + uint32_t address; + uint32_t r1, npc, ppc; + int state; + //uint32_t insRead; + + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_du *du_core = rl_jtag_to_du(&rv12->jtag); + + printf("\n-- Testing rv12-SoC CPU\n"); + printf("writing instructions\n"); + + insn[0] = 0x00004033; /* xor x0,x0,x0 */ + insn[1] = 0x00000093; /* addi x1,x0,0x0 */ + insn[2] = 0x00010137; /* lui x2,0x00010 (RAM_BASE) */ + insn[3] = 0x03016113; /* ori x2,x2,0x30 */ + insn[4] = 0x00108093; /* addi x1,x1,1 */ + insn[5] = 0x00108093; /* addi x1,x1,1 */ + insn[6] = 0x00112023; /* sw 0(x2),x1 */ + insn[7] = 0x00108093; /* addi x1,x1,1 */ + insn[8] = 0x00012183; /* lw x3,0(x2) */ + insn[9] = 0x003080b3; /* add x1,x1,x3 */ + insn[10] = 0xfe9ff06f; /* j (base+0x10) */ + + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + } + + + for(int i = 0; i < 11; i++) + { + address = baseAddress + (i * 4); + if(rv12_write_memory(target, address, 4, 1, (uint8_t*)&insn[i]) !=ERROR_OK) + { + printf("Error: Cannot write memory\n"); + return ERROR_FAIL; + } + } + + //Fill rest of memory with 0x00 (C.ILLEGAL). Avoid CPU going nuts on 0xxxxxx + for (uint32_t i = baseAddress+0x2c; i < baseAddress+0x4c; i=i+4) + { + if(rv12_write_memory(target, i, 4, 1, (uint8_t*)&illIns) !=ERROR_OK) + { + printf("Error: Cannot write memory\n"); + return ERROR_FAIL; + } + } + + printf("Setting up CPU\n"); + + /* + * Test 1 + */ + printf("- Test 1, save context\n"); + + if(rv12_save_context(target) == ERROR_OK) + { + printf("Success: Saved context\n"); + for (int i = 0; i < GDB_REGNO_FPR0; i++) + { + printf("reg: %s, value: %08x\n",rv12_init_reg_list[i].name, rv12->core_regs[i]); + } + } + else + { + printf("Error: cannot save context\n"); + return ERROR_FAIL; + } + + /* + * Test 2 + */ + printf("- Test 2, Single stepping\n"); + + printf("Write NPC: %08x\n", baseAddress); + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_GPRS + 0x200), 1, &baseAddress) != ERROR_OK) + { + printf("Error: cannot set NPC\n"); + return ERROR_FAIL; + } + + for(int i = 0; i < 11; i++) + { + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + return ERROR_FAIL; + } + + printf("Starting CPU, waiting for breakpoint... \n"); + + if(rv12_step(target, 1, 0x00, 0) != ERROR_OK) + { + printf("Error: stepping\n"); + return ERROR_FAIL; + } + + do + { + if (rl_is_cpu_running(target, &state) != ERROR_OK) { + LOG_ERROR("Error while calling rl_is_cpu_running"); + return ERROR_FAIL; + } + + } while(!state); + + printf("Got breakpoint \n"); + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x200), 1, &npc) != ERROR_OK) + { + printf("Error: cannot read NPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x201), 1, &ppc) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_RF + 1), 1, &r1) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + printf("Read: npc: %08x, ppc: %08x, r1: %08x\n", npc, ppc, r1); + printf("Expected: npc: %08x, ppc: %08x, r1: %08x\n", 0x00010010, 0x00010028, 5); + + if(npc != 0x00010010 || ppc != 0x00010028 || r1 != 5) + { + return ERROR_FAIL; + } + + printf("Passed test 2\n"); + + /* + * Test 3 + */ + printf("- Test 3, Breakpoint hit\n"); + + struct breakpoint testBreakPoint = {0}; + + testBreakPoint.type = BKPT_SOFT; + testBreakPoint.address = (baseAddress + 0x24); + testBreakPoint.length = 4; + + printf("Add breakpoint\n"); + rv12_add_breakpoint(target, &testBreakPoint); + + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + return ERROR_FAIL; + } + + if(rv12_read_memory(target, (baseAddress + 0x24), 4, 1, (uint8_t*)&r1) !=ERROR_OK) + { + printf("Cannot read back memory!\n"); + return ERROR_FAIL; + } + + if(r1 != RV_EBREAK_INSTR) + { + printf("SW breakpoint not set!\n"); + return ERROR_FAIL; + } + + printf("Resuming target\n"); + rv12_resume(target,1, 0, 0, 0); + + do + { + if (rl_is_cpu_running(target, &state) != ERROR_OK) { + LOG_ERROR("Error while calling rl_is_cpu_running"); + return ERROR_FAIL; + } + + } while(!state); + + printf("Remove breakpoint\n"); + rv12_remove_breakpoint(target, &testBreakPoint); + + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + return ERROR_FAIL; + } + + if(rv12_read_memory(target, (baseAddress + 0x24), 4, 1, (uint8_t*)&r1) != ERROR_OK) + { + printf("Cannot read back memory!\n"); + return ERROR_FAIL; + } + + if(r1 != insn[9]) + { + printf("Breakpoing not removed: Expected: %08x, Received: %08x\n", insn[9], r1); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x200), 1, &npc) != ERROR_OK) + { + printf("Error: cannot read NPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x201), 1, &ppc) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_RF + 1), 1, &r1) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + + printf("Read: npc: %08x, ppc: %08x, r1: %08x\n", npc, ppc, r1); + printf("Expected: npc: %08x, ppc: %08x, r1: %08x\n", 0x00010028, 0x00010024, 8); + + if(ppc != 0x00010024 || r1 != 8) + { + return ERROR_FAIL; + } + + + printf("Passed test 3\n"); + + printf("- Test 4, Breakpoint hit2\n"); + + testBreakPoint.type = BKPT_SOFT; + testBreakPoint.address = (baseAddress + 0x28); + testBreakPoint.length = 4; + + printf("Add breakpoint\n"); + rv12_add_breakpoint(target, &testBreakPoint); + + if(rv12_poll(target) != ERROR_OK) + { + printf("Error while polling target\n"); + return ERROR_FAIL; + } + + ppc = (baseAddress + 0x10); + printf("Write NPC: %08x\n", ppc); + if(du_core->rl_jtag_write_cpu(&rv12->jtag, (GROUP_GPRS + 0x200), 1, &ppc) != ERROR_OK) + { + printf("Error: cannot set NPC\n"); + return ERROR_FAIL; + } + + printf("Resuming target\n"); + rv12_resume(target,1, 0, 0, 0); + + do + { + if (rl_is_cpu_running(target, &state) != ERROR_OK) { + LOG_ERROR("Error while calling rl_is_cpu_running"); + return ERROR_FAIL; + } + + } while(!state); + + printf("Remove breakpoint\n"); + rv12_remove_breakpoint(target, &testBreakPoint); + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x200), 1, &npc) != ERROR_OK) + { + printf("Error: cannot read NPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_GPRS + 0x201), 1, &ppc) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + + if(du_core->rl_jtag_read_cpu(&rv12->jtag, (GROUP_RF + 1), 1, &r1) != ERROR_OK) + { + printf("Error: cannot read PPC\n"); + return ERROR_FAIL; + } + + printf("Read: npc: %08x, ppc: %08x, r1: %08x\n", npc, ppc, r1); + printf("Expected: npc: %08x, ppc: %08x, r1: %08x\n", 0x0001002C, 0x00010028, 21); + + if(ppc != 0x00010028 || r1 != 21) + { + return ERROR_FAIL; + } + + + printf("Passed test 4\n"); + + return ERROR_OK; +} + +COMMAND_HANDLER(rl_tap_select_command_handler) +{ + struct target *target = get_current_target(CMD_CTX); + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_jtag *jtag = &rv12->jtag; + struct rl_tap_ip *rl_tap; + + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(rl_tap, &rl_tap_list, list) { + if (rl_tap->name) { + if (!strcmp(CMD_ARGV[0], rl_tap->name)) { + jtag->tap_ip = rl_tap; + LOG_INFO("%s tap selected", rl_tap->name); + return ERROR_OK; + } + } + } + + LOG_ERROR("%s unknown, no tap selected", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(rl_tap_list_command_handler) +{ + struct rl_tap_ip *rl_tap; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(rl_tap, &rl_tap_list, list) { + if (rl_tap->name) + command_print(CMD, "%s", rl_tap->name); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(rl_du_select_command_handler) +{ + struct target *target = get_current_target(CMD_CTX); + struct rv12_common *rv12 = target_to_rv12(target); + struct rl_jtag *jtag = &rv12->jtag; + struct rl_du *rl_du; + + if (CMD_ARGC > 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(rl_du, &rl_du_list, list) { + if (rl_du->name) { + if (!strcmp(CMD_ARGV[0], rl_du->name)) { + jtag->du_core = rl_du; + LOG_INFO("%s debug unit selected", rl_du->name); + + if (CMD_ARGC == 2) { + int options; + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], options); + rl_du->options = options; + LOG_INFO("Option %x is passed to %s debug unit" + , options, rl_du->name); + } + + return ERROR_OK; + } + } + } + + LOG_ERROR("%s unknown, no debug unit selected", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(rl_du_list_command_handler) +{ + struct rl_du *rl_du; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(rl_du, &rl_du_list, list) { + if (rl_du->name) + command_print(CMD, "%s", rl_du->name); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(rv12_addreg_command_handler) +{ + struct target *target = get_current_target(CMD_CTX); + struct rv12_core_reg new_reg; + + if (CMD_ARGC != 4) + return ERROR_COMMAND_SYNTAX_ERROR; + + new_reg.target = NULL; + new_reg.rv12_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]); + + rv12_add_reg(target, &new_reg); + + LOG_DEBUG("Add reg \"%s\" @ 0x%08" PRIx32 ", group \"%s\", feature \"%s\"", + new_reg.name, addr, new_reg.group, new_reg.feature); + + return ERROR_OK; +} + +COMMAND_HANDLER(rv12_test_handler) +{ + struct target *target = get_current_target(CMD_CTX); + + if (CMD_ARGC == 0 || CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + int options; + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], options); + + printf("rv12 Test\n"); + + printf("Testing options: %u\n", options); + + printf("\n rv12-SoC Stalling CPU\n"); + + if(rv12_halt(target) == ERROR_OK) + { + printf("Success: CPU(s) stalled\n"); + } + else + { + printf("Error: CPU(s) not stalled!\n"); + return ERROR_FAIL; + } + + if((options & 0x00000001) > 0) + { + printf("\n rv12 SoC on-chip SRAM test: \n"); + + if(rv12_soc_test_sram(target) == ERROR_OK) + { + printf("Success: sram test success\n"); + } + else + { + printf("Error: sram test failed!\n"); + return ERROR_FAIL; + } + } + else + { + printf("\n rv12 SoC SRAM test disabled\n"); + } + + if((options & 0x00000002) > 0) + { + if(rv12_soc_test_cpu(target) == ERROR_OK) + { + printf("Success: CPU test Passed\n"); + } + else + { + printf("Error: CPU test failed!\n"); + return ERROR_FAIL; + } + } + + + printf("Succesfully passed given tests\n"); + + return ERROR_OK; +} + +static const struct command_registration rv12_hw_ip_command_handlers[] = { + { + .name = "rl_tap_select", + .handler = rl_tap_select_command_handler, + .mode = COMMAND_ANY, + .usage = "name", + .help = "Select the TAP core to use", + }, + { + .name = "rl_tap_list", + .handler = rl_tap_list_command_handler, + .mode = COMMAND_ANY, + .usage = "", + .help = "Display available TAP core", + }, + { + .name = "rl_du_select", + .handler = rl_du_select_command_handler, + .mode = COMMAND_ANY, + .usage = "name", + .help = "Select the Debug Unit core to use", + }, + { + .name = "rl_du_list", + .handler = rl_du_list_command_handler, + .mode = COMMAND_ANY, + .usage = "select_tap name", + .help = "Display available Debug Unit core", + }, + { + .name = "rv12_test", + .handler = rv12_test_handler, + .mode = COMMAND_ANY, + .usage = "Test the rv12 SoC", + .help = "Test the rv12 SoC implementation", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rv12_reg_command_handlers[] = { + { + .name = "addreg", + .handler = rv12_addreg_command_handler, + .mode = COMMAND_ANY, + .usage = "name addr feature group", + .help = "Add a register to the register list", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rv12_command_handlers[] = { + { + .chain = rv12_reg_command_handlers, + }, + { + .chain = rv12_hw_ip_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + + +struct target_type rv12_target = { + .name = "rv12", + + .poll = rv12_poll, + .arch_state = rv12_arch_state, + + .target_request_data = NULL, + + .halt = rv12_halt, + .resume = rv12_resume, + .step = rv12_step, + + .assert_reset = rv12_assert_reset, + .deassert_reset = rv12_deassert_reset, + .soft_reset_halt = rv12_soft_reset_halt, + + .get_gdb_reg_list = rv12_get_gdb_reg_list, + + .read_memory = rv12_read_memory, + .write_memory = rv12_write_memory, + .checksum_memory = rv12_checksum_memory, + + .commands = rv12_command_handlers, + .add_breakpoint = rv12_add_breakpoint, + .remove_breakpoint = rv12_remove_breakpoint, + .add_watchpoint = rv12_add_watchpoint, + .remove_watchpoint = rv12_remove_watchpoint, + + .target_create = rv12_target_create, + .init_target = rv12_init_target, + .examine = rv12_examine, + + .get_gdb_fileio_info = rv12_get_gdb_fileio_info, + + .profiling = rv12_profiling, +}; diff --git a/src/target/roalogic/rv12.h b/src/target/roalogic/rv12.h new file mode 100644 index 0000000000..80c441b06c --- /dev/null +++ b/src/target/roalogic/rv12.h @@ -0,0 +1,266 @@ +/*************************************************************************** + * Copyright (C) 2022 by Richard Herveille * + * richard.hervei...@roalogic.com * + * * + * Copyright (C) 2022 by Bjorn Schouteten * + * bjorn.schoute...@roalogic.com * + * * + * Based on the OR1K version * + * * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ROALOGIC_rv12_H +#define OPENOCD_TARGET_ROALOGIC_rv12_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <target/target.h> +#include <target/riscv/encoding.h> + +/* Debug Register Access Groups Start Addresses + * INTERNAL = Debug Unit internal registers + * GPRS = INR_RF, FP_RF, NPC, PPC + * CSRS = RISC-V State CSRs + * */ +#define GROUP_DBG (0 << 12) +#define GROUP_GPRS (1 << 12) +#define GROUP_CSR (2 << 12) + +#define CSR_USTATUS 0x0 +#define CSR_UIE 0x4 +#define CSR_UTVEC 0x5 +#define CSR_USCRATCH 0x40 +#define CSR_UEPC 0x41 +#define CSR_UCAUSE 0x42 +#define CSR_UTVAL 0x43 +#define CSR_UIP 0x44 +#define CSR_MNMIVEC 0x70C + +/* Integer Register File + * Floating Point Register File + * NPC + * PPC + */ +#define GROUP_RF (GROUP_GPRS + 0) +#define GROUP_FPRF (GROUP_GPRS + (1 << 8)) + + +/* rv12 registers */ +/* gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in + * its source tree. We must interpret the numbers the same here. */ +enum gdb_regno { + GDB_REGNO_ZERO = 0, /* Read-only register, always 0. */ + GDB_REGNO_RA = 1, /* Return Address. */ + GDB_REGNO_SP = 2, /* Stack Pointer. */ + GDB_REGNO_GP = 3, /* Global Pointer. */ + GDB_REGNO_TP = 4, /* Thread Pointer. */ + GDB_REGNO_T0, + GDB_REGNO_T1, + GDB_REGNO_T2, + GDB_REGNO_S0 = 8, + GDB_REGNO_FP = 8, /* Frame Pointer. */ + GDB_REGNO_S1, + GDB_REGNO_A0 = 10, /* First argument. */ + GDB_REGNO_A1 = 11, /* Second argument. */ + GDB_REGNO_A2, + GDB_REGNO_A3, + GDB_REGNO_A4, + GDB_REGNO_A5, + GDB_REGNO_XPR15 = GDB_REGNO_A5, + GDB_REGNO_A6, + GDB_REGNO_A7, + GDB_REGNO_S2, + GDB_REGNO_S3, + GDB_REGNO_S4, + GDB_REGNO_S5, + GDB_REGNO_S6, + GDB_REGNO_S7, + GDB_REGNO_S8, + GDB_REGNO_S9, + GDB_REGNO_S10, + GDB_REGNO_S11, + GDB_REGNO_T3, + GDB_REGNO_T4, + GDB_REGNO_T5, + GDB_REGNO_T6, + GDB_REGNO_XPR31 = GDB_REGNO_T6, + + GDB_REGNO_PC = 32, + GDB_REGNO_FPR0 = 33, + GDB_REGNO_FT0 = GDB_REGNO_FPR0, + GDB_REGNO_FT1, + GDB_REGNO_FT2, + GDB_REGNO_FT3, + GDB_REGNO_FT4, + GDB_REGNO_FT5, + GDB_REGNO_FT6, + GDB_REGNO_FT7, + GDB_REGNO_FS0, + GDB_REGNO_FS1, + GDB_REGNO_FA0, + GDB_REGNO_FA1, + GDB_REGNO_FA2, + GDB_REGNO_FA3, + GDB_REGNO_FA4, + GDB_REGNO_FA5, + GDB_REGNO_FA6, + GDB_REGNO_FA7, + GDB_REGNO_FS2, + GDB_REGNO_FS3, + GDB_REGNO_FS4, + GDB_REGNO_FS5, + GDB_REGNO_FS6, + GDB_REGNO_FS7, + GDB_REGNO_FS8, + GDB_REGNO_FS9, + GDB_REGNO_FS10, + GDB_REGNO_FS11, + GDB_REGNO_FT8, + GDB_REGNO_FT9, + GDB_REGNO_FT10, + GDB_REGNO_FT11, + GDB_REGNO_FPR31 = GDB_REGNO_FT11, + GDB_REGNO_CSR0 = 65, + GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0, + GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0, + GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0, + GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0, + GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0, + GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0, + GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0, + GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0, + GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0, + GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0, + GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0, + GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0, + GDB_REGNO_DSCRATCH0 = CSR_DSCRATCH0 + GDB_REGNO_CSR0, + GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0, + GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0, + GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0, + GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0, + GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095, + GDB_REGNO_PRIV = 4161, + /* It's still undecided what register numbers GDB will actually use for + * these. See + * https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/7lQYiTUN9Ms/gTxGhzaYBQAJ + */ + GDB_REGNO_V0, GDB_REGNO_V1, GDB_REGNO_V2, GDB_REGNO_V3, + GDB_REGNO_V4, GDB_REGNO_V5, GDB_REGNO_V6, GDB_REGNO_V7, + GDB_REGNO_V8, GDB_REGNO_V9, GDB_REGNO_V10, GDB_REGNO_V11, + GDB_REGNO_V12, GDB_REGNO_V13, GDB_REGNO_V14, GDB_REGNO_V15, + GDB_REGNO_V16, GDB_REGNO_V17, GDB_REGNO_V18, GDB_REGNO_V19, + GDB_REGNO_V20, GDB_REGNO_V21, GDB_REGNO_V22, GDB_REGNO_V23, + GDB_REGNO_V24, GDB_REGNO_V25, GDB_REGNO_V26, GDB_REGNO_V27, + GDB_REGNO_V28, GDB_REGNO_V29, GDB_REGNO_V30, GDB_REGNO_V31, + GDB_REGNO_COUNT +}; + +const char *gdb_regno_name(enum gdb_regno regno); + + +struct rl_jtag { + struct jtag_tap *tap; + int rl_jtag_inited; + int rl_jtag_module_selected; + int rl_jtag_cpu_selected; + int rl_jtag_address_size; + uint8_t *current_reg_idx; + struct rl_tap_ip *tap_ip; + struct rl_du *du_core; + struct target *target; +}; + + +struct rv12_common { + struct rl_jtag jtag; + struct reg_cache *core_cache; + uint32_t core_regs[GDB_REGNO_COUNT]; + int nb_regs; + struct rv12_core_reg *arch_info; +}; + +static inline struct rv12_common * +target_to_rv12(struct target *target) +{ + return (struct rv12_common *)target->arch_info; +} + +struct rv12_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 rv12_common *rv12_common; + const char *feature; /* feature name in XML tdesc file */ + const char *group; /* register group in XML tdesc file */ +}; + +struct rv12_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 */ +}; + +/* RISC-V EBREAK instruction */ +#define RV_EBREAK_INSTR 0x00100073 +#define RV_EBREAK16_INSTR 0x9002 + +enum rv12_debug_reg_nums { + rv12_DEBUG_REG_CTRL = 0, + rv12_DEBUG_REG_HIT, + rv12_DEBUG_REG_IE, + rv12_DEBUG_REG_CAUSE, + rv12_DEBUG_REG_BPCTRL0 = 0x10, + rv12_DEBUG_REG_BPDATA0, + rv12_DEBUG_REG_BPCTRL1, + rv12_DEBUG_REG_BPDATA1, + rv12_DEBUG_REG_BPCTRL2, + rv12_DEBUG_REG_BPDATA2 +}; + +#define rv12_JTAG_DBG_REGS 10 + +#define NO_SINGLE_STEP 0 +#define SINGLE_STEP 1 + + +// DBG CTRL +//Bit definitions +#define DBG_CTRL_SINGLE_STEP_TRACE 0x01 /* Enable single-step trace */ +#define DBG_CTRL_BRANCH_TRACE 0x02 /* Enable branch-trace */ + +#define DBG_CSR_MSTASUS_MIE 0x08 + +#define DBG_IE_INST_MISALIGNED 0x00001 +#define DBG_IE_INST_ACCESS_FAULT 0x00002 +#define DBG_IE_ILLEGAL 0x00004 +#define DBG_IE_BREAKPOINT 0x00008 +#define DBG_IE_LOAD_MISALIGNED 0x00010 +#define DBG_IE_LOAD_ACCESS_FAULT 0x00020 +#define DBG_IE_AMO_MISALIGNED 0x00040 +#define DBG_IE_STORE_ACCESS_FAULT 0x00080 +#define DBG_IE_UMODE_ECALL 0x00100 +#define DBG_IE_SMODE_ECALL 0x00200 +#define DBG_IE_HMODE_ECALL 0x00400 +#define DBG_IE_MMODE_ECALL 0x00800 +#define DBG_IE_SOFTWARE_INT 0x10000 +#define DBG_IE_TIMER_INT 0x20000 +#define DBG_IE_UART 0x40000 + +#endif /* OPENOCD_TARGET_ROALOGIC_rv12_H */ diff --git a/src/target/target.c b/src/target/target.c index 9b07dbf618..e9ed5b17fc 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -105,6 +105,7 @@ extern struct target_type stm8_target; extern struct target_type riscv_target; extern struct target_type mem_ap_target; extern struct target_type esirisc_target; +extern struct target_type rv12_target; extern struct target_type arcv2_target; static struct target_type *target_types[] = { @@ -147,6 +148,7 @@ static struct target_type *target_types[] = { &esirisc_target, &arcv2_target, &aarch64_target, + &rv12_target, &mips_mips64_target, NULL, }; --