aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------jimtcl0
-rw-r--r--src/flash/nor/Makefile.am1
-rw-r--r--src/flash/nor/drivers.c2
-rw-r--r--src/flash/nor/fespi.c1158
-rw-r--r--src/flash/nor/spi.c1
-rw-r--r--src/helper/log.c2
-rw-r--r--src/jtag/core.c118
-rw-r--r--src/jtag/drivers/ftdi.c35
-rw-r--r--src/jtag/drivers/jtag_vpi.c2
-rw-r--r--src/jtag/drivers/remote_bitbang.c2
-rw-r--r--src/rtos/Makefile.am4
-rw-r--r--src/rtos/riscv_debug.c316
-rw-r--r--src/rtos/riscv_debug.h9
-rw-r--r--src/rtos/rtos.c5
-rw-r--r--src/rtos/rtos.h1
-rw-r--r--src/server/gdb_server.c52
-rw-r--r--src/server/gdb_server.h3
-rw-r--r--src/target/Makefile.am8
-rw-r--r--src/target/register.h15
-rw-r--r--src/target/riscv/asm.h36
-rw-r--r--src/target/riscv/batch.c156
-rw-r--r--src/target/riscv/batch.h64
-rw-r--r--src/target/riscv/debug_defines.h1410
-rw-r--r--src/target/riscv/encoding.h1313
-rw-r--r--src/target/riscv/gdb_regs.h28
-rw-r--r--src/target/riscv/opcodes.h295
-rw-r--r--src/target/riscv/program.c491
-rw-r--r--src/target/riscv/program.h142
-rw-r--r--src/target/riscv/riscv-011.c2609
-rw-r--r--src/target/riscv/riscv-013.c1994
-rw-r--r--src/target/riscv/riscv.c1227
-rw-r--r--src/target/riscv/riscv.h217
-rw-r--r--src/target/target.c35
-rw-r--r--src/target/target_type.h1
34 files changed, 11727 insertions, 25 deletions
diff --git a/jimtcl b/jimtcl
-Subproject a9bf5975fd0f89974d689a2d9ebd0873c8d6478
+Subproject 51f65c6d38fbf86e1f0b036ad336761fd2ab7fa
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 5a992fe..759e98c 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -22,6 +22,7 @@ NOR_DRIVERS = \
%D%/dsp5680xx_flash.c \
%D%/efm32.c \
%D%/em357.c \
+ %D%/fespi.c \
%D%/faux.c \
%D%/fm3.c \
%D%/fm4.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 4ad1d92..56b451c 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -38,6 +38,7 @@ extern struct flash_driver em357_flash;
extern struct flash_driver faux_flash;
extern struct flash_driver fm3_flash;
extern struct flash_driver fm4_flash;
+extern struct flash_driver fespi_flash;
extern struct flash_driver jtagspi_flash;
extern struct flash_driver kinetis_flash;
extern struct flash_driver kinetis_ke_flash;
@@ -91,6 +92,7 @@ static struct flash_driver *flash_drivers[] = {
&faux_flash,
&fm3_flash,
&fm4_flash,
+ &fespi_flash,
&jtagspi_flash,
&kinetis_flash,
&kinetis_ke_flash,
diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c
new file mode 100644
index 0000000..ed72678
--- /dev/null
+++ b/src/flash/nor/fespi.c
@@ -0,0 +1,1158 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
+ * Modified by Megan Wachs <megan@sifive.com> from the original stmsmi.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/>. *
+ ***************************************************************************/
+
+/* The Freedom E SPI controller is a SPI bus controller
+ * specifically designed for SPI Flash Memories on Freedom E platforms.
+ *
+ * Two working modes are available:
+ * - SW mode: the SPI is controlled by SW. Any custom commands can be sent
+ * on the bus. Writes are only possible in this mode.
+ * - HW mode: Memory content is directly
+ * accessible in CPU memory space. CPU can read, write and execute memory
+ * content. */
+
+/* ATTENTION:
+ * To have flash memory mapped in CPU memory space, the controller
+ * must have "HW mode" enabled.
+ * 1) The command "reset init" has to initialize the controller and put
+ * it in HW mode (this is actually the default out of reset for Freedom E systems).
+ * 2) every command in this file have to return to prompt in HW mode. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <jtag/jtag.h>
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include "target/riscv/riscv.h"
+
+/* Register offsets */
+
+#define FESPI_REG_SCKDIV 0x00
+#define FESPI_REG_SCKMODE 0x04
+#define FESPI_REG_CSID 0x10
+#define FESPI_REG_CSDEF 0x14
+#define FESPI_REG_CSMODE 0x18
+
+#define FESPI_REG_DCSSCK 0x28
+#define FESPI_REG_DSCKCS 0x2a
+#define FESPI_REG_DINTERCS 0x2c
+#define FESPI_REG_DINTERXFR 0x2e
+
+#define FESPI_REG_FMT 0x40
+#define FESPI_REG_TXFIFO 0x48
+#define FESPI_REG_RXFIFO 0x4c
+#define FESPI_REG_TXCTRL 0x50
+#define FESPI_REG_RXCTRL 0x54
+
+#define FESPI_REG_FCTRL 0x60
+#define FESPI_REG_FFMT 0x64
+
+#define FESPI_REG_IE 0x70
+#define FESPI_REG_IP 0x74
+
+/* Fields */
+
+#define FESPI_SCK_POL 0x1
+#define FESPI_SCK_PHA 0x2
+
+#define FESPI_FMT_PROTO(x) ((x) & 0x3)
+#define FESPI_FMT_ENDIAN(x) (((x) & 0x1) << 2)
+#define FESPI_FMT_DIR(x) (((x) & 0x1) << 3)
+#define FESPI_FMT_LEN(x) (((x) & 0xf) << 16)
+
+/* TXCTRL register */
+#define FESPI_TXWM(x) ((x) & 0xffff)
+/* RXCTRL register */
+#define FESPI_RXWM(x) ((x) & 0xffff)
+
+#define FESPI_IP_TXWM 0x1
+#define FESPI_IP_RXWM 0x2
+
+#define FESPI_FCTRL_EN 0x1
+
+#define FESPI_INSN_CMD_EN 0x1
+#define FESPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1)
+#define FESPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4)
+#define FESPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8)
+#define FESPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10)
+#define FESPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12)
+#define FESPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16)
+#define FESPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24)
+
+/* Values */
+
+#define FESPI_CSMODE_AUTO 0
+#define FESPI_CSMODE_HOLD 2
+#define FESPI_CSMODE_OFF 3
+
+#define FESPI_DIR_RX 0
+#define FESPI_DIR_TX 1
+
+#define FESPI_PROTO_S 0
+#define FESPI_PROTO_D 1
+#define FESPI_PROTO_Q 2
+
+#define FESPI_ENDIAN_MSB 0
+#define FESPI_ENDIAN_LSB 1
+
+
+/* Timeout in ms */
+#define FESPI_CMD_TIMEOUT (100)
+#define FESPI_PROBE_TIMEOUT (100)
+#define FESPI_MAX_TIMEOUT (3000)
+
+
+#define FESPI_READ_REG(a) (_FESPI_READ_REG(a))
+#define _FESPI_READ_REG(a) \
+{ \
+ int __a; \
+ uint32_t __v; \
+ \
+ __a = target_read_u32(target, ctrl_base + (a), &__v); \
+ if (__a != ERROR_OK) { \
+ LOG_ERROR("FESPI_READ_REG error"); \
+ return __a; \
+ } \
+ __v; \
+}
+
+#define FESPI_WRITE_REG(a, v) \
+{ \
+ int __r; \
+ \
+ __r = target_write_u32(target, ctrl_base + (a), (v)); \
+ if (__r != ERROR_OK) { \
+ LOG_ERROR("FESPI_WRITE_REG error"); \
+ return __r; \
+ } \
+}
+
+#define FESPI_DISABLE_HW_MODE() FESPI_WRITE_REG(FESPI_REG_FCTRL, \
+ FESPI_READ_REG(FESPI_REG_FCTRL) & ~FESPI_FCTRL_EN)
+#define FESPI_ENABLE_HW_MODE() FESPI_WRITE_REG(FESPI_REG_FCTRL, \
+ FESPI_READ_REG(FESPI_REG_FCTRL) | FESPI_FCTRL_EN)
+
+struct fespi_flash_bank {
+ int probed;
+ uint32_t ctrl_base;
+ const struct flash_device *dev;
+};
+
+struct fespi_target {
+ char *name;
+ uint32_t tap_idcode;
+ uint32_t ctrl_base;
+};
+
+//TODO !!! What is the right naming convention here?
+static const struct fespi_target target_devices[] = {
+ /* name, tap_idcode, ctrl_base */
+ { "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 },
+ { NULL, 0, 0 }
+};
+
+FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command)
+{
+ struct fespi_flash_bank *fespi_info;
+
+ LOG_DEBUG("%s", __func__);
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ fespi_info = malloc(sizeof(struct fespi_flash_bank));
+ if (fespi_info == NULL) {
+ LOG_ERROR("not enough memory");
+ return ERROR_FAIL;
+ }
+
+ bank->driver_priv = fespi_info;
+ fespi_info->probed = 0;
+ fespi_info->ctrl_base = 0;
+ if (CMD_ARGC >= 7) {
+ int temp;
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[6], temp);
+ fespi_info->ctrl_base = (uint32_t) temp;
+ LOG_DEBUG("ASSUMING FESPI device at ctrl_base = 0x%x", fespi_info->ctrl_base);
+ }
+
+ return ERROR_OK;
+}
+
+static int fespi_set_dir (struct flash_bank * bank, bool dir) {
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+
+ FESPI_WRITE_REG(FESPI_REG_FMT,
+ (FESPI_READ_REG(FESPI_REG_FMT) & ~(FESPI_FMT_DIR(0xFFFFFFFF))) |
+ FESPI_FMT_DIR(dir));
+
+ return ERROR_OK;
+
+}
+
+static int fespi_txwm_wait(struct flash_bank *bank) {
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+
+ int64_t start = timeval_ms();
+
+ while (1) {
+ if (FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM) {
+ break;
+ }
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("ip.txwm didn't get set.");
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ return ERROR_OK;
+
+}
+
+static int fespi_tx(struct flash_bank *bank, uint8_t in){
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+
+ int64_t start = timeval_ms();
+
+ while (1) {
+ if ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) >= 0) {
+ break;
+ }
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("txfifo stayed negative.");
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ FESPI_WRITE_REG(FESPI_REG_TXFIFO, in);
+
+ return ERROR_OK;
+}
+
+static int fespi_rx(struct flash_bank *bank, uint8_t *out)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+
+ int64_t start = timeval_ms();
+ int32_t value;
+
+ while (1) {
+ value = (int32_t) FESPI_READ_REG(FESPI_REG_RXFIFO);
+ if (value >= 0)
+ break;
+ int64_t now = timeval_ms();
+ if (now - start > 1000) {
+ LOG_ERROR("rxfifo didn't go positive (value=0x%x).", value);
+ return ERROR_TARGET_TIMEOUT;
+ }
+ }
+
+ if (out) {
+ *out = value & 0xff;
+ }
+ return ERROR_OK;
+}
+
+//TODO!!! Why don't we need to call this after writing?
+static int fespi_wip (struct flash_bank * bank, int timeout)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+
+ int64_t endtime;
+
+ fespi_set_dir(bank, FESPI_DIR_RX);
+
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+ endtime = timeval_ms() + timeout;
+
+ fespi_tx(bank, SPIFLASH_READ_STATUS);
+ if (fespi_rx(bank, NULL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ do {
+ alive_sleep(1);
+
+ fespi_tx(bank, 0);
+ uint8_t rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ if ((rx & SPIFLASH_BSY_BIT) == 0) {
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+ fespi_set_dir(bank, FESPI_DIR_TX);
+ return ERROR_OK;
+ }
+ } while (timeval_ms() < endtime);
+
+ LOG_ERROR("timeout");
+ return ERROR_FAIL;
+}
+
+static int fespi_erase_sector(struct flash_bank *bank, int sector)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ int retval;
+
+ retval = fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
+ if (retval != ERROR_OK) {return retval;}
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK) {return retval;}
+
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+ retval = fespi_tx(bank, fespi_info->dev->erase_cmd);
+ if (retval != ERROR_OK) {return retval;}
+ sector = bank->sectors[sector].offset;
+ retval = fespi_tx(bank, sector >> 16);
+ if (retval != ERROR_OK) {return retval;}
+ retval = fespi_tx(bank, sector >> 8);
+ if (retval != ERROR_OK) {return retval;}
+ retval = fespi_tx(bank, sector);
+ if (retval != ERROR_OK) {return retval;}
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK) {return retval;}
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+
+ retval = fespi_wip(bank, FESPI_MAX_TIMEOUT);
+ if (retval != ERROR_OK){return retval;}
+
+ return ERROR_OK;
+}
+
+static int fespi_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ int retval = ERROR_OK;
+ int sector;
+
+ LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+ LOG_ERROR("Flash sector invalid");
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ if (!(fespi_info->probed)) {
+ LOG_ERROR("Flash bank not probed");
+ return ERROR_FLASH_BANK_NOT_PROBED;
+ }
+
+ for (sector = first; sector <= last; sector++) {
+ if (bank->sectors[sector].is_protected) {
+ LOG_ERROR("Flash sector %d protected", sector);
+ return ERROR_FAIL;
+ }
+ }
+
+ FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1));
+ retval = fespi_txwm_wait(bank);
+ if (retval != ERROR_OK){
+ LOG_ERROR("WM Didn't go high before attempting.");
+ return retval;
+ }
+
+ /* Disable Hardware accesses*/
+ FESPI_DISABLE_HW_MODE();
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (sector = first; sector <= last; sector++) {
+ retval = fespi_erase_sector(bank, sector);
+ if (retval != ERROR_OK)
+ break;
+ keep_alive();
+ }
+
+ /* Switch to HW mode before return to prompt */
+ FESPI_ENABLE_HW_MODE();
+ return retval;
+}
+
+static int fespi_protect(struct flash_bank *bank, int set,
+ int first, int last)
+{
+ int sector;
+
+ for (sector = first; sector <= last; sector++)
+ bank->sectors[sector].is_protected = set;
+ return ERROR_OK;
+}
+
+static int slow_fespi_write_buffer(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t len)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ uint32_t ii;
+
+ //TODO!!! assert that len < page size
+
+ fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
+ fespi_txwm_wait(bank);
+
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+
+ fespi_tx(bank, SPIFLASH_PAGE_PROGRAM);
+
+ fespi_tx(bank, offset >> 16);
+ fespi_tx(bank, offset >> 8);
+ fespi_tx(bank, offset);
+
+ for (ii = 0; ii < len; ii++) {
+ fespi_tx(bank, buffer[ii]);
+ }
+
+ fespi_txwm_wait(bank);
+
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+
+ keep_alive();
+
+ return ERROR_OK;
+}
+
+/*
+ * Here's the source for the algorithm.
+ * You can turn it into the array below using:
+ sed -n '/ALGO_START$/,/ALGO_END/ p' fespi.c | \
+ riscv32-unknown-elf-gcc -x assembler-with-cpp - -nostdlib -nostartfiles -o tmp.o && \
+ riscv32-unknown-elf-objcopy -O binary tmp.o algorithm.bin && \
+ xxd -i algorithm.bin
+
+// ALGO_START
+#define SPIFLASH_READ_STATUS 0x05 // Read Status Register
+#define SPIFLASH_BSY_BIT 0x00000001 // WIP Bit of SPI SR on SMI SR
+
+// Register offsets
+#define FESPI_REG_FMT 0x40
+#define FESPI_REG_TXFIFO 0x48
+#define FESPI_REG_RXFIFO 0x4c
+#define FESPI_REG_IP 0x74
+
+// Fields
+#define FESPI_IP_TXWM 0x1
+#define FESPI_FMT_DIR(x) (((x) & 0x1) << 3)
+
+// To enter, jump to the start of command_table (ie. offset 0).
+// a0 - FESPI base address
+// a1 - start address of buffer
+
+// The buffer contains a "program" in byte sequences. The first byte in a
+// sequence determines the operation. Some operation will read more data from
+// the program, while some will not. The operation byte is the offset into
+// command_table, so eg. 4 means exit, 8 means transmit, and so on.
+
+ .global _start
+_start:
+command_table:
+ j main // 0
+ ebreak // 4
+ j tx // 8
+ j txwm_wait // 12
+ j write_reg // 16
+ j wip_wait // 20
+ j set_dir // 24
+
+// Execute the program.
+main:
+ lbu t0, 0(a1)
+ addi a1, a1, 1
+ la t1, command_table
+ add t0, t0, t1
+ jr t0
+
+// Read 1 byte the contains the number of bytes to transmit. Then read those
+// bytes from the program and transmit them one by one.
+tx:
+ lbu t1, 0(a1) // read number of bytes to transmit
+ addi a1, a1, 1
+1: lw t0, FESPI_REG_TXFIFO(a0) // wait for FIFO clear
+ bltz t0, 1b
+ lbu t0, 0(a1) // Load byte to write
+ sw t0, FESPI_REG_TXFIFO(a0)
+ addi a1, a1, 1
+ addi t1, t1, -1
+ bgtz t1, 1b
+ j main
+
+// Wait until TXWM is set.
+txwm_wait:
+1: lw t0, FESPI_REG_IP(a0)
+ andi t0, t0, FESPI_IP_TXWM
+ beqz t0, 1b
+ j main
+
+// Read 1 byte that contains the offset of the register to write, and 1 byte
+// that contains the data to write.
+write_reg:
+ lbu t0, 0(a1) // read register to write
+ add t0, t0, a0
+ lbu t1, 1(a1) // read value to write
+ addi a1, a1, 2
+ sw t1, 0(t0)
+ j main
+
+wip_wait:
+ li a2, SPIFLASH_READ_STATUS
+ jal txrx_byte
+ // discard first result
+1: li a2, 0
+ jal txrx_byte
+ andi t0, a2, SPIFLASH_BSY_BIT
+ bnez t0, 1b
+ j main
+
+txrx_byte: // transmit the byte in a2, receive a bit into a2
+ lw t0, FESPI_REG_TXFIFO(a0) // wait for FIFO clear
+ bltz t0, txrx_byte
+ sw a2, FESPI_REG_TXFIFO(a0)
+1: lw a2, FESPI_REG_RXFIFO(a0)
+ bltz a2, 1b
+ ret
+
+set_dir:
+ lw t0, FESPI_REG_FMT(a0)
+ li t1, ~(FESPI_FMT_DIR(0xFFFFFFFF))
+ and t0, t0, t1
+ lbu t1, 0(a1) // read value to OR in
+ addi a1, a1, 1
+ or t0, t0, t1
+ sw t0, FESPI_REG_FMT(a0)
+ j main
+
+// ALGO_END
+ */
+static const uint8_t algorithm_bin[] = {
+ 0x6f, 0x00, 0xc0, 0x01, 0x73, 0x00, 0x10, 0x00, 0x6f, 0x00, 0xc0, 0x02,
+ 0x6f, 0x00, 0x00, 0x05, 0x6f, 0x00, 0xc0, 0x05, 0x6f, 0x00, 0x00, 0x07,
+ 0x6f, 0x00, 0x00, 0x0a, 0x83, 0xc2, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00,
+ 0x17, 0x03, 0x00, 0x00, 0x13, 0x03, 0xc3, 0xfd, 0xb3, 0x82, 0x62, 0x00,
+ 0x67, 0x80, 0x02, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00,
+ 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe, 0x83, 0xc2, 0x05, 0x00,
+ 0x23, 0x24, 0x55, 0x04, 0x93, 0x85, 0x15, 0x00, 0x13, 0x03, 0xf3, 0xff,
+ 0xe3, 0x44, 0x60, 0xfe, 0x6f, 0xf0, 0x5f, 0xfc, 0x83, 0x22, 0x45, 0x07,
+ 0x93, 0xf2, 0x12, 0x00, 0xe3, 0x8c, 0x02, 0xfe, 0x6f, 0xf0, 0x5f, 0xfb,
+ 0x83, 0xc2, 0x05, 0x00, 0xb3, 0x82, 0xa2, 0x00, 0x03, 0xc3, 0x15, 0x00,
+ 0x93, 0x85, 0x25, 0x00, 0x23, 0xa0, 0x62, 0x00, 0x6f, 0xf0, 0xdf, 0xf9,
+ 0x13, 0x06, 0x50, 0x00, 0xef, 0x00, 0x80, 0x01, 0x13, 0x06, 0x00, 0x00,
+ 0xef, 0x00, 0x00, 0x01, 0x93, 0x72, 0x16, 0x00, 0xe3, 0x9a, 0x02, 0xfe,
+ 0x6f, 0xf0, 0x1f, 0xf8, 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe,
+ 0x23, 0x24, 0xc5, 0x04, 0x03, 0x26, 0xc5, 0x04, 0xe3, 0x4e, 0x06, 0xfe,
+ 0x67, 0x80, 0x00, 0x00, 0x83, 0x22, 0x05, 0x04, 0x13, 0x03, 0x70, 0xff,
+ 0xb3, 0xf2, 0x62, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00,
+ 0xb3, 0xe2, 0x62, 0x00, 0x23, 0x20, 0x55, 0x04, 0x6f, 0xf0, 0x9f, 0xf4
+};
+#define STEP_EXIT 4
+#define STEP_TX 8
+#define STEP_TXWM_WAIT 12
+#define STEP_WRITE_REG 16
+#define STEP_WIP_WAIT 20
+#define STEP_SET_DIR 24
+#define STEP_NOP 0xff
+
+struct algorithm_steps {
+ unsigned size;
+ unsigned used;
+ uint8_t **steps;
+};
+
+struct algorithm_steps *as_new(unsigned size)
+{
+ struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps));
+ as->size = size;
+ as->steps = calloc(size, sizeof(as->steps[0]));
+ return as;
+}
+
+struct algorithm_steps *as_delete(struct algorithm_steps *as)
+{
+ for (unsigned step = 0; step < as->used; step++) {
+ free(as->steps[step]);
+ as->steps[step] = NULL;
+ }
+ free(as);
+ return NULL;
+}
+
+int as_empty(struct algorithm_steps *as)
+{
+ for (unsigned s = 0; s < as->used; s++) {
+ if (as->steps[s][0] != STEP_NOP)
+ return 0;
+ }
+ return 1;
+}
+
+// Return size of compiled program.
+unsigned as_compile(struct algorithm_steps *as, uint8_t *target,
+ unsigned target_size)
+{
+ unsigned offset = 0;
+ bool finish_early = false;
+ for (unsigned s = 0; s < as->used && !finish_early; s++) {
+ unsigned bytes_left = target_size - offset;
+ switch (as->steps[s][0]) {
+ case STEP_NOP:
+ break;
+ case STEP_TX:
+ {
+ unsigned size = as->steps[s][1];
+ if (size + 3 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], size + 2);
+ offset += size + 2;
+ break;
+ }
+ case STEP_WRITE_REG:
+ if (4 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 3);
+ offset += 3;
+ break;
+ case STEP_SET_DIR:
+ if (3 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 2);
+ offset += 2;
+ break;
+ case STEP_TXWM_WAIT:
+ case STEP_WIP_WAIT:
+ if (2 > bytes_left) {
+ finish_early = true;
+ break;
+ }
+ memcpy(target + offset, as->steps[s], 1);
+ offset += 1;
+ break;
+ default:
+ assert(0);
+ }
+ if (!finish_early)
+ as->steps[s][0] = STEP_NOP;
+ }
+ assert(offset + 1 <= target_size);
+ target[offset++] = STEP_EXIT;
+
+ LOG_DEBUG("%d-byte program:", offset);
+ for (unsigned i = 0; i < offset;) {
+ char buf[80];
+ for (unsigned x = 0; i < offset && x < 16; x++, i++) {
+ sprintf(buf + x*3, "%02x ", target[i]);
+ }
+ LOG_DEBUG("%s", buf);
+ }
+
+ return offset;
+}
+
+void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data)
+{
+ LOG_DEBUG("count=%d", count);
+ while (count > 0) {
+ unsigned step_count = MIN(count, 255);
+ assert(as->used < as->size);
+ as->steps[as->used] = malloc(step_count + 2);
+ as->steps[as->used][0] = STEP_TX;
+ as->steps[as->used][1] = step_count;
+ memcpy(as->steps[as->used] + 2, data, step_count);
+ as->used++;
+ data += step_count;
+ count -= step_count;
+ }
+}
+
+void as_add_tx1(struct algorithm_steps *as, uint8_t byte)
+{
+ uint8_t data[1];
+ data[0] = byte;
+ as_add_tx(as, 1, data);
+}
+
+void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data)
+{
+ assert(as->used < as->size);
+ as->steps[as->used] = malloc(3);
+ as->steps[as->used][0] = STEP_WRITE_REG;
+ as->steps[as->used][1] = offset;
+ as->steps[as->used][2] = data;
+ as->used++;
+}
+
+void as_add_txwm_wait(struct algorithm_steps *as)
+{
+ assert(as->used < as->size);
+ as->steps[as->used] = malloc(1);
+ as->steps[as->used][0] = STEP_TXWM_WAIT;
+ as->used++;
+}
+
+void as_add_wip_wait(struct algorithm_steps *as)
+{
+ assert(as->used < as->size);
+ as->steps[as->used] = malloc(1);
+ as->steps[as->used][0] = STEP_WIP_WAIT;
+ as->used++;
+}
+
+void as_add_set_dir(struct algorithm_steps *as, bool dir)
+{
+ assert(as->used < as->size);
+ as->steps[as->used] = malloc(2);
+ as->steps[as->used][0] = STEP_SET_DIR;
+ as->steps[as->used][1] = FESPI_FMT_DIR(dir);
+ as->used++;
+}
+
+/* This should write something less than or equal to a page.*/
+static int steps_add_buffer_write(struct algorithm_steps *as,
+ const uint8_t *buffer, uint32_t chip_offset, uint32_t len)
+{
+ as_add_tx1(as, SPIFLASH_WRITE_ENABLE);
+ as_add_txwm_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+
+ uint8_t setup[] = {
+ SPIFLASH_PAGE_PROGRAM,
+ chip_offset >> 16,
+ chip_offset >> 8,
+ chip_offset,
+ };
+ as_add_tx(as, sizeof(setup), setup);
+
+ as_add_tx(as, len, buffer);
+ as_add_txwm_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+
+ // fespi_wip()
+ as_add_set_dir(as, FESPI_DIR_RX);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+ as_add_wip_wait(as);
+ as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+ as_add_set_dir(as, FESPI_DIR_TX);
+
+ return ERROR_OK;
+}
+
+static int steps_execute(struct algorithm_steps *as,
+ struct flash_bank *bank, struct working_area *algorithm_wa,
+ struct working_area *data_wa)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ uint8_t *data_buf = malloc(data_wa->size);
+ int xlen = riscv_xlen(target);
+
+ struct reg_param reg_params[2];
+ init_reg_param(&reg_params[0], "x10", xlen, PARAM_OUT);
+ init_reg_param(&reg_params[1], "x11", xlen, PARAM_OUT);
+ buf_set_u64(reg_params[0].value, 0, xlen, ctrl_base);
+ buf_set_u64(reg_params[1].value, 0, xlen, data_wa->address);
+ while (!as_empty(as)) {
+ keep_alive();
+ unsigned bytes = as_compile(as, data_buf, data_wa->size);
+ int retval = target_write_buffer(target, data_wa->address, bytes,
+ data_buf);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write data to 0x%x: %d", data_wa->address,
+ retval);
+ return retval;
+ }
+
+ retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
+ algorithm_wa->address, algorithm_wa->address + 4,
+ 10000, NULL);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to execute algorithm at 0x%x: %d", algorithm_wa->address,
+ retval);
+ return retval;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ uint32_t cur_count, page_size, page_offset;
+ int sector;
+ int retval = ERROR_OK;
+
+ LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+ __func__, offset, count);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (offset + count > fespi_info->dev->size_in_bytes) {
+ LOG_WARNING("Write past end of flash. Extra data discarded.");
+ count = fespi_info->dev->size_in_bytes - offset;
+ }
+
+ /* Check sector protection */
+ for (sector = 0; sector < bank->num_sectors; sector++) {
+ /* Start offset in or before this sector? */
+ /* End offset in or behind this sector? */
+ if ((offset <
+ (bank->sectors[sector].offset + bank->sectors[sector].size))
+ && ((offset + count - 1) >= bank->sectors[sector].offset)
+ && bank->sectors[sector].is_protected) {
+ LOG_ERROR("Flash sector %d protected", sector);
+ return ERROR_FAIL;
+ }
+ }
+
+ struct working_area *algorithm_wa;
+ if (target_alloc_working_area(target, sizeof(algorithm_bin),
+ &algorithm_wa) != ERROR_OK) {
+ LOG_WARNING("Couldn't allocate %zd-byte working area.",
+ sizeof(algorithm_bin));
+ algorithm_wa = NULL;
+ } else {
+ retval = target_write_buffer(target, algorithm_wa->address,
+ sizeof(algorithm_bin), algorithm_bin);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write code to 0x%x: %d", algorithm_wa->address,
+ retval);
+ target_free_working_area(target, algorithm_wa);
+ algorithm_wa = NULL;
+ }
+ }
+
+ struct working_area *data_wa = NULL;
+ unsigned data_wa_size = 2 * count;
+ while (1) {
+ if (data_wa_size < 128) {
+ LOG_WARNING("Couldn't allocate data working area.");
+ target_free_working_area(target, algorithm_wa);
+ algorithm_wa = NULL;
+ }
+ if (target_alloc_working_area_try(target, data_wa_size, &data_wa) ==
+ ERROR_OK) {
+ break;
+ }
+
+ data_wa_size /= 2;
+ }
+
+ page_size = fespi_info->dev->pagesize;
+
+ fespi_txwm_wait(bank);
+
+ /* Disable Hardware accesses*/
+ FESPI_DISABLE_HW_MODE();
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ struct algorithm_steps *as = as_new(count / 4);
+
+ /* unaligned buffer head */
+ if (count > 0 && (offset & 3) != 0) {
+ cur_count = 4 - (offset & 3);
+ if (cur_count > count)
+ cur_count = count;
+ if (algorithm_wa) {
+ retval = steps_add_buffer_write(as, buffer, offset, cur_count);
+ } else {
+ retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count);
+ }
+ if (retval != ERROR_OK)
+ goto err;
+ offset += cur_count;
+ buffer += cur_count;
+ count -= cur_count;
+ }
+
+ page_offset = offset % page_size;
+ /* central part, aligned words */
+ while (count >= 4) {
+ /* clip block at page boundary */
+ if (page_offset + count > page_size)
+ cur_count = page_size - page_offset;
+ else
+ cur_count = count & ~3;
+
+ if (algorithm_wa) {
+ retval = steps_add_buffer_write(as, buffer, offset, cur_count);
+ } else {
+ retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count);
+ }
+ if (retval != ERROR_OK)
+ goto err;
+
+ page_offset = 0;
+ buffer += cur_count;
+ offset += cur_count;
+ count -= cur_count;
+ }
+
+ /* buffer tail */
+ if (count > 0) {
+ if (algorithm_wa) {
+ retval = steps_add_buffer_write(as, buffer, offset, count);
+ } else {
+ retval = slow_fespi_write_buffer(bank, buffer, offset, count);
+ }
+ if (retval != ERROR_OK)
+ goto err;
+ }
+
+ if (algorithm_wa) {
+ retval = steps_execute(as, bank, algorithm_wa, data_wa);
+ }
+
+err:
+ if (algorithm_wa) {
+ target_free_working_area(target, data_wa);
+ target_free_working_area(target, algorithm_wa);
+ }
+
+ /* Switch to HW mode before return to prompt */
+ FESPI_ENABLE_HW_MODE();
+ return retval;
+}
+
+/* Return ID of flash device */
+/* On exit, SW mode is kept */
+static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base = fespi_info->ctrl_base;
+ int retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ fespi_txwm_wait(bank);
+
+ /* Disable Hardware accesses*/
+ FESPI_DISABLE_HW_MODE();
+
+ /* poll WIP */
+ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ fespi_set_dir(bank, FESPI_DIR_RX);
+
+ /* Send SPI command "read ID" */
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
+
+ fespi_tx(bank, SPIFLASH_READ_ID);
+ /* Send dummy bytes to actually read the ID.*/
+ fespi_tx(bank, 0);
+ fespi_tx(bank, 0);
+ fespi_tx(bank, 0);
+
+ /* read ID from Receive Register */
+ *id = 0;
+ if (fespi_rx(bank, NULL) != ERROR_OK)
+ return ERROR_FAIL;
+ uint8_t rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id = rx;
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id |= (rx << 8);
+ if (fespi_rx(bank, &rx) != ERROR_OK)
+ return ERROR_FAIL;
+ *id |= (rx << 16);
+
+ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
+
+ fespi_set_dir(bank, FESPI_DIR_TX);
+
+ return ERROR_OK;
+}
+
+static int fespi_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ uint32_t ctrl_base;
+ struct flash_sector *sectors;
+ uint32_t id = 0; /* silence uninitialized warning */
+ const struct fespi_target *target_device;
+ int retval;
+
+ if (fespi_info->probed)
+ free(bank->sectors);
+ fespi_info->probed = 0;
+
+ if (fespi_info->ctrl_base == 0) {
+ for (target_device = target_devices ; target_device->name ; ++target_device)
+ if (target_device->tap_idcode == target->tap->idcode)
+ break;
+
+ if (!target_device->name) {
+ LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable",
+ target->tap->idcode);
+ return ERROR_FAIL;
+ }
+
+ fespi_info->ctrl_base = target_device->ctrl_base;
+
+ LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32,
+ target_device->name, bank->base);
+
+ } else {
+ LOG_DEBUG("Assuming FESPI as specified at address 0x%x with ctrl at 0x%x",
+ fespi_info->ctrl_base,
+ bank->base);
+ }
+ ctrl_base = fespi_info->ctrl_base;
+
+ /* read and decode flash ID; returns in SW mode */
+ FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1));
+ fespi_set_dir(bank, FESPI_DIR_TX);
+
+ retval = fespi_read_flash_id(bank, &id);
+
+ FESPI_ENABLE_HW_MODE();
+ if (retval != ERROR_OK)
+ return retval;
+
+ fespi_info->dev = NULL;
+ for (const struct flash_device *p = flash_devices; p->name ; p++)
+ if (p->device_id == id) {
+ fespi_info->dev = p;
+ break;
+ }
+
+ if (!fespi_info->dev) {
+ LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
+ return ERROR_FAIL;
+ }
+
+ LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+ fespi_info->dev->name, fespi_info->dev->device_id);
+
+ /* Set correct size value */
+ bank->size = fespi_info->dev->size_in_bytes;
+
+ /* create and fill sectors array */
+ bank->num_sectors =
+ fespi_info->dev->size_in_bytes / fespi_info->dev->sectorsize;
+ sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+ if (sectors == NULL) {
+ LOG_ERROR("not enough memory");
+ return ERROR_FAIL;
+ }
+
+ for (int sector = 0; sector < bank->num_sectors; sector++) {
+ sectors[sector].offset = sector * fespi_info->dev->sectorsize;
+ sectors[sector].size = fespi_info->dev->sectorsize;
+ sectors[sector].is_erased = -1;
+ sectors[sector].is_protected = 1;
+ }
+
+ bank->sectors = sectors;
+ fespi_info->probed = 1;
+ return ERROR_OK;
+}
+
+static int fespi_auto_probe(struct flash_bank *bank)
+{
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+ if (fespi_info->probed)
+ return ERROR_OK;
+ return fespi_probe(bank);
+}
+
+static int fespi_protect_check(struct flash_bank *bank)
+{
+ /* Nothing to do. Protection is only handled in SW. */
+ return ERROR_OK;
+}
+
+static int get_fespi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct fespi_flash_bank *fespi_info = bank->driver_priv;
+
+ if (!(fespi_info->probed)) {
+ snprintf(buf, buf_size,
+ "\nFESPI flash bank not probed yet\n");
+ return ERROR_OK;
+ }
+
+ snprintf(buf, buf_size, "\nFESPI flash information:\n"
+ " Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+ fespi_info->dev->name, fespi_info->dev->device_id);
+
+ return ERROR_OK;
+}
+
+struct flash_driver fespi_flash = {
+ .name = "fespi",
+ .flash_bank_command = fespi_flash_bank_command,
+ .erase = fespi_erase,
+ .protect = fespi_protect,
+ .write = fespi_write,
+ .read = default_flash_read,
+ .probe = fespi_probe,
+ .auto_probe = fespi_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = fespi_protect_check,
+ .info = get_fespi_info
+};
diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c
index 6501fbc..c8239d2 100644
--- a/src/flash/nor/spi.c
+++ b/src/flash/nor/spi.c
@@ -69,6 +69,7 @@ const struct flash_device flash_devices[] = {
FLASH_ID("mac 25l6405", 0xd8, 0xc7, 0x001720c2, 0x100, 0x10000, 0x800000),
FLASH_ID("micron n25q064", 0xd8, 0xc7, 0x0017ba20, 0x100, 0x10000, 0x800000),
FLASH_ID("micron n25q128", 0xd8, 0xc7, 0x0018ba20, 0x100, 0x10000, 0x1000000),
+ FLASH_ID("issi is25lp128", 0xd8, 0xc7, 0x0018609d, 0x100, 0x10000, 0x1000000),
FLASH_ID("win w25q80bv", 0xd8, 0xc7, 0x001440ef, 0x100, 0x10000, 0x100000),
FLASH_ID("win w25q32fv", 0xd8, 0xc7, 0x001640ef, 0x100, 0x10000, 0x400000),
FLASH_ID("win w25q32dw", 0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 0x400000),
diff --git a/src/helper/log.c b/src/helper/log.c
index 891613d..d4e87f6 100644
--- a/src/helper/log.c
+++ b/src/helper/log.c
@@ -103,7 +103,7 @@ static void log_forward(const char *file, unsigned line, const char *function, c
}
}
-/* The log_puts() serves to somewhat different goals:
+/* The log_puts() serves two somewhat different goals:
*
* - logging
* - feeding low-level info to the user in GDB or Telnet
diff --git a/src/jtag/core.c b/src/jtag/core.c
index 8c79eb2..e21d476 100644
--- a/src/jtag/core.c
+++ b/src/jtag/core.c
@@ -836,7 +836,120 @@ int default_interface_jtag_execute_queue(void)
return ERROR_FAIL;
}
- return jtag->execute_queue();
+ int result = jtag->execute_queue();
+
+#if 0
+ // TODO: I like these better than some of the other JTAG debug statements,
+ // but having both is silly.
+ struct jtag_command *cmd = jtag_command_queue;
+ while (debug_level >= LOG_LVL_DEBUG && cmd) {
+ switch (cmd->type) {
+ case JTAG_SCAN:
+#if 0
+ LOG_DEBUG("JTAG %s SCAN to %s",
+ cmd->cmd.scan->ir_scan ? "IR" : "DR",
+ tap_state_name(cmd->cmd.scan->end_state));
+ for (int i = 0; i < cmd->cmd.scan->num_fields; i++) {
+ struct scan_field *field = cmd->cmd.scan->fields + i;
+ if (field->out_value) {
+ char *str = buf_to_str(field->out_value, field->num_bits, 16);
+ LOG_DEBUG(" %db out: %s", field->num_bits, str);
+ free(str);
+ }
+ if (field->in_value) {
+ char *str = buf_to_str(field->in_value, field->num_bits, 16);
+ LOG_DEBUG(" %db in: %s", field->num_bits, str);
+ free(str);
+ }
+ if (field->check_value) {
+ char *str = buf_to_str(field->check_value, field->num_bits, 16);
+ LOG_DEBUG(" %db check: %s", field->num_bits, str);
+ free(str);
+ }
+ if (field->check_mask) {
+ char *str = buf_to_str(field->check_mask, field->num_bits, 16);
+ LOG_DEBUG(" %db mask: %s", field->num_bits, str);
+ free(str);
+ }
+ }
+#endif
+ {
+ uint8_t *buf = NULL;
+ int scan_bits = jtag_build_buffer(cmd->cmd.scan, &buf);
+ char *str_out = buf_to_str(buf, scan_bits, 16);
+ free(buf);
+ LOG_DEBUG("vvv jtag_scan(%d, %d, %d'h%s, %d); // %s",
+ cmd->cmd.scan->ir_scan,
+ scan_bits,
+ scan_bits, str_out,
+ cmd->cmd.scan->end_state, tap_state_name(cmd->cmd.scan->end_state));
+ free(str_out);
+
+ struct scan_field *last_field = cmd->cmd.scan->fields + cmd->cmd.scan->num_fields - 1;
+ if (last_field->in_value) {
+ char *str_in = buf_to_str(last_field->in_value, last_field->num_bits, 16);
+ LOG_DEBUG("vvv jtag_check_tdo(%d, %d'h%s);",
+ last_field->num_bits,
+ last_field->num_bits, str_in);
+ free(str_in);
+ }
+ }
+ break;
+ case JTAG_TLR_RESET:
+#if 0
+ LOG_DEBUG("JTAG TLR RESET to %s",
+ tap_state_name(cmd->cmd.statemove->end_state));
+#endif
+ LOG_DEBUG("vvv jtag_tlr_reset(%d); // %s",
+ cmd->cmd.statemove->end_state,
+ tap_state_name(cmd->cmd.statemove->end_state));
+ break;
+ case JTAG_RUNTEST:
+#if 0
+ LOG_DEBUG("JTAG RUNTEST %d cycles to %s",
+ cmd->cmd.runtest->num_cycles,
+ tap_state_name(cmd->cmd.runtest->end_state));
+#endif
+ LOG_DEBUG("vvv jtag_runtest(%d, %d); // %s",
+ cmd->cmd.runtest->num_cycles,
+ cmd->cmd.runtest->end_state,
+ tap_state_name(cmd->cmd.runtest->end_state));
+ break;
+ case JTAG_RESET:
+ {
+#if 0
+ const char *reset_str[3] = {
+ "leave", "deassert", "assert"
+ };
+ LOG_DEBUG("JTAG RESET %s TRST, %s SRST",
+ reset_str[cmd->cmd.reset->trst + 1],
+ reset_str[cmd->cmd.reset->srst + 1]);
+#endif
+ LOG_DEBUG("vvv jtag_reset(%d, %d);",
+ cmd->cmd.reset->trst, cmd->cmd.reset->srst);
+ }
+ break;
+ case JTAG_PATHMOVE:
+ LOG_DEBUG("JTAG PATHMOVE (TODO)");
+ break;
+ case JTAG_SLEEP:
+ LOG_DEBUG("JTAG SLEEP (TODO)");
+ break;
+ case JTAG_STABLECLOCKS:
+ LOG_DEBUG("JTAG STABLECLOCKS (TODO)");
+ break;
+ case JTAG_TMS:
+ LOG_DEBUG("JTAG STABLECLOCKS (TODO)");
+ break;
+ default:
+ LOG_ERROR("Unknown JTAG command: %d", cmd->type);
+ break;
+ }
+ cmd = cmd->next;
+ }
+#endif
+
+ return result;
}
void jtag_execute_queue_noclear(void)
@@ -1107,7 +1220,8 @@ static int jtag_examine_chain(void)
if ((idcode & 1) == 0) {
/* Zero for LSB indicates a device in bypass */
- LOG_INFO("TAP %s does not have IDCODE", tap->dotted_name);
+ LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%x)",
+ tap->dotted_name, idcode);
tap->hasidcode = false;
tap->idcode = 0;
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index 00fe37f..8b78fe8 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -430,10 +430,40 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd)
tap_set_end_state(tap_get_state());
}
+#ifdef _DEBUG_JTAG_IO_
+static void debug_jtag_io_value(const char *prefix, const uint8_t *value,
+ unsigned int num_bits)
+{
+ if (!value) {
+ return;
+ }
+
+ char buf[33];
+ char *bufp = buf;
+ unsigned int chars = (num_bits + 3) / 4;
+ for (unsigned int i = 0; i < chars; i++) {
+ if (i && (i % 32) == 0) {
+ DEBUG_JTAG_IO(" %s%s", prefix, buf);
+ bufp = buf;
+ }
+ int start_bit = 4 * (chars - i - 1);
+ sprintf(bufp, "%01x", buf_get_u32(value, start_bit, 4));
+ bufp++;
+ }
+ if (bufp != buf) {
+ DEBUG_JTAG_IO(" %s%s", prefix, buf);
+ }
+}
+#endif
+
static void ftdi_execute_scan(struct jtag_command *cmd)
{
DEBUG_JTAG_IO("%s type:%d", cmd->cmd.scan->ir_scan ? "IRSCAN" : "DRSCAN",
jtag_scan_type(cmd->cmd.scan));
+#ifdef _DEBUG_JTAG_IO_
+ debug_jtag_io_value(" out=", cmd->cmd.scan->fields->out_value,
+ cmd->cmd.scan->fields->num_bits);
+#endif
/* Make sure there are no trailing fields with num_bits == 0, or the logic below will fail. */
while (cmd->cmd.scan->num_fields > 0
@@ -515,6 +545,11 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
DEBUG_JTAG_IO("%s scan, %i bits, end in %s",
(cmd->cmd.scan->ir_scan) ? "IR" : "DR", scan_size,
tap_state_name(tap_get_end_state()));
+
+#ifdef _DEBUG_JTAG_IO_
+ debug_jtag_io_value(" in=", cmd->cmd.scan->fields->in_value,
+ cmd->cmd.scan->fields->num_bits);
+#endif
}
static void ftdi_execute_reset(struct jtag_command *cmd)
diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c
index a1787d4..1b0fcba 100644
--- a/src/jtag/drivers/jtag_vpi.c
+++ b/src/jtag/drivers/jtag_vpi.c
@@ -318,7 +318,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state)
if (retval != ERROR_OK)
return retval;
- retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT);
+ retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT);
if (retval != ERROR_OK)
return retval;
diff --git a/src/jtag/drivers/remote_bitbang.c b/src/jtag/drivers/remote_bitbang.c
index c8d0136..b898e95 100644
--- a/src/jtag/drivers/remote_bitbang.c
+++ b/src/jtag/drivers/remote_bitbang.c
@@ -131,7 +131,7 @@ static int remote_bitbang_init_tcp(void)
{
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };
struct addrinfo *result, *rp;
- int fd;
+ int fd = 0;
LOG_INFO("Connecting to %s:%s",
remote_bitbang_host ? remote_bitbang_host : "localhost",
diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am
index c59ee3f..22f7da5 100644
--- a/src/rtos/Makefile.am
+++ b/src/rtos/Makefile.am
@@ -14,6 +14,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/ChibiOS.c \
%D%/embKernel.c \
%D%/mqx.c \
+ %D%/riscv_debug.c \
%D%/uCOS-III.c \
%D%/rtos.h \
%D%/rtos_standard_stackings.h \
@@ -22,7 +23,8 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/rtos_chibios_stackings.h \
%D%/rtos_embkernel_stackings.h \
%D%/rtos_mqx_stackings.h \
- %D%/rtos_ucos_iii_stackings.h
+ %D%/rtos_ucos_iii_stackings.h \
+ %D%/riscv_debug.h
%C%_librtos_la_CFLAGS = $(AM_CFLAGS)
diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c
new file mode 100644
index 0000000..dbd7238
--- /dev/null
+++ b/src/rtos/riscv_debug.c
@@ -0,0 +1,316 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "riscv_debug.h"
+#include "target/target.h"
+#include "target/riscv/riscv.h"
+#include "rtos.h"
+#include "server/gdb_server.h"
+
+static int riscv_update_threads(struct rtos *rtos);
+static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size);
+static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size);
+
+static int riscv_detect_rtos(struct target *target)
+{
+ LOG_ERROR("riscv_detect_rtos() unimplemented");
+ return -1;
+}
+
+static int riscv_create_rtos(struct target *target)
+{
+ LOG_DEBUG("RISC-V Debug 'RTOS' created: this doesn't mean you're running an RTOS, just that you have multi-hart support on RISC-V");
+
+ struct riscv_rtos *r = calloc(1, sizeof(*r));
+ target->rtos->rtos_specific_params = r;
+#if 0
+ r->target_hartid = 0;
+ r->target_any_hart = true;
+ r->target_every_hart = true;
+#endif
+
+ target->rtos->current_threadid = 1;
+ target->rtos->current_thread = 1;
+ riscv_update_threads(target->rtos);
+
+ target->rtos->gdb_thread_packet = riscv_gdb_thread_packet;
+ target->rtos->gdb_v_packet = riscv_gdb_v_packet;
+
+ return JIM_OK;
+}
+
+static int riscv_update_threads(struct rtos *rtos)
+{
+ LOG_DEBUG("Updating the RISC-V Hart List");
+
+ /* Figures out how many harts there are on the system. */
+ int hart_count = riscv_count_harts(rtos->target);
+ if (rtos->thread_count != hart_count) {
+ rtos_free_threadlist(rtos);
+ rtos->thread_count = hart_count;
+ rtos->thread_details = calloc(rtos->thread_count, sizeof(*rtos->thread_details));
+ for (int i = 0; i < rtos->thread_count; ++i) {
+ LOG_DEBUG(" Setting up Hart %d", i);
+ rtos->thread_details[i].threadid = i + 1;
+ rtos->thread_details[i].exists = true;
+ if (asprintf(&rtos->thread_details[i].thread_name_str, "Hart %d", i) < 0)
+ LOG_ERROR("riscv_update_threads() failed asprintf");
+ if (asprintf(&rtos->thread_details[i].extra_info_str, "RV64") < 0)
+ LOG_ERROR("riscv_update_threads() failed asprintf");
+ }
+ }
+ return JIM_OK;
+}
+
+static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ struct target *target = get_target_from_connection(connection);
+ struct rtos *rtos = target->rtos;
+ struct riscv_rtos *r = (struct riscv_rtos *)(target->rtos->rtos_specific_params);
+
+ char *packet_stttrr = malloc(packet_size + 1);
+ memset(packet_stttrr, '\0', packet_size + 1);
+ memcpy(packet_stttrr, packet, packet_size);
+ LOG_DEBUG("handling packet '%s'", packet_stttrr);
+
+ switch (packet[0]) {
+ case 'q':
+ if (strncmp(packet, "qfThreadInfo", 12) == 0) {
+ riscv_update_threads(target->rtos);
+ r->qs_thread_info_offset = 1;
+
+ char m[16];
+ snprintf(m, 16, "m%08x", (int)rtos->thread_details[0].threadid);
+ gdb_put_packet(connection, m, strlen(m));
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qsThreadInfo", 12) == 0) {
+ if (r->qs_thread_info_offset >= rtos->thread_count) {
+ gdb_put_packet(connection, "l", 1);
+ return ERROR_OK;
+ }
+
+ int tid = r->qs_thread_info_offset++;
+ char m[16];
+ snprintf(m, 16, "m%08x", (int)rtos->thread_details[tid].threadid);
+ gdb_put_packet(connection, m, strlen(m));
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qAttached", 9) == 0) {
+ gdb_put_packet(connection, "1", 1);
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qThreadExtraInfo", 16) == 0) {
+ char tid_str[32];
+ memcpy(tid_str, packet + 17, packet_size - 17);
+ tid_str[packet_size - 17] = '\0';
+ char *end;
+ int tid = strtol(tid_str, &end, 16);
+ if (*end != '\0') {
+ LOG_ERROR("Got qThreadExtraInfo with non-numeric TID: '%s'", tid_str);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+
+ char m[16];
+ snprintf(m, 16, "hart %d", tid);
+ char h[33];
+ h[0] = '\0';
+ for (size_t i = 0; i < strlen(m); ++i) {
+ char byte[3];
+ snprintf(byte, 3, "%02x", m[i]);
+ strncat(h, byte, 32);
+ }
+ gdb_put_packet(connection, h, strlen(h));
+ return ERROR_OK;
+ }
+
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+
+ case 'Q':
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+
+ case 'H':
+ /* ‘H op thread-id’
+ *
+ * Set thread for subsequent operations (‘m’, ‘M’, ‘g’, ‘G’,
+ * et.al.). Depending on the operation to be performed, op
+ * should be ‘c’ for step and continue operations (note that
+ * this is deprecated, supporting the ‘vCont’ command is a
+ * better option), and ‘g’ for other operations. The thread
+ * designator thread-id has the format and interpretation
+ * described in thread-id syntax.
+ *
+ * Reply:
+ * ‘OK’ for success
+ * ‘E NN’ for an error
+ */
+ {
+ char tid_str[32];
+ memcpy(tid_str, packet + 2, packet_size - 2);
+ tid_str[packet_size - 2] = '\0';
+ char *entptr;
+ int tid = strtol(tid_str, &entptr, 16);
+ if (*entptr != '\0') {
+ LOG_ERROR("Got H packet, but without integer: %s", tid_str);
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+ }
+
+ switch (tid) {
+ case 0:
+ case -1:
+ riscv_set_all_rtos_harts(target);
+ break;
+ default:
+ riscv_set_rtos_hartid(target, tid - 1);
+ break;
+ }
+
+ switch (packet[1]) {
+ case 'g':
+ case 'c':
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ default:
+ LOG_ERROR("Unknown H packet subtype %2x\n", packet[1]);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+ }
+
+ case 'T':
+ {
+ char tid_str[32];
+ memcpy(tid_str, packet + 1, packet_size - 1);
+ tid_str[packet_size - 1] = '\0';
+ char *end;
+ int tid = strtol(tid_str, &end, 16);
+ if (*end != '\0') {
+ LOG_ERROR("T packet with non-numeric tid %s", tid_str);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+
+ riscv_update_threads(target->rtos);
+ if (tid <= target->rtos->thread_count) {
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ } else {
+ gdb_put_packet(connection, "E00", 3);
+ return ERROR_OK;
+ }
+ }
+
+ case 'c':
+ case 's':
+ target->state = TARGET_HALTED;
+ return JIM_OK;
+
+ case 'R':
+ gdb_put_packet(connection, "E00", 3);
+ return JIM_OK;
+
+ default:
+ LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
+ gdb_put_packet(connection, NULL, 0);
+ return JIM_OK;
+ }
+}
+
+static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ char *packet_stttrr = malloc(packet_size + 1);
+ memset(packet_stttrr, '\0', packet_size + 1);
+ memcpy(packet_stttrr, packet, packet_size);
+ LOG_DEBUG("handling packet '%s'", packet_stttrr);
+
+ struct target *target = get_target_from_connection(connection);
+
+ if (strcmp(packet_stttrr, "vCont?") == 0) {
+ static const char *message = "OK";
+ gdb_put_packet(connection, (char *)message, strlen(message));
+ return JIM_OK;
+ }
+
+ int threadid;
+ if (sscanf(packet_stttrr, "vCont;s:%d;c", &threadid) == 1) {
+ riscv_set_rtos_hartid(target, threadid - 1);
+ riscv_step_rtos_hart(target);
+
+ gdb_put_packet(connection, "S05", 3);
+ return JIM_OK;
+ }
+
+ if (strcmp(packet_stttrr, "vCont;c") == 0) {
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
+ riscv_resume_all_harts(target);
+ target->state = TARGET_RUNNING;
+ gdb_set_frontend_state_running(connection);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUME_END);
+ return JIM_OK;
+ }
+
+ if (strncmp(packet_stttrr, "vCont", 5) == 0)
+ LOG_ERROR("Got unknown vCont-type packet");
+
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+}
+
+static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list)
+{
+ LOG_DEBUG("Updating RISC-V regiser list for hart %d", (int)(thread_id - 1));
+
+#if 0
+ LOG_ERROR(" Not actually updating");
+ *hex_reg_list = 0;
+ return JIM_OK;
+#endif
+
+ size_t n_regs = 32;
+ size_t xlen = 64;
+ size_t reg_chars = xlen / 8 * 2;
+
+ ssize_t hex_reg_list_length = n_regs * reg_chars + 2;
+ *hex_reg_list = malloc(hex_reg_list_length);
+ *hex_reg_list[0] = '\0';
+ for (size_t i = 0; i < n_regs; ++i) {
+ if (riscv_has_register(rtos->target, thread_id, i)) {
+ uint64_t reg_value = riscv_get_register_on_hart(rtos->target, thread_id - 1, i);
+ for (size_t byte = 0; byte < xlen / 8; ++byte) {
+ uint8_t reg_byte = reg_value >> (byte * 8);
+ char hex[3] = {'x', 'x', 'x'};
+ snprintf(hex, 3, "%02x", reg_byte);
+ strncat(*hex_reg_list, hex, hex_reg_list_length);
+ }
+ } else {
+ for (size_t byte = 0; byte < xlen / 8; ++byte)
+ strncat(*hex_reg_list, "xx", hex_reg_list_length);
+ }
+ }
+ LOG_DEBUG("%s", *hex_reg_list);
+ return JIM_OK;
+}
+
+static int riscv_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
+{
+ *symbol_list = calloc(1, sizeof(symbol_table_elem_t));
+ (*symbol_list)[0].symbol_name = NULL;
+ (*symbol_list)[0].optional = false;
+ return JIM_OK;
+}
+
+const struct rtos_type riscv_rtos =
+{
+ .name = "riscv",
+ .detect_rtos = riscv_detect_rtos,
+ .create = riscv_create_rtos,
+ .update_threads = riscv_update_threads,
+ .get_thread_reg_list = riscv_get_thread_reg_list,
+ .get_symbol_list_to_lookup = riscv_get_symbol_list_to_lookup,
+};
diff --git a/src/rtos/riscv_debug.h b/src/rtos/riscv_debug.h
new file mode 100644
index 0000000..bcc7411
--- /dev/null
+++ b/src/rtos/riscv_debug.h
@@ -0,0 +1,9 @@
+#ifndef RTOS__RISCV_H
+#define RTOS__RISCV_H
+
+struct riscv_rtos {
+ /* The index into the thread list used to handle */
+ int qs_thread_info_offset;
+};
+
+#endif
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index 84ee498..5e8ce48 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -35,6 +35,7 @@ extern struct rtos_type ChibiOS_rtos;
extern struct rtos_type embKernel_rtos;
extern struct rtos_type mqx_rtos;
extern struct rtos_type uCOS_III_rtos;
+extern struct rtos_type riscv_rtos;
static struct rtos_type *rtos_types[] = {
&ThreadX_rtos,
@@ -45,6 +46,7 @@ static struct rtos_type *rtos_types[] = {
&embKernel_rtos,
&mqx_rtos,
&uCOS_III_rtos,
+ &riscv_rtos,
NULL
};
@@ -72,6 +74,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
+ os->gdb_v_packet = NULL;
return JIM_OK;
}
@@ -431,7 +434,7 @@ int rtos_get_gdb_reg_list(struct connection *connection)
(target->smp))) { /* in smp several current thread are possible */
char *hex_reg_list;
- LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
+ LOG_INFO("RTOS: getting register list for thread 0x%" PRIx64
", target->rtos->current_thread=0x%" PRIx64 "\r\n",
current_threadid,
target->rtos->current_thread);
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index 70c1193..70cec87 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -54,6 +54,7 @@ struct rtos {
struct thread_detail *thread_details;
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
+ int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size);
void *rtos_specific_params;
};
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 483e551..e7fe657 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -1167,8 +1167,15 @@ static int gdb_get_registers_packet(struct connection *connection,
reg_packet_p = reg_packet;
for (i = 0; i < reg_list_size; i++) {
- if (!reg_list[i]->valid)
- reg_list[i]->type->get(reg_list[i]);
+ if (!reg_list[i]->valid) {
+ retval = reg_list[i]->type->get(reg_list[i]);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name);
+ free(reg_packet);
+ free(reg_list);
+ return gdb_error(connection, retval);
+ }
+ }
gdb_str_to_target(target, reg_packet_p, reg_list[i]);
reg_packet_p += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
}
@@ -1229,7 +1236,13 @@ static int gdb_set_registers_packet(struct connection *connection,
bin_buf = malloc(DIV_ROUND_UP(reg_list[i]->size, 8));
gdb_target_to_reg(target, packet_p, chars, bin_buf);
- reg_list[i]->type->set(reg_list[i], bin_buf);
+ retval = reg_list[i]->type->set(reg_list[i], bin_buf);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name);
+ free(reg_list);
+ free(bin_buf);
+ return gdb_error(connection, retval);
+ }
/* advance packet pointer */
packet_p += chars;
@@ -1269,8 +1282,14 @@ static int gdb_get_register_packet(struct connection *connection,
return ERROR_SERVER_REMOTE_CLOSED;
}
- if (!reg_list[reg_num]->valid)
- reg_list[reg_num]->type->get(reg_list[reg_num]);
+ if (!reg_list[reg_num]->valid) {
+ retval = reg_list[reg_num]->type->get(reg_list[reg_num]);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name);
+ free (reg_list);
+ return gdb_error(connection, retval);
+ }
+ }
reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */
@@ -1324,7 +1343,13 @@ static int gdb_set_register_packet(struct connection *connection,
gdb_target_to_reg(target, separator + 1, chars, bin_buf);
- reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+ retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+ if (retval != ERROR_OK){
+ LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name);
+ free(bin_buf);
+ free(reg_list);
+ return gdb_error(connection, retval);
+ }
gdb_put_packet(connection, "OK", 2);
@@ -2561,6 +2586,13 @@ static int gdb_v_packet(struct connection *connection,
struct gdb_service *gdb_service = connection->service->priv;
int result;
+ struct target *target = get_target_from_connection(connection);
+ if (target->rtos != NULL && target->rtos->gdb_v_packet != NULL) {
+ int out = target->rtos->gdb_v_packet(connection, packet, packet_size);
+ if (out != GDB_THREAD_PACKET_NOT_CONSUMED)
+ return out;
+ }
+
/* if flash programming disabled - send a empty reply */
if (gdb_flash_program == 0) {
@@ -2759,7 +2791,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
gdb_output_con(connection, string);
}
-static void gdb_sig_halted(struct connection *connection)
+void gdb_sig_halted(struct connection *connection)
{
char sig_reply[4];
snprintf(sig_reply, 4, "T%2.2x", 2);
@@ -3316,3 +3348,9 @@ int gdb_register_commands(struct command_context *cmd_ctx)
gdb_port_next = strdup("3333");
return register_commands(cmd_ctx, NULL, gdb_command_handlers);
}
+
+void gdb_set_frontend_state_running(struct connection *connection)
+{
+ struct gdb_connection *gdb_con = connection->priv;
+ gdb_con->frontend_state = TARGET_RUNNING;
+}
diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h
index 2b4ac4e..1dc30e0 100644
--- a/src/server/gdb_server.h
+++ b/src/server/gdb_server.h
@@ -45,6 +45,9 @@ static inline struct target *get_target_from_connection(struct connection *conne
return gdb_service->target;
}
+void gdb_set_frontend_state_running(struct connection *connection);
+void gdb_sig_halted(struct connection *connection);
+
#define ERROR_GDB_BUFFER_TOO_SMALL (-800)
#define ERROR_GDB_TIMEOUT (-801)
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 597070c..1e81f4a 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -20,6 +20,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la
$(MIPS32_SRC) \
$(NDS32_SRC) \
$(INTEL_IA32_SRC) \
+ $(RISCV_SRC) \
%D%/avrt.c \
%D%/dsp563xx.c \
%D%/dsp563xx_once.c \
@@ -130,6 +131,13 @@ INTEL_IA32_SRC = \
%D%/lakemont.c \
%D%/x86_32_common.c
+RISCV_SRC = \
+ %D%/riscv/riscv-011.c \
+ %D%/riscv/riscv-013.c \
+ %D%/riscv/riscv.c \
+ %D%/riscv/program.c \
+ %D%/riscv/batch.c
+
%C%_libtarget_la_SOURCES += \
%D%/algorithm.h \
%D%/arm.h \
diff --git a/src/target/register.h b/src/target/register.h
index d3b2c31..d4c3281 100644
--- a/src/target/register.h
+++ b/src/target/register.h
@@ -114,17 +114,32 @@ struct reg_data_type {
};
struct reg {
+ /** Canonical name of the register. */
const char *name;
+ /** Number that gdb uses to access this register. */
uint32_t number;
+ /* TODO. This should probably be const. */
struct reg_feature *feature;
+ /* TODO: When true, the caller will save this register before running any algorithm. */
bool caller_save;
+ /* Pointer to place where the value is stored, in the format understood by
+ * the binarybuffer.h functions. */
void *value;
+ /* The stored value needs to be written to the target. */
bool dirty;
+ /* When true, value is valid. */
bool valid;
+ /* When false, the register doesn't actually exist in the target. */
bool exist;
+ /* Size of the register in bits. */
uint32_t size;
+ /* Used for generating XML description of registers. Can be set to NULL for
+ * targets that don't use that. */
struct reg_data_type *reg_data_type;
+ /* Used for generating XML description of registers. Can be set to NULL for
+ * targets that don't use that. */
const char *group;
+ /* Pointer to architecture-specific info for this register. */
void *arch_info;
const struct reg_arch_type *type;
};
diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h
new file mode 100644
index 0000000..051e0f9
--- /dev/null
+++ b/src/target/riscv/asm.h
@@ -0,0 +1,36 @@
+#ifndef TARGET__RISCV__ASM_H
+#define TARGET__RISCV__ASM_H
+
+#include "riscv.h"
+
+/*** Version-independent functions that we don't want in the main address space. ***/
+
+static uint32_t load(const struct target *target, unsigned int rd,
+ unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t load(const struct target *target, unsigned int rd,
+ unsigned int base, uint16_t offset)
+{
+ switch (riscv_xlen(target)) {
+ case 32:
+ return lw(rd, base, offset);
+ case 64:
+ return ld(rd, base, offset);
+ }
+ assert(0);
+}
+
+static uint32_t store(const struct target *target, unsigned int src,
+ unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t store(const struct target *target, unsigned int src,
+ unsigned int base, uint16_t offset)
+{
+ switch (riscv_xlen(target)) {
+ case 32:
+ return sw(src, base, offset);
+ case 64:
+ return sd(src, base, offset);
+ }
+ assert(0);
+}
+
+#endif
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
new file mode 100644
index 0000000..d48b34a
--- /dev/null
+++ b/src/target/riscv/batch.c
@@ -0,0 +1,156 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "batch.h"
+#include "debug_defines.h"
+#include "riscv.h"
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+static void dump_field(const struct scan_field *field);
+
+struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
+{
+ scans += 4;
+ struct riscv_batch *out = malloc(sizeof(*out));
+ memset(out, 0, sizeof(*out));
+ out->target = target;
+ out->allocated_scans = scans;
+ out->used_scans = 0;
+ out->idle_count = idle;
+ out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t));
+ out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t));
+ out->fields = malloc(sizeof(*out->fields) * (scans));
+ out->last_scan = RISCV_SCAN_TYPE_INVALID;
+ out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
+ out->read_keys_used = 0;
+ return out;
+}
+
+void riscv_batch_free(struct riscv_batch *batch)
+{
+ free(batch->data_in);
+ free(batch->data_out);
+ free(batch->fields);
+ free(batch);
+}
+
+bool riscv_batch_full(struct riscv_batch *batch)
+{
+ return batch->used_scans > (batch->allocated_scans - 4);
+}
+
+void riscv_batch_run(struct riscv_batch *batch)
+{
+ LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans);
+ riscv_batch_add_nop(batch);
+
+ for (size_t i = 0; i < batch->used_scans; ++i) {
+ dump_field(batch->fields + i);
+ jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
+ if (batch->idle_count > 0)
+ jtag_add_runtest(batch->idle_count, TAP_IDLE);
+ }
+
+ LOG_DEBUG("executing queue");
+ if (jtag_execute_queue() != ERROR_OK) {
+ LOG_ERROR("Unable to execute JTAG queue");
+ abort();
+ }
+
+ for (size_t i = 0; i < batch->used_scans; ++i)
+ dump_field(batch->fields + i);
+}
+
+void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
+{
+ assert(batch->used_scans < batch->allocated_scans);
+ struct scan_field *field = batch->fields + batch->used_scans;
+ field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
+ field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
+ riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
+ riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ batch->last_scan = RISCV_SCAN_TYPE_WRITE;
+ batch->used_scans++;
+}
+
+size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
+{
+ assert(batch->used_scans < batch->allocated_scans);
+ struct scan_field *field = batch->fields + batch->used_scans;
+ field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
+ field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
+ riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
+ riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ batch->last_scan = RISCV_SCAN_TYPE_READ;
+ batch->used_scans++;
+
+ /* FIXME We get the read response back on the next scan. For now I'm
+ * just sticking a NOP in there, but this should be coelesced away. */
+ riscv_batch_add_nop(batch);
+
+ batch->read_keys[batch->read_keys_used] = batch->used_scans - 1;
+ LOG_DEBUG("read key %ld for batch 0x%p is %ld (0x%p)", batch->read_keys_used, batch, batch->used_scans - 1, (uint64_t*)batch->data_in + (batch->used_scans + 1));
+ return batch->read_keys_used++;
+}
+
+uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key)
+{
+ assert(key < batch->read_keys_used);
+ size_t index = batch->read_keys[key];
+ assert(index <= batch->used_scans);
+ uint64_t *addr = ((uint64_t *)(batch->data_in) + index);
+ return *addr;
+}
+
+void riscv_batch_add_nop(struct riscv_batch *batch)
+{
+ assert(batch->used_scans < batch->allocated_scans);
+ struct scan_field *field = batch->fields + batch->used_scans;
+ field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
+ field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
+ riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
+ riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ batch->last_scan = RISCV_SCAN_TYPE_NOP;
+ batch->used_scans++;
+ LOG_DEBUG(" added NOP with in_value=0x%p", field->in_value);
+}
+
+void dump_field(const struct scan_field *field)
+{
+ static const char *op_string[] = {"-", "r", "w", "?"};
+ static const char *status_string[] = {"+", "?", "F", "b"};
+
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ assert(field->out_value != NULL);
+ uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
+ unsigned int out_op = get_field(out, DTM_DMI_OP);
+ unsigned int out_data = get_field(out, DTM_DMI_DATA);
+ unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
+
+ if (field->in_value) {
+ uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+ unsigned int in_op = get_field(in, DTM_DMI_OP);
+ unsigned int in_data = get_field(in, DTM_DMI_DATA);
+ unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
+
+ log_printf_lf(LOG_LVL_DEBUG,
+ __FILE__, __LINE__, __PRETTY_FUNCTION__,
+ "%db %s %08x @%02x -> %s %08x @%02x [0x%p -> 0x%p]",
+ field->num_bits,
+ op_string[out_op], out_data, out_address,
+ status_string[in_op], in_data, in_address,
+ field->out_value, field->in_value);
+ } else {
+ log_printf_lf(LOG_LVL_DEBUG,
+ __FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?",
+ field->num_bits, op_string[out_op], out_data, out_address);
+ }
+}
diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h
new file mode 100644
index 0000000..776d93a
--- /dev/null
+++ b/src/target/riscv/batch.h
@@ -0,0 +1,64 @@
+#ifndef TARGET__RISCV__SCANS_H
+#define TARGET__RISCV__SCANS_H
+
+#include "target/target.h"
+#include "jtag/jtag.h"
+
+enum riscv_scan_type {
+ RISCV_SCAN_TYPE_INVALID,
+ RISCV_SCAN_TYPE_NOP,
+ RISCV_SCAN_TYPE_READ,
+ RISCV_SCAN_TYPE_WRITE,
+};
+
+/* A batch of multiple JTAG scans, which are grouped together to avoid the
+ * overhead of some JTAG adapters when sending single commands. This is
+ * designed to support block copies, as that's what we actually need to go
+ * fast. */
+struct riscv_batch {
+ struct target *target;
+
+ size_t allocated_scans;
+ size_t used_scans;
+
+ size_t idle_count;
+
+ char *data_out;
+ char *data_in;
+ struct scan_field *fields;
+
+ /* In JTAG we scan out the previous value's output when performing a
+ * scan. This is a pain for users, so we just provide them the
+ * illusion of not having to do this by eliding all but the last NOP.
+ * */
+ enum riscv_scan_type last_scan;
+
+ /* The read keys. */
+ size_t *read_keys;
+ size_t read_keys_used;
+};
+
+/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
+ * scans that can be issued to this object, and idle is the number of JTAG idle
+ * cycles between every real scan. */
+struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle);
+void riscv_batch_free(struct riscv_batch *batch);
+
+/* Checks to see if this batch is full. */
+bool riscv_batch_full(struct riscv_batch *batch);
+
+/* Executes this scan batch. */
+void riscv_batch_run(struct riscv_batch *batch);
+
+/* Adds a DMI write to this batch. */
+void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
+
+/* DMI reads must be handled in two parts: the first one schedules a read and
+ * provides a key, the second one actually obtains the value of that read .*/
+size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address);
+uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key);
+
+/* Scans in a NOP. */
+void riscv_batch_add_nop(struct riscv_batch *batch);
+
+#endif
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
new file mode 100644
index 0000000..2fb541b
--- /dev/null
+++ b/src/target/riscv/debug_defines.h
@@ -0,0 +1,1410 @@
+#define DTM_IDCODE 0x01
+/*
+* Identifies the release version of this part.
+ */
+#define DTM_IDCODE_VERSION_OFFSET 28
+#define DTM_IDCODE_VERSION_LENGTH 4
+#define DTM_IDCODE_VERSION (0xf << DTM_IDCODE_VERSION_OFFSET)
+/*
+* Identifies the designer's part number of this part.
+ */
+#define DTM_IDCODE_PARTNUMBER_OFFSET 12
+#define DTM_IDCODE_PARTNUMBER_LENGTH 16
+#define DTM_IDCODE_PARTNUMBER (0xffff << DTM_IDCODE_PARTNUMBER_OFFSET)
+/*
+* Identifies the designer/manufacturer of this part. Bits 6:0 must be
+* bits 6:0 of the designer/manufacturer's Identification Code as
+* assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16
+* count of the number of continuation characters (0x7f) in that same
+* Identification Code.
+ */
+#define DTM_IDCODE_MANUFID_OFFSET 1
+#define DTM_IDCODE_MANUFID_LENGTH 11
+#define DTM_IDCODE_MANUFID (0x7ff << DTM_IDCODE_MANUFID_OFFSET)
+#define DTM_IDCODE_1_OFFSET 0
+#define DTM_IDCODE_1_LENGTH 1
+#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET)
+#define DTM_DTMCS 0x10
+/*
+* Writing 1 to this bit does a hard reset of the DTM,
+* causing the DTM to forget about any outstanding DMI transactions.
+* In general this should only be used when the Debugger has
+* reason to expect that the outstanding DMI transaction will never
+* complete (e.g. a reset condition caused an inflight DMI transaction to
+* be cancelled).
+ */
+#define DTM_DTMCS_DMIHARDRESET_OFFSET 17
+#define DTM_DTMCS_DMIHARDRESET_LENGTH 1
+#define DTM_DTMCS_DMIHARDRESET (0x1 << DTM_DTMCS_DMIHARDRESET_OFFSET)
+/*
+* Writing 1 to this bit clears the sticky error state
+* and allows the DTM to retry or complete the previous
+* transaction.
+ */
+#define DTM_DTMCS_DMIRESET_OFFSET 16
+#define DTM_DTMCS_DMIRESET_LENGTH 1
+#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET)
+/*
+* This is a hint to the debugger of the minimum number of
+* cycles a debugger should spend in
+* Run-Test/Idle after every DMI scan to avoid a `busy'
+* return code (\Fdmistat of 3). A debugger must still
+* check \Fdmistat when necessary.
+*
+* 0: It is not necessary to enter Run-Test/Idle at all.
+*
+* 1: Enter Run-Test/Idle and leave it immediately.
+*
+* 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving.
+*
+* And so on.
+ */
+#define DTM_DTMCS_IDLE_OFFSET 12
+#define DTM_DTMCS_IDLE_LENGTH 3
+#define DTM_DTMCS_IDLE (0x7 << DTM_DTMCS_IDLE_OFFSET)
+/*
+* 0: No error.
+*
+* 1: Reserved. Interpret the same as 2.
+*
+* 2: An operation failed (resulted in \Fop of 2).
+*
+* 3: An operation was attempted while a DMI access was still in
+* progress (resulted in \Fop of 3).
+ */
+#define DTM_DTMCS_DMISTAT_OFFSET 10
+#define DTM_DTMCS_DMISTAT_LENGTH 2
+#define DTM_DTMCS_DMISTAT (0x3 << DTM_DTMCS_DMISTAT_OFFSET)
+/*
+* The size of \Faddress in \Rdmi.
+ */
+#define DTM_DTMCS_ABITS_OFFSET 4
+#define DTM_DTMCS_ABITS_LENGTH 6
+#define DTM_DTMCS_ABITS (0x3f << DTM_DTMCS_ABITS_OFFSET)
+/*
+* 0: Version described in spec version 0.11.
+*
+* 1: Version described in spec version 0.12 (and later?), which
+* reduces the DMI data width to 32 bits.
+*
+* Other values are reserved for future use.
+ */
+#define DTM_DTMCS_VERSION_OFFSET 0
+#define DTM_DTMCS_VERSION_LENGTH 4
+#define DTM_DTMCS_VERSION (0xf << DTM_DTMCS_VERSION_OFFSET)
+#define DTM_DMI 0x11
+/*
+* Address used for DMI access. In Update-DR this value is used
+* to access the DM over the DMI.
+ */
+#define DTM_DMI_ADDRESS_OFFSET 34
+#define DTM_DMI_ADDRESS_LENGTH abits
+#define DTM_DMI_ADDRESS (((1L<<abits)-1) << DTM_DMI_ADDRESS_OFFSET)
+/*
+* The data to send to the DM over the DMI during Update-DR, and
+* the data returned from the DM as a result of the previous operation.
+ */
+#define DTM_DMI_DATA_OFFSET 2
+#define DTM_DMI_DATA_LENGTH 32
+#define DTM_DMI_DATA (0xffffffffL << DTM_DMI_DATA_OFFSET)
+/*
+* When the debugger writes this field, it has the following meaning:
+*
+* 0: Ignore \Fdata. (nop)
+*
+* 1: Read from \Faddress. (read)
+*
+* 2: Write \Fdata to \Faddress. (write)
+*
+* 3: Reserved.
+*
+* When the debugger reads this field, it means the following:
+*
+* 0: The previous operation completed successfully.
+*
+* 1: Reserved.
+*
+* 2: A previous operation failed. The data scanned into \Rdmi in
+* this access will be ignored. This status is sticky and can be
+* cleared by writing \Fdmireset in \Rdtmcs.
+*
+* This indicates that the DM itself responded with an error, e.g.
+* in the System Bus and Serial Port overflow/underflow cases.
+*
+* 3: An operation was attempted while a DMI request is still in
+* progress. The data scanned into \Rdmi in this access will be
+* ignored. This status is sticky and can be cleared by writing
+* \Fdmireset in \Rdtmcs. If a debugger sees this status, it
+* needs to give the target more TCK edges between Update-DR and
+* Capture-DR. The simplest way to do that is to add extra transitions
+* in Run-Test/Idle.
+*
+* (The DTM, DM, and/or component may be in different clock domains,
+* so synchronization may be required. Some relatively fixed number of
+* TCK ticks may be needed for the request to reach the DM, complete,
+* and for the response to be synchronized back into the TCK domain.)
+ */
+#define DTM_DMI_OP_OFFSET 0
+#define DTM_DMI_OP_LENGTH 2
+#define DTM_DMI_OP (0x3L << DTM_DMI_OP_OFFSET)
+#define CSR_DCSR 0x7b0
+/*
+* 0: There is no external debug support.
+*
+* 1: External debug support exists as it is described in this document.
+*
+* Other values are reserved for future standards.
+ */
+#define CSR_DCSR_XDEBUGVER_OFFSET 30
+#define CSR_DCSR_XDEBUGVER_LENGTH 2
+#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKM_OFFSET 15
+#define CSR_DCSR_EBREAKM_LENGTH 1
+#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKH_OFFSET 14
+#define CSR_DCSR_EBREAKH_LENGTH 1
+#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKS_OFFSET 13
+#define CSR_DCSR_EBREAKS_LENGTH 1
+#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in User/Application Mode enter
+* Debug Mode.
+ */
+#define CSR_DCSR_EBREAKU_OFFSET 12
+#define CSR_DCSR_EBREAKU_LENGTH 1
+#define CSR_DCSR_EBREAKU (0x1 << CSR_DCSR_EBREAKU_OFFSET)
+/*
+* 0: Increment counters as usual.
+*
+* 1: Don't increment any counters while in Debug Mode. This includes
+* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most
+* debugging scenarios.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPCOUNT_OFFSET 10
+#define CSR_DCSR_STOPCOUNT_LENGTH 1
+#define CSR_DCSR_STOPCOUNT (0x1 << CSR_DCSR_STOPCOUNT_OFFSET)
+/*
+* 0: Increment timers as usual.
+*
+* 1: Don't increment any hart-local timers while in Debug Mode.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPTIME_OFFSET 9
+#define CSR_DCSR_STOPTIME_LENGTH 1
+#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET)
+/*
+* Explains why Debug Mode was entered.
+*
+* When there are multiple reasons to enter Debug Mode in a single
+* cycle, the cause with the highest priority is the one written.
+*
+* 1: An {\tt ebreak} instruction was executed. (priority 3)
+*
+* 2: The Trigger Module caused a halt. (priority 4)
+*
+* 3: \Fhaltreq was set. (priority 2)
+*
+* 4: The hart single stepped because \Fstep was set. (priority 1)
+*
+* Other values are reserved for future use.
+ */
+#define CSR_DCSR_CAUSE_OFFSET 6
+#define CSR_DCSR_CAUSE_LENGTH 3
+#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET)
+/*
+* When set and not in Debug Mode, the hart will only execute a single
+* instruction, and then enter Debug Mode. Interrupts are disabled
+* when this bit is set.
+ */
+#define CSR_DCSR_STEP_OFFSET 2
+#define CSR_DCSR_STEP_LENGTH 1
+#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET)
+/*
+* Contains the privilege level the hart was operating in when Debug
+* Mode was entered. The encoding is described in Table
+* \ref{tab:privlevel}. A debugger can change this value to change
+* the hart's privilege level when exiting Debug Mode.
+*
+* Not all privilege levels are supported on all harts. If the
+* encoding written is not supported or the debugger is not allowed to
+* change to it, the hart may change to any supported privilege level.
+ */
+#define CSR_DCSR_PRV_OFFSET 0
+#define CSR_DCSR_PRV_LENGTH 2
+#define CSR_DCSR_PRV (0x3 << CSR_DCSR_PRV_OFFSET)
+#define CSR_DPC 0x7b1
+#define CSR_DPC_DPC_OFFSET 0
+#define CSR_DPC_DPC_LENGTH XLEN
+#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET)
+#define CSR_DSCRATCH0 0x7b2
+#define CSR_DSCRATCH1 0x7b3
+#define CSR_PRIV virtual
+/*
+* Contains the privilege level the hart was operating in when Debug
+* Mode was entered. The encoding is described in Table
+* \ref{tab:privlevel}. A user can write this value to change the
+* hart's privilege level when exiting Debug Mode.
+ */
+#define CSR_PRIV_PRV_OFFSET 0
+#define CSR_PRIV_PRV_LENGTH 2
+#define CSR_PRIV_PRV (0x3 << CSR_PRIV_PRV_OFFSET)
+#define CSR_TSELECT 0x7a0
+#define CSR_TSELECT_INDEX_OFFSET 0
+#define CSR_TSELECT_INDEX_LENGTH XLEN
+#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
+#define CSR_TDATA1 0x7a1
+/*
+* 0: There is no trigger at this \Rtselect.
+*
+* 1: The trigger is a legacy SiFive address match trigger. These
+* should not be implemented and aren't further documented here.
+*
+* 2: The trigger is an address/data match trigger. The remaining bits
+* in this register act as described in \Rmcontrol.
+*
+* 3: The trigger is an instruction count trigger. The remaining bits
+* in this register act as described in \Ricount.
+*
+* 15: This trigger exists (so enumeration shouldn't terminate), but
+* is not currently available.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_TDATA1_TYPE_OFFSET XLEN-4
+#define CSR_TDATA1_TYPE_LENGTH 4
+#define CSR_TDATA1_TYPE (0xfL << CSR_TDATA1_TYPE_OFFSET)
+/*
+* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
+* selected \Rtselect.
+*
+* 1: Only Debug Mode can write the {\tt tdata} registers at the
+* selected \Rtselect. Writes from other modes are ignored.
+*
+* This bit is only writable from Debug Mode.
+ */
+#define CSR_TDATA1_HMODE_OFFSET XLEN-5
+#define CSR_TDATA1_HMODE_LENGTH 1
+#define CSR_TDATA1_HMODE (0x1L << CSR_TDATA1_HMODE_OFFSET)
+/*
+* Trigger-specific data.
+ */
+#define CSR_TDATA1_DATA_OFFSET 0
+#define CSR_TDATA1_DATA_LENGTH XLEN - 5
+#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA2_DATA_OFFSET 0
+#define CSR_TDATA2_DATA_LENGTH XLEN
+#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
+#define CSR_TDATA3 0x7a3
+#define CSR_TDATA3_DATA_OFFSET 0
+#define CSR_TDATA3_DATA_LENGTH XLEN
+#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_MCONTROL 0x7a1
+#define CSR_MCONTROL_TYPE_OFFSET XLEN-4
+#define CSR_MCONTROL_TYPE_LENGTH 4
+#define CSR_MCONTROL_TYPE (0xfL << CSR_MCONTROL_TYPE_OFFSET)
+#define CSR_MCONTROL_DMODE_OFFSET XLEN-5
+#define CSR_MCONTROL_DMODE_LENGTH 1
+#define CSR_MCONTROL_DMODE (0x1L << CSR_MCONTROL_DMODE_OFFSET)
+/*
+* Specifies the largest naturally aligned powers-of-two (NAPOT) range
+* supported by the hardware. The value is the logarithm base 2 of the
+* number of bytes in that range. A value of 0 indicates that only
+* exact value matches are supported (one byte range). A value of 63
+* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
+* size.
+ */
+#define CSR_MCONTROL_MASKMAX_OFFSET XLEN-11
+#define CSR_MCONTROL_MASKMAX_LENGTH 6
+#define CSR_MCONTROL_MASKMAX (0x3fL << CSR_MCONTROL_MASKMAX_OFFSET)
+/*
+* 0: Perform a match on the virtual address.
+*
+* 1: Perform a match on the data value loaded/stored, or the
+* instruction executed.
+ */
+#define CSR_MCONTROL_SELECT_OFFSET 19
+#define CSR_MCONTROL_SELECT_LENGTH 1
+#define CSR_MCONTROL_SELECT (0x1L << CSR_MCONTROL_SELECT_OFFSET)
+/*
+* 0: The action for this trigger will be taken just before the
+* instruction that triggered it is executed, but after all preceding
+* instructions are are committed.
+*
+* 1: The action for this trigger will be taken after the instruction
+* that triggered it is executed. It should be taken before the next
+* instruction is executed, but it is better to implement triggers and
+* not implement that suggestion than to not implement them at all.
+*
+* Most hardware will only implement one timing or the other, possibly
+* dependent on \Fselect, \Fexecute, \Fload, and \Fstore. This bit
+* primarily exists for the hardware to communicate to the debugger
+* what will happen. Hardware may implement the bit fully writable, in
+* which case the debugger has a little more control.
+*
+* Data load triggers with \Ftiming of 0 will result in the same load
+* happening again when the debugger lets the core run. For data load
+* triggers, debuggers must first attempt to set the breakpoint with
+* \Ftiming of 1.
+*
+* A chain of triggers that don't all have the same \Ftiming value
+* will never fire (unless consecutive instructions match the
+* appropriate triggers).
+ */
+#define CSR_MCONTROL_TIMING_OFFSET 18
+#define CSR_MCONTROL_TIMING_LENGTH 1
+#define CSR_MCONTROL_TIMING (0x1L << CSR_MCONTROL_TIMING_OFFSET)
+/*
+* Determines what happens when this trigger matches.
+*
+* 0: Raise a breakpoint exception. (Used when software wants to use
+* the trigger module without an external debugger attached.)
+*
+* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
+*
+* 2: Start tracing.
+*
+* 3: Stop tracing.
+*
+* 4: Emit trace data for this match. If it is a data access match,
+* emit appropriate Load/Store Address/Data. If it is an instruction
+* execution, emit its PC.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_MCONTROL_ACTION_OFFSET 12
+#define CSR_MCONTROL_ACTION_LENGTH 6
+#define CSR_MCONTROL_ACTION (0x3fL << CSR_MCONTROL_ACTION_OFFSET)
+/*
+* 0: When this trigger matches, the configured action is taken.
+*
+* 1: While this trigger does not match, it prevents the trigger with
+* the next index from matching.
+ */
+#define CSR_MCONTROL_CHAIN_OFFSET 11
+#define CSR_MCONTROL_CHAIN_LENGTH 1
+#define CSR_MCONTROL_CHAIN (0x1L << CSR_MCONTROL_CHAIN_OFFSET)
+/*
+* 0: Matches when the value equals \Rtdatatwo.
+*
+* 1: Matches when the top M bits of the value match the top M bits of
+* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
+* bit containing 0 in \Rtdatatwo.
+*
+* 2: Matches when the value is greater than or equal to \Rtdatatwo.
+*
+* 3: Matches when the value is less than \Rtdatatwo.
+*
+* 4: Matches when the lower half of the value equals the lower half
+* of \Rtdatatwo after the lower half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* 5: Matches when the upper half of the value equals the lower half
+* of \Rtdatatwo after the upper half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_MCONTROL_MATCH_OFFSET 7
+#define CSR_MCONTROL_MATCH_LENGTH 4
+#define CSR_MCONTROL_MATCH (0xfL << CSR_MCONTROL_MATCH_OFFSET)
+/*
+* When set, enable this trigger in M mode.
+ */
+#define CSR_MCONTROL_M_OFFSET 6
+#define CSR_MCONTROL_M_LENGTH 1
+#define CSR_MCONTROL_M (0x1L << CSR_MCONTROL_M_OFFSET)
+/*
+* When set, enable this trigger in H mode.
+ */
+#define CSR_MCONTROL_H_OFFSET 5
+#define CSR_MCONTROL_H_LENGTH 1
+#define CSR_MCONTROL_H (0x1L << CSR_MCONTROL_H_OFFSET)
+/*
+* When set, enable this trigger in S mode.
+ */
+#define CSR_MCONTROL_S_OFFSET 4
+#define CSR_MCONTROL_S_LENGTH 1
+#define CSR_MCONTROL_S (0x1L << CSR_MCONTROL_S_OFFSET)
+/*
+* When set, enable this trigger in U mode.
+ */
+#define CSR_MCONTROL_U_OFFSET 3
+#define CSR_MCONTROL_U_LENGTH 1
+#define CSR_MCONTROL_U (0x1L << CSR_MCONTROL_U_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or opcode of an
+* instruction that is executed.
+ */
+#define CSR_MCONTROL_EXECUTE_OFFSET 2
+#define CSR_MCONTROL_EXECUTE_LENGTH 1
+#define CSR_MCONTROL_EXECUTE (0x1L << CSR_MCONTROL_EXECUTE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a store.
+ */
+#define CSR_MCONTROL_STORE_OFFSET 1
+#define CSR_MCONTROL_STORE_LENGTH 1
+#define CSR_MCONTROL_STORE (0x1L << CSR_MCONTROL_STORE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a load.
+ */
+#define CSR_MCONTROL_LOAD_OFFSET 0
+#define CSR_MCONTROL_LOAD_LENGTH 1
+#define CSR_MCONTROL_LOAD (0x1L << CSR_MCONTROL_LOAD_OFFSET)
+#define CSR_ICOUNT 0x7a1
+#define CSR_ICOUNT_TYPE_OFFSET XLEN-4
+#define CSR_ICOUNT_TYPE_LENGTH 4
+#define CSR_ICOUNT_TYPE (0xfL << CSR_ICOUNT_TYPE_OFFSET)
+#define CSR_ICOUNT_DMODE_OFFSET XLEN-5
+#define CSR_ICOUNT_DMODE_LENGTH 1
+#define CSR_ICOUNT_DMODE (0x1L << CSR_ICOUNT_DMODE_OFFSET)
+/*
+* When count is decremented to 0, the trigger fires. Instead of
+* changing \Fcount from 1 to 0, it is also acceptable for hardware to
+* clear \Fm, \Fh, \Fs, and \Fu. This allows \Fcount to be hard-wired
+* to 1 if this register just exists for single step.
+ */
+#define CSR_ICOUNT_COUNT_OFFSET 10
+#define CSR_ICOUNT_COUNT_LENGTH 14
+#define CSR_ICOUNT_COUNT (0x3fffL << CSR_ICOUNT_COUNT_OFFSET)
+/*
+* When set, every instruction completed in M mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_M_OFFSET 9
+#define CSR_ICOUNT_M_LENGTH 1
+#define CSR_ICOUNT_M (0x1L << CSR_ICOUNT_M_OFFSET)
+/*
+* When set, every instruction completed in H mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_H_OFFSET 8
+#define CSR_ICOUNT_H_LENGTH 1
+#define CSR_ICOUNT_H (0x1L << CSR_ICOUNT_H_OFFSET)
+/*
+* When set, every instruction completed in S mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_S_OFFSET 7
+#define CSR_ICOUNT_S_LENGTH 1
+#define CSR_ICOUNT_S (0x1L << CSR_ICOUNT_S_OFFSET)
+/*
+* When set, every instruction completed in U mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_U_OFFSET 6
+#define CSR_ICOUNT_U_LENGTH 1
+#define CSR_ICOUNT_U (0x1L << CSR_ICOUNT_U_OFFSET)
+/*
+* Determines what happens when this trigger matches.
+*
+* 0: Raise a breakpoint exception. (Used when software wants to use the
+* trigger module without an external debugger attached.)
+*
+* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
+*
+* 2: Start tracing.
+*
+* 3: Stop tracing.
+*
+* 4: Emit trace data for this match. If it is a data access match,
+* emit appropriate Load/Store Address/Data. If it is an instruction
+* execution, emit its PC.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_ICOUNT_ACTION_OFFSET 0
+#define CSR_ICOUNT_ACTION_LENGTH 6
+#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET)
+#define DMI_DMSTATUS 0x11
+/*
+* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq.
+ */
+#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17
+#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1
+#define DMI_DMSTATUS_ALLRESUMEACK (0x1 << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
+/*
+* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq.
+ */
+#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16
+#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1
+#define DMI_DMSTATUS_ANYRESUMEACK (0x1 << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
+/*
+* This field is 1 when all currently selected harts do not exist in this system.
+ */
+#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
+#define DMI_DMSTATUS_ALLNONEXISTENT_LENGTH 1
+#define DMI_DMSTATUS_ALLNONEXISTENT (0x1 << DMI_DMSTATUS_ALLNONEXISTENT_OFFSET)
+/*
+* This field is 1 when any currently selected hart does not exist in this system.
+ */
+#define DMI_DMSTATUS_ANYNONEXISTENT_OFFSET 14
+#define DMI_DMSTATUS_ANYNONEXISTENT_LENGTH 1
+#define DMI_DMSTATUS_ANYNONEXISTENT (0x1 << DMI_DMSTATUS_ANYNONEXISTENT_OFFSET)
+/*
+* This field is 1 when all currently selected harts are unavailable.
+ */
+#define DMI_DMSTATUS_ALLUNAVAIL_OFFSET 13
+#define DMI_DMSTATUS_ALLUNAVAIL_LENGTH 1
+#define DMI_DMSTATUS_ALLUNAVAIL (0x1 << DMI_DMSTATUS_ALLUNAVAIL_OFFSET)
+/*
+* This field is 1 when any currently selected hart is unavailable.
+ */
+#define DMI_DMSTATUS_ANYUNAVAIL_OFFSET 12
+#define DMI_DMSTATUS_ANYUNAVAIL_LENGTH 1
+#define DMI_DMSTATUS_ANYUNAVAIL (0x1 << DMI_DMSTATUS_ANYUNAVAIL_OFFSET)
+/*
+* This field is 1 when all currently selected harts are running.
+ */
+#define DMI_DMSTATUS_ALLRUNNING_OFFSET 11
+#define DMI_DMSTATUS_ALLRUNNING_LENGTH 1
+#define DMI_DMSTATUS_ALLRUNNING (0x1 << DMI_DMSTATUS_ALLRUNNING_OFFSET)
+/*
+* This field is 1 when any currently selected hart is running.
+ */
+#define DMI_DMSTATUS_ANYRUNNING_OFFSET 10
+#define DMI_DMSTATUS_ANYRUNNING_LENGTH 1
+#define DMI_DMSTATUS_ANYRUNNING (0x1 << DMI_DMSTATUS_ANYRUNNING_OFFSET)
+/*
+* This field is 1 when all currently selected harts are halted.
+ */
+#define DMI_DMSTATUS_ALLHALTED_OFFSET 9
+#define DMI_DMSTATUS_ALLHALTED_LENGTH 1
+#define DMI_DMSTATUS_ALLHALTED (0x1 << DMI_DMSTATUS_ALLHALTED_OFFSET)
+/*
+* This field is 1 when any currently selected hart is halted.
+ */
+#define DMI_DMSTATUS_ANYHALTED_OFFSET 8
+#define DMI_DMSTATUS_ANYHALTED_LENGTH 1
+#define DMI_DMSTATUS_ANYHALTED (0x1 << DMI_DMSTATUS_ANYHALTED_OFFSET)
+/*
+* 0 when authentication is required before using the DM. 1 when the
+* authentication check has passed. On components that don't implement
+* authentication, this bit must be preset as 1.
+ */
+#define DMI_DMSTATUS_AUTHENTICATED_OFFSET 7
+#define DMI_DMSTATUS_AUTHENTICATED_LENGTH 1
+#define DMI_DMSTATUS_AUTHENTICATED (0x1 << DMI_DMSTATUS_AUTHENTICATED_OFFSET)
+/*
+* 0: The authentication module is ready to process the next
+* read/write to \Rauthdata.
+*
+* 1: The authentication module is busy. Accessing \Rauthdata results
+* in unspecified behavior.
+*
+* \Fauthbusy only becomes set in immediate response to an access to
+* \Rauthdata.
+ */
+#define DMI_DMSTATUS_AUTHBUSY_OFFSET 6
+#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1
+#define DMI_DMSTATUS_AUTHBUSY (0x1 << DMI_DMSTATUS_AUTHBUSY_OFFSET)
+#define DMI_DMSTATUS_CFGSTRVALID_OFFSET 4
+#define DMI_DMSTATUS_CFGSTRVALID_LENGTH 1
+#define DMI_DMSTATUS_CFGSTRVALID (0x1 << DMI_DMSTATUS_CFGSTRVALID_OFFSET)
+/*
+* Reserved for future use. Reads as 0.
+ */
+#define DMI_DMSTATUS_VERSIONHI_OFFSET 2
+#define DMI_DMSTATUS_VERSIONHI_LENGTH 2
+#define DMI_DMSTATUS_VERSIONHI (0x3 << DMI_DMSTATUS_VERSIONHI_OFFSET)
+/*
+* 00: There is no Debug Module present.
+*
+* 01: There is a Debug Module and it conforms to version 0.11 of this
+* specification.
+*
+* 10: There is a Debug Module and it conforms to version 0.13 of this
+* specification.
+*
+* 11: Reserved for future use.
+ */
+#define DMI_DMSTATUS_VERSIONLO_OFFSET 0
+#define DMI_DMSTATUS_VERSIONLO_LENGTH 2
+#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET)
+#define DMI_DMCONTROL 0x10
+/*
+* Halt request signal for all currently selected harts. When set to 1, the
+* hart will halt if it is not currently halted.
+* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HALTREQ_OFFSET 31
+#define DMI_DMCONTROL_HALTREQ_LENGTH 1
+#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET)
+/*
+* Resume request signal for all currently selected harts. When set to 1,
+* the hart will resume if it is currently halted.
+* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_RESUMEREQ_OFFSET 30
+#define DMI_DMCONTROL_RESUMEREQ_LENGTH 1
+#define DMI_DMCONTROL_RESUMEREQ (0x1 << DMI_DMCONTROL_RESUMEREQ_OFFSET)
+/*
+* This optional bit controls reset to all the currently selected harts.
+* To perform a reset the debugger writes 1, and then writes 0 to
+* deassert the reset signal.
+*
+* If this feature is not implemented, the bit always stays 0, so
+* after writing 1 the debugger can read the register back to see if
+* the feature is supported.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HARTRESET_OFFSET 29
+#define DMI_DMCONTROL_HARTRESET_LENGTH 1
+#define DMI_DMCONTROL_HARTRESET (0x1 << DMI_DMCONTROL_HARTRESET_OFFSET)
+/*
+* Selects the definition of currently selected harts.
+*
+* 0: There is a single currently selected hart, that selected by \Fhartsel.
+*
+* 1: There may be multiple currently selected harts -- that selected by \Fhartsel,
+* plus those selected by the hart array mask register.
+*
+* An implementation which does not implement the hart array mask register
+* should tie this field to 0. A debugger which wishes to use the hart array
+* mask register feature should set this bit and read back to see if the functionality
+* is supported.
+ */
+#define DMI_DMCONTROL_HASEL_OFFSET 26
+#define DMI_DMCONTROL_HASEL_LENGTH 1
+#define DMI_DMCONTROL_HASEL (0x1 << DMI_DMCONTROL_HASEL_OFFSET)
+/*
+* The DM-specific index of the hart to select. This hart is always part of the
+* currently selected harts.
+ */
+#define DMI_DMCONTROL_HARTSEL_OFFSET 16
+#define DMI_DMCONTROL_HARTSEL_LENGTH 10
+#define DMI_DMCONTROL_HARTSEL (0x3ff << DMI_DMCONTROL_HARTSEL_OFFSET)
+/*
+* This bit controls the reset signal from the DM to the rest of the
+* system. To perform a reset the debugger writes 1, and then writes 0
+* to deassert the reset.
+ */
+#define DMI_DMCONTROL_NDMRESET_OFFSET 1
+#define DMI_DMCONTROL_NDMRESET_LENGTH 1
+#define DMI_DMCONTROL_NDMRESET (0x1 << DMI_DMCONTROL_NDMRESET_OFFSET)
+/*
+* This bit serves as a reset signal for the Debug Module itself.
+*
+* 0: The module's state, including authentication mechanism,
+* takes its reset values (the \Fdmactive bit is the only bit which can
+* be written to something other than its reset value).
+*
+* 1: The module functions normally.
+*
+* No other mechanism should exist that may result in resetting the
+* Debug Module after power up, including the platform's system reset
+* or Debug Transport reset signals.
+*
+* A debugger should pulse this bit low to ensure that the Debug
+* Module is fully reset and ready to use.
+*
+* Implementations may use this bit to aid debugging, for example by
+* preventing the Debug Module from being power gated while debugging
+* is active.
+ */
+#define DMI_DMCONTROL_DMACTIVE_OFFSET 0
+#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
+#define DMI_DMCONTROL_DMACTIVE (0x1 << DMI_DMCONTROL_DMACTIVE_OFFSET)
+#define DMI_HARTINFO 0x12
+/*
+* Number of {\tt dscratch} registers available for the debugger
+* to use during program buffer execution, starting from \Rdscratchzero.
+* The debugger can make no assumptions about the contents of these
+* registers between commands.
+ */
+#define DMI_HARTINFO_NSCRATCH_OFFSET 20
+#define DMI_HARTINFO_NSCRATCH_LENGTH 4
+#define DMI_HARTINFO_NSCRATCH (0xf << DMI_HARTINFO_NSCRATCH_OFFSET)
+/*
+* 0: The {\tt data} registers are shadowed in the hart by CSR
+* registers. Each CSR register is XLEN bits in size, and corresponds
+* to a single argument, per Table~\ref{tab:datareg}.
+*
+* 1: The {\tt data} registers are shadowed in the hart's memory map.
+* Each register takes up 4 bytes in the memory map.
+ */
+#define DMI_HARTINFO_DATAACCESS_OFFSET 16
+#define DMI_HARTINFO_DATAACCESS_LENGTH 1
+#define DMI_HARTINFO_DATAACCESS (0x1 << DMI_HARTINFO_DATAACCESS_OFFSET)
+/*
+* If \Fdataaccess is 0: Number of CSR registers dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Number of 32-bit words in the memory map
+* dedicated to shadowing the {\tt data} registers.
+ */
+#define DMI_HARTINFO_DATASIZE_OFFSET 12
+#define DMI_HARTINFO_DATASIZE_LENGTH 4
+#define DMI_HARTINFO_DATASIZE (0xf << DMI_HARTINFO_DATASIZE_OFFSET)
+/*
+* If \Fdataaccess is 0: The number of the first CSR dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Signed address of RAM where the {\tt data}
+* registers are shadowed.
+ */
+#define DMI_HARTINFO_DATAADDR_OFFSET 0
+#define DMI_HARTINFO_DATAADDR_LENGTH 12
+#define DMI_HARTINFO_DATAADDR (0xfff << DMI_HARTINFO_DATAADDR_OFFSET)
+#define DMI_HALTSUM 0x13
+#define DMI_HALTSUM_HALT1023_992_OFFSET 31
+#define DMI_HALTSUM_HALT1023_992_LENGTH 1
+#define DMI_HALTSUM_HALT1023_992 (0x1 << DMI_HALTSUM_HALT1023_992_OFFSET)
+#define DMI_HALTSUM_HALT991_960_OFFSET 30
+#define DMI_HALTSUM_HALT991_960_LENGTH 1
+#define DMI_HALTSUM_HALT991_960 (0x1 << DMI_HALTSUM_HALT991_960_OFFSET)
+#define DMI_HALTSUM_HALT959_928_OFFSET 29
+#define DMI_HALTSUM_HALT959_928_LENGTH 1
+#define DMI_HALTSUM_HALT959_928 (0x1 << DMI_HALTSUM_HALT959_928_OFFSET)
+#define DMI_HALTSUM_HALT927_896_OFFSET 28
+#define DMI_HALTSUM_HALT927_896_LENGTH 1
+#define DMI_HALTSUM_HALT927_896 (0x1 << DMI_HALTSUM_HALT927_896_OFFSET)
+#define DMI_HALTSUM_HALT895_864_OFFSET 27
+#define DMI_HALTSUM_HALT895_864_LENGTH 1
+#define DMI_HALTSUM_HALT895_864 (0x1 << DMI_HALTSUM_HALT895_864_OFFSET)
+#define DMI_HALTSUM_HALT863_832_OFFSET 26
+#define DMI_HALTSUM_HALT863_832_LENGTH 1
+#define DMI_HALTSUM_HALT863_832 (0x1 << DMI_HALTSUM_HALT863_832_OFFSET)
+#define DMI_HALTSUM_HALT831_800_OFFSET 25
+#define DMI_HALTSUM_HALT831_800_LENGTH 1
+#define DMI_HALTSUM_HALT831_800 (0x1 << DMI_HALTSUM_HALT831_800_OFFSET)
+#define DMI_HALTSUM_HALT799_768_OFFSET 24
+#define DMI_HALTSUM_HALT799_768_LENGTH 1
+#define DMI_HALTSUM_HALT799_768 (0x1 << DMI_HALTSUM_HALT799_768_OFFSET)
+#define DMI_HALTSUM_HALT767_736_OFFSET 23
+#define DMI_HALTSUM_HALT767_736_LENGTH 1
+#define DMI_HALTSUM_HALT767_736 (0x1 << DMI_HALTSUM_HALT767_736_OFFSET)
+#define DMI_HALTSUM_HALT735_704_OFFSET 22
+#define DMI_HALTSUM_HALT735_704_LENGTH 1
+#define DMI_HALTSUM_HALT735_704 (0x1 << DMI_HALTSUM_HALT735_704_OFFSET)
+#define DMI_HALTSUM_HALT703_672_OFFSET 21
+#define DMI_HALTSUM_HALT703_672_LENGTH 1
+#define DMI_HALTSUM_HALT703_672 (0x1 << DMI_HALTSUM_HALT703_672_OFFSET)
+#define DMI_HALTSUM_HALT671_640_OFFSET 20
+#define DMI_HALTSUM_HALT671_640_LENGTH 1
+#define DMI_HALTSUM_HALT671_640 (0x1 << DMI_HALTSUM_HALT671_640_OFFSET)
+#define DMI_HALTSUM_HALT639_608_OFFSET 19
+#define DMI_HALTSUM_HALT639_608_LENGTH 1
+#define DMI_HALTSUM_HALT639_608 (0x1 << DMI_HALTSUM_HALT639_608_OFFSET)
+#define DMI_HALTSUM_HALT607_576_OFFSET 18
+#define DMI_HALTSUM_HALT607_576_LENGTH 1
+#define DMI_HALTSUM_HALT607_576 (0x1 << DMI_HALTSUM_HALT607_576_OFFSET)
+#define DMI_HALTSUM_HALT575_544_OFFSET 17
+#define DMI_HALTSUM_HALT575_544_LENGTH 1
+#define DMI_HALTSUM_HALT575_544 (0x1 << DMI_HALTSUM_HALT575_544_OFFSET)
+#define DMI_HALTSUM_HALT543_512_OFFSET 16
+#define DMI_HALTSUM_HALT543_512_LENGTH 1
+#define DMI_HALTSUM_HALT543_512 (0x1 << DMI_HALTSUM_HALT543_512_OFFSET)
+#define DMI_HALTSUM_HALT511_480_OFFSET 15
+#define DMI_HALTSUM_HALT511_480_LENGTH 1
+#define DMI_HALTSUM_HALT511_480 (0x1 << DMI_HALTSUM_HALT511_480_OFFSET)
+#define DMI_HALTSUM_HALT479_448_OFFSET 14
+#define DMI_HALTSUM_HALT479_448_LENGTH 1
+#define DMI_HALTSUM_HALT479_448 (0x1 << DMI_HALTSUM_HALT479_448_OFFSET)
+#define DMI_HALTSUM_HALT447_416_OFFSET 13
+#define DMI_HALTSUM_HALT447_416_LENGTH 1
+#define DMI_HALTSUM_HALT447_416 (0x1 << DMI_HALTSUM_HALT447_416_OFFSET)
+#define DMI_HALTSUM_HALT415_384_OFFSET 12
+#define DMI_HALTSUM_HALT415_384_LENGTH 1
+#define DMI_HALTSUM_HALT415_384 (0x1 << DMI_HALTSUM_HALT415_384_OFFSET)
+#define DMI_HALTSUM_HALT383_352_OFFSET 11
+#define DMI_HALTSUM_HALT383_352_LENGTH 1
+#define DMI_HALTSUM_HALT383_352 (0x1 << DMI_HALTSUM_HALT383_352_OFFSET)
+#define DMI_HALTSUM_HALT351_320_OFFSET 10
+#define DMI_HALTSUM_HALT351_320_LENGTH 1
+#define DMI_HALTSUM_HALT351_320 (0x1 << DMI_HALTSUM_HALT351_320_OFFSET)
+#define DMI_HALTSUM_HALT319_288_OFFSET 9
+#define DMI_HALTSUM_HALT319_288_LENGTH 1
+#define DMI_HALTSUM_HALT319_288 (0x1 << DMI_HALTSUM_HALT319_288_OFFSET)
+#define DMI_HALTSUM_HALT287_256_OFFSET 8
+#define DMI_HALTSUM_HALT287_256_LENGTH 1
+#define DMI_HALTSUM_HALT287_256 (0x1 << DMI_HALTSUM_HALT287_256_OFFSET)
+#define DMI_HALTSUM_HALT255_224_OFFSET 7
+#define DMI_HALTSUM_HALT255_224_LENGTH 1
+#define DMI_HALTSUM_HALT255_224 (0x1 << DMI_HALTSUM_HALT255_224_OFFSET)
+#define DMI_HALTSUM_HALT223_192_OFFSET 6
+#define DMI_HALTSUM_HALT223_192_LENGTH 1
+#define DMI_HALTSUM_HALT223_192 (0x1 << DMI_HALTSUM_HALT223_192_OFFSET)
+#define DMI_HALTSUM_HALT191_160_OFFSET 5
+#define DMI_HALTSUM_HALT191_160_LENGTH 1
+#define DMI_HALTSUM_HALT191_160 (0x1 << DMI_HALTSUM_HALT191_160_OFFSET)
+#define DMI_HALTSUM_HALT159_128_OFFSET 4
+#define DMI_HALTSUM_HALT159_128_LENGTH 1
+#define DMI_HALTSUM_HALT159_128 (0x1 << DMI_HALTSUM_HALT159_128_OFFSET)
+#define DMI_HALTSUM_HALT127_96_OFFSET 3
+#define DMI_HALTSUM_HALT127_96_LENGTH 1
+#define DMI_HALTSUM_HALT127_96 (0x1 << DMI_HALTSUM_HALT127_96_OFFSET)
+#define DMI_HALTSUM_HALT95_64_OFFSET 2
+#define DMI_HALTSUM_HALT95_64_LENGTH 1
+#define DMI_HALTSUM_HALT95_64 (0x1 << DMI_HALTSUM_HALT95_64_OFFSET)
+#define DMI_HALTSUM_HALT63_32_OFFSET 1
+#define DMI_HALTSUM_HALT63_32_LENGTH 1
+#define DMI_HALTSUM_HALT63_32 (0x1 << DMI_HALTSUM_HALT63_32_OFFSET)
+#define DMI_HALTSUM_HALT31_0_OFFSET 0
+#define DMI_HALTSUM_HALT31_0_LENGTH 1
+#define DMI_HALTSUM_HALT31_0 (0x1 << DMI_HALTSUM_HALT31_0_OFFSET)
+#define DMI_HAWINDOWSEL 0x14
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH 5
+#define DMI_HAWINDOWSEL_HAWINDOWSEL (0x1f << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET)
+#define DMI_HAWINDOW 0x15
+#define DMI_HAWINDOW_MASKDATA_OFFSET 0
+#define DMI_HAWINDOW_MASKDATA_LENGTH 32
+#define DMI_HAWINDOW_MASKDATA (0xffffffff << DMI_HAWINDOW_MASKDATA_OFFSET)
+#define DMI_ABSTRACTCS 0x16
+/*
+* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16.
+*
+* TODO: Explain what can be done with each size of the buffer, to suggest
+* why you would want more or less words.
+ */
+#define DMI_ABSTRACTCS_PROGSIZE_OFFSET 24
+#define DMI_ABSTRACTCS_PROGSIZE_LENGTH 5
+#define DMI_ABSTRACTCS_PROGSIZE (0x1f << DMI_ABSTRACTCS_PROGSIZE_OFFSET)
+/*
+* 1: An abstract command is currently being executed.
+*
+* This bit is set as soon as \Rcommand is written, and is
+* not cleared until that command has completed.
+ */
+#define DMI_ABSTRACTCS_BUSY_OFFSET 12
+#define DMI_ABSTRACTCS_BUSY_LENGTH 1
+#define DMI_ABSTRACTCS_BUSY (0x1 << DMI_ABSTRACTCS_BUSY_OFFSET)
+/*
+* Gets set if an abstract command fails. The bits in this field remain set until
+* they are cleared by writing 1 to them. No abstract command is
+* started until the value is reset to 0.
+*
+* 0 (none): No error.
+*
+* 1 (busy): An abstract command was executing while \Rcommand or one
+* of the {\tt data} registers was accessed.
+*
+* 2 (not supported): The requested command is not supported. A
+* command that is not supported while the hart is running may be
+* supported when it is halted.
+*
+* 3 (exception): An exception occurred while executing the command
+* (eg. while executing the Program Buffer).
+*
+* 4 (halt/resume): An abstract command couldn't execute because the
+* hart wasn't in the expected state (running/halted).
+*
+* 7 (other): The command failed for another reason.
+ */
+#define DMI_ABSTRACTCS_CMDERR_OFFSET 8
+#define DMI_ABSTRACTCS_CMDERR_LENGTH 3
+#define DMI_ABSTRACTCS_CMDERR (0x7 << DMI_ABSTRACTCS_CMDERR_OFFSET)
+/*
+* Number of {\tt data} registers that are implemented as part of the
+* abstract command interface. Valid sizes are 0 - 8.
+ */
+#define DMI_ABSTRACTCS_DATACOUNT_OFFSET 0
+#define DMI_ABSTRACTCS_DATACOUNT_LENGTH 5
+#define DMI_ABSTRACTCS_DATACOUNT (0x1f << DMI_ABSTRACTCS_DATACOUNT_OFFSET)
+#define DMI_COMMAND 0x17
+/*
+* The type determines the overall functionality of this
+* abstract command.
+ */
+#define DMI_COMMAND_CMDTYPE_OFFSET 24
+#define DMI_COMMAND_CMDTYPE_LENGTH 8
+#define DMI_COMMAND_CMDTYPE (0xff << DMI_COMMAND_CMDTYPE_OFFSET)
+/*
+* This field is interpreted in a command-specific manner,
+* described for each abstract command.
+ */
+#define DMI_COMMAND_CONTROL_OFFSET 0
+#define DMI_COMMAND_CONTROL_LENGTH 24
+#define DMI_COMMAND_CONTROL (0xffffff << DMI_COMMAND_CONTROL_OFFSET)
+#define DMI_ABSTRACTAUTO 0x18
+/*
+* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF (0xffff << DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET)
+/*
+* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 12
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA (0xfff << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET)
+#define DMI_CFGSTRADDR0 0x19
+#define DMI_CFGSTRADDR0_ADDR_OFFSET 0
+#define DMI_CFGSTRADDR0_ADDR_LENGTH 32
+#define DMI_CFGSTRADDR0_ADDR (0xffffffff << DMI_CFGSTRADDR0_ADDR_OFFSET)
+#define DMI_CFGSTRADDR1 0x1a
+#define DMI_CFGSTRADDR2 0x1b
+#define DMI_CFGSTRADDR3 0x1c
+#define DMI_DATA0 0x04
+#define DMI_DATA0_DATA_OFFSET 0
+#define DMI_DATA0_DATA_LENGTH 32
+#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET)
+#define DMI_DATA11 0x0f
+#define DMI_PROGBUF0 0x20
+#define DMI_PROGBUF0_DATA_OFFSET 0
+#define DMI_PROGBUF0_DATA_LENGTH 32
+#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET)
+#define DMI_PROGBUF15 0x2f
+#define DMI_AUTHDATA 0x30
+#define DMI_AUTHDATA_DATA_OFFSET 0
+#define DMI_AUTHDATA_DATA_LENGTH 32
+#define DMI_AUTHDATA_DATA (0xffffffff << DMI_AUTHDATA_DATA_OFFSET)
+#define DMI_SERCS 0x34
+/*
+* Number of supported serial ports.
+ */
+#define DMI_SERCS_SERIALCOUNT_OFFSET 28
+#define DMI_SERCS_SERIALCOUNT_LENGTH 4
+#define DMI_SERCS_SERIALCOUNT (0xf << DMI_SERCS_SERIALCOUNT_OFFSET)
+/*
+* Select which serial port is accessed by \Rserrx and \Rsertx.
+ */
+#define DMI_SERCS_SERIAL_OFFSET 24
+#define DMI_SERCS_SERIAL_LENGTH 3
+#define DMI_SERCS_SERIAL (0x7 << DMI_SERCS_SERIAL_OFFSET)
+#define DMI_SERCS_ERROR7_OFFSET 23
+#define DMI_SERCS_ERROR7_LENGTH 1
+#define DMI_SERCS_ERROR7 (0x1 << DMI_SERCS_ERROR7_OFFSET)
+#define DMI_SERCS_VALID7_OFFSET 22
+#define DMI_SERCS_VALID7_LENGTH 1
+#define DMI_SERCS_VALID7 (0x1 << DMI_SERCS_VALID7_OFFSET)
+#define DMI_SERCS_FULL7_OFFSET 21
+#define DMI_SERCS_FULL7_LENGTH 1
+#define DMI_SERCS_FULL7 (0x1 << DMI_SERCS_FULL7_OFFSET)
+#define DMI_SERCS_ERROR6_OFFSET 20
+#define DMI_SERCS_ERROR6_LENGTH 1
+#define DMI_SERCS_ERROR6 (0x1 << DMI_SERCS_ERROR6_OFFSET)
+#define DMI_SERCS_VALID6_OFFSET 19
+#define DMI_SERCS_VALID6_LENGTH 1
+#define DMI_SERCS_VALID6 (0x1 << DMI_SERCS_VALID6_OFFSET)
+#define DMI_SERCS_FULL6_OFFSET 18
+#define DMI_SERCS_FULL6_LENGTH 1
+#define DMI_SERCS_FULL6 (0x1 << DMI_SERCS_FULL6_OFFSET)
+#define DMI_SERCS_ERROR5_OFFSET 17
+#define DMI_SERCS_ERROR5_LENGTH 1
+#define DMI_SERCS_ERROR5 (0x1 << DMI_SERCS_ERROR5_OFFSET)
+#define DMI_SERCS_VALID5_OFFSET 16
+#define DMI_SERCS_VALID5_LENGTH 1
+#define DMI_SERCS_VALID5 (0x1 << DMI_SERCS_VALID5_OFFSET)
+#define DMI_SERCS_FULL5_OFFSET 15
+#define DMI_SERCS_FULL5_LENGTH 1
+#define DMI_SERCS_FULL5 (0x1 << DMI_SERCS_FULL5_OFFSET)
+#define DMI_SERCS_ERROR4_OFFSET 14
+#define DMI_SERCS_ERROR4_LENGTH 1
+#define DMI_SERCS_ERROR4 (0x1 << DMI_SERCS_ERROR4_OFFSET)
+#define DMI_SERCS_VALID4_OFFSET 13
+#define DMI_SERCS_VALID4_LENGTH 1
+#define DMI_SERCS_VALID4 (0x1 << DMI_SERCS_VALID4_OFFSET)
+#define DMI_SERCS_FULL4_OFFSET 12
+#define DMI_SERCS_FULL4_LENGTH 1
+#define DMI_SERCS_FULL4 (0x1 << DMI_SERCS_FULL4_OFFSET)
+#define DMI_SERCS_ERROR3_OFFSET 11
+#define DMI_SERCS_ERROR3_LENGTH 1
+#define DMI_SERCS_ERROR3 (0x1 << DMI_SERCS_ERROR3_OFFSET)
+#define DMI_SERCS_VALID3_OFFSET 10
+#define DMI_SERCS_VALID3_LENGTH 1
+#define DMI_SERCS_VALID3 (0x1 << DMI_SERCS_VALID3_OFFSET)
+#define DMI_SERCS_FULL3_OFFSET 9
+#define DMI_SERCS_FULL3_LENGTH 1
+#define DMI_SERCS_FULL3 (0x1 << DMI_SERCS_FULL3_OFFSET)
+#define DMI_SERCS_ERROR2_OFFSET 8
+#define DMI_SERCS_ERROR2_LENGTH 1
+#define DMI_SERCS_ERROR2 (0x1 << DMI_SERCS_ERROR2_OFFSET)
+#define DMI_SERCS_VALID2_OFFSET 7
+#define DMI_SERCS_VALID2_LENGTH 1
+#define DMI_SERCS_VALID2 (0x1 << DMI_SERCS_VALID2_OFFSET)
+#define DMI_SERCS_FULL2_OFFSET 6
+#define DMI_SERCS_FULL2_LENGTH 1
+#define DMI_SERCS_FULL2 (0x1 << DMI_SERCS_FULL2_OFFSET)
+#define DMI_SERCS_ERROR1_OFFSET 5
+#define DMI_SERCS_ERROR1_LENGTH 1
+#define DMI_SERCS_ERROR1 (0x1 << DMI_SERCS_ERROR1_OFFSET)
+#define DMI_SERCS_VALID1_OFFSET 4
+#define DMI_SERCS_VALID1_LENGTH 1
+#define DMI_SERCS_VALID1 (0x1 << DMI_SERCS_VALID1_OFFSET)
+#define DMI_SERCS_FULL1_OFFSET 3
+#define DMI_SERCS_FULL1_LENGTH 1
+#define DMI_SERCS_FULL1 (0x1 << DMI_SERCS_FULL1_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 has
+* over or underflowed. This bit will remain set until it is reset by
+* writing 1 to this bit.
+ */
+#define DMI_SERCS_ERROR0_OFFSET 2
+#define DMI_SERCS_ERROR0_LENGTH 1
+#define DMI_SERCS_ERROR0 (0x1 << DMI_SERCS_ERROR0_OFFSET)
+/*
+* 1 when the core-to-debugger queue for serial port 0 is not empty.
+ */
+#define DMI_SERCS_VALID0_OFFSET 1
+#define DMI_SERCS_VALID0_LENGTH 1
+#define DMI_SERCS_VALID0 (0x1 << DMI_SERCS_VALID0_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 is full.
+ */
+#define DMI_SERCS_FULL0_OFFSET 0
+#define DMI_SERCS_FULL0_LENGTH 1
+#define DMI_SERCS_FULL0 (0x1 << DMI_SERCS_FULL0_OFFSET)
+#define DMI_SERTX 0x35
+#define DMI_SERTX_DATA_OFFSET 0
+#define DMI_SERTX_DATA_LENGTH 32
+#define DMI_SERTX_DATA (0xffffffff << DMI_SERTX_DATA_OFFSET)
+#define DMI_SERRX 0x36
+#define DMI_SERRX_DATA_OFFSET 0
+#define DMI_SERRX_DATA_LENGTH 32
+#define DMI_SERRX_DATA (0xffffffff << DMI_SERRX_DATA_OFFSET)
+#define DMI_SBCS 0x38
+/*
+* When a 1 is written here, triggers a read at the address in {\tt
+* sbaddress} using the access size set by \Fsbaccess.
+ */
+#define DMI_SBCS_SBSINGLEREAD_OFFSET 20
+#define DMI_SBCS_SBSINGLEREAD_LENGTH 1
+#define DMI_SBCS_SBSINGLEREAD (0x1 << DMI_SBCS_SBSINGLEREAD_OFFSET)
+/*
+* Select the access size to use for system bus accesses triggered by
+* writes to the {\tt sbaddress} registers or \Rsbdatazero.
+*
+* 0: 8-bit
+*
+* 1: 16-bit
+*
+* 2: 32-bit
+*
+* 3: 64-bit
+*
+* 4: 128-bit
+*
+* If an unsupported system bus access size is written here,
+* the DM may not perform the access, or may perform the access
+* with any access size.
+ */
+#define DMI_SBCS_SBACCESS_OFFSET 17
+#define DMI_SBCS_SBACCESS_LENGTH 3
+#define DMI_SBCS_SBACCESS (0x7 << DMI_SBCS_SBACCESS_OFFSET)
+/*
+* When 1, the internal address value (used by the system bus master)
+* is incremented by the access size (in bytes) selected in \Fsbaccess
+* after every system bus access.
+ */
+#define DMI_SBCS_SBAUTOINCREMENT_OFFSET 16
+#define DMI_SBCS_SBAUTOINCREMENT_LENGTH 1
+#define DMI_SBCS_SBAUTOINCREMENT (0x1 << DMI_SBCS_SBAUTOINCREMENT_OFFSET)
+/*
+* When 1, every read from \Rsbdatazero automatically triggers a system
+* bus read at the new address.
+ */
+#define DMI_SBCS_SBAUTOREAD_OFFSET 15
+#define DMI_SBCS_SBAUTOREAD_LENGTH 1
+#define DMI_SBCS_SBAUTOREAD (0x1 << DMI_SBCS_SBAUTOREAD_OFFSET)
+/*
+* When the debug module's system bus
+* master causes a bus error, this field gets set. The bits in this
+* field remain set until they are cleared by writing 1 to them.
+* While this field is non-zero, no more system bus accesses can be
+* initiated by the debug module.
+*
+* 0: There was no bus error.
+*
+* 1: There was a timeout.
+*
+* 2: A bad address was accessed.
+*
+* 3: There was some other error (eg. alignment).
+*
+* 4: The system bus master was busy when a one of the
+* {\tt sbaddress} or {\tt sbdata} registers was written,
+* or the {\tt sbdata} register was read when it had
+* stale data.
+ */
+#define DMI_SBCS_SBERROR_OFFSET 12
+#define DMI_SBCS_SBERROR_LENGTH 3
+#define DMI_SBCS_SBERROR (0x7 << DMI_SBCS_SBERROR_OFFSET)
+/*
+* Width of system bus addresses in bits. (0 indicates there is no bus
+* access support.)
+ */
+#define DMI_SBCS_SBASIZE_OFFSET 5
+#define DMI_SBCS_SBASIZE_LENGTH 7
+#define DMI_SBCS_SBASIZE (0x7f << DMI_SBCS_SBASIZE_OFFSET)
+/*
+* 1 when 128-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS128_OFFSET 4
+#define DMI_SBCS_SBACCESS128_LENGTH 1
+#define DMI_SBCS_SBACCESS128 (0x1 << DMI_SBCS_SBACCESS128_OFFSET)
+/*
+* 1 when 64-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS64_OFFSET 3
+#define DMI_SBCS_SBACCESS64_LENGTH 1
+#define DMI_SBCS_SBACCESS64 (0x1 << DMI_SBCS_SBACCESS64_OFFSET)
+/*
+* 1 when 32-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS32_OFFSET 2
+#define DMI_SBCS_SBACCESS32_LENGTH 1
+#define DMI_SBCS_SBACCESS32 (0x1 << DMI_SBCS_SBACCESS32_OFFSET)
+/*
+* 1 when 16-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS16_OFFSET 1
+#define DMI_SBCS_SBACCESS16_LENGTH 1
+#define DMI_SBCS_SBACCESS16 (0x1 << DMI_SBCS_SBACCESS16_OFFSET)
+/*
+* 1 when 8-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS8_OFFSET 0
+#define DMI_SBCS_SBACCESS8_LENGTH 1
+#define DMI_SBCS_SBACCESS8 (0x1 << DMI_SBCS_SBACCESS8_OFFSET)
+#define DMI_SBADDRESS0 0x39
+/*
+* Accesses bits 31:0 of the internal address.
+ */
+#define DMI_SBADDRESS0_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS0_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS0_ADDRESS (0xffffffff << DMI_SBADDRESS0_ADDRESS_OFFSET)
+#define DMI_SBADDRESS1 0x3a
+/*
+* Accesses bits 63:32 of the internal address (if the system address
+* bus is that wide).
+ */
+#define DMI_SBADDRESS1_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS1_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS1_ADDRESS (0xffffffff << DMI_SBADDRESS1_ADDRESS_OFFSET)
+#define DMI_SBADDRESS2 0x3b
+/*
+* Accesses bits 95:64 of the internal address (if the system address
+* bus is that wide).
+ */
+#define DMI_SBADDRESS2_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS2_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS2_ADDRESS (0xffffffff << DMI_SBADDRESS2_ADDRESS_OFFSET)
+#define DMI_SBDATA0 0x3c
+/*
+* Accesses bits 31:0 of the internal data.
+ */
+#define DMI_SBDATA0_DATA_OFFSET 0
+#define DMI_SBDATA0_DATA_LENGTH 32
+#define DMI_SBDATA0_DATA (0xffffffff << DMI_SBDATA0_DATA_OFFSET)
+#define DMI_SBDATA1 0x3d
+/*
+* Accesses bits 63:32 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA1_DATA_OFFSET 0
+#define DMI_SBDATA1_DATA_LENGTH 32
+#define DMI_SBDATA1_DATA (0xffffffff << DMI_SBDATA1_DATA_OFFSET)
+#define DMI_SBDATA2 0x3e
+/*
+* Accesses bits 95:64 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA2_DATA_OFFSET 0
+#define DMI_SBDATA2_DATA_LENGTH 32
+#define DMI_SBDATA2_DATA (0xffffffff << DMI_SBDATA2_DATA_OFFSET)
+#define DMI_SBDATA3 0x3f
+/*
+* Accesses bits 127:96 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA3_DATA_OFFSET 0
+#define DMI_SBDATA3_DATA_LENGTH 32
+#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET)
+#define TRACE 0x728
+/*
+* 1 if the trace buffer has wrapped since the last time \Fdiscard was
+* written. 0 otherwise.
+ */
+#define TRACE_WRAPPED_OFFSET 24
+#define TRACE_WRAPPED_LENGTH 1
+#define TRACE_WRAPPED (0x1 << TRACE_WRAPPED_OFFSET)
+/*
+* Emit Timestamp trace sequences.
+ */
+#define TRACE_EMITTIMESTAMP_OFFSET 23
+#define TRACE_EMITTIMESTAMP_LENGTH 1
+#define TRACE_EMITTIMESTAMP (0x1 << TRACE_EMITTIMESTAMP_OFFSET)
+/*
+* Emit Store Data trace sequences.
+ */
+#define TRACE_EMITSTOREDATA_OFFSET 22
+#define TRACE_EMITSTOREDATA_LENGTH 1
+#define TRACE_EMITSTOREDATA (0x1 << TRACE_EMITSTOREDATA_OFFSET)
+/*
+* Emit Load Data trace sequences.
+ */
+#define TRACE_EMITLOADDATA_OFFSET 21
+#define TRACE_EMITLOADDATA_LENGTH 1
+#define TRACE_EMITLOADDATA (0x1 << TRACE_EMITLOADDATA_OFFSET)
+/*
+* Emit Store Address trace sequences.
+ */
+#define TRACE_EMITSTOREADDR_OFFSET 20
+#define TRACE_EMITSTOREADDR_LENGTH 1
+#define TRACE_EMITSTOREADDR (0x1 << TRACE_EMITSTOREADDR_OFFSET)
+/*
+* Emit Load Address trace sequences.
+ */
+#define TRACE_EMITLOADADDR_OFFSET 19
+#define TRACE_EMITLOADADDR_LENGTH 1
+#define TRACE_EMITLOADADDR (0x1 << TRACE_EMITLOADADDR_OFFSET)
+/*
+* Emit Privilege Level trace sequences.
+ */
+#define TRACE_EMITPRIV_OFFSET 18
+#define TRACE_EMITPRIV_LENGTH 1
+#define TRACE_EMITPRIV (0x1 << TRACE_EMITPRIV_OFFSET)
+/*
+* Emit Branch Taken and Branch Not Taken trace sequences.
+ */
+#define TRACE_EMITBRANCH_OFFSET 17
+#define TRACE_EMITBRANCH_LENGTH 1
+#define TRACE_EMITBRANCH (0x1 << TRACE_EMITBRANCH_OFFSET)
+/*
+* Emit PC trace sequences.
+ */
+#define TRACE_EMITPC_OFFSET 16
+#define TRACE_EMITPC_LENGTH 1
+#define TRACE_EMITPC (0x1 << TRACE_EMITPC_OFFSET)
+/*
+* Determine what happens when the trace buffer is full. 0 means wrap
+* and overwrite. 1 means turn off trace until \Fdiscard is written as 1.
+* 2 means cause a trace full exception. 3 is reserved for future use.
+ */
+#define TRACE_FULLACTION_OFFSET 8
+#define TRACE_FULLACTION_LENGTH 2
+#define TRACE_FULLACTION (0x3 << TRACE_FULLACTION_OFFSET)
+/*
+* 0: Trace to a dedicated on-core RAM (which is not further defined in
+* this spec).
+*
+* 1: Trace to RAM on the system bus.
+*
+* 2: Send trace data to a dedicated off-chip interface (which is not
+* defined in this spec). This does not affect execution speed.
+*
+* 3: Reserved for future use.
+*
+* Options 0 and 1 slow down execution (eg. because of system bus
+* contention).
+ */
+#define TRACE_DESTINATION_OFFSET 4
+#define TRACE_DESTINATION_LENGTH 2
+#define TRACE_DESTINATION (0x3 << TRACE_DESTINATION_OFFSET)
+/*
+* When 1, the trace logic may stall processor execution to ensure it
+* can emit all the trace sequences required. When 0 individual trace
+* sequences may be dropped.
+ */
+#define TRACE_STALL_OFFSET 2
+#define TRACE_STALL_LENGTH 1
+#define TRACE_STALL (0x1 << TRACE_STALL_OFFSET)
+/*
+* Writing 1 to this bit tells the trace logic that any trace
+* collected is no longer required. When tracing to RAM, it resets the
+* trace write pointer to the start of the memory, as well as
+* \Fwrapped.
+ */
+#define TRACE_DISCARD_OFFSET 1
+#define TRACE_DISCARD_LENGTH 1
+#define TRACE_DISCARD (0x1 << TRACE_DISCARD_OFFSET)
+#define TRACE_SUPPORTED_OFFSET 0
+#define TRACE_SUPPORTED_LENGTH 1
+#define TRACE_SUPPORTED (0x1 << TRACE_SUPPORTED_OFFSET)
+#define TBUFSTART 0x729
+#define TBUFEND 0x72a
+#define TBUFWRITE 0x72b
+#define SHORTNAME 0x123
+/*
+* Description of what this field is used for.
+ */
+#define SHORTNAME_FIELD_OFFSET 0
+#define SHORTNAME_FIELD_LENGTH 8
+#define SHORTNAME_FIELD (0xff << SHORTNAME_FIELD_OFFSET)
+#define AC_ACCESS_REGISTER None
+/*
+* This is 0 to indicate Access Register Command.
+ */
+#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 24
+#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8
+#define AC_ACCESS_REGISTER_CMDTYPE (0xff << AC_ACCESS_REGISTER_CMDTYPE_OFFSET)
+/*
+* 2: Access the lowest 32 bits of the register.
+*
+* 3: Access the lowest 64 bits of the register.
+*
+* 4: Access the lowest 128 bits of the register.
+*
+* If \Fsize specifies a size larger than the register's actual size,
+* then the access must fail. If a register is accessible, then reads of \Fsize
+* less than or equal to the register's actual size must be supported.
+ */
+#define AC_ACCESS_REGISTER_SIZE_OFFSET 20
+#define AC_ACCESS_REGISTER_SIZE_LENGTH 3
+#define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET)
+/*
+* When 1, execute the program in the Program Buffer exactly once
+* after performing the transfer, if any.
+ */
+#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18
+#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1
+#define AC_ACCESS_REGISTER_POSTEXEC (0x1 << AC_ACCESS_REGISTER_POSTEXEC_OFFSET)
+/*
+* 0: Don't do the operation specified by \Fwrite.
+*
+* 1: Do the operation specified by \Fwrite.
+ */
+#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 17
+#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1
+#define AC_ACCESS_REGISTER_TRANSFER (0x1 << AC_ACCESS_REGISTER_TRANSFER_OFFSET)
+/*
+* When \Ftransfer is set:
+* 0: Copy data from the specified register into {\tt arg0} portion
+* of {\tt data}.
+*
+* 1: Copy data from {\tt arg0} portion of {\tt data} into the
+* specified register.
+ */
+#define AC_ACCESS_REGISTER_WRITE_OFFSET 16
+#define AC_ACCESS_REGISTER_WRITE_LENGTH 1
+#define AC_ACCESS_REGISTER_WRITE (0x1 << AC_ACCESS_REGISTER_WRITE_OFFSET)
+/*
+* Number of the register to access, as described in Table~\ref{tab:regno}.
+ */
+#define AC_ACCESS_REGISTER_REGNO_OFFSET 0
+#define AC_ACCESS_REGISTER_REGNO_LENGTH 16
+#define AC_ACCESS_REGISTER_REGNO (0xffff << AC_ACCESS_REGISTER_REGNO_OFFSET)
+#define AC_QUICK_ACCESS None
+/*
+* This is 1 to indicate Quick Access command.
+ */
+#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 24
+#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8
+#define AC_QUICK_ACCESS_CMDTYPE (0xff << AC_QUICK_ACCESS_CMDTYPE_OFFSET)
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h
new file mode 100644
index 0000000..35e0f9f
--- /dev/null
+++ b/src/target/riscv/encoding.h
@@ -0,0 +1,1313 @@
+// See LICENSE for license details.
+
+#ifndef RISCV_CSR_ENCODING_H
+#define RISCV_CSR_ENCODING_H
+
+#define MSTATUS_UIE 0x00000001
+#define MSTATUS_SIE 0x00000002
+#define MSTATUS_HIE 0x00000004
+#define MSTATUS_MIE 0x00000008
+#define MSTATUS_UPIE 0x00000010
+#define MSTATUS_SPIE 0x00000020
+#define MSTATUS_HPIE 0x00000040
+#define MSTATUS_MPIE 0x00000080
+#define MSTATUS_SPP 0x00000100
+#define MSTATUS_HPP 0x00000600
+#define MSTATUS_MPP 0x00001800
+#define MSTATUS_FS 0x00006000
+#define MSTATUS_XS 0x00018000
+#define MSTATUS_MPRV 0x00020000
+#define MSTATUS_PUM 0x00040000
+#define MSTATUS_MXR 0x00080000
+#define MSTATUS_VM 0x1F000000
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#define SSTATUS_UIE 0x00000001
+#define SSTATUS_SIE 0x00000002
+#define SSTATUS_UPIE 0x00000010
+#define SSTATUS_SPIE 0x00000020
+#define SSTATUS_SPP 0x00000100
+#define SSTATUS_FS 0x00006000
+#define SSTATUS_XS 0x00018000
+#define SSTATUS_PUM 0x00040000
+#define SSTATUS32_SD 0x80000000
+#define SSTATUS64_SD 0x8000000000000000
+
+#define DCSR_XDEBUGVER (3U<<30)
+#define DCSR_NDRESET (1<<29)
+#define DCSR_FULLRESET (1<<28)
+#define DCSR_EBREAKM (1<<15)
+#define DCSR_EBREAKH (1<<14)
+#define DCSR_EBREAKS (1<<13)
+#define DCSR_EBREAKU (1<<12)
+#define DCSR_STOPCYCLE (1<<10)
+#define DCSR_STOPTIME (1<<9)
+#define DCSR_CAUSE (7<<6)
+#define DCSR_DEBUGINT (1<<5)
+#define DCSR_HALT (1<<3)
+#define DCSR_STEP (1<<2)
+#define DCSR_PRV (3<<0)
+
+#define DCSR_CAUSE_NONE 0
+#define DCSR_CAUSE_SWBP 1
+#define DCSR_CAUSE_HWBP 2
+#define DCSR_CAUSE_DEBUGINT 3
+#define DCSR_CAUSE_STEP 4
+#define DCSR_CAUSE_HALT 5
+
+#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4))
+#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5))
+#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
+
+#define MCONTROL_SELECT (1<<19)
+#define MCONTROL_TIMING (1<<18)
+#define MCONTROL_ACTION (0x3f<<12)
+#define MCONTROL_CHAIN (1<<11)
+#define MCONTROL_MATCH (0xf<<7)
+#define MCONTROL_M (1<<6)
+#define MCONTROL_H (1<<5)
+#define MCONTROL_S (1<<4)
+#define MCONTROL_U (1<<3)
+#define MCONTROL_EXECUTE (1<<2)
+#define MCONTROL_STORE (1<<1)
+#define MCONTROL_LOAD (1<<0)
+
+#define MCONTROL_TYPE_NONE 0
+#define MCONTROL_TYPE_MATCH 2
+
+#define MCONTROL_ACTION_DEBUG_EXCEPTION 0
+#define MCONTROL_ACTION_DEBUG_MODE 1
+#define MCONTROL_ACTION_TRACE_START 2
+#define MCONTROL_ACTION_TRACE_STOP 3
+#define MCONTROL_ACTION_TRACE_EMIT 4
+
+#define MCONTROL_MATCH_EQUAL 0
+#define MCONTROL_MATCH_NAPOT 1
+#define MCONTROL_MATCH_GE 2
+#define MCONTROL_MATCH_LT 3
+#define MCONTROL_MATCH_MASK_LOW 4
+#define MCONTROL_MATCH_MASK_HIGH 5
+
+#define MIP_SSIP (1 << IRQ_S_SOFT)
+#define MIP_HSIP (1 << IRQ_H_SOFT)
+#define MIP_MSIP (1 << IRQ_M_SOFT)
+#define MIP_STIP (1 << IRQ_S_TIMER)
+#define MIP_HTIP (1 << IRQ_H_TIMER)
+#define MIP_MTIP (1 << IRQ_M_TIMER)
+#define MIP_SEIP (1 << IRQ_S_EXT)
+#define MIP_HEIP (1 << IRQ_H_EXT)
+#define MIP_MEIP (1 << IRQ_M_EXT)
+
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define VM_MBARE 0
+#define VM_MBB 1
+#define VM_MBBID 2
+#define VM_SV32 8
+#define VM_SV39 9
+#define VM_SV48 10
+
+#define IRQ_S_SOFT 1
+#define IRQ_H_SOFT 2
+#define IRQ_M_SOFT 3
+#define IRQ_S_TIMER 5
+#define IRQ_H_TIMER 6
+#define IRQ_M_TIMER 7
+#define IRQ_S_EXT 9
+#define IRQ_H_EXT 10
+#define IRQ_M_EXT 11
+#define IRQ_COP 12
+#define IRQ_HOST 13
+
+#define DEFAULT_RSTVEC 0x00001000
+#define DEFAULT_NMIVEC 0x00001004
+#define DEFAULT_MTVEC 0x00001010
+#define CONFIG_STRING_ADDR 0x0000100C
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+// page table entry (PTE) fields
+#define PTE_V 0x001 // Valid
+#define PTE_R 0x002 // Read
+#define PTE_W 0x004 // Write
+#define PTE_X 0x008 // Execute
+#define PTE_U 0x010 // User
+#define PTE_G 0x020 // Global
+#define PTE_A 0x040 // Accessed
+#define PTE_D 0x080 // Dirty
+#define PTE_SOFT 0x300 // Reserved for Software
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#ifdef __riscv
+
+#ifdef __riscv64
+# define MSTATUS_SD MSTATUS64_SD
+# define SSTATUS_SD SSTATUS64_SD
+# define RISCV_PGLEVEL_BITS 9
+#else
+# define MSTATUS_SD MSTATUS32_SD
+# define SSTATUS_SD SSTATUS32_SD
+# define RISCV_PGLEVEL_BITS 10
+#endif
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
+
+#ifndef __ASSEMBLER__
+
+#ifdef __GNUC__
+
+#define read_csr(reg) ({ unsigned long __tmp; \
+ asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
+ __tmp; })
+
+#define write_csr(reg, val) ({ \
+ if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
+ asm volatile ("csrw " #reg ", %0" :: "i"(val)); \
+ else \
+ asm volatile ("csrw " #reg ", %0" :: "r"(val)); })
+
+#define swap_csr(reg, val) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
+ asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "i"(val)); \
+ else \
+ asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "r"(val)); \
+ __tmp; })
+
+#define set_csr(reg, bit) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
+ asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
+ else \
+ asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
+ __tmp; })
+
+#define clear_csr(reg, bit) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
+ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
+ else \
+ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
+ __tmp; })
+
+#define rdtime() read_csr(time)
+#define rdcycle() read_csr(cycle)
+#define rdinstret() read_csr(instret)
+
+#endif
+
+#endif
+
+#endif
+
+#endif
+/* Automatically generated by parse-opcodes */
+#ifndef RISCV_ENCODING_H
+#define RISCV_ENCODING_H
+#define MATCH_BEQ 0x63
+#define MASK_BEQ 0x707f
+#define MATCH_BNE 0x1063
+#define MASK_BNE 0x707f
+#define MATCH_BLT 0x4063
+#define MASK_BLT 0x707f
+#define MATCH_BGE 0x5063
+#define MASK_BGE 0x707f
+#define MATCH_BLTU 0x6063
+#define MASK_BLTU 0x707f
+#define MATCH_BGEU 0x7063
+#define MASK_BGEU 0x707f
+#define MATCH_JALR 0x67
+#define MASK_JALR 0x707f
+#define MATCH_JAL 0x6f
+#define MASK_JAL 0x7f
+#define MATCH_LUI 0x37
+#define MASK_LUI 0x7f
+#define MATCH_AUIPC 0x17
+#define MASK_AUIPC 0x7f
+#define MATCH_ADDI 0x13
+#define MASK_ADDI 0x707f
+#define MATCH_SLLI 0x1013
+#define MASK_SLLI 0xfc00707f
+#define MATCH_SLTI 0x2013
+#define MASK_SLTI 0x707f
+#define MATCH_SLTIU 0x3013
+#define MASK_SLTIU 0x707f
+#define MATCH_XORI 0x4013
+#define MASK_XORI 0x707f
+#define MATCH_SRLI 0x5013
+#define MASK_SRLI 0xfc00707f
+#define MATCH_SRAI 0x40005013
+#define MASK_SRAI 0xfc00707f
+#define MATCH_ORI 0x6013
+#define MASK_ORI 0x707f
+#define MATCH_ANDI 0x7013
+#define MASK_ANDI 0x707f
+#define MATCH_ADD 0x33
+#define MASK_ADD 0xfe00707f
+#define MATCH_SUB 0x40000033
+#define MASK_SUB 0xfe00707f
+#define MATCH_SLL 0x1033
+#define MASK_SLL 0xfe00707f
+#define MATCH_SLT 0x2033
+#define MASK_SLT 0xfe00707f
+#define MATCH_SLTU 0x3033
+#define MASK_SLTU 0xfe00707f
+#define MATCH_XOR 0x4033
+#define MASK_XOR 0xfe00707f
+#define MATCH_SRL 0x5033
+#define MASK_SRL 0xfe00707f
+#define MATCH_SRA 0x40005033
+#define MASK_SRA 0xfe00707f
+#define MATCH_OR 0x6033
+#define MASK_OR 0xfe00707f
+#define MATCH_AND 0x7033
+#define MASK_AND 0xfe00707f
+#define MATCH_ADDIW 0x1b
+#define MASK_ADDIW 0x707f
+#define MATCH_SLLIW 0x101b
+#define MASK_SLLIW 0xfe00707f
+#define MATCH_SRLIW 0x501b
+#define MASK_SRLIW 0xfe00707f
+#define MATCH_SRAIW 0x4000501b
+#define MASK_SRAIW 0xfe00707f
+#define MATCH_ADDW 0x3b
+#define MASK_ADDW 0xfe00707f
+#define MATCH_SUBW 0x4000003b
+#define MASK_SUBW 0xfe00707f
+#define MATCH_SLLW 0x103b
+#define MASK_SLLW 0xfe00707f
+#define MATCH_SRLW 0x503b
+#define MASK_SRLW 0xfe00707f
+#define MATCH_SRAW 0x4000503b
+#define MASK_SRAW 0xfe00707f
+#define MATCH_LB 0x3
+#define MASK_LB 0x707f
+#define MATCH_LH 0x1003
+#define MASK_LH 0x707f
+#define MATCH_LW 0x2003
+#define MASK_LW 0x707f
+#define MATCH_LD 0x3003
+#define MASK_LD 0x707f
+#define MATCH_LBU 0x4003
+#define MASK_LBU 0x707f
+#define MATCH_LHU 0x5003
+#define MASK_LHU 0x707f
+#define MATCH_LWU 0x6003
+#define MASK_LWU 0x707f
+#define MATCH_SB 0x23
+#define MASK_SB 0x707f
+#define MATCH_SH 0x1023
+#define MASK_SH 0x707f
+#define MATCH_SW 0x2023
+#define MASK_SW 0x707f
+#define MATCH_SD 0x3023
+#define MASK_SD 0x707f
+#define MATCH_FENCE 0xf
+#define MASK_FENCE 0x707f
+#define MATCH_FENCE_I 0x100f
+#define MASK_FENCE_I 0x707f
+#define MATCH_MUL 0x2000033
+#define MASK_MUL 0xfe00707f
+#define MATCH_MULH 0x2001033
+#define MASK_MULH 0xfe00707f
+#define MATCH_MULHSU 0x2002033
+#define MASK_MULHSU 0xfe00707f
+#define MATCH_MULHU 0x2003033
+#define MASK_MULHU 0xfe00707f
+#define MATCH_DIV 0x2004033
+#define MASK_DIV 0xfe00707f
+#define MATCH_DIVU 0x2005033
+#define MASK_DIVU 0xfe00707f
+#define MATCH_REM 0x2006033
+#define MASK_REM 0xfe00707f
+#define MATCH_REMU 0x2007033
+#define MASK_REMU 0xfe00707f
+#define MATCH_MULW 0x200003b
+#define MASK_MULW 0xfe00707f
+#define MATCH_DIVW 0x200403b
+#define MASK_DIVW 0xfe00707f
+#define MATCH_DIVUW 0x200503b
+#define MASK_DIVUW 0xfe00707f
+#define MATCH_REMW 0x200603b
+#define MASK_REMW 0xfe00707f
+#define MATCH_REMUW 0x200703b
+#define MASK_REMUW 0xfe00707f
+#define MATCH_AMOADD_W 0x202f
+#define MASK_AMOADD_W 0xf800707f
+#define MATCH_AMOXOR_W 0x2000202f
+#define MASK_AMOXOR_W 0xf800707f
+#define MATCH_AMOOR_W 0x4000202f
+#define MASK_AMOOR_W 0xf800707f
+#define MATCH_AMOAND_W 0x6000202f
+#define MASK_AMOAND_W 0xf800707f
+#define MATCH_AMOMIN_W 0x8000202f
+#define MASK_AMOMIN_W 0xf800707f
+#define MATCH_AMOMAX_W 0xa000202f
+#define MASK_AMOMAX_W 0xf800707f
+#define MATCH_AMOMINU_W 0xc000202f
+#define MASK_AMOMINU_W 0xf800707f
+#define MATCH_AMOMAXU_W 0xe000202f
+#define MASK_AMOMAXU_W 0xf800707f
+#define MATCH_AMOSWAP_W 0x800202f
+#define MASK_AMOSWAP_W 0xf800707f
+#define MATCH_LR_W 0x1000202f
+#define MASK_LR_W 0xf9f0707f
+#define MATCH_SC_W 0x1800202f
+#define MASK_SC_W 0xf800707f
+#define MATCH_AMOADD_D 0x302f
+#define MASK_AMOADD_D 0xf800707f
+#define MATCH_AMOXOR_D 0x2000302f
+#define MASK_AMOXOR_D 0xf800707f
+#define MATCH_AMOOR_D 0x4000302f
+#define MASK_AMOOR_D 0xf800707f
+#define MATCH_AMOAND_D 0x6000302f
+#define MASK_AMOAND_D 0xf800707f
+#define MATCH_AMOMIN_D 0x8000302f
+#define MASK_AMOMIN_D 0xf800707f
+#define MATCH_AMOMAX_D 0xa000302f
+#define MASK_AMOMAX_D 0xf800707f
+#define MATCH_AMOMINU_D 0xc000302f
+#define MASK_AMOMINU_D 0xf800707f
+#define MATCH_AMOMAXU_D 0xe000302f
+#define MASK_AMOMAXU_D 0xf800707f
+#define MATCH_AMOSWAP_D 0x800302f
+#define MASK_AMOSWAP_D 0xf800707f
+#define MATCH_LR_D 0x1000302f
+#define MASK_LR_D 0xf9f0707f
+#define MATCH_SC_D 0x1800302f
+#define MASK_SC_D 0xf800707f
+#define MATCH_ECALL 0x73
+#define MASK_ECALL 0xffffffff
+#define MATCH_EBREAK 0x100073
+#define MASK_EBREAK 0xffffffff
+#define MATCH_URET 0x200073
+#define MASK_URET 0xffffffff
+#define MATCH_SRET 0x10200073
+#define MASK_SRET 0xffffffff
+#define MATCH_HRET 0x20200073
+#define MASK_HRET 0xffffffff
+#define MATCH_MRET 0x30200073
+#define MASK_MRET 0xffffffff
+#define MATCH_DRET 0x7b200073
+#define MASK_DRET 0xffffffff
+#define MATCH_SFENCE_VM 0x10400073
+#define MASK_SFENCE_VM 0xfff07fff
+#define MATCH_WFI 0x10500073
+#define MASK_WFI 0xffffffff
+#define MATCH_CSRRW 0x1073
+#define MASK_CSRRW 0x707f
+#define MATCH_CSRRS 0x2073
+#define MASK_CSRRS 0x707f
+#define MATCH_CSRRC 0x3073
+#define MASK_CSRRC 0x707f
+#define MATCH_CSRRWI 0x5073
+#define MASK_CSRRWI 0x707f
+#define MATCH_CSRRSI 0x6073
+#define MASK_CSRRSI 0x707f
+#define MATCH_CSRRCI 0x7073
+#define MASK_CSRRCI 0x707f
+#define MATCH_FADD_S 0x53
+#define MASK_FADD_S 0xfe00007f
+#define MATCH_FSUB_S 0x8000053
+#define MASK_FSUB_S 0xfe00007f
+#define MATCH_FMUL_S 0x10000053
+#define MASK_FMUL_S 0xfe00007f
+#define MATCH_FDIV_S 0x18000053
+#define MASK_FDIV_S 0xfe00007f
+#define MATCH_FSGNJ_S 0x20000053
+#define MASK_FSGNJ_S 0xfe00707f
+#define MATCH_FSGNJN_S 0x20001053
+#define MASK_FSGNJN_S 0xfe00707f
+#define MATCH_FSGNJX_S 0x20002053
+#define MASK_FSGNJX_S 0xfe00707f
+#define MATCH_FMIN_S 0x28000053
+#define MASK_FMIN_S 0xfe00707f
+#define MATCH_FMAX_S 0x28001053
+#define MASK_FMAX_S 0xfe00707f
+#define MATCH_FSQRT_S 0x58000053
+#define MASK_FSQRT_S 0xfff0007f
+#define MATCH_FADD_D 0x2000053
+#define MASK_FADD_D 0xfe00007f
+#define MATCH_FSUB_D 0xa000053
+#define MASK_FSUB_D 0xfe00007f
+#define MATCH_FMUL_D 0x12000053
+#define MASK_FMUL_D 0xfe00007f
+#define MATCH_FDIV_D 0x1a000053
+#define MASK_FDIV_D 0xfe00007f
+#define MATCH_FSGNJ_D 0x22000053
+#define MASK_FSGNJ_D 0xfe00707f
+#define MATCH_FSGNJN_D 0x22001053
+#define MASK_FSGNJN_D 0xfe00707f
+#define MATCH_FSGNJX_D 0x22002053
+#define MASK_FSGNJX_D 0xfe00707f
+#define MATCH_FMIN_D 0x2a000053
+#define MASK_FMIN_D 0xfe00707f
+#define MATCH_FMAX_D 0x2a001053
+#define MASK_FMAX_D 0xfe00707f
+#define MATCH_FCVT_S_D 0x40100053
+#define MASK_FCVT_S_D 0xfff0007f
+#define MATCH_FCVT_D_S 0x42000053
+#define MASK_FCVT_D_S 0xfff0007f
+#define MATCH_FSQRT_D 0x5a000053
+#define MASK_FSQRT_D 0xfff0007f
+#define MATCH_FLE_S 0xa0000053
+#define MASK_FLE_S 0xfe00707f
+#define MATCH_FLT_S 0xa0001053
+#define MASK_FLT_S 0xfe00707f
+#define MATCH_FEQ_S 0xa0002053
+#define MASK_FEQ_S 0xfe00707f
+#define MATCH_FLE_D 0xa2000053
+#define MASK_FLE_D 0xfe00707f
+#define MATCH_FLT_D 0xa2001053
+#define MASK_FLT_D 0xfe00707f
+#define MATCH_FEQ_D 0xa2002053
+#define MASK_FEQ_D 0xfe00707f
+#define MATCH_FCVT_W_S 0xc0000053
+#define MASK_FCVT_W_S 0xfff0007f
+#define MATCH_FCVT_WU_S 0xc0100053
+#define MASK_FCVT_WU_S 0xfff0007f
+#define MATCH_FCVT_L_S 0xc0200053
+#define MASK_FCVT_L_S 0xfff0007f
+#define MATCH_FCVT_LU_S 0xc0300053
+#define MASK_FCVT_LU_S 0xfff0007f
+#define MATCH_FMV_X_S 0xe0000053
+#define MASK_FMV_X_S 0xfff0707f
+#define MATCH_FCLASS_S 0xe0001053
+#define MASK_FCLASS_S 0xfff0707f
+#define MATCH_FCVT_W_D 0xc2000053
+#define MASK_FCVT_W_D 0xfff0007f
+#define MATCH_FCVT_WU_D 0xc2100053
+#define MASK_FCVT_WU_D 0xfff0007f
+#define MATCH_FCVT_L_D 0xc2200053
+#define MASK_FCVT_L_D 0xfff0007f
+#define MATCH_FCVT_LU_D 0xc2300053
+#define MASK_FCVT_LU_D 0xfff0007f
+#define MATCH_FMV_X_D 0xe2000053
+#define MASK_FMV_X_D 0xfff0707f
+#define MATCH_FCLASS_D 0xe2001053
+#define MASK_FCLASS_D 0xfff0707f
+#define MATCH_FCVT_S_W 0xd0000053
+#define MASK_FCVT_S_W 0xfff0007f
+#define MATCH_FCVT_S_WU 0xd0100053
+#define MASK_FCVT_S_WU 0xfff0007f
+#define MATCH_FCVT_S_L 0xd0200053
+#define MASK_FCVT_S_L 0xfff0007f
+#define MATCH_FCVT_S_LU 0xd0300053
+#define MASK_FCVT_S_LU 0xfff0007f
+#define MATCH_FMV_S_X 0xf0000053
+#define MASK_FMV_S_X 0xfff0707f
+#define MATCH_FCVT_D_W 0xd2000053
+#define MASK_FCVT_D_W 0xfff0007f
+#define MATCH_FCVT_D_WU 0xd2100053
+#define MASK_FCVT_D_WU 0xfff0007f
+#define MATCH_FCVT_D_L 0xd2200053
+#define MASK_FCVT_D_L 0xfff0007f
+#define MATCH_FCVT_D_LU 0xd2300053
+#define MASK_FCVT_D_LU 0xfff0007f
+#define MATCH_FMV_D_X 0xf2000053
+#define MASK_FMV_D_X 0xfff0707f
+#define MATCH_FLW 0x2007
+#define MASK_FLW 0x707f
+#define MATCH_FLD 0x3007
+#define MASK_FLD 0x707f
+#define MATCH_FSW 0x2027
+#define MASK_FSW 0x707f
+#define MATCH_FSD 0x3027
+#define MASK_FSD 0x707f
+#define MATCH_FMADD_S 0x43
+#define MASK_FMADD_S 0x600007f
+#define MATCH_FMSUB_S 0x47
+#define MASK_FMSUB_S 0x600007f
+#define MATCH_FNMSUB_S 0x4b
+#define MASK_FNMSUB_S 0x600007f
+#define MATCH_FNMADD_S 0x4f
+#define MASK_FNMADD_S 0x600007f
+#define MATCH_FMADD_D 0x2000043
+#define MASK_FMADD_D 0x600007f
+#define MATCH_FMSUB_D 0x2000047
+#define MASK_FMSUB_D 0x600007f
+#define MATCH_FNMSUB_D 0x200004b
+#define MASK_FNMSUB_D 0x600007f
+#define MATCH_FNMADD_D 0x200004f
+#define MASK_FNMADD_D 0x600007f
+#define MATCH_C_NOP 0x1
+#define MASK_C_NOP 0xffff
+#define MATCH_C_ADDI16SP 0x6101
+#define MASK_C_ADDI16SP 0xef83
+#define MATCH_C_JR 0x8002
+#define MASK_C_JR 0xf07f
+#define MATCH_C_JALR 0x9002
+#define MASK_C_JALR 0xf07f
+#define MATCH_C_EBREAK 0x9002
+#define MASK_C_EBREAK 0xffff
+#define MATCH_C_LD 0x6000
+#define MASK_C_LD 0xe003
+#define MATCH_C_SD 0xe000
+#define MASK_C_SD 0xe003
+#define MATCH_C_ADDIW 0x2001
+#define MASK_C_ADDIW 0xe003
+#define MATCH_C_LDSP 0x6002
+#define MASK_C_LDSP 0xe003
+#define MATCH_C_SDSP 0xe002
+#define MASK_C_SDSP 0xe003
+#define MATCH_C_ADDI4SPN 0x0
+#define MASK_C_ADDI4SPN 0xe003
+#define MATCH_C_FLD 0x2000
+#define MASK_C_FLD 0xe003
+#define MATCH_C_LW 0x4000
+#define MASK_C_LW 0xe003
+#define MATCH_C_FLW 0x6000
+#define MASK_C_FLW 0xe003
+#define MATCH_C_FSD 0xa000
+#define MASK_C_FSD 0xe003
+#define MATCH_C_SW 0xc000
+#define MASK_C_SW 0xe003
+#define MATCH_C_FSW 0xe000
+#define MASK_C_FSW 0xe003
+#define MATCH_C_ADDI 0x1
+#define MASK_C_ADDI 0xe003
+#define MATCH_C_JAL 0x2001
+#define MASK_C_JAL 0xe003
+#define MATCH_C_LI 0x4001
+#define MASK_C_LI 0xe003
+#define MATCH_C_LUI 0x6001
+#define MASK_C_LUI 0xe003
+#define MATCH_C_SRLI 0x8001
+#define MASK_C_SRLI 0xec03
+#define MATCH_C_SRAI 0x8401
+#define MASK_C_SRAI 0xec03
+#define MATCH_C_ANDI 0x8801
+#define MASK_C_ANDI 0xec03
+#define MATCH_C_SUB 0x8c01
+#define MASK_C_SUB 0xfc63
+#define MATCH_C_XOR 0x8c21
+#define MASK_C_XOR 0xfc63
+#define MATCH_C_OR 0x8c41
+#define MASK_C_OR 0xfc63
+#define MATCH_C_AND 0x8c61
+#define MASK_C_AND 0xfc63
+#define MATCH_C_SUBW 0x9c01
+#define MASK_C_SUBW 0xfc63
+#define MATCH_C_ADDW 0x9c21
+#define MASK_C_ADDW 0xfc63
+#define MATCH_C_J 0xa001
+#define MASK_C_J 0xe003
+#define MATCH_C_BEQZ 0xc001
+#define MASK_C_BEQZ 0xe003
+#define MATCH_C_BNEZ 0xe001
+#define MASK_C_BNEZ 0xe003
+#define MATCH_C_SLLI 0x2
+#define MASK_C_SLLI 0xe003
+#define MATCH_C_FLDSP 0x2002
+#define MASK_C_FLDSP 0xe003
+#define MATCH_C_LWSP 0x4002
+#define MASK_C_LWSP 0xe003
+#define MATCH_C_FLWSP 0x6002
+#define MASK_C_FLWSP 0xe003
+#define MATCH_C_MV 0x8002
+#define MASK_C_MV 0xf003
+#define MATCH_C_ADD 0x9002
+#define MASK_C_ADD 0xf003
+#define MATCH_C_FSDSP 0xa002
+#define MASK_C_FSDSP 0xe003
+#define MATCH_C_SWSP 0xc002
+#define MASK_C_SWSP 0xe003
+#define MATCH_C_FSWSP 0xe002
+#define MASK_C_FSWSP 0xe003
+#define MATCH_CUSTOM0 0xb
+#define MASK_CUSTOM0 0x707f
+#define MATCH_CUSTOM0_RS1 0x200b
+#define MASK_CUSTOM0_RS1 0x707f
+#define MATCH_CUSTOM0_RS1_RS2 0x300b
+#define MASK_CUSTOM0_RS1_RS2 0x707f
+#define MATCH_CUSTOM0_RD 0x400b
+#define MASK_CUSTOM0_RD 0x707f
+#define MATCH_CUSTOM0_RD_RS1 0x600b
+#define MASK_CUSTOM0_RD_RS1 0x707f
+#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b
+#define MASK_CUSTOM0_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM1 0x2b
+#define MASK_CUSTOM1 0x707f
+#define MATCH_CUSTOM1_RS1 0x202b
+#define MASK_CUSTOM1_RS1 0x707f
+#define MATCH_CUSTOM1_RS1_RS2 0x302b
+#define MASK_CUSTOM1_RS1_RS2 0x707f
+#define MATCH_CUSTOM1_RD 0x402b
+#define MASK_CUSTOM1_RD 0x707f
+#define MATCH_CUSTOM1_RD_RS1 0x602b
+#define MASK_CUSTOM1_RD_RS1 0x707f
+#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b
+#define MASK_CUSTOM1_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM2 0x5b
+#define MASK_CUSTOM2 0x707f
+#define MATCH_CUSTOM2_RS1 0x205b
+#define MASK_CUSTOM2_RS1 0x707f
+#define MATCH_CUSTOM2_RS1_RS2 0x305b
+#define MASK_CUSTOM2_RS1_RS2 0x707f
+#define MATCH_CUSTOM2_RD 0x405b
+#define MASK_CUSTOM2_RD 0x707f
+#define MATCH_CUSTOM2_RD_RS1 0x605b
+#define MASK_CUSTOM2_RD_RS1 0x707f
+#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b
+#define MASK_CUSTOM2_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM3 0x7b
+#define MASK_CUSTOM3 0x707f
+#define MATCH_CUSTOM3_RS1 0x207b
+#define MASK_CUSTOM3_RS1 0x707f
+#define MATCH_CUSTOM3_RS1_RS2 0x307b
+#define MASK_CUSTOM3_RS1_RS2 0x707f
+#define MATCH_CUSTOM3_RD 0x407b
+#define MASK_CUSTOM3_RD 0x707f
+#define MATCH_CUSTOM3_RD_RS1 0x607b
+#define MASK_CUSTOM3_RD_RS1 0x707f
+#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b
+#define MASK_CUSTOM3_RD_RS1_RS2 0x707f
+#define CSR_FFLAGS 0x1
+#define CSR_FRM 0x2
+#define CSR_FCSR 0x3
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_SBADADDR 0x143
+#define CSR_SIP 0x144
+#define CSR_SPTBR 0x180
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MBADADDR 0x343
+#define CSR_MIP 0x344
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+#define CAUSE_MISALIGNED_FETCH 0x0
+#define CAUSE_FAULT_FETCH 0x1
+#define CAUSE_ILLEGAL_INSTRUCTION 0x2
+#define CAUSE_BREAKPOINT 0x3
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_FAULT_LOAD 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_FAULT_STORE 0x7
+#define CAUSE_USER_ECALL 0x8
+#define CAUSE_SUPERVISOR_ECALL 0x9
+#define CAUSE_HYPERVISOR_ECALL 0xa
+#define CAUSE_MACHINE_ECALL 0xb
+#endif
+#ifdef DECLARE_INSN
+DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ)
+DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
+DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
+DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
+DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
+DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
+DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR)
+DECLARE_INSN(jal, MATCH_JAL, MASK_JAL)
+DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
+DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC)
+DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
+DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
+DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI)
+DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU)
+DECLARE_INSN(xori, MATCH_XORI, MASK_XORI)
+DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
+DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
+DECLARE_INSN(ori, MATCH_ORI, MASK_ORI)
+DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI)
+DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
+DECLARE_INSN(sub, MATCH_SUB, MASK_SUB)
+DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
+DECLARE_INSN(slt, MATCH_SLT, MASK_SLT)
+DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU)
+DECLARE_INSN(xor, MATCH_XOR, MASK_XOR)
+DECLARE_INSN(srl, MATCH_SRL, MASK_SRL)
+DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
+DECLARE_INSN(or, MATCH_OR, MASK_OR)
+DECLARE_INSN(and, MATCH_AND, MASK_AND)
+DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
+DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
+DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
+DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
+DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
+DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
+DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
+DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
+DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
+DECLARE_INSN(lb, MATCH_LB, MASK_LB)
+DECLARE_INSN(lh, MATCH_LH, MASK_LH)
+DECLARE_INSN(lw, MATCH_LW, MASK_LW)
+DECLARE_INSN(ld, MATCH_LD, MASK_LD)
+DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
+DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
+DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
+DECLARE_INSN(sb, MATCH_SB, MASK_SB)
+DECLARE_INSN(sh, MATCH_SH, MASK_SH)
+DECLARE_INSN(sw, MATCH_SW, MASK_SW)
+DECLARE_INSN(sd, MATCH_SD, MASK_SD)
+DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE)
+DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I)
+DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
+DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH)
+DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU)
+DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU)
+DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
+DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
+DECLARE_INSN(rem, MATCH_REM, MASK_REM)
+DECLARE_INSN(remu, MATCH_REMU, MASK_REMU)
+DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW)
+DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW)
+DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
+DECLARE_INSN(remw, MATCH_REMW, MASK_REMW)
+DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW)
+DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W)
+DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W)
+DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W)
+DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W)
+DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W)
+DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W)
+DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W)
+DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W)
+DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W)
+DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
+DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W)
+DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D)
+DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D)
+DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D)
+DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D)
+DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D)
+DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D)
+DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D)
+DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D)
+DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D)
+DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
+DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D)
+DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL)
+DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK)
+DECLARE_INSN(uret, MATCH_URET, MASK_URET)
+DECLARE_INSN(sret, MATCH_SRET, MASK_SRET)
+DECLARE_INSN(hret, MATCH_HRET, MASK_HRET)
+DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
+DECLARE_INSN(dret, MATCH_DRET, MASK_DRET)
+DECLARE_INSN(sfence_vm, MATCH_SFENCE_VM, MASK_SFENCE_VM)
+DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
+DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
+DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS)
+DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC)
+DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
+DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI)
+DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI)
+DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S)
+DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S)
+DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S)
+DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S)
+DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S)
+DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S)
+DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S)
+DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S)
+DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S)
+DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S)
+DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D)
+DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D)
+DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D)
+DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D)
+DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D)
+DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D)
+DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D)
+DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D)
+DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D)
+DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D)
+DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S)
+DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D)
+DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S)
+DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S)
+DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S)
+DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D)
+DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D)
+DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D)
+DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S)
+DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S)
+DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S)
+DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S)
+DECLARE_INSN(fmv_x_s, MATCH_FMV_X_S, MASK_FMV_X_S)
+DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S)
+DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D)
+DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D)
+DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D)
+DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D)
+DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D)
+DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D)
+DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W)
+DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU)
+DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L)
+DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU)
+DECLARE_INSN(fmv_s_x, MATCH_FMV_S_X, MASK_FMV_S_X)
+DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W)
+DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU)
+DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L)
+DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU)
+DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X)
+DECLARE_INSN(flw, MATCH_FLW, MASK_FLW)
+DECLARE_INSN(fld, MATCH_FLD, MASK_FLD)
+DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW)
+DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD)
+DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S)
+DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S)
+DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S)
+DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S)
+DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D)
+DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D)
+DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D)
+DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D)
+DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
+DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP)
+DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
+DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
+DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK)
+DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
+DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
+DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW)
+DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
+DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
+DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN)
+DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD)
+DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
+DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW)
+DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD)
+DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
+DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW)
+DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI)
+DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
+DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
+DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
+DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
+DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
+DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI)
+DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
+DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
+DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
+DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND)
+DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
+DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW)
+DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
+DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ)
+DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ)
+DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
+DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP)
+DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
+DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP)
+DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
+DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
+DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP)
+DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
+DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP)
+DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0)
+DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1)
+DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2)
+DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD)
+DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1)
+DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2)
+DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1)
+DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1)
+DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2)
+DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD)
+DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1)
+DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2)
+DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2)
+DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1)
+DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2)
+DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD)
+DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1)
+DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2)
+DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3)
+DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1)
+DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2)
+DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD)
+DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1)
+DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2)
+#endif
+#ifdef DECLARE_CSR
+DECLARE_CSR(fflags, CSR_FFLAGS)
+DECLARE_CSR(frm, CSR_FRM)
+DECLARE_CSR(fcsr, CSR_FCSR)
+DECLARE_CSR(cycle, CSR_CYCLE)
+DECLARE_CSR(time, CSR_TIME)
+DECLARE_CSR(instret, CSR_INSTRET)
+DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3)
+DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4)
+DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5)
+DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6)
+DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7)
+DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8)
+DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9)
+DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10)
+DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11)
+DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12)
+DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13)
+DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14)
+DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15)
+DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16)
+DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17)
+DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18)
+DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19)
+DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20)
+DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21)
+DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22)
+DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23)
+DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24)
+DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25)
+DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26)
+DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27)
+DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28)
+DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29)
+DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30)
+DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31)
+DECLARE_CSR(sstatus, CSR_SSTATUS)
+DECLARE_CSR(sie, CSR_SIE)
+DECLARE_CSR(stvec, CSR_STVEC)
+DECLARE_CSR(sscratch, CSR_SSCRATCH)
+DECLARE_CSR(sepc, CSR_SEPC)
+DECLARE_CSR(scause, CSR_SCAUSE)
+DECLARE_CSR(sbadaddr, CSR_SBADADDR)
+DECLARE_CSR(sip, CSR_SIP)
+DECLARE_CSR(sptbr, CSR_SPTBR)
+DECLARE_CSR(mstatus, CSR_MSTATUS)
+DECLARE_CSR(misa, CSR_MISA)
+DECLARE_CSR(medeleg, CSR_MEDELEG)
+DECLARE_CSR(mideleg, CSR_MIDELEG)
+DECLARE_CSR(mie, CSR_MIE)
+DECLARE_CSR(mtvec, CSR_MTVEC)
+DECLARE_CSR(mscratch, CSR_MSCRATCH)
+DECLARE_CSR(mepc, CSR_MEPC)
+DECLARE_CSR(mcause, CSR_MCAUSE)
+DECLARE_CSR(mbadaddr, CSR_MBADADDR)
+DECLARE_CSR(mip, CSR_MIP)
+DECLARE_CSR(tselect, CSR_TSELECT)
+DECLARE_CSR(tdata1, CSR_TDATA1)
+DECLARE_CSR(tdata2, CSR_TDATA2)
+DECLARE_CSR(tdata3, CSR_TDATA3)
+DECLARE_CSR(dcsr, CSR_DCSR)
+DECLARE_CSR(dpc, CSR_DPC)
+DECLARE_CSR(dscratch, CSR_DSCRATCH)
+DECLARE_CSR(mcycle, CSR_MCYCLE)
+DECLARE_CSR(minstret, CSR_MINSTRET)
+DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3)
+DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4)
+DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5)
+DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6)
+DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7)
+DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8)
+DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9)
+DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10)
+DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11)
+DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12)
+DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13)
+DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14)
+DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15)
+DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16)
+DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17)
+DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18)
+DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19)
+DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20)
+DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21)
+DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22)
+DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23)
+DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24)
+DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25)
+DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26)
+DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27)
+DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28)
+DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29)
+DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30)
+DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31)
+DECLARE_CSR(mucounteren, CSR_MUCOUNTEREN)
+DECLARE_CSR(mscounteren, CSR_MSCOUNTEREN)
+DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3)
+DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4)
+DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5)
+DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6)
+DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7)
+DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8)
+DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9)
+DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10)
+DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11)
+DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12)
+DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13)
+DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14)
+DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15)
+DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16)
+DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17)
+DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18)
+DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19)
+DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20)
+DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21)
+DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22)
+DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23)
+DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24)
+DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25)
+DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26)
+DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27)
+DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28)
+DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29)
+DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30)
+DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31)
+DECLARE_CSR(mvendorid, CSR_MVENDORID)
+DECLARE_CSR(marchid, CSR_MARCHID)
+DECLARE_CSR(mimpid, CSR_MIMPID)
+DECLARE_CSR(mhartid, CSR_MHARTID)
+DECLARE_CSR(cycleh, CSR_CYCLEH)
+DECLARE_CSR(timeh, CSR_TIMEH)
+DECLARE_CSR(instreth, CSR_INSTRETH)
+DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H)
+DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H)
+DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H)
+DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H)
+DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H)
+DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H)
+DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H)
+DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H)
+DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H)
+DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H)
+DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H)
+DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H)
+DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H)
+DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H)
+DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H)
+DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H)
+DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H)
+DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H)
+DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H)
+DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H)
+DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H)
+DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H)
+DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H)
+DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H)
+DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H)
+DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H)
+DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
+DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
+DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
+DECLARE_CSR(mcycleh, CSR_MCYCLEH)
+DECLARE_CSR(minstreth, CSR_MINSTRETH)
+DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H)
+DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H)
+DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H)
+DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H)
+DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H)
+DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H)
+DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H)
+DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H)
+DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H)
+DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H)
+DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H)
+DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H)
+DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H)
+DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H)
+DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H)
+DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H)
+DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H)
+DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H)
+DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H)
+DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H)
+DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H)
+DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H)
+DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H)
+DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H)
+DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H)
+DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H)
+DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H)
+DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H)
+DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H)
+#endif
+#ifdef DECLARE_CAUSE
+DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH)
+DECLARE_CAUSE("fault fetch", CAUSE_FAULT_FETCH)
+DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION)
+DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT)
+DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD)
+DECLARE_CAUSE("fault load", CAUSE_FAULT_LOAD)
+DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE)
+DECLARE_CAUSE("fault store", CAUSE_FAULT_STORE)
+DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL)
+DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL)
+DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL)
+DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL)
+#endif
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
new file mode 100644
index 0000000..e366f7d
--- /dev/null
+++ b/src/target/riscv/gdb_regs.h
@@ -0,0 +1,28 @@
+#ifndef TARGET__RISCV__GDB_REGS_H
+#define TARGET__RISCV__GDB_REGS_H
+
+enum gdb_regno {
+ GDB_REGNO_XPR0 = 0,
+ GDB_REGNO_X0 = GDB_REGNO_XPR0 + 0,
+ GDB_REGNO_ZERO = GDB_REGNO_XPR0 + 0,
+ GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8,
+ GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9,
+ GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31,
+ GDB_REGNO_PC = 32,
+ GDB_REGNO_FPR0 = 33,
+ GDB_REGNO_FPR31 = GDB_REGNO_FPR0 + 31,
+ GDB_REGNO_CSR0 = 65,
+ 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_DSCRATCH = CSR_DSCRATCH + GDB_REGNO_CSR0,
+ GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
+ GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
+ GDB_REGNO_PRIV = 4161,
+ GDB_REGNO_COUNT
+};
+
+#endif
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
new file mode 100644
index 0000000..3e1f8a2
--- /dev/null
+++ b/src/target/riscv/opcodes.h
@@ -0,0 +1,295 @@
+#include "encoding.h"
+
+#define ZERO 0
+#define T0 5
+#define S0 8
+#define S1 9
+
+static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) {
+ return (value >> lo) & ((1 << (hi+1-lo)) - 1);
+}
+
+static uint32_t bit(uint32_t value, unsigned int b) {
+ return (value >> b) & 1;
+}
+
+static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused));
+static uint32_t jal(unsigned int rd, uint32_t imm) {
+ return (bit(imm, 20) << 31) |
+ (bits(imm, 10, 1) << 21) |
+ (bit(imm, 11) << 20) |
+ (bits(imm, 19, 12) << 12) |
+ (rd << 7) |
+ MATCH_JAL;
+}
+
+static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused));
+static uint32_t csrsi(unsigned int csr, uint16_t imm) {
+ return (csr << 20) |
+ (bits(imm, 4, 0) << 15) |
+ MATCH_CSRRSI;
+}
+
+static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (src << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_SW;
+}
+
+static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (src << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_SD;
+}
+
+static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (src << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_SH;
+}
+
+static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (src << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_SB;
+}
+
+static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(rd, 4, 0) << 7) |
+ MATCH_LD;
+}
+
+static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(rd, 4, 0) << 7) |
+ MATCH_LW;
+}
+
+static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(rd, 4, 0) << 7) |
+ MATCH_LH;
+}
+
+static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(rd, 4, 0) << 7) |
+ MATCH_LB;
+}
+
+static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused));
+static uint32_t csrw(unsigned int source, unsigned int csr) {
+ return (csr << 20) | (source << 15) | MATCH_CSRRW;
+}
+
+static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
+static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm)
+{
+ return (bits(imm, 11, 0) << 20) |
+ (src << 15) |
+ (dest << 7) |
+ MATCH_ADDI;
+}
+
+static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused));
+static uint32_t csrr(unsigned int rd, unsigned int csr) {
+ return (csr << 20) | (rd << 7) | MATCH_CSRRS;
+}
+
+static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
+static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) {
+ return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS;
+}
+
+static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
+static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) {
+ return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW;
+}
+
+static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (bits(src, 4, 0) << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_FSW;
+}
+
+static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (bits(src, 4, 0) << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_FSD;
+}
+
+static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(dest, 4, 0) << 7) |
+ MATCH_FLW;
+}
+
+static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 0) << 20) |
+ (base << 15) |
+ (bits(dest, 4, 0) << 7) |
+ MATCH_FLD;
+}
+
+static uint32_t fmv_x_s(unsigned dest, unsigned src) __attribute__ ((unused));
+static uint32_t fmv_x_s(unsigned dest, unsigned src)
+{
+ return src << 15 |
+ dest << 7 |
+ MATCH_FMV_X_S;
+}
+
+static uint32_t fmv_x_d(unsigned dest, unsigned src) __attribute__ ((unused));
+static uint32_t fmv_x_d(unsigned dest, unsigned src)
+{
+ return src << 15 |
+ dest << 7 |
+ MATCH_FMV_X_D;
+}
+
+static uint32_t fmv_s_x(unsigned dest, unsigned src) __attribute__ ((unused));
+static uint32_t fmv_s_x(unsigned dest, unsigned src)
+{
+ return src << 15 |
+ dest << 7 |
+ MATCH_FMV_S_X;
+}
+
+static uint32_t fmv_d_x(unsigned dest, unsigned src) __attribute__ ((unused));
+static uint32_t fmv_d_x(unsigned dest, unsigned src)
+{
+ return src << 15 |
+ dest << 7 |
+ MATCH_FMV_D_X;
+}
+
+static uint32_t ebreak(void) __attribute__ ((unused));
+static uint32_t ebreak(void) { return MATCH_EBREAK; }
+static uint32_t ebreak_c(void) __attribute__ ((unused));
+static uint32_t ebreak_c(void) { return MATCH_C_EBREAK; }
+
+static uint32_t fence_i(void) __attribute__ ((unused));
+static uint32_t fence_i(void)
+{
+ return MATCH_FENCE_I;
+}
+
+static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
+static uint32_t lui(unsigned int dest, uint32_t imm)
+{
+ return (bits(imm, 19, 0) << 12) |
+ (dest << 7) |
+ MATCH_LUI;
+}
+
+/*
+static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
+static uint32_t csrci(unsigned int csr, uint16_t imm) {
+ return (csr << 20) |
+ (bits(imm, 4, 0) << 15) |
+ MATCH_CSRRCI;
+}
+
+static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused));
+static uint32_t li(unsigned int dest, uint16_t imm)
+{
+ return addi(dest, 0, imm);
+}
+
+static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
+static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
+{
+ return (bits(offset, 11, 5) << 25) |
+ (bits(src, 4, 0) << 20) |
+ (base << 15) |
+ (bits(offset, 4, 0) << 7) |
+ MATCH_FSD;
+}
+
+static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
+static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm)
+{
+ return (bits(imm, 11, 0) << 20) |
+ (src << 15) |
+ (dest << 7) |
+ MATCH_ORI;
+}
+
+static uint32_t nop(void) __attribute__ ((unused));
+static uint32_t nop(void)
+{
+ return addi(0, 0, 0);
+}
+*/
+
+static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
+static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm)
+{
+ return (bits(imm, 11, 0) << 20) |
+ (src << 15) |
+ (dest << 7) |
+ MATCH_XORI;
+}
+
+static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused));
+static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
+{
+ return (bits(shamt, 4, 0) << 20) |
+ (src << 15) |
+ (dest << 7) |
+ MATCH_SRLI;
+}
+
+static uint32_t fence(void) __attribute__((unused));
+static uint32_t fence(void)
+{
+ return MATCH_FENCE;
+}
+
+static uint32_t auipc(unsigned int dest) __attribute__((unused));
+static uint32_t auipc(unsigned int dest)
+{
+ return MATCH_AUIPC | (dest << 7);
+}
diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c
new file mode 100644
index 0000000..6f7b10b
--- /dev/null
+++ b/src/target/riscv/program.c
@@ -0,0 +1,491 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target/target.h"
+#include "riscv.h"
+#include "program.h"
+#include "helper/log.h"
+
+#include "asm.h"
+#include "encoding.h"
+
+riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr);
+int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+
+/* Program interface. */
+int riscv_program_init(struct riscv_program *p, struct target *target)
+{
+ LOG_DEBUG("riscv_program_init: p=%p", p);
+
+ memset(p, 0, sizeof(*p));
+ p->target = target;
+ p->instruction_count = 0;
+ p->data_count = 0;
+ p->writes_memory = 0;
+ p->target_xlen = riscv_xlen(target);
+ for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) {
+ p->writes_xreg[i] = 0;
+ p->in_use[i] = 0;
+ }
+
+ for(size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
+ p->debug_buffer[i] = -1;
+
+ if (riscv_debug_buffer_enter(target, p) != ERROR_OK) {
+ LOG_ERROR("unable to write progam buffer enter code");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+int riscv_program_exec(struct riscv_program *p, struct target *t)
+{
+ if (riscv_debug_buffer_leave(t, p) != ERROR_OK) {
+ LOG_ERROR("unable to write program buffer exit code");
+ return ERROR_FAIL;
+ }
+
+ riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
+ for (size_t i = GDB_REGNO_XPR0 + 1; i <= GDB_REGNO_XPR31; ++i) {
+ if (p->writes_xreg[i]) {
+ LOG_DEBUG("Saving register %d as used by program", (int)i);
+ saved_registers[i] = riscv_get_register(t, i);
+ }
+ }
+
+ if (p->writes_memory && (riscv_program_fence(p) != ERROR_OK)) {
+ LOG_ERROR("Unable to write fence");
+ for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
+ LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]);
+ abort();
+ return ERROR_FAIL;
+ }
+
+ if (riscv_program_ebreak(p) != ERROR_OK) {
+ LOG_ERROR("Unable to write ebreak");
+ for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
+ LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]);
+ abort();
+ return ERROR_FAIL;
+ }
+
+ for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) {
+ LOG_DEBUG("Executing program %p: debug_buffer[%02x] = DASM(0x%08lx)", p, (int)i, (long)p->debug_buffer[i]);
+ if (i <= p->instruction_count || i >= riscv_debug_buffer_size(p->target) - p->data_count)
+ riscv_write_debug_buffer(t, i, p->debug_buffer[i]);
+ }
+
+ if (riscv_execute_debug_buffer(t) != ERROR_OK) {
+ LOG_DEBUG("Unable to execute program %p", p);
+ return ERROR_FAIL;
+ }
+
+ for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
+ if (i >= riscv_debug_buffer_size(p->target) - p->data_count)
+ p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
+
+ for (size_t i = GDB_REGNO_XPR0; i <= GDB_REGNO_XPR31; ++i)
+ if (p->writes_xreg[i])
+ riscv_set_register(t, i, saved_registers[i]);
+
+ return ERROR_OK;
+}
+
+riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes)
+{
+ LOG_DEBUG("allocating %d bytes of data", (int)bytes);
+
+ riscv_addr_t addr =
+ riscv_debug_buffer_addr(p->target)
+ + riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])
+ - p->data_count * sizeof(p->debug_buffer[0])
+ - bytes;
+ while (addr % bytes != 0) addr--;
+
+ riscv_addr_t ptop =
+ riscv_debug_buffer_addr(p->target)
+ + p->instruction_count * sizeof(p->debug_buffer[0]);
+
+ if (addr <= ptop) {
+ LOG_DEBUG("unable to allocate %d bytes", (int)bytes);
+ return RISCV_PROGRAM_ALLOC_FAIL;
+ }
+
+ LOG_DEBUG("allocated %d bytes at 0x%08lx", (int)bytes, (long)addr);
+ p->data_count =
+ + riscv_debug_buffer_size(p->target)
+ - (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
+ return addr;
+}
+
+riscv_addr_t riscv_program_alloc_x(struct riscv_program *p)
+{
+ return riscv_program_alloc_data(p, p->target_xlen / 8);
+}
+
+riscv_addr_t riscv_program_alloc_d(struct riscv_program *p)
+{
+ return riscv_program_alloc_data(p, 8);
+}
+
+riscv_addr_t riscv_program_alloc_w(struct riscv_program *p)
+{
+ return riscv_program_alloc_data(p, 4);
+}
+
+riscv_addr_t riscv_program_alloc_h(struct riscv_program *p)
+{
+ return riscv_program_alloc_data(p, 2);
+}
+
+riscv_addr_t riscv_program_alloc_b(struct riscv_program *p)
+{
+ return riscv_program_alloc_data(p, 1);
+}
+
+riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr)
+{
+ if (addr < riscv_debug_buffer_addr(p->target))
+ return -1;
+ if ((size_t)addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
+ return -1;
+
+ int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
+ return p->debug_buffer[off];
+}
+
+void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t addr, uint64_t d)
+{
+ if (addr < riscv_debug_buffer_addr(p->target))
+ return;
+ if ((size_t)addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
+ return;
+
+ int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
+ p->debug_buffer[off] = d;
+}
+
+int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, sw(d, b, offset));
+}
+
+int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, sh(d, b, offset));
+}
+
+int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, sb(d, b, offset));
+}
+
+int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, lw(d, b, offset));
+}
+
+int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, lh(d, b, offset));
+}
+
+int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
+{
+ p->writes_memory = 1;
+ return riscv_program_insert(p, lb(d, b, offset));
+}
+
+int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ switch (p->target_xlen) {
+ case 64: return riscv_program_ld(p, d, addr);
+ case 32: return riscv_program_lw(p, d, addr);
+ }
+
+ LOG_ERROR("unknown xlen %d", p->target_xlen);
+ abort();
+ return -1;
+}
+
+int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
+ if (riscv_program_lah(p, d, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, ld(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
+ if (riscv_program_lah(p, d, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, lw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
+ if (riscv_program_lah(p, d, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, lh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, lb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_sx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ switch (p->target_xlen) {
+ case 64: return riscv_program_sd(p, d, addr);
+ case 32: return riscv_program_sw(p, d, addr);
+ }
+
+ LOG_ERROR("unknown xlen %d", p->target_xlen);
+ abort();
+ return -1;
+}
+
+int riscv_program_sd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0
+ ? GDB_REGNO_X0
+ : riscv_program_gettemp(p);
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, sd(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_puttemp(p, t);
+ p->writes_memory = true;
+ return ERROR_OK;
+}
+
+int riscv_program_sw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0
+ ? GDB_REGNO_X0
+ : riscv_program_gettemp(p);
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, sw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_puttemp(p, t);
+ p->writes_memory = true;
+ return ERROR_OK;
+}
+
+int riscv_program_sh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0
+ ? GDB_REGNO_X0
+ : riscv_program_gettemp(p);
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, sh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_puttemp(p, t);
+ p->writes_memory = true;
+ return ERROR_OK;
+}
+
+int riscv_program_sb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0
+ ? GDB_REGNO_X0
+ : riscv_program_gettemp(p);
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, sb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_puttemp(p, t);
+ p->writes_memory = true;
+ return ERROR_OK;
+}
+
+int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr)
+{
+ assert(csr >= GDB_REGNO_CSR0);
+ return riscv_program_insert(p, csrrs(d, GDB_REGNO_X0, csr - GDB_REGNO_CSR0));
+}
+
+int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
+{
+ assert(csr >= GDB_REGNO_CSR0);
+ return riscv_program_insert(p, csrrw(GDB_REGNO_X0, s, csr - GDB_REGNO_CSR0));
+}
+
+int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr)
+{
+ assert(csr >= GDB_REGNO_CSR0);
+ return riscv_program_insert(p, csrrw(d, s, csr - GDB_REGNO_CSR0));
+}
+
+int riscv_program_fence_i(struct riscv_program *p)
+{
+ return riscv_program_insert(p, fence_i());
+}
+
+int riscv_program_fence(struct riscv_program *p)
+{
+ return riscv_program_insert(p, fence());
+}
+
+int riscv_program_ebreak(struct riscv_program *p)
+{
+ return riscv_program_insert(p, ebreak());
+}
+
+int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u)
+{
+ return riscv_program_insert(p, lui(d, u));
+}
+
+int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t u)
+{
+ return riscv_program_insert(p, addi(d, s, u));
+}
+
+int riscv_program_fsd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ assert(d >= GDB_REGNO_FPR0);
+ assert(d <= GDB_REGNO_FPR31);
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0
+ ? GDB_REGNO_X0
+ : riscv_program_gettemp(p);
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, fsd(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_puttemp(p, t);
+ p->writes_memory = true;
+ return ERROR_OK;
+}
+
+int riscv_program_fld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ assert(d >= GDB_REGNO_FPR0);
+ assert(d <= GDB_REGNO_FPR31);
+ enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
+ if (riscv_program_lah(p, t, addr) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(p, fld(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr))) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c)
+{
+ if (riscv_program_lui(p, d, c >> 12) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_addi(p, d, d, c & 0xFFF) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
+}
+
+int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r)
+{
+ assert(r < RISCV_REGISTER_COUNT);
+ p->writes_xreg[r] = 0;
+ return ERROR_OK;
+}
+
+int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r)
+{
+ assert(r < RISCV_REGISTER_COUNT);
+ p->writes_xreg[r] = 1;
+ return ERROR_OK;
+}
+
+void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r)
+{
+ assert(r < RISCV_REGISTER_COUNT);
+ assert(p->in_use[r] == 0);
+ p->in_use[r] = 1;
+}
+
+enum gdb_regno riscv_program_gettemp(struct riscv_program *p)
+{
+ for (size_t i = GDB_REGNO_S0; i <= GDB_REGNO_XPR31; ++i) {
+ if (p->in_use[i]) continue;
+
+ riscv_program_do_restore_register(p, i);
+ p->in_use[i] = 1;
+ return i;
+ }
+
+ LOG_ERROR("You've run out of temporary registers. This is impossible.");
+ abort();
+}
+
+void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r)
+{
+ assert(r < RISCV_REGISTER_COUNT);
+ p->in_use[r] = 0;
+}
+
+/* Helper functions. */
+riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr)
+{
+ return addr >> 12;
+}
+
+riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr)
+{
+ return ((addr > 0) ? 1 : 0) * (abs(addr) & 0x7FF);
+}
+
+int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ riscv_addr_t ah = riscv_program_gah(p, addr);
+ if (ah == 0)
+ return ERROR_OK;
+ return riscv_program_lui(p, d, ah);
+}
+
+int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
+{
+ riscv_addr_t al = riscv_program_gal(p, addr);
+ if (al == 0)
+ return ERROR_OK;
+ return riscv_program_addi(p, d, d, al);
+}
+
+int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
+{
+ LOG_DEBUG("instruction_count: %d (p=%p)", (int)p->instruction_count, p);
+
+ if (p->instruction_count + p->data_count + 1 > riscv_debug_buffer_size(p->target)) {
+ LOG_DEBUG("Unable to insert instruction:");
+ LOG_DEBUG(" instruction_count=%d", (int)p->instruction_count);
+ LOG_DEBUG(" data_count =%d", (int)p->data_count);
+ LOG_DEBUG(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("PROGBUF[%d] = DASM(0x%08x) [0x%08x]", (int)p->instruction_count, i, i);
+ p->debug_buffer[p->instruction_count] = i;
+ p->instruction_count++;
+ return ERROR_OK;
+}
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
new file mode 100644
index 0000000..1efdb12
--- /dev/null
+++ b/src/target/riscv/program.h
@@ -0,0 +1,142 @@
+#ifndef TARGET__RISCV__PROGRAM_H
+#define TARGET__RISCV__PROGRAM_H
+
+#include "riscv.h"
+
+#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
+#define RISCV_REGISTER_COUNT 32
+#define RISCV_DSCRATCH_COUNT 2
+
+/* The various RISC-V debug specifications all revolve around setting up
+ * program buffers and executing them on the target. This structure contains a
+ * single program, which can then be executed on targets. */
+struct riscv_program {
+ struct target *target;
+
+ uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
+
+ /* The debug buffer is allocated in two directions: instructions go at
+ * the start, while data goes at the end. When they meet in the middle
+ * this blows up. */
+ size_t instruction_count;
+ size_t data_count;
+
+ /* Side effects of executing this program. These must be accounted for
+ * in order to maintain correct executing of the target system. */
+ bool writes_xreg[RISCV_REGISTER_COUNT];
+ bool writes_memory;
+
+ /* When a register is used it will be set in this array. */
+ bool in_use[RISCV_REGISTER_COUNT];
+
+ /* Remembers the registers that have been saved into dscratch
+ * registers. These are restored */
+ enum gdb_regno dscratch_saved[RISCV_DSCRATCH_COUNT];
+
+ /* XLEN on the target. */
+ int target_xlen;
+};
+
+/* Initializes a program with the header. */
+int riscv_program_init(struct riscv_program *p, struct target *t);
+
+/* Executes a program, returning 0 if the program successfully executed. Note
+ * that this may cause registers to be saved or restored, which could result to
+ * calls to things like riscv_save_register which itself could require a
+ * program to execute. That's OK, just make sure this eventually terminates.
+ * */
+int riscv_program_exec(struct riscv_program *p, struct target *t);
+int riscv_program_load(struct riscv_program *p, struct target *t);
+
+/* Clears a program, removing all the state associated with it. */
+int riscv_program_clear(struct riscv_program *p, struct target *t);
+
+/* A lower level interface, you shouldn't use this unless you have a reason. */
+int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
+
+/* There is hardware support for saving at least one register. This register
+ * doesn't need to be saved/restored the usual way, which is useful during
+ * early initialization when we can't save/restore arbitrary registerrs to host
+ * memory. */
+int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
+
+/* Allocates data of various sizes. Either returns the absolute physical
+ * address or RISCV_PROGRAM_ALLOC_FAIL on failure. */
+riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes);
+riscv_addr_t riscv_program_alloc_x(struct riscv_program *p);
+riscv_addr_t riscv_program_alloc_d(struct riscv_program *p);
+riscv_addr_t riscv_program_alloc_w(struct riscv_program *p);
+riscv_addr_t riscv_program_alloc_h(struct riscv_program *p);
+riscv_addr_t riscv_program_alloc_b(struct riscv_program *p);
+#define RISCV_PROGRAM_ALLOC_FAIL ((riscv_addr_t)(-1))
+
+/* Reads a word of memory from this program's internal view of the debug RAM.
+ * This is what you want to use to get data back from the program after it
+ * executes. */
+riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr);
+void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t a, uint64_t d);
+
+/* Helpers to assembly various instructions. Return 0 on success. These might
+ * assembly into a multi-instruction sequence that overwrites some other
+ * register, but those will be properly saved and restored. */
+int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+
+int riscv_program_sx(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+int riscv_program_sd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+int riscv_program_sw(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+int riscv_program_sh(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+int riscv_program_sb(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+
+int riscv_program_lxr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+
+int riscv_program_sxr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+
+int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr);
+int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
+int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr);
+
+int riscv_program_fence_i(struct riscv_program *p);
+int riscv_program_fence(struct riscv_program *p);
+int riscv_program_ebreak(struct riscv_program *p);
+
+int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u);
+int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
+
+int riscv_program_fsd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
+int riscv_program_fld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
+
+/* Assembler macros. */
+int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c);
+int riscv_program_la(struct riscv_program *p, enum gdb_regno d, riscv_addr_t a);
+
+/* Register allocation. The user is expected to have obtained temporary
+ * registers using these fuctions. Additionally, there is an interface for
+ * reserving registers -- it's expected that this has been called as the first
+ * thing in the program's execution to reserve registers that can't be touched
+ * by the program's execution. */
+void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r);
+enum gdb_regno riscv_program_gettemp(struct riscv_program *p);
+void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r);
+
+/* Executing a program usually causes the registers that get overwritten to be
+ * saved and restored. Calling this prevents the given register from actually
+ * being restored as a result of all activity in this program. */
+int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r);
+int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r);
+
+/* Addressing functions. */
+riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr);
+
+#endif
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
new file mode 100644
index 0000000..6ed3ba1
--- /dev/null
+++ b/src/target/riscv/riscv-011.c
@@ -0,0 +1,2609 @@
+/*
+ * Support for RISC-V, debug version 0.11. This was never an officially adopted
+ * spec, but SiFive made some silicon that uses it.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target.h"
+#include "target/algorithm.h"
+#include "target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "register.h"
+#include "breakpoints.h"
+#include "helper/time_support.h"
+#include "riscv.h"
+#include "asm.h"
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+/**
+ * Code structure
+ *
+ * At the bottom of the stack are the OpenOCD JTAG functions:
+ * jtag_add_[id]r_scan
+ * jtag_execute_query
+ * jtag_add_runtest
+ *
+ * There are a few functions to just instantly shift a register and get its
+ * value:
+ * dtmcontrol_scan
+ * idcode_scan
+ * dbus_scan
+ *
+ * Because doing one scan and waiting for the result is slow, most functions
+ * batch up a bunch of dbus writes and then execute them all at once. They use
+ * the scans "class" for this:
+ * scans_new
+ * scans_delete
+ * scans_execute
+ * scans_add_...
+ * Usually you new(), call a bunch of add functions, then execute() and look
+ * at the results by calling scans_get...()
+ *
+ * Optimized functions will directly use the scans class above, but slightly
+ * lazier code will use the cache functions that in turn use the scans
+ * functions:
+ * cache_get...
+ * cache_set...
+ * cache_write
+ * cache_set... update a local structure, which is then synced to the target
+ * with cache_write(). Only Debug RAM words that are actually changed are sent
+ * to the target. Afterwards use cache_get... to read results.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x) (sizeof(x)/sizeof(*x))
+
+// Constants for legacy SiFive hardware breakpoints.
+#define CSR_BPCONTROL_X (1<<0)
+#define CSR_BPCONTROL_W (1<<1)
+#define CSR_BPCONTROL_R (1<<2)
+#define CSR_BPCONTROL_U (1<<3)
+#define CSR_BPCONTROL_S (1<<4)
+#define CSR_BPCONTROL_H (1<<5)
+#define CSR_BPCONTROL_M (1<<6)
+#define CSR_BPCONTROL_BPMATCH (0xf<<7)
+#define CSR_BPCONTROL_BPACTION (0xff<<11)
+
+#define DEBUG_ROM_START 0x800
+#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
+#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
+#define DEBUG_RAM_START 0x400
+
+#define SETHALTNOT 0x10c
+
+/*** JTAG registers. ***/
+
+#define DTMCONTROL 0x10
+#define DTMCONTROL_DBUS_RESET (1<<16)
+#define DTMCONTROL_IDLE (7<<10)
+#define DTMCONTROL_ADDRBITS (0xf<<4)
+#define DTMCONTROL_VERSION (0xf)
+
+#define DBUS 0x11
+#define DBUS_OP_START 0
+#define DBUS_OP_SIZE 2
+typedef enum {
+ DBUS_OP_NOP = 0,
+ DBUS_OP_READ = 1,
+ DBUS_OP_WRITE = 2
+} dbus_op_t;
+typedef enum {
+ DBUS_STATUS_SUCCESS = 0,
+ DBUS_STATUS_FAILED = 2,
+ DBUS_STATUS_BUSY = 3
+} dbus_status_t;
+#define DBUS_DATA_START 2
+#define DBUS_DATA_SIZE 34
+#define DBUS_ADDRESS_START 36
+
+typedef enum {
+ RE_OK,
+ RE_FAIL,
+ RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+ SLOT0,
+ SLOT1,
+ SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define DMCONTROL 0x10
+#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33)
+#define DMCONTROL_HALTNOT (((uint64_t)1)<<32)
+#define DMCONTROL_BUSERROR (7<<19)
+#define DMCONTROL_SERIAL (3<<16)
+#define DMCONTROL_AUTOINCREMENT (1<<15)
+#define DMCONTROL_ACCESS (7<<12)
+#define DMCONTROL_HARTID (0x3ff<<2)
+#define DMCONTROL_NDRESET (1<<1)
+#define DMCONTROL_FULLRESET 1
+
+#define DMINFO 0x11
+#define DMINFO_ABUSSIZE (0x7fU<<25)
+#define DMINFO_SERIALCOUNT (0xf<<21)
+#define DMINFO_ACCESS128 (1<<20)
+#define DMINFO_ACCESS64 (1<<19)
+#define DMINFO_ACCESS32 (1<<18)
+#define DMINFO_ACCESS16 (1<<17)
+#define DMINFO_ACCESS8 (1<<16)
+#define DMINFO_DRAMSIZE (0x3f<<10)
+#define DMINFO_AUTHENTICATED (1<<5)
+#define DMINFO_AUTHBUSY (1<<4)
+#define DMINFO_AUTHTYPE (3<<2)
+#define DMINFO_VERSION 3
+
+/*** Info about the core being debugged. ***/
+
+#define DBUS_ADDRESS_UNKNOWN 0xffff
+#define WALL_CLOCK_TIMEOUT 2
+
+// 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 {
+ REG_XPR0 = 0,
+ REG_XPR31 = 31,
+ REG_PC = 32,
+ REG_FPR0 = 33,
+ REG_FPR31 = 64,
+ REG_CSR0 = 65,
+ REG_MSTATUS = CSR_MSTATUS + REG_CSR0,
+ REG_CSR4095 = 4160,
+ REG_PRIV = 4161,
+ REG_COUNT
+};
+
+#define MAX_HWBPS 16
+#define DRAM_CACHE_SIZE 16
+
+struct trigger {
+ uint64_t address;
+ uint32_t length;
+ uint64_t mask;
+ uint64_t value;
+ bool read, write, execute;
+ int unique_id;
+};
+
+struct memory_cache_line {
+ uint32_t data;
+ bool valid;
+ bool dirty;
+};
+
+typedef struct {
+ /* Number of address bits in the dbus register. */
+ uint8_t addrbits;
+ /* Number of words in Debug RAM. */
+ unsigned int dramsize;
+ uint64_t dcsr;
+ uint64_t dpc;
+ uint64_t misa;
+ uint64_t tselect;
+ bool tselect_dirty;
+ /* The value that mstatus actually has on the target right now. This is not
+ * the value we present to the user. That one may be stored in the
+ * reg_cache. */
+ uint64_t mstatus_actual;
+
+ struct memory_cache_line dram_cache[DRAM_CACHE_SIZE];
+
+ /* Single buffer that contains all register names, instead of calling
+ * malloc for each register. Needs to be freed when reg_list is freed. */
+ char *reg_names;
+ /* Single buffer that contains all register values. */
+ void *reg_values;
+
+ // For each physical trigger, contains -1 if the hwbp is available, or the
+ // unique_id of the breakpoint/watchpoint that is using it.
+ int trigger_unique_id[MAX_HWBPS];
+
+ unsigned int trigger_count;
+
+ // Number of run-test/idle cycles the target requests we do after each dbus
+ // access.
+ unsigned int dtmcontrol_idle;
+
+ // This value is incremented every time a dbus access comes back as "busy".
+ // It's used to determine how many run-test/idle cycles to feed the target
+ // in between accesses.
+ unsigned int dbus_busy_delay;
+
+ // This value is incremented every time we read the debug interrupt as
+ // high. It's used to add extra run-test/idle cycles after setting debug
+ // interrupt high, so ideally we never have to perform a whole extra scan
+ // before the interrupt is cleared.
+ unsigned int interrupt_high_delay;
+
+ bool need_strict_step;
+ bool never_halted;
+} riscv011_info_t;
+
+typedef struct {
+ bool haltnot;
+ bool interrupt;
+} bits_t;
+
+/*** Necessary prototypes. ***/
+
+static int poll_target(struct target *target, bool announce);
+static int riscv011_poll(struct target *target);
+static int register_get(struct reg *reg);
+
+/*** Utility functions. ***/
+
+#define DEBUG_LENGTH 264
+
+static riscv011_info_t *get_info(const struct target *target)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ return (riscv011_info_t *) info->version_specific;
+}
+
+static unsigned int slot_offset(const struct target *target, slot_t slot)
+{
+ riscv011_info_t *info = get_info(target);
+ switch (riscv_xlen(target)) {
+ case 32:
+ switch (slot) {
+ case SLOT0: return 4;
+ case SLOT1: return 5;
+ case SLOT_LAST: return info->dramsize-1;
+ }
+ case 64:
+ switch (slot) {
+ case SLOT0: return 4;
+ case SLOT1: return 6;
+ case SLOT_LAST: return info->dramsize-2;
+ }
+ }
+ LOG_ERROR("slot_offset called with xlen=%d, slot=%d",
+ riscv_xlen(target), slot);
+ assert(0);
+}
+
+static uint32_t load_slot(const struct target *target, unsigned int dest,
+ slot_t slot)
+{
+ unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+ return load(target, dest, ZERO, offset);
+}
+
+static uint32_t store_slot(const struct target *target, unsigned int src,
+ slot_t slot)
+{
+ unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+ return store(target, src, ZERO, offset);
+}
+
+static uint16_t dram_address(unsigned int index)
+{
+ if (index < 0x10)
+ return index;
+ else
+ return 0x40 + index - 0x10;
+}
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+ struct scan_field field;
+ uint8_t in_value[4];
+ uint8_t out_value[4];
+
+ buf_set_u32(out_value, 0, 32, out);
+
+ jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+ field.num_bits = 32;
+ field.out_value = out_value;
+ field.in_value = in_value;
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ /* Always return to dbus. */
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ uint32_t in = buf_get_u32(field.in_value, 0, 32);
+ LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
+
+ return in;
+}
+
+static uint32_t idcode_scan(struct target *target)
+{
+ struct scan_field field;
+ uint8_t in_value[4];
+
+ jtag_add_ir_scan(target->tap, &select_idcode, TAP_IDLE);
+
+ field.num_bits = 32;
+ field.out_value = NULL;
+ field.in_value = in_value;
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ /* Always return to dbus. */
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ uint32_t in = buf_get_u32(field.in_value, 0, 32);
+ LOG_DEBUG("IDCODE: 0x0 -> 0x%x", in);
+
+ return in;
+}
+
+static void increase_dbus_busy_delay(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ info->dbus_busy_delay += info->dbus_busy_delay / 10 + 1;
+ LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
+ info->dtmcontrol_idle, info->dbus_busy_delay,
+ info->interrupt_high_delay);
+
+ dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
+}
+
+static void increase_interrupt_high_delay(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ info->interrupt_high_delay += info->interrupt_high_delay / 10 + 1;
+ LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
+ info->dtmcontrol_idle, info->dbus_busy_delay,
+ info->interrupt_high_delay);
+}
+
+static void add_dbus_scan(const struct target *target, struct scan_field *field,
+ uint8_t *out_value, uint8_t *in_value, dbus_op_t op,
+ uint16_t address, uint64_t data)
+{
+ riscv011_info_t *info = get_info(target);
+
+ field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
+ field->in_value = in_value;
+ field->out_value = out_value;
+
+ buf_set_u64(out_value, DBUS_OP_START, DBUS_OP_SIZE, op);
+ buf_set_u64(out_value, DBUS_DATA_START, DBUS_DATA_SIZE, data);
+ buf_set_u64(out_value, DBUS_ADDRESS_START, info->addrbits, address);
+
+ jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
+
+ int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay;
+ if (data & DMCONTROL_INTERRUPT) {
+ idle_count += info->interrupt_high_delay;
+ }
+
+ if (idle_count) {
+ jtag_add_runtest(idle_count, TAP_IDLE);
+ }
+}
+
+static void dump_field(const struct scan_field *field)
+{
+ static const char *op_string[] = {"nop", "r", "w", "?"};
+ static const char *status_string[] = {"+", "?", "F", "b"};
+
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
+ unsigned int out_op = (out >> DBUS_OP_START) & ((1 << DBUS_OP_SIZE) - 1);
+ char out_interrupt = ((out >> DBUS_DATA_START) & DMCONTROL_INTERRUPT) ? 'i' : '.';
+ char out_haltnot = ((out >> DBUS_DATA_START) & DMCONTROL_HALTNOT) ? 'h' : '.';
+ unsigned int out_data = out >> 2;
+ unsigned int out_address = out >> DBUS_ADDRESS_START;
+ uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+ unsigned int in_op = (in >> DBUS_OP_START) & ((1 << DBUS_OP_SIZE) - 1);
+ char in_interrupt = ((in >> DBUS_DATA_START) & DMCONTROL_INTERRUPT) ? 'i' : '.';
+ char in_haltnot = ((in >> DBUS_DATA_START) & DMCONTROL_HALTNOT) ? 'h' : '.';
+ unsigned int in_data = in >> 2;
+ unsigned int in_address = in >> DBUS_ADDRESS_START;
+
+ log_printf_lf(LOG_LVL_DEBUG,
+ __FILE__, __LINE__, "scan",
+ "%db %s %c%c:%08x @%02x -> %s %c%c:%08x @%02x",
+ field->num_bits,
+ op_string[out_op], out_interrupt, out_haltnot, out_data,
+ out_address,
+ status_string[in_op], in_interrupt, in_haltnot, in_data,
+ in_address);
+}
+
+static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
+ uint64_t *data_in, dbus_op_t op, uint16_t address_out, uint64_t data_out)
+{
+ riscv011_info_t *info = get_info(target);
+ uint8_t in[8] = {0};
+ uint8_t out[8];
+ struct scan_field field = {
+ .num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE,
+ .out_value = out,
+ .in_value = in
+ };
+
+ assert(info->addrbits != 0);
+
+ buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op);
+ buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out);
+ buf_set_u64(out, DBUS_ADDRESS_START, info->addrbits, address_out);
+
+ /* Assume dbus is already selected. */
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay;
+
+ if (idle_count) {
+ jtag_add_runtest(idle_count, TAP_IDLE);
+ }
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("dbus_scan failed jtag scan");
+ return retval;
+ }
+
+ if (data_in) {
+ *data_in = buf_get_u64(in, DBUS_DATA_START, DBUS_DATA_SIZE);
+ }
+
+ if (address_in) {
+ *address_in = buf_get_u32(in, DBUS_ADDRESS_START, info->addrbits);
+ }
+
+ dump_field(&field);
+
+ return buf_get_u32(in, DBUS_OP_START, DBUS_OP_SIZE);
+}
+
+static uint64_t dbus_read(struct target *target, uint16_t address)
+{
+ uint64_t value;
+ dbus_status_t status;
+ uint16_t address_in;
+
+ unsigned i = 0;
+ do {
+ status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, address, 0);
+ if (status == DBUS_STATUS_BUSY) {
+ increase_dbus_busy_delay(target);
+ }
+ } while (((status == DBUS_STATUS_BUSY) || (address_in != address)) &&
+ i++ < 256);
+
+ if (status != DBUS_STATUS_SUCCESS) {
+ LOG_ERROR("failed read from 0x%x; value=0x%" PRIx64 ", status=%d\n", address, value, status);
+ }
+
+ return value;
+}
+
+static void dbus_write(struct target *target, uint16_t address, uint64_t value)
+{
+ dbus_status_t status = DBUS_STATUS_BUSY;
+ unsigned i = 0;
+ while (status == DBUS_STATUS_BUSY && i++ < 256) {
+ status = dbus_scan(target, NULL, NULL, DBUS_OP_WRITE, address, value);
+ if (status == DBUS_STATUS_BUSY) {
+ increase_dbus_busy_delay(target);
+ }
+ }
+ if (status != DBUS_STATUS_SUCCESS) {
+ LOG_ERROR("failed to write 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status);
+ }
+}
+
+/*** scans "class" ***/
+
+typedef struct {
+ // Number of scans that space is reserved for.
+ unsigned int scan_count;
+ // Size reserved in memory for each scan, in bytes.
+ unsigned int scan_size;
+ unsigned int next_scan;
+ uint8_t *in;
+ uint8_t *out;
+ struct scan_field *field;
+ const struct target *target;
+} scans_t;
+
+static scans_t *scans_new(struct target *target, unsigned int scan_count)
+{
+ scans_t *scans = malloc(sizeof(scans_t));
+ scans->scan_count = scan_count;
+ // This code also gets called before xlen is detected.
+ if (riscv_xlen(target))
+ scans->scan_size = 2 + riscv_xlen(target) / 8;
+ else
+ scans->scan_size = 2 + 128 / 8;
+ scans->next_scan = 0;
+ scans->in = calloc(scans->scan_size, scans->scan_count);
+ scans->out = calloc(scans->scan_size, scans->scan_count);
+ scans->field = calloc(scans->scan_count, sizeof(struct scan_field));
+ scans->target = target;
+ return scans;
+}
+
+static scans_t *scans_delete(scans_t *scans)
+{
+ assert(scans);
+ free(scans->field);
+ free(scans->out);
+ free(scans->in);
+ free(scans);
+ return NULL;
+}
+
+static void scans_reset(scans_t *scans)
+{
+ scans->next_scan = 0;
+}
+
+static void scans_dump(scans_t *scans)
+{
+ for (unsigned int i = 0; i < scans->next_scan; i++) {
+ dump_field(&scans->field[i]);
+ }
+}
+
+static int scans_execute(scans_t *scans)
+{
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ scans_dump(scans);
+
+ return ERROR_OK;
+}
+
+/** Add a 32-bit dbus write to the scans structure. */
+static void scans_add_write32(scans_t *scans, uint16_t address, uint32_t data,
+ bool set_interrupt)
+{
+ const unsigned int i = scans->next_scan;
+ int data_offset = scans->scan_size * i;
+ add_dbus_scan(scans->target, &scans->field[i], scans->out + data_offset,
+ scans->in + data_offset, DBUS_OP_WRITE, address,
+ (set_interrupt ? DMCONTROL_INTERRUPT : 0) | DMCONTROL_HALTNOT | data);
+ scans->next_scan++;
+ assert(scans->next_scan <= scans->scan_count);
+}
+
+/** Add a 32-bit dbus write for an instruction that jumps to the beginning of
+ * debug RAM. */
+static void scans_add_write_jump(scans_t *scans, uint16_t address,
+ bool set_interrupt)
+{
+ scans_add_write32(scans, address,
+ jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*address))),
+ set_interrupt);
+}
+
+/** Add a 32-bit dbus write for an instruction that loads from the indicated
+ * slot. */
+static void scans_add_write_load(scans_t *scans, uint16_t address,
+ unsigned int reg, slot_t slot, bool set_interrupt)
+{
+ scans_add_write32(scans, address, load_slot(scans->target, reg, slot),
+ set_interrupt);
+}
+
+/** Add a 32-bit dbus write for an instruction that stores to the indicated
+ * slot. */
+static void scans_add_write_store(scans_t *scans, uint16_t address,
+ unsigned int reg, slot_t slot, bool set_interrupt)
+{
+ scans_add_write32(scans, address, store_slot(scans->target, reg, slot),
+ set_interrupt);
+}
+
+/** Add a 32-bit dbus read. */
+static void scans_add_read32(scans_t *scans, uint16_t address, bool set_interrupt)
+{
+ assert(scans->next_scan < scans->scan_count);
+ const unsigned int i = scans->next_scan;
+ int data_offset = scans->scan_size * i;
+ add_dbus_scan(scans->target, &scans->field[i], scans->out + data_offset,
+ scans->in + data_offset, DBUS_OP_READ, address,
+ (set_interrupt ? DMCONTROL_INTERRUPT : 0) | DMCONTROL_HALTNOT);
+ scans->next_scan++;
+}
+
+/** Add one or more scans to read the indicated slot. */
+static void scans_add_read(scans_t *scans, slot_t slot, bool set_interrupt)
+{
+ const struct target *target = scans->target;
+ switch (riscv_xlen(target)) {
+ case 32:
+ scans_add_read32(scans, slot_offset(target, slot), set_interrupt);
+ break;
+ case 64:
+ scans_add_read32(scans, slot_offset(target, slot), false);
+ scans_add_read32(scans, slot_offset(target, slot) + 1, set_interrupt);
+ break;
+ }
+}
+
+static uint32_t scans_get_u32(scans_t *scans, unsigned int index,
+ unsigned first, unsigned num)
+{
+ return buf_get_u32(scans->in + scans->scan_size * index, first, num);
+}
+
+static uint64_t scans_get_u64(scans_t *scans, unsigned int index,
+ unsigned first, unsigned num)
+{
+ return buf_get_u64(scans->in + scans->scan_size * index, first, num);
+}
+
+/*** end of scans class ***/
+
+static uint32_t dram_read32(struct target *target, unsigned int index)
+{
+ uint16_t address = dram_address(index);
+ uint32_t value = dbus_read(target, address);
+ return value;
+}
+
+static void dram_write32(struct target *target, unsigned int index, uint32_t value,
+ bool set_interrupt)
+{
+ uint64_t dbus_value = DMCONTROL_HALTNOT | value;
+ if (set_interrupt)
+ dbus_value |= DMCONTROL_INTERRUPT;
+ dbus_write(target, dram_address(index), dbus_value);
+}
+
+/** Read the haltnot and interrupt bits. */
+static bits_t read_bits(struct target *target)
+{
+ uint64_t value;
+ dbus_status_t status;
+ uint16_t address_in;
+ riscv011_info_t *info = get_info(target);
+
+ bits_t err_result = {
+ .haltnot = 0,
+ .interrupt = 0
+ };
+
+ do {
+ unsigned i = 0;
+ do {
+ status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, 0, 0);
+ if (status == DBUS_STATUS_BUSY) {
+ if (address_in == (1<<info->addrbits) - 1 &&
+ value == (1ULL<<DBUS_DATA_SIZE) - 1) {
+ LOG_ERROR("TDO seems to be stuck high.");
+ return err_result;
+ }
+ increase_dbus_busy_delay(target);
+ }
+ } while (status == DBUS_STATUS_BUSY && i++ < 256);
+
+ if (i >= 256) {
+ LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status);
+ return err_result;
+ }
+ } while (address_in > 0x10 && address_in != DMCONTROL);
+
+ bits_t result = {
+ .haltnot = get_field(value, DMCONTROL_HALTNOT),
+ .interrupt = get_field(value, DMCONTROL_INTERRUPT)
+ };
+ return result;
+}
+
+static int wait_for_debugint_clear(struct target *target, bool ignore_first)
+{
+ time_t start = time(NULL);
+ if (ignore_first) {
+ // Throw away the results of the first read, since they'll contain the
+ // result of the read that happened just before debugint was set.
+ // (Assuming the last scan before calling this function was one that
+ // sets debugint.)
+ read_bits(target);
+ }
+ while (1) {
+ bits_t bits = read_bits(target);
+ if (!bits.interrupt) {
+ return ERROR_OK;
+ }
+ if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
+ LOG_ERROR("Timed out waiting for debug int to clear.");
+ return ERROR_FAIL;
+ }
+ }
+}
+
+static int dram_check32(struct target *target, unsigned int index,
+ uint32_t expected)
+{
+ uint16_t address = dram_address(index);
+ uint32_t actual = dbus_read(target, address);
+ if (expected != actual) {
+ LOG_ERROR("Wrote 0x%x to Debug RAM at %d, but read back 0x%x",
+ expected, index, actual);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+static void cache_set32(struct target *target, unsigned int index, uint32_t data)
+{
+ riscv011_info_t *info = get_info(target);
+ if (info->dram_cache[index].valid &&
+ info->dram_cache[index].data == data) {
+ // This is already preset on the target.
+ LOG_DEBUG("cache[0x%x] = 0x%x (hit)", index, data);
+ return;
+ }
+ LOG_DEBUG("cache[0x%x] = 0x%x", index, data);
+ info->dram_cache[index].data = data;
+ info->dram_cache[index].valid = true;
+ info->dram_cache[index].dirty = true;
+}
+
+static void cache_set(struct target *target, slot_t slot, uint64_t data)
+{
+ unsigned int offset = slot_offset(target, slot);
+ cache_set32(target, offset, data);
+ if (riscv_xlen(target) > 32) {
+ cache_set32(target, offset + 1, data >> 32);
+ }
+}
+
+static void cache_set_jump(struct target *target, unsigned int index)
+{
+ cache_set32(target, index,
+ jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))));
+}
+
+static void cache_set_load(struct target *target, unsigned int index,
+ unsigned int reg, slot_t slot)
+{
+ uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+ cache_set32(target, index, load(target, reg, ZERO, offset));
+}
+
+static void cache_set_store(struct target *target, unsigned int index,
+ unsigned int reg, slot_t slot)
+{
+ uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+ cache_set32(target, index, store(target, reg, ZERO, offset));
+}
+
+static void dump_debug_ram(struct target *target)
+{
+ for (unsigned int i = 0; i < DRAM_CACHE_SIZE; i++) {
+ uint32_t value = dram_read32(target, i);
+ LOG_ERROR("Debug RAM 0x%x: 0x%08x", i, value);
+ }
+}
+
+/* Call this if the code you just ran writes to debug RAM entries 0 through 3. */
+static void cache_invalidate(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ info->dram_cache[i].valid = false;
+ info->dram_cache[i].dirty = false;
+ }
+}
+
+/* Called by cache_write() after the program has run. Also call this if you're
+ * running programs without calling cache_write(). */
+static void cache_clean(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ if (i >= 4) {
+ info->dram_cache[i].valid = false;
+ }
+ info->dram_cache[i].dirty = false;
+ }
+}
+
+static int cache_check(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ int error = 0;
+
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ if (info->dram_cache[i].valid && !info->dram_cache[i].dirty) {
+ if (dram_check32(target, i, info->dram_cache[i].data) != ERROR_OK) {
+ error++;
+ }
+ }
+ }
+
+ if (error) {
+ dump_debug_ram(target);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+/** Write cache to the target, and optionally run the program.
+ * Then read the value at address into the cache, assuming address < 128. */
+#define CACHE_NO_READ 128
+static int cache_write(struct target *target, unsigned int address, bool run)
+{
+ LOG_DEBUG("enter");
+ riscv011_info_t *info = get_info(target);
+ scans_t *scans = scans_new(target, info->dramsize + 2);
+
+ unsigned int last = info->dramsize;
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ if (info->dram_cache[i].dirty) {
+ last = i;
+ }
+ }
+
+ if (last == info->dramsize) {
+ // Nothing needs to be written to RAM.
+ dbus_write(target, DMCONTROL, DMCONTROL_HALTNOT | (run ? DMCONTROL_INTERRUPT : 0));
+
+ } else {
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ if (info->dram_cache[i].dirty) {
+ bool set_interrupt = (i == last && run);
+ scans_add_write32(scans, i, info->dram_cache[i].data,
+ set_interrupt);
+ }
+ }
+ }
+
+ if (run || address < CACHE_NO_READ) {
+ // Throw away the results of the first read, since it'll contain the
+ // result of the read that happened just before debugint was set.
+ scans_add_read32(scans, address, false);
+
+ // This scan contains the results of the read the caller requested, as
+ // well as an interrupt bit worth looking at.
+ scans_add_read32(scans, address, false);
+ }
+
+ int retval = scans_execute(scans);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("JTAG execute failed.");
+ return retval;
+ }
+
+ int errors = 0;
+ for (unsigned int i = 0; i < scans->next_scan; i++) {
+ dbus_status_t status = scans_get_u32(scans, i, DBUS_OP_START,
+ DBUS_OP_SIZE);
+ switch (status) {
+ case DBUS_STATUS_SUCCESS:
+ break;
+ case DBUS_STATUS_FAILED:
+ LOG_ERROR("Debug RAM write failed. Hardware error?");
+ return ERROR_FAIL;
+ case DBUS_STATUS_BUSY:
+ errors++;
+ break;
+ default:
+ LOG_ERROR("Got invalid bus access status: %d", status);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (errors) {
+ increase_dbus_busy_delay(target);
+
+ // Try again, using the slow careful code.
+ // Write all RAM, just to be extra cautious.
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ if (i == last && run) {
+ dram_write32(target, last, info->dram_cache[last].data, true);
+ } else {
+ dram_write32(target, i, info->dram_cache[i].data, false);
+ }
+ info->dram_cache[i].dirty = false;
+ }
+ if (run) {
+ cache_clean(target);
+ }
+
+ if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+ LOG_ERROR("Debug interrupt didn't clear.");
+ dump_debug_ram(target);
+ return ERROR_FAIL;
+ }
+
+ } else {
+ if (run) {
+ cache_clean(target);
+ } else {
+ for (unsigned int i = 0; i < info->dramsize; i++) {
+ info->dram_cache[i].dirty = false;
+ }
+ }
+
+ if (run || address < CACHE_NO_READ) {
+ int interrupt = scans_get_u32(scans, scans->next_scan-1,
+ DBUS_DATA_START + 33, 1);
+ if (interrupt) {
+ increase_interrupt_high_delay(target);
+ // Slow path wait for it to clear.
+ if (wait_for_debugint_clear(target, false) != ERROR_OK) {
+ LOG_ERROR("Debug interrupt didn't clear.");
+ dump_debug_ram(target);
+ return ERROR_FAIL;
+ }
+ } else {
+ // We read a useful value in that last scan.
+ unsigned int read_addr = scans_get_u32(scans, scans->next_scan-1,
+ DBUS_ADDRESS_START, info->addrbits);
+ if (read_addr != address) {
+ LOG_INFO("Got data from 0x%x but expected it from 0x%x",
+ read_addr, address);
+ }
+ info->dram_cache[read_addr].data =
+ scans_get_u32(scans, scans->next_scan-1, DBUS_DATA_START, 32);
+ info->dram_cache[read_addr].valid = true;
+ }
+ }
+ }
+
+ scans_delete(scans);
+ LOG_DEBUG("exit");
+
+ return ERROR_OK;
+}
+
+static uint32_t cache_get32(struct target *target, unsigned int address)
+{
+ riscv011_info_t *info = get_info(target);
+ if (!info->dram_cache[address].valid) {
+ info->dram_cache[address].data = dram_read32(target, address);
+ info->dram_cache[address].valid = true;
+ }
+ return info->dram_cache[address].data;
+}
+
+static uint64_t cache_get(struct target *target, slot_t slot)
+{
+ unsigned int offset = slot_offset(target, slot);
+ uint64_t value = cache_get32(target, offset);
+ if (riscv_xlen(target) > 32) {
+ value |= ((uint64_t) cache_get32(target, offset + 1)) << 32;
+ }
+ return value;
+}
+
+/* Write instruction that jumps from the specified word in Debug RAM to resume
+ * in Debug ROM. */
+static void dram_write_jump(struct target *target, unsigned int index,
+ bool set_interrupt)
+{
+ dram_write32(target, index,
+ jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))),
+ set_interrupt);
+}
+
+static int wait_for_state(struct target *target, enum target_state state)
+{
+ time_t start = time(NULL);
+ while (1) {
+ int result = riscv011_poll(target);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ if (target->state == state) {
+ return ERROR_OK;
+ }
+ if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
+ LOG_ERROR("Timed out waiting for state %d.", state);
+ return ERROR_FAIL;
+ }
+ }
+}
+
+static int read_csr(struct target *target, uint64_t *value, uint32_t csr)
+{
+ cache_set32(target, 0, csrr(S0, csr));
+ cache_set_store(target, 1, S0, SLOT0);
+ cache_set_jump(target, 2);
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ *value = cache_get(target, SLOT0);
+ LOG_DEBUG("csr 0x%x = 0x%" PRIx64, csr, *value);
+
+ return ERROR_OK;
+}
+
+static int write_csr(struct target *target, uint32_t csr, uint64_t value)
+{
+ LOG_DEBUG("csr 0x%x <- 0x%" PRIx64, csr, value);
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set32(target, 1, csrw(S0, csr));
+ cache_set_jump(target, 2);
+ cache_set(target, SLOT0, value);
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int write_gpr(struct target *target, unsigned int gpr, uint64_t value)
+{
+ cache_set_load(target, 0, gpr, SLOT0);
+ cache_set_jump(target, 1);
+ cache_set(target, SLOT0, value);
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+static int maybe_read_tselect(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+
+ if (info->tselect_dirty) {
+ int result = read_csr(target, &info->tselect, CSR_TSELECT);
+ if (result != ERROR_OK)
+ return result;
+ info->tselect_dirty = false;
+ }
+
+ return ERROR_OK;
+}
+
+static int maybe_write_tselect(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+
+ if (!info->tselect_dirty) {
+ int result = write_csr(target, CSR_TSELECT, info->tselect);
+ if (result != ERROR_OK)
+ return result;
+ info->tselect_dirty = true;
+ }
+
+ return ERROR_OK;
+}
+
+static int execute_resume(struct target *target, bool step)
+{
+ riscv011_info_t *info = get_info(target);
+
+ LOG_DEBUG("step=%d", step);
+
+ maybe_write_tselect(target);
+
+ // TODO: check if dpc is dirty (which also is true if an exception was hit
+ // at any time)
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set32(target, 1, csrw(S0, CSR_DPC));
+ cache_set_jump(target, 2);
+ cache_set(target, SLOT0, info->dpc);
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ struct reg *mstatus_reg = &target->reg_cache->reg_list[REG_MSTATUS];
+ if (mstatus_reg->valid) {
+ uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, riscv_xlen(target));
+ if (mstatus_user != info->mstatus_actual) {
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set32(target, 1, csrw(S0, CSR_MSTATUS));
+ cache_set_jump(target, 2);
+ cache_set(target, SLOT0, mstatus_user);
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ }
+ }
+
+ info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU;
+ info->dcsr &= ~DCSR_HALT;
+
+ if (step) {
+ info->dcsr |= DCSR_STEP;
+ } else {
+ info->dcsr &= ~DCSR_STEP;
+ }
+
+ dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false);
+ dram_write32(target, 1, csrw(S0, CSR_DCSR), false);
+ dram_write32(target, 2, fence_i(), false);
+ dram_write_jump(target, 3, false);
+
+ // Write DCSR value, set interrupt and clear haltnot.
+ uint64_t dbus_value = DMCONTROL_INTERRUPT | info->dcsr;
+ dbus_write(target, dram_address(4), dbus_value);
+
+ cache_invalidate(target);
+
+ if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+ LOG_ERROR("Debug interrupt didn't clear.");
+ return ERROR_FAIL;
+ }
+
+ target->state = TARGET_RUNNING;
+ register_cache_invalidate(target->reg_cache);
+
+ return ERROR_OK;
+}
+
+// Execute a step, and wait for reentry into Debug Mode.
+static int full_step(struct target *target, bool announce)
+{
+ int result = execute_resume(target, true);
+ if (result != ERROR_OK)
+ return result;
+ time_t start = time(NULL);
+ while (1) {
+ result = poll_target(target, announce);
+ if (result != ERROR_OK)
+ return result;
+ if (target->state != TARGET_DEBUG_RUNNING)
+ break;
+ if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
+ LOG_ERROR("Timed out waiting for step to complete.");
+ return ERROR_FAIL;
+ }
+ }
+ return ERROR_OK;
+}
+
+static int resume(struct target *target, int debug_execution, bool step)
+{
+ if (debug_execution) {
+ LOG_ERROR("TODO: debug_execution is true");
+ return ERROR_FAIL;
+ }
+
+ return execute_resume(target, step);
+}
+
+/** Update register sizes based on xlen. */
+static void update_reg_list(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ if (info->reg_values) {
+ free(info->reg_values);
+ }
+ info->reg_values = malloc(REG_COUNT * riscv_xlen(target) / 4);
+
+ for (unsigned int i = 0; i < REG_COUNT; i++) {
+ struct reg *r = &target->reg_cache->reg_list[i];
+ r->value = info->reg_values + i * riscv_xlen(target) / 4;
+ if (r->dirty) {
+ LOG_ERROR("Register %d was dirty. Its value is lost.", i);
+ }
+ if (i == REG_PRIV) {
+ r->size = 8;
+ } else {
+ r->size = riscv_xlen(target);
+ }
+ r->valid = false;
+ }
+}
+
+static uint64_t reg_cache_get(struct target *target, unsigned int number)
+{
+ struct reg *r = &target->reg_cache->reg_list[number];
+ if (!r->valid) {
+ LOG_ERROR("Register cache entry for %d is invalid!", number);
+ assert(r->valid);
+ }
+ uint64_t value = buf_get_u64(r->value, 0, r->size);
+ LOG_DEBUG("%s = 0x%" PRIx64, r->name, value);
+ return value;
+}
+
+static void reg_cache_set(struct target *target, unsigned int number,
+ uint64_t value)
+{
+ struct reg *r = &target->reg_cache->reg_list[number];
+ LOG_DEBUG("%s <= 0x%" PRIx64, r->name, value);
+ r->valid = true;
+ buf_set_u64(r->value, 0, r->size, value);
+}
+
+static int update_mstatus_actual(struct target *target)
+{
+ struct reg *mstatus_reg = &target->reg_cache->reg_list[REG_MSTATUS];
+ if (mstatus_reg->valid) {
+ // We previously made it valid.
+ return ERROR_OK;
+ }
+
+ // Force reading the register. In that process mstatus_actual will be
+ // updated.
+ return register_get(&target->reg_cache->reg_list[REG_MSTATUS]);
+}
+
+/*** OpenOCD target functions. ***/
+
+static int register_get(struct reg *reg)
+{
+ struct target *target = (struct target *) reg->arch_info;
+ riscv011_info_t *info = get_info(target);
+
+ maybe_write_tselect(target);
+
+ if (reg->number <= REG_XPR31) {
+ buf_set_u64(reg->value, 0, riscv_xlen(target), reg_cache_get(target, reg->number));
+ LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number));
+ return ERROR_OK;
+ } else if (reg->number == REG_PC) {
+ buf_set_u32(reg->value, 0, 32, info->dpc);
+ reg->valid = true;
+ LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc);
+ return ERROR_OK;
+ } else if (reg->number >= REG_FPR0 && reg->number <= REG_FPR31) {
+ int result = update_mstatus_actual(target);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ unsigned i = 0;
+ if ((info->mstatus_actual & MSTATUS_FS) == 0) {
+ info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
+ cache_set_load(target, i++, S0, SLOT1);
+ cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
+ cache_set(target, SLOT1, info->mstatus_actual);
+ }
+
+ if (riscv_xlen(target) == 32) {
+ cache_set32(target, i++, fsw(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
+ } else {
+ cache_set32(target, i++, fsd(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
+ }
+ cache_set_jump(target, i++);
+ } else if (reg->number >= REG_CSR0 && reg->number <= REG_CSR4095) {
+ cache_set32(target, 0, csrr(S0, reg->number - REG_CSR0));
+ cache_set_store(target, 1, S0, SLOT0);
+ cache_set_jump(target, 2);
+ } else if (reg->number == REG_PRIV) {
+ buf_set_u64(reg->value, 0, 8, get_field(info->dcsr, DCSR_PRV));
+ LOG_DEBUG("%s=%d (cached)", reg->name,
+ (int) get_field(info->dcsr, DCSR_PRV));
+ return ERROR_OK;
+ } else {
+ LOG_ERROR("Don't know how to read register %d (%s)", reg->number, reg->name);
+ return ERROR_FAIL;
+ }
+
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ uint32_t exception = cache_get32(target, info->dramsize-1);
+ if (exception) {
+ LOG_ERROR("Got exception 0x%x when reading register %d", exception,
+ reg->number);
+ buf_set_u64(reg->value, 0, riscv_xlen(target), ~0);
+ return ERROR_FAIL;
+ }
+
+ uint64_t value = cache_get(target, SLOT0);
+ LOG_DEBUG("%s=0x%" PRIx64, reg->name, value);
+ buf_set_u64(reg->value, 0, riscv_xlen(target), value);
+
+ if (reg->number == REG_MSTATUS) {
+ info->mstatus_actual = value;
+ reg->valid = true;
+ }
+
+ return ERROR_OK;
+}
+
+static int register_write(struct target *target, unsigned int number,
+ uint64_t value)
+{
+ riscv011_info_t *info = get_info(target);
+
+ maybe_write_tselect(target);
+
+ if (number == S0) {
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set32(target, 1, csrw(S0, CSR_DSCRATCH));
+ cache_set_jump(target, 2);
+ } else if (number == S1) {
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set_store(target, 1, S0, SLOT_LAST);
+ cache_set_jump(target, 2);
+ } else if (number <= REG_XPR31) {
+ cache_set_load(target, 0, number - REG_XPR0, SLOT0);
+ cache_set_jump(target, 1);
+ } else if (number == REG_PC) {
+ info->dpc = value;
+ return ERROR_OK;
+ } else if (number >= REG_FPR0 && number <= REG_FPR31) {
+ int result = update_mstatus_actual(target);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ unsigned i = 0;
+ if ((info->mstatus_actual & MSTATUS_FS) == 0) {
+ info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
+ cache_set_load(target, i++, S0, SLOT1);
+ cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
+ cache_set(target, SLOT1, info->mstatus_actual);
+ }
+
+ if (riscv_xlen(target) == 32) {
+ cache_set32(target, i++, flw(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
+ } else {
+ cache_set32(target, i++, fld(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
+ }
+ cache_set_jump(target, i++);
+ } else if (number >= REG_CSR0 && number <= REG_CSR4095) {
+ cache_set_load(target, 0, S0, SLOT0);
+ cache_set32(target, 1, csrw(S0, number - REG_CSR0));
+ cache_set_jump(target, 2);
+
+ if (number == REG_MSTATUS) {
+ info->mstatus_actual = value;
+ }
+ } else if (number == REG_PRIV) {
+ info->dcsr = set_field(info->dcsr, DCSR_PRV, value);
+ return ERROR_OK;
+ } else {
+ LOG_ERROR("Don't know how to write register %d", number);
+ return ERROR_FAIL;
+ }
+
+ cache_set(target, SLOT0, value);
+ if (cache_write(target, info->dramsize - 1, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ uint32_t exception = cache_get32(target, info->dramsize-1);
+ if (exception) {
+ LOG_ERROR("Got exception 0x%x when writing register %d", exception,
+ number);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int register_set(struct reg *reg, uint8_t *buf)
+{
+ struct target *target = (struct target *) reg->arch_info;
+
+ uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
+
+ LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
+ struct reg *r = &target->reg_cache->reg_list[reg->number];
+ r->valid = true;
+ memcpy(r->value, buf, (r->size + 7) / 8);
+
+ return register_write(target, reg->number, value);
+}
+
+static struct reg_arch_type riscv_reg_arch_type = {
+ .get = register_get,
+ .set = register_set
+};
+
+static int halt(struct target *target)
+{
+ LOG_DEBUG("riscv_halt()");
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ cache_set32(target, 0, csrsi(CSR_DCSR, DCSR_HALT));
+ cache_set32(target, 1, csrr(S0, CSR_MHARTID));
+ cache_set32(target, 2, sw(S0, ZERO, SETHALTNOT));
+ cache_set_jump(target, 3);
+
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ LOG_ERROR("cache_write() failed.");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int init_target(struct command_context *cmd_ctx,
+ struct target *target)
+{
+ LOG_DEBUG("init");
+ riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+ generic_info->get_register = NULL;
+ generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
+ if (!generic_info->version_specific)
+ return ERROR_FAIL;
+ riscv011_info_t *info = get_info(target);
+
+ target->reg_cache = calloc(1, sizeof(*target->reg_cache));
+ target->reg_cache->name = "RISC-V registers";
+ target->reg_cache->num_regs = REG_COUNT;
+
+ target->reg_cache->reg_list = calloc(REG_COUNT, sizeof(struct reg));
+
+ const unsigned int max_reg_name_len = 12;
+ info->reg_names = calloc(1, REG_COUNT * max_reg_name_len);
+ char *reg_name = info->reg_names;
+ info->reg_values = NULL;
+
+ for (unsigned int i = 0; i < REG_COUNT; i++) {
+ struct reg *r = &target->reg_cache->reg_list[i];
+ r->number = i;
+ r->caller_save = true;
+ r->dirty = false;
+ r->valid = false;
+ r->exist = true;
+ r->type = &riscv_reg_arch_type;
+ r->arch_info = target;
+ if (i <= REG_XPR31) {
+ sprintf(reg_name, "x%d", i);
+ } else if (i == REG_PC) {
+ sprintf(reg_name, "pc");
+ } else if (i >= REG_FPR0 && i <= REG_FPR31) {
+ sprintf(reg_name, "f%d", i - REG_FPR0);
+ } else if (i >= REG_CSR0 && i <= REG_CSR4095) {
+ sprintf(reg_name, "csr%d", i - REG_CSR0);
+ } else if (i == REG_PRIV) {
+ sprintf(reg_name, "priv");
+ }
+ if (reg_name[0]) {
+ r->name = reg_name;
+ }
+ reg_name += strlen(reg_name) + 1;
+ assert(reg_name < info->reg_names + REG_COUNT * max_reg_name_len);
+ }
+ update_reg_list(target);
+
+ memset(info->trigger_unique_id, 0xff, sizeof(info->trigger_unique_id));
+
+ return ERROR_OK;
+}
+
+static void deinit_target(struct target *target)
+{
+ LOG_DEBUG("riscv_deinit_target()");
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ free(info->version_specific);
+ info->version_specific = NULL;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+ riscv011_info_t *info = get_info(target);
+
+ maybe_read_tselect(target);
+
+ unsigned int i;
+ for (i = 0; i < info->trigger_count; i++) {
+ if (info->trigger_unique_id[i] != -1) {
+ continue;
+ }
+
+ write_csr(target, CSR_TSELECT, i);
+
+ uint64_t tdata1;
+ read_csr(target, &tdata1, CSR_TDATA1);
+ int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+
+ if (type != 2) {
+ continue;
+ }
+
+ if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
+ // Trigger is already in use, presumably by user code.
+ continue;
+ }
+
+ // address/data match trigger
+ tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+ tdata1 = set_field(tdata1, MCONTROL_ACTION,
+ MCONTROL_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
+ tdata1 |= MCONTROL_M;
+ if (info->misa & (1 << ('H' - 'A')))
+ tdata1 |= MCONTROL_H;
+ if (info->misa & (1 << ('S' - 'A')))
+ tdata1 |= MCONTROL_S;
+ if (info->misa & (1 << ('U' - 'A')))
+ tdata1 |= MCONTROL_U;
+
+ if (trigger->execute)
+ tdata1 |= MCONTROL_EXECUTE;
+ if (trigger->read)
+ tdata1 |= MCONTROL_LOAD;
+ if (trigger->write)
+ tdata1 |= MCONTROL_STORE;
+
+ write_csr(target, CSR_TDATA1, tdata1);
+
+ uint64_t tdata1_rb;
+ read_csr(target, &tdata1_rb, CSR_TDATA1);
+ LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+ if (tdata1 != tdata1_rb) {
+ LOG_DEBUG("Trigger %d doesn't support what we need; After writing 0x%"
+ PRIx64 " to tdata1 it contains 0x%" PRIx64,
+ i, tdata1, tdata1_rb);
+ write_csr(target, CSR_TDATA1, 0);
+ continue;
+ }
+
+ write_csr(target, CSR_TDATA2, trigger->address);
+
+ LOG_DEBUG("Using resource %d for bp %d", i,
+ trigger->unique_id);
+ info->trigger_unique_id[i] = trigger->unique_id;
+ break;
+ }
+ if (i >= info->trigger_count) {
+ LOG_ERROR("Couldn't find an available hardware trigger.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ return ERROR_OK;
+}
+
+static int remove_trigger(struct target *target, struct trigger *trigger)
+{
+ riscv011_info_t *info = get_info(target);
+
+ maybe_read_tselect(target);
+
+ unsigned int i;
+ for (i = 0; i < info->trigger_count; i++) {
+ if (info->trigger_unique_id[i] == trigger->unique_id) {
+ break;
+ }
+ }
+ if (i >= info->trigger_count) {
+ LOG_ERROR("Couldn't find the hardware resources used by hardware "
+ "trigger.");
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id);
+ write_csr(target, CSR_TSELECT, i);
+ write_csr(target, CSR_TDATA1, 0);
+ info->trigger_unique_id[i] = -1;
+
+ return ERROR_OK;
+}
+
+static void trigger_from_breakpoint(struct trigger *trigger,
+ const struct breakpoint *breakpoint)
+{
+ trigger->address = breakpoint->address;
+ trigger->length = breakpoint->length;
+ trigger->mask = ~0LL;
+ trigger->read = false;
+ trigger->write = false;
+ trigger->execute = true;
+ // unique_id is unique across both breakpoints and watchpoints.
+ trigger->unique_id = breakpoint->unique_id;
+}
+
+static void trigger_from_watchpoint(struct trigger *trigger,
+ const struct watchpoint *watchpoint)
+{
+ trigger->address = watchpoint->address;
+ trigger->length = watchpoint->length;
+ trigger->mask = watchpoint->mask;
+ trigger->value = watchpoint->value;
+ trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
+ trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
+ trigger->execute = false;
+ // unique_id is unique across both breakpoints and watchpoints.
+ trigger->unique_id = watchpoint->unique_id;
+}
+
+static int add_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (breakpoint->type == BKPT_SOFT) {
+ if (target_read_memory(target, breakpoint->address, breakpoint->length, 1,
+ breakpoint->orig_instr) != ERROR_OK) {
+ LOG_ERROR("Failed to read original instruction at 0x%x",
+ breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ int retval;
+ if (breakpoint->length == 4) {
+ retval = target_write_u32(target, breakpoint->address, ebreak());
+ } else {
+ retval = target_write_u16(target, breakpoint->address, ebreak_c());
+ }
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%x",
+ breakpoint->length, breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ } else if (breakpoint->type == BKPT_HARD) {
+ struct trigger trigger;
+ trigger_from_breakpoint(&trigger, breakpoint);
+ int result = add_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+
+ } else {
+ LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ breakpoint->set = true;
+
+ return ERROR_OK;
+}
+
+static int remove_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (breakpoint->type == BKPT_SOFT) {
+ if (target_write_memory(target, breakpoint->address, breakpoint->length, 1,
+ breakpoint->orig_instr) != ERROR_OK) {
+ LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
+ "0x%x", breakpoint->length, breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ } else if (breakpoint->type == BKPT_HARD) {
+ struct trigger trigger;
+ trigger_from_breakpoint(&trigger, breakpoint);
+ int result = remove_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+
+ } else {
+ LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ breakpoint->set = false;
+
+ return ERROR_OK;
+}
+
+static int add_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct trigger trigger;
+ trigger_from_watchpoint(&trigger, watchpoint);
+
+ int result = add_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ watchpoint->set = true;
+
+ return ERROR_OK;
+}
+
+static int remove_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct trigger trigger;
+ trigger_from_watchpoint(&trigger, watchpoint);
+
+ int result = remove_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ watchpoint->set = false;
+
+ return ERROR_OK;
+}
+
+static int strict_step(struct target *target, bool announce)
+{
+ riscv011_info_t *info = get_info(target);
+
+ LOG_DEBUG("enter");
+
+ struct breakpoint *breakpoint = target->breakpoints;
+ while (breakpoint) {
+ remove_breakpoint(target, breakpoint);
+ breakpoint = breakpoint->next;
+ }
+
+ struct watchpoint *watchpoint = target->watchpoints;
+ while (watchpoint) {
+ remove_watchpoint(target, watchpoint);
+ watchpoint = watchpoint->next;
+ }
+
+ int result = full_step(target, announce);
+ if (result != ERROR_OK)
+ return result;
+
+ breakpoint = target->breakpoints;
+ while (breakpoint) {
+ add_breakpoint(target, breakpoint);
+ breakpoint = breakpoint->next;
+ }
+
+ watchpoint = target->watchpoints;
+ while (watchpoint) {
+ add_watchpoint(target, watchpoint);
+ watchpoint = watchpoint->next;
+ }
+
+ info->need_strict_step = false;
+
+ return ERROR_OK;
+}
+
+static int step(struct target *target, int current, uint32_t address,
+ int handle_breakpoints)
+{
+ riscv011_info_t *info = get_info(target);
+
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ if (!current) {
+ if (riscv_xlen(target) > 32) {
+ LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
+ riscv_xlen(target));
+ }
+ int result = register_write(target, REG_PC, address);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ if (info->need_strict_step || handle_breakpoints) {
+ int result = strict_step(target, true);
+ if (result != ERROR_OK)
+ return result;
+ } else {
+ return resume(target, 0, true);
+ }
+
+ return ERROR_OK;
+}
+
+static int examine(struct target *target)
+{
+ // Don't need to select dbus, since the first thing we do is read dtmcontrol.
+
+ uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+ LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
+ LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
+ LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE));
+ if (dtmcontrol == 0) {
+ LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+ return ERROR_FAIL;
+ }
+ if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) {
+ LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
+ get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
+ return ERROR_FAIL;
+ }
+
+ RISCV_INFO(r);
+ r->hart_count = 1;
+
+ riscv011_info_t *info = get_info(target);
+ info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
+ info->dtmcontrol_idle = get_field(dtmcontrol, DTMCONTROL_IDLE);
+ if (info->dtmcontrol_idle == 0) {
+ // Some old SiFive cores don't set idle but need it to be 1.
+ uint32_t idcode = idcode_scan(target);
+ if (idcode == 0x10e31913)
+ info->dtmcontrol_idle = 1;
+ }
+
+ uint32_t dminfo = dbus_read(target, DMINFO);
+ LOG_DEBUG("dminfo: 0x%08x", dminfo);
+ LOG_DEBUG(" abussize=0x%x", get_field(dminfo, DMINFO_ABUSSIZE));
+ LOG_DEBUG(" serialcount=0x%x", get_field(dminfo, DMINFO_SERIALCOUNT));
+ LOG_DEBUG(" access128=%d", get_field(dminfo, DMINFO_ACCESS128));
+ LOG_DEBUG(" access64=%d", get_field(dminfo, DMINFO_ACCESS64));
+ LOG_DEBUG(" access32=%d", get_field(dminfo, DMINFO_ACCESS32));
+ LOG_DEBUG(" access16=%d", get_field(dminfo, DMINFO_ACCESS16));
+ LOG_DEBUG(" access8=%d", get_field(dminfo, DMINFO_ACCESS8));
+ LOG_DEBUG(" dramsize=0x%x", get_field(dminfo, DMINFO_DRAMSIZE));
+ LOG_DEBUG(" authenticated=0x%x", get_field(dminfo, DMINFO_AUTHENTICATED));
+ LOG_DEBUG(" authbusy=0x%x", get_field(dminfo, DMINFO_AUTHBUSY));
+ LOG_DEBUG(" authtype=0x%x", get_field(dminfo, DMINFO_AUTHTYPE));
+ LOG_DEBUG(" version=0x%x", get_field(dminfo, DMINFO_VERSION));
+
+ if (get_field(dminfo, DMINFO_VERSION) != 1) {
+ LOG_ERROR("OpenOCD only supports Debug Module version 1, not %d "
+ "(dminfo=0x%x)", get_field(dminfo, DMINFO_VERSION), dminfo);
+ return ERROR_FAIL;
+ }
+
+ info->dramsize = get_field(dminfo, DMINFO_DRAMSIZE) + 1;
+
+ if (get_field(dminfo, DMINFO_AUTHTYPE) != 0) {
+ LOG_ERROR("Authentication required by RISC-V core but not "
+ "supported by OpenOCD. dminfo=0x%x", dminfo);
+ return ERROR_FAIL;
+ }
+
+ // Figure out XLEN, and test writing all of Debug RAM while we're at it.
+ cache_set32(target, 0, xori(S1, ZERO, -1));
+ // 0xffffffff 0xffffffff:ffffffff 0xffffffff:ffffffff:ffffffff:ffffffff
+ cache_set32(target, 1, srli(S1, S1, 31));
+ // 0x00000001 0x00000001:ffffffff 0x00000001:ffffffff:ffffffff:ffffffff
+ cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START));
+ cache_set32(target, 3, srli(S1, S1, 31));
+ // 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff
+ cache_set32(target, 4, sw(S1, ZERO, DEBUG_RAM_START + 4));
+ cache_set_jump(target, 5);
+ for (unsigned i = 6; i < info->dramsize; i++) {
+ cache_set32(target, i, i * 0x01020304);
+ }
+
+ cache_write(target, 0, false);
+
+ // Check that we can actually read/write dram.
+ if (cache_check(target) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ cache_write(target, 0, true);
+ cache_invalidate(target);
+
+ uint32_t word0 = cache_get32(target, 0);
+ uint32_t word1 = cache_get32(target, 1);
+ riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+ if (word0 == 1 && word1 == 0) {
+ generic_info->xlen[0] = 32;
+ } else if (word0 == 0xffffffff && word1 == 3) {
+ generic_info->xlen[0] = 64;
+ } else if (word0 == 0xffffffff && word1 == 0xffffffff) {
+ generic_info->xlen[0] = 128;
+ } else {
+ uint32_t exception = cache_get32(target, info->dramsize-1);
+ LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x",
+ word0, word1, exception);
+ dump_debug_ram(target);
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
+
+ // Update register list to match discovered XLEN.
+ update_reg_list(target);
+
+ if (read_csr(target, &info->misa, CSR_MISA) != ERROR_OK) {
+ LOG_ERROR("Failed to read misa.");
+ return ERROR_FAIL;
+ }
+
+ info->never_halted = true;
+
+ int result = riscv011_poll(target);
+ if (result != ERROR_OK) {
+ return result;
+ }
+
+ target_set_examined(target);
+ riscv_set_current_hartid(target, 0);
+ for (size_t i = 0; i < 32; ++i)
+ reg_cache_set(target, i, -1);
+ LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), info->misa);
+
+ return ERROR_OK;
+}
+
+static riscv_error_t handle_halt_routine(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+
+ scans_t *scans = scans_new(target, 256);
+
+ // Read all GPRs as fast as we can, because gdb is going to ask for them
+ // anyway. Reading them one at a time is much slower.
+
+ // Write the jump back to address 1.
+ scans_add_write_jump(scans, 1, false);
+ for (int reg = 1; reg < 32; reg++) {
+ if (reg == S0 || reg == S1) {
+ continue;
+ }
+
+ // Write store instruction.
+ scans_add_write_store(scans, 0, reg, SLOT0, true);
+
+ // Read value.
+ scans_add_read(scans, SLOT0, false);
+ }
+
+ // Write store of s0 at index 1.
+ scans_add_write_store(scans, 1, S0, SLOT0, false);
+ // Write jump at index 2.
+ scans_add_write_jump(scans, 2, false);
+
+ // Read S1 from debug RAM
+ scans_add_write_load(scans, 0, S0, SLOT_LAST, true);
+ // Read value.
+ scans_add_read(scans, SLOT0, false);
+
+ // Read S0 from dscratch
+ unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR};
+ for (unsigned int i = 0; i < DIM(csr); i++) {
+ scans_add_write32(scans, 0, csrr(S0, csr[i]), true);
+ scans_add_read(scans, SLOT0, false);
+ }
+
+ // Final read to get the last value out.
+ scans_add_read32(scans, 4, false);
+
+ int retval = scans_execute(scans);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("JTAG execute failed: %d", retval);
+ goto error;
+ }
+
+ unsigned int dbus_busy = 0;
+ unsigned int interrupt_set = 0;
+ unsigned result = 0;
+ uint64_t value = 0;
+ reg_cache_set(target, 0, 0);
+ // The first scan result is the result from something old we don't care
+ // about.
+ for (unsigned int i = 1; i < scans->next_scan && dbus_busy == 0; i++) {
+ dbus_status_t status = scans_get_u32(scans, i, DBUS_OP_START,
+ DBUS_OP_SIZE);
+ uint64_t data = scans_get_u64(scans, i, DBUS_DATA_START, DBUS_DATA_SIZE);
+ uint32_t address = scans_get_u32(scans, i, DBUS_ADDRESS_START,
+ info->addrbits);
+ switch (status) {
+ case DBUS_STATUS_SUCCESS:
+ break;
+ case DBUS_STATUS_FAILED:
+ LOG_ERROR("Debug access failed. Hardware error?");
+ goto error;
+ case DBUS_STATUS_BUSY:
+ dbus_busy++;
+ break;
+ default:
+ LOG_ERROR("Got invalid bus access status: %d", status);
+ return ERROR_FAIL;
+ }
+ if (data & DMCONTROL_INTERRUPT) {
+ interrupt_set++;
+ break;
+ }
+ if (address == 4 || address == 5) {
+ unsigned int reg;
+ switch (result) {
+ case 0: reg = 1; break;
+ case 1: reg = 2; break;
+ case 2: reg = 3; break;
+ case 3: reg = 4; break;
+ case 4: reg = 5; break;
+ case 5: reg = 6; break;
+ case 6: reg = 7; break;
+ // S0
+ // S1
+ case 7: reg = 10; break;
+ case 8: reg = 11; break;
+ case 9: reg = 12; break;
+ case 10: reg = 13; break;
+ case 11: reg = 14; break;
+ case 12: reg = 15; break;
+ case 13: reg = 16; break;
+ case 14: reg = 17; break;
+ case 15: reg = 18; break;
+ case 16: reg = 19; break;
+ case 17: reg = 20; break;
+ case 18: reg = 21; break;
+ case 19: reg = 22; break;
+ case 20: reg = 23; break;
+ case 21: reg = 24; break;
+ case 22: reg = 25; break;
+ case 23: reg = 26; break;
+ case 24: reg = 27; break;
+ case 25: reg = 28; break;
+ case 26: reg = 29; break;
+ case 27: reg = 30; break;
+ case 28: reg = 31; break;
+ case 29: reg = S1; break;
+ case 30: reg = S0; break;
+ case 31: reg = CSR_DPC; break;
+ case 32: reg = CSR_DCSR; break;
+ default:
+ assert(0);
+ }
+ if (riscv_xlen(target) == 32) {
+ reg_cache_set(target, reg, data & 0xffffffff);
+ result++;
+ } else if (riscv_xlen(target) == 64) {
+ if (address == 4) {
+ value = data & 0xffffffff;
+ } else if (address == 5) {
+ reg_cache_set(target, reg, ((data & 0xffffffff) << 32) | value);
+ value = 0;
+ result++;
+ }
+ }
+ }
+ }
+
+ if (dbus_busy) {
+ increase_dbus_busy_delay(target);
+ return RE_AGAIN;
+ }
+ if (interrupt_set) {
+ increase_interrupt_high_delay(target);
+ return RE_AGAIN;
+ }
+
+ // TODO: get rid of those 2 variables and talk to the cache directly.
+ info->dpc = reg_cache_get(target, CSR_DPC);
+ info->dcsr = reg_cache_get(target, CSR_DCSR);
+
+ scans = scans_delete(scans);
+
+ cache_invalidate(target);
+
+ return RE_OK;
+
+error:
+ scans = scans_delete(scans);
+ return RE_FAIL;
+}
+
+static int handle_halt(struct target *target, bool announce)
+{
+ riscv011_info_t *info = get_info(target);
+ target->state = TARGET_HALTED;
+
+ riscv_error_t re;
+ do {
+ re = handle_halt_routine(target);
+ } while (re == RE_AGAIN);
+ if (re != RE_OK) {
+ LOG_ERROR("handle_halt_routine failed");
+ return ERROR_FAIL;
+ }
+
+ int cause = get_field(info->dcsr, DCSR_CAUSE);
+ switch (cause) {
+ case DCSR_CAUSE_SWBP:
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case DCSR_CAUSE_HWBP:
+ target->debug_reason = DBG_REASON_WPTANDBKPT;
+ // If we halted because of a data trigger, gdb doesn't know to do
+ // the disable-breakpoints-step-enable-breakpoints dance.
+ info->need_strict_step = true;
+ break;
+ case DCSR_CAUSE_DEBUGINT:
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case DCSR_CAUSE_STEP:
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ break;
+ case DCSR_CAUSE_HALT:
+ default:
+ LOG_ERROR("Invalid halt cause %d in DCSR (0x%" PRIx64 ")",
+ cause, info->dcsr);
+ }
+
+ if (info->never_halted) {
+ info->never_halted = false;
+
+ // Disable any hardware triggers that have dmode set. We can't have set
+ // them ourselves. Maybe they're left over from some killed debug
+ // session.
+ // Count the number of triggers while we're at it.
+
+ int result = maybe_read_tselect(target);
+ if (result != ERROR_OK)
+ return result;
+ for (info->trigger_count = 0; info->trigger_count < MAX_HWBPS; info->trigger_count++) {
+ write_csr(target, CSR_TSELECT, info->trigger_count);
+ uint64_t tselect_rb;
+ read_csr(target, &tselect_rb, CSR_TSELECT);
+ if (info->trigger_count != tselect_rb)
+ break;
+ uint64_t tdata1;
+ read_csr(target, &tdata1, CSR_TDATA1);
+ if ((tdata1 & MCONTROL_DMODE(riscv_xlen(target))) &&
+ (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD))) {
+ write_csr(target, CSR_TDATA1, 0);
+ }
+ }
+ }
+
+ if (announce) {
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+
+ const char *cause_string[] = {
+ "none",
+ "software breakpoint",
+ "hardware trigger",
+ "debug interrupt",
+ "step",
+ "halt"
+ };
+ // This is logged to the user so that gdb will show it when a user types
+ // 'monitor reset init'. At that time gdb appears to have the pc cached
+ // still so if a user manually inspects the pc it will still have the old
+ // value.
+ LOG_USER("halted at 0x%" PRIx64 " due to %s", info->dpc, cause_string[cause]);
+
+ return ERROR_OK;
+}
+
+static int poll_target(struct target *target, bool announce)
+{
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ // Inhibit debug logging during poll(), which isn't usually interesting and
+ // just fills up the screen/logs with clutter.
+ int old_debug_level = debug_level;
+ if (debug_level >= LOG_LVL_DEBUG) {
+ debug_level = LOG_LVL_INFO;
+ }
+ bits_t bits = read_bits(target);
+ debug_level = old_debug_level;
+
+ if (bits.haltnot && bits.interrupt) {
+ target->state = TARGET_DEBUG_RUNNING;
+ LOG_DEBUG("debug running");
+ } else if (bits.haltnot && !bits.interrupt) {
+ if (target->state != TARGET_HALTED) {
+ return handle_halt(target, announce);
+ }
+ } else if (!bits.haltnot && bits.interrupt) {
+ // Target is halting. There is no state for that, so don't change anything.
+ LOG_DEBUG("halting");
+ } else if (!bits.haltnot && !bits.interrupt) {
+ target->state = TARGET_RUNNING;
+ }
+
+ return ERROR_OK;
+}
+
+static int riscv011_poll(struct target *target)
+{
+ return poll_target(target, true);
+}
+
+static int riscv011_resume(struct target *target, int current, uint32_t address,
+ int handle_breakpoints, int debug_execution)
+{
+ riscv011_info_t *info = get_info(target);
+
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ if (!current) {
+ if (riscv_xlen(target) > 32) {
+ LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
+ riscv_xlen(target));
+ }
+ int result = register_write(target, REG_PC, address);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ if (info->need_strict_step || handle_breakpoints) {
+ int result = strict_step(target, false);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ return resume(target, debug_execution, false);
+}
+
+static int assert_reset(struct target *target)
+{
+ riscv011_info_t *info = get_info(target);
+ // TODO: Maybe what I implemented here is more like soft_reset_halt()?
+
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ // The only assumption we can make is that the TAP was reset.
+ if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+ LOG_ERROR("Debug interrupt didn't clear.");
+ return ERROR_FAIL;
+ }
+
+ // Not sure what we should do when there are multiple cores.
+ // Here just reset the single hart we're talking to.
+ info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS |
+ DCSR_EBREAKU | DCSR_HALT;
+ if (target->reset_halt) {
+ info->dcsr |= DCSR_NDRESET;
+ } else {
+ info->dcsr |= DCSR_FULLRESET;
+ }
+ dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false);
+ dram_write32(target, 1, csrw(S0, CSR_DCSR), false);
+ // We shouldn't actually need the jump because a reset should happen.
+ dram_write_jump(target, 2, false);
+ dram_write32(target, 4, info->dcsr, true);
+ cache_invalidate(target);
+
+ target->state = TARGET_RESET;
+
+ return ERROR_OK;
+}
+
+static int deassert_reset(struct target *target)
+{
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+ if (target->reset_halt) {
+ return wait_for_state(target, TARGET_HALTED);
+ } else {
+ return wait_for_state(target, TARGET_RUNNING);
+ }
+}
+
+static int read_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
+ switch (size) {
+ case 1:
+ cache_set32(target, 1, lb(S1, S0, 0));
+ cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+ break;
+ case 2:
+ cache_set32(target, 1, lh(S1, S0, 0));
+ cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+ break;
+ case 4:
+ cache_set32(target, 1, lw(S1, S0, 0));
+ cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
+ cache_set_jump(target, 3);
+ cache_write(target, CACHE_NO_READ, false);
+
+ riscv011_info_t *info = get_info(target);
+ const int max_batch_size = 256;
+ scans_t *scans = scans_new(target, max_batch_size);
+
+ uint32_t result_value = 0x777;
+ uint32_t i = 0;
+ while (i < count + 3) {
+ unsigned int batch_size = MIN(count + 3 - i, max_batch_size);
+ scans_reset(scans);
+
+ for (unsigned int j = 0; j < batch_size; j++) {
+ if (i + j == count) {
+ // Just insert a read so we can scan out the last value.
+ scans_add_read32(scans, 4, false);
+ } else if (i + j >= count + 1) {
+ // And check for errors.
+ scans_add_read32(scans, info->dramsize-1, false);
+ } else {
+ // Write the next address and set interrupt.
+ uint32_t offset = size * (i + j);
+ scans_add_write32(scans, 4, address + offset, true);
+ }
+ }
+
+ int retval = scans_execute(scans);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("JTAG execute failed: %d", retval);
+ goto error;
+ }
+
+ int dbus_busy = 0;
+ int execute_busy = 0;
+ for (unsigned int j = 0; j < batch_size; j++) {
+ dbus_status_t status = scans_get_u32(scans, j, DBUS_OP_START,
+ DBUS_OP_SIZE);
+ switch (status) {
+ case DBUS_STATUS_SUCCESS:
+ break;
+ case DBUS_STATUS_FAILED:
+ LOG_ERROR("Debug RAM write failed. Hardware error?");
+ goto error;
+ case DBUS_STATUS_BUSY:
+ dbus_busy++;
+ break;
+ default:
+ LOG_ERROR("Got invalid bus access status: %d", status);
+ return ERROR_FAIL;
+ }
+ uint64_t data = scans_get_u64(scans, j, DBUS_DATA_START,
+ DBUS_DATA_SIZE);
+ if (data & DMCONTROL_INTERRUPT) {
+ execute_busy++;
+ }
+ if (i + j == count + 2) {
+ result_value = data;
+ } else if (i + j > 1) {
+ uint32_t offset = size * (i + j - 2);
+ switch (size) {
+ case 1:
+ buffer[offset] = data;
+ break;
+ case 2:
+ buffer[offset] = data;
+ buffer[offset+1] = data >> 8;
+ break;
+ case 4:
+ buffer[offset] = data;
+ buffer[offset+1] = data >> 8;
+ buffer[offset+2] = data >> 16;
+ buffer[offset+3] = data >> 24;
+ break;
+ }
+ }
+ LOG_DEBUG("j=%d status=%d data=%09" PRIx64, j, status, data);
+ }
+ if (dbus_busy) {
+ increase_dbus_busy_delay(target);
+ }
+ if (execute_busy) {
+ increase_interrupt_high_delay(target);
+ }
+ if (dbus_busy || execute_busy) {
+ wait_for_debugint_clear(target, false);
+
+ // Retry.
+ LOG_INFO("Retrying memory read starting from 0x%x with more delays",
+ address + size * i);
+ } else {
+ i += batch_size;
+ }
+ }
+
+ if (result_value != 0) {
+ LOG_USER("Core got an exception (0x%x) while reading from 0x%x",
+ result_value, address + size * (count-1));
+ if (count > 1) {
+ LOG_USER("(It may have failed between 0x%x and 0x%x as well, but we "
+ "didn't check then.)",
+ address, address + size * (count-2) + size - 1);
+ }
+ goto error;
+ }
+
+ scans_delete(scans);
+ cache_clean(target);
+ return ERROR_OK;
+
+error:
+ scans_delete(scans);
+ cache_clean(target);
+ return ERROR_FAIL;
+}
+
+static int setup_write_memory(struct target *target, uint32_t size)
+{
+ switch (size) {
+ case 1:
+ cache_set32(target, 0, lb(S0, ZERO, DEBUG_RAM_START + 16));
+ cache_set32(target, 1, sb(S0, T0, 0));
+ break;
+ case 2:
+ cache_set32(target, 0, lh(S0, ZERO, DEBUG_RAM_START + 16));
+ cache_set32(target, 1, sh(S0, T0, 0));
+ break;
+ case 4:
+ cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
+ cache_set32(target, 1, sw(S0, T0, 0));
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
+ cache_set32(target, 2, addi(T0, T0, size));
+ cache_set_jump(target, 3);
+ cache_write(target, 4, false);
+
+ return ERROR_OK;
+}
+
+static int write_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ riscv011_info_t *info = get_info(target);
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ // Set up the address.
+ cache_set_store(target, 0, T0, SLOT1);
+ cache_set_load(target, 1, T0, SLOT0);
+ cache_set_jump(target, 2);
+ cache_set(target, SLOT0, address);
+ if (cache_write(target, 5, true) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ uint64_t t0 = cache_get(target, SLOT1);
+ LOG_DEBUG("t0 is 0x%" PRIx64, t0);
+
+ if (setup_write_memory(target, size) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ const int max_batch_size = 256;
+ scans_t *scans = scans_new(target, max_batch_size);
+
+ uint32_t result_value = 0x777;
+ uint32_t i = 0;
+ while (i < count + 2) {
+ unsigned int batch_size = MIN(count + 2 - i, max_batch_size);
+ scans_reset(scans);
+
+ for (unsigned int j = 0; j < batch_size; j++) {
+ if (i + j >= count) {
+ // Check for an exception.
+ scans_add_read32(scans, info->dramsize-1, false);
+ } else {
+ // Write the next value and set interrupt.
+ uint32_t value;
+ uint32_t offset = size * (i + j);
+ switch (size) {
+ case 1:
+ value = buffer[offset];
+ break;
+ case 2:
+ value = buffer[offset] |
+ (buffer[offset+1] << 8);
+ break;
+ case 4:
+ value = buffer[offset] |
+ ((uint32_t) buffer[offset+1] << 8) |
+ ((uint32_t) buffer[offset+2] << 16) |
+ ((uint32_t) buffer[offset+3] << 24);
+ break;
+ default:
+ goto error;
+ }
+
+ scans_add_write32(scans, 4, value, true);
+ }
+ }
+
+ int retval = scans_execute(scans);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("JTAG execute failed: %d", retval);
+ goto error;
+ }
+
+ int dbus_busy = 0;
+ int execute_busy = 0;
+ for (unsigned int j = 0; j < batch_size; j++) {
+ dbus_status_t status = scans_get_u32(scans, j, DBUS_OP_START,
+ DBUS_OP_SIZE);
+ switch (status) {
+ case DBUS_STATUS_SUCCESS:
+ break;
+ case DBUS_STATUS_FAILED:
+ LOG_ERROR("Debug RAM write failed. Hardware error?");
+ goto error;
+ case DBUS_STATUS_BUSY:
+ dbus_busy++;
+ break;
+ default:
+ LOG_ERROR("Got invalid bus access status: %d", status);
+ return ERROR_FAIL;
+ }
+ int interrupt = scans_get_u32(scans, j, DBUS_DATA_START + 33, 1);
+ if (interrupt) {
+ execute_busy++;
+ }
+ if (i + j == count + 1) {
+ result_value = scans_get_u32(scans, j, DBUS_DATA_START, 32);
+ }
+ }
+ if (dbus_busy) {
+ increase_dbus_busy_delay(target);
+ }
+ if (execute_busy) {
+ increase_interrupt_high_delay(target);
+ }
+ if (dbus_busy || execute_busy) {
+ wait_for_debugint_clear(target, false);
+
+ // Retry.
+ // Set t0 back to what it should have been at the beginning of this
+ // batch.
+ LOG_INFO("Retrying memory write starting from 0x%x with more delays",
+ address + size * i);
+
+ cache_clean(target);
+
+ if (write_gpr(target, T0, address + size * i) != ERROR_OK) {
+ goto error;
+ }
+
+ if (setup_write_memory(target, size) != ERROR_OK) {
+ goto error;
+ }
+ } else {
+ i += batch_size;
+ }
+ }
+
+ if (result_value != 0) {
+ LOG_ERROR("Core got an exception (0x%x) while writing to 0x%x",
+ result_value, address + size * (count-1));
+ if (count > 1) {
+ LOG_ERROR("(It may have failed between 0x%x and 0x%x as well, but we "
+ "didn't check then.)",
+ address, address + size * (count-2) + size - 1);
+ }
+ goto error;
+ }
+
+ cache_clean(target);
+ return register_write(target, T0, t0);
+
+error:
+ scans_delete(scans);
+ cache_clean(target);
+ return ERROR_FAIL;
+}
+
+static int arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+struct target_type riscv011_target =
+{
+ .name = "riscv",
+
+ .init_target = init_target,
+ .deinit_target = deinit_target,
+ .examine = examine,
+
+ /* poll current target status */
+ .poll = riscv011_poll,
+
+ .halt = halt,
+ .resume = riscv011_resume,
+ .step = step,
+
+ .assert_reset = assert_reset,
+ .deassert_reset = deassert_reset,
+
+ .read_memory = read_memory,
+ .write_memory = write_memory,
+
+ .add_breakpoint = add_breakpoint,
+ .remove_breakpoint = remove_breakpoint,
+
+ .add_watchpoint = add_watchpoint,
+ .remove_watchpoint = remove_watchpoint,
+
+ .arch_state = arch_state,
+};
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
new file mode 100644
index 0000000..eaefaed
--- /dev/null
+++ b/src/target/riscv/riscv-013.c
@@ -0,0 +1,1994 @@
+/*
+ * Support for RISC-V, debug version 0.13, which is currently (2/4/17) the
+ * latest draft.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target.h"
+#include "target/algorithm.h"
+#include "target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "register.h"
+#include "breakpoints.h"
+#include "helper/time_support.h"
+#include "riscv.h"
+#include "debug_defines.h"
+#include "rtos/rtos.h"
+#include "program.h"
+#include "asm.h"
+#include "batch.h"
+
+#define DMI_DATA1 (DMI_DATA0 + 1)
+
+static void riscv013_on_step_or_resume(struct target *target, bool step);
+static void riscv013_step_or_resume_current_hart(struct target *target, bool step);
+static riscv_addr_t riscv013_progbuf_addr(struct target *target);
+static riscv_addr_t riscv013_progbuf_size(struct target *target);
+static riscv_addr_t riscv013_data_size(struct target *target);
+static riscv_addr_t riscv013_data_addr(struct target *target);
+static void riscv013_set_autoexec(struct target *target, int offset, bool enabled);
+static int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr);
+static void riscv013_clear_abstract_error(struct target *target);
+
+/* Implementations of the functions in riscv_info_t. */
+static riscv_reg_t riscv013_get_register(struct target *target, int hartid, int regid);
+static void riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
+static void riscv013_select_current_hart(struct target *target);
+static void riscv013_halt_current_hart(struct target *target);
+static void riscv013_resume_current_hart(struct target *target);
+static void riscv013_step_current_hart(struct target *target);
+static void riscv013_on_halt(struct target *target);
+static void riscv013_on_step(struct target *target);
+static void riscv013_on_resume(struct target *target);
+static bool riscv013_is_halted(struct target *target);
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
+static void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *p);
+static void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *p);
+static void riscv013_write_debug_buffer(struct target *target, int i, riscv_insn_t d);
+static riscv_insn_t riscv013_read_debug_buffer(struct target *target, int i);
+static int riscv013_execute_debug_buffer(struct target *target);
+static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
+static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
+static int riscv013_dmi_write_u64_bits(struct target *target);
+static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
+static void riscv013_reset_current_hart(struct target *target);
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x) (sizeof(x)/sizeof(*x))
+
+#define CSR_DCSR_CAUSE_SWBP 1
+#define CSR_DCSR_CAUSE_TRIGGER 2
+#define CSR_DCSR_CAUSE_DEBUGINT 3
+#define CSR_DCSR_CAUSE_STEP 4
+#define CSR_DCSR_CAUSE_HALT 5
+
+#define RISCV013_INFO(r) riscv013_info_t *r = get_info(target)
+
+/*** JTAG registers. ***/
+
+typedef enum {
+ DMI_OP_NOP = 0,
+ DMI_OP_READ = 1,
+ DMI_OP_WRITE = 2
+} dmi_op_t;
+typedef enum {
+ DMI_STATUS_SUCCESS = 0,
+ DMI_STATUS_FAILED = 2,
+ DMI_STATUS_BUSY = 3
+} dmi_status_t;
+
+typedef enum {
+ RE_OK,
+ RE_FAIL,
+ RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+ SLOT0,
+ SLOT1,
+ SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define CMDERR_NONE 0
+#define CMDERR_BUSY 1
+#define CMDERR_NOT_SUPPORTED 2
+#define CMDERR_EXCEPTION 3
+#define CMDERR_HALT_RESUME 4
+#define CMDERR_OTHER 7
+
+/*** Info about the core being debugged. ***/
+
+#define WALL_CLOCK_TIMEOUT 2
+
+#define MAX_HWBPS 16
+
+struct trigger {
+ uint64_t address;
+ uint32_t length;
+ uint64_t mask;
+ uint64_t value;
+ bool read, write, execute;
+ int unique_id;
+};
+
+struct memory_cache_line {
+ uint32_t data;
+ bool valid;
+ bool dirty;
+};
+
+typedef struct {
+ /* Number of address bits in the dbus register. */
+ unsigned abits;
+ /* Number of abstract command data registers. */
+ unsigned datacount;
+ /* Number of words in the Program Buffer. */
+ unsigned progsize;
+ /* Number of Program Buffer registers. */
+ /* Number of words in Debug RAM. */
+ uint64_t misa;
+ uint64_t tselect;
+ bool tselect_dirty;
+ /* The value that mstatus actually has on the target right now. This is not
+ * the value we present to the user. That one may be stored in the
+ * reg_cache. */
+ uint64_t mstatus_actual;
+
+ /* Single buffer that contains all register names, instead of calling
+ * malloc for each register. Needs to be freed when reg_list is freed. */
+ char *reg_names;
+ /* Single buffer that contains all register values. */
+ void *reg_values;
+
+ // For each physical trigger, contains -1 if the hwbp is available, or the
+ // unique_id of the breakpoint/watchpoint that is using it.
+ int trigger_unique_id[MAX_HWBPS];
+
+ // Number of run-test/idle cycles the target requests we do after each dbus
+ // access.
+ unsigned int dtmcontrol_idle;
+
+ // This value is incremented every time a dbus access comes back as "busy".
+ // It's used to determine how many run-test/idle cycles to feed the target
+ // in between accesses.
+ unsigned int dmi_busy_delay;
+
+ // This value is increased every time we tried to execute two commands
+ // consecutively, and the second one failed because the previous hadn't
+ // completed yet. It's used to add extra run-test/idle cycles after
+ // starting a command, so we don't have to waste time checking for busy to
+ // go low.
+ unsigned int ac_busy_delay;
+
+ bool need_strict_step;
+
+ // Some memoized values
+ int progbuf_size, progbuf_addr, data_addr, data_size;
+} riscv013_info_t;
+
+static void dump_field(const struct scan_field *field)
+{
+ static const char *op_string[] = {"-", "r", "w", "?"};
+ static const char *status_string[] = {"+", "?", "F", "b"};
+
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
+ unsigned int out_op = get_field(out, DTM_DMI_OP);
+ unsigned int out_data = get_field(out, DTM_DMI_DATA);
+ unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
+
+ uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+ unsigned int in_op = get_field(in, DTM_DMI_OP);
+ unsigned int in_data = get_field(in, DTM_DMI_DATA);
+ unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
+
+ log_printf_lf(LOG_LVL_DEBUG,
+ __FILE__, __LINE__, "scan",
+ "%db %s %08x @%02x -> %s %08x @%02x",
+ field->num_bits,
+ op_string[out_op], out_data, out_address,
+ status_string[in_op], in_data, in_address);
+
+}
+
+static riscv013_info_t *get_info(const struct target *target)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ return (riscv013_info_t *) info->version_specific;
+}
+
+/*** Necessary prototypes. ***/
+
+static int register_get(struct reg *reg);
+
+/*** Utility functions. ***/
+
+bool supports_extension(struct target *target, char letter)
+{
+ riscv013_info_t *info = get_info(target);
+ unsigned num;
+ if (letter >= 'a' && letter <= 'z') {
+ num = letter - 'a';
+ } else if (letter >= 'A' && letter <= 'Z') {
+ num = letter - 'A';
+ } else {
+ return false;
+ }
+ return info->misa & (1 << num);
+}
+
+static void select_dmi(struct target *target)
+{
+ static uint8_t ir_dmi[1] = {DTM_DMI};
+ struct scan_field field = {
+ .num_bits = target->tap->ir_length,
+ .out_value = ir_dmi,
+ .in_value = NULL,
+ .check_value = NULL,
+ .check_mask = NULL
+ };
+
+ jtag_add_ir_scan(target->tap, &field, TAP_IDLE);
+}
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+ struct scan_field field;
+ uint8_t in_value[4];
+ uint8_t out_value[4];
+
+ buf_set_u32(out_value, 0, 32, out);
+
+ jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+ field.num_bits = 32;
+ field.out_value = out_value;
+ field.in_value = in_value;
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ /* Always return to dmi. */
+ select_dmi(target);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ uint32_t in = buf_get_u32(field.in_value, 0, 32);
+ LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
+
+ return in;
+}
+
+static void increase_dmi_busy_delay(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+ info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
+ LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+ info->dtmcontrol_idle, info->dmi_busy_delay,
+ info->ac_busy_delay);
+
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+}
+
+static void increase_ac_busy_delay(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+ info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
+ LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+ info->dtmcontrol_idle, info->dmi_busy_delay,
+ info->ac_busy_delay);
+
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+}
+
+/**
+ * exec: If this is set, assume the scan results in an execution, so more
+ * run-test/idle cycles may be required.
+ */
+static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in,
+ uint64_t *data_in, dmi_op_t op, uint16_t address_out, uint64_t data_out,
+ bool exec)
+{
+ riscv013_info_t *info = get_info(target);
+ uint8_t in[8] = {0};
+ uint8_t out[8];
+ struct scan_field field = {
+ .num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH,
+ .out_value = out,
+ .in_value = in
+ };
+
+ assert(info->abits != 0);
+
+ buf_set_u64(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
+ buf_set_u64(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
+ buf_set_u64(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
+
+ /* Assume dbus is already selected. */
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ int idle_count = info->dmi_busy_delay;
+ if (exec)
+ idle_count += info->ac_busy_delay;
+
+ if (idle_count) {
+ jtag_add_runtest(idle_count, TAP_IDLE);
+ }
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("dmi_scan failed jtag scan");
+ return DMI_STATUS_FAILED;
+ }
+
+ if (data_in) {
+ *data_in = buf_get_u64(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
+ }
+
+ if (address_in) {
+ *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
+ }
+
+ dump_field(&field);
+
+ return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
+}
+
+static uint64_t dmi_read(struct target *target, uint16_t address)
+{
+ select_dmi(target);
+
+ uint64_t value;
+ dmi_status_t status;
+ uint16_t address_in;
+
+ unsigned i = 0;
+
+ // This first loop ensures that the read request was actually sent
+ // to the target. Note that if for some reason this stays busy,
+ // it is actually due to the Previous dmi_read or dmi_write.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, NULL, NULL, DMI_OP_READ, address, 0,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed read from 0x%x, status=%d", address, status);
+ break;
+ }
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed read from 0x%x; value=0x%" PRIx64 ", status=%d",
+ address, value, status);
+ abort();
+ }
+
+ // This second loop ensures that we got the read
+ // data back. Note that NOP can result in a 'busy' result as well, but
+ // that would be noticed on the next DMI access we do.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, &address_in, &value, DMI_OP_NOP, address, 0,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed read (NOP) at 0x%x, status=%d\n", address, status);
+ break;
+ }
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%" PRIx64 ", status=%d",
+ address, value, status);
+ abort();
+ }
+
+ return value;
+}
+
+static void dmi_write(struct target *target, uint16_t address, uint64_t value)
+{
+ select_dmi(target);
+ dmi_status_t status = DMI_STATUS_BUSY;
+ unsigned i = 0;
+
+ // The first loop ensures that we successfully sent the write request.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value,
+ address == DMI_COMMAND);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed write to 0x%x, status=%d\n", address, status);
+ break;
+ }
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed write to 0x%x;, status=%d\n",
+ address, status);
+ abort();
+ }
+
+ // The second loop isn't strictly necessary, but would ensure that
+ // the write is complete/ has no non-busy errors before returning from this function.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, address, 0,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed write (NOP) at 0x%x, status=%d\n", address, status);
+ break;
+ }
+ }
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("failed to write (NOP) 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status);
+ abort();
+ }
+}
+
+uint32_t abstract_register_size(unsigned width)
+{
+ switch (width) {
+ case 32:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 2);
+ case 64:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 3);
+ break;
+ case 128:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 4);
+ break;
+ default:
+ LOG_ERROR("Unsupported register width: %d", width);
+ return 0;
+ }
+}
+
+static int wait_for_idle(struct target *target, uint32_t *abstractcs)
+{
+ time_t start = time(NULL);
+ while (1) {
+ *abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+
+ if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0) {
+ return ERROR_OK;
+ }
+
+ if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
+ if (get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR) != CMDERR_NONE) {
+ const char *errors[8] = {
+ "none",
+ "busy",
+ "not supported",
+ "exception",
+ "halt/resume",
+ "reserved",
+ "reserved",
+ "other" };
+
+ LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)",
+ errors[get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR)],
+ *abstractcs);
+ }
+
+ LOG_ERROR("Timed out waiting for busy to go low. (abstractcs=0x%x)",
+ *abstractcs);
+ return ERROR_FAIL;
+ }
+ }
+}
+
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
+
+static int register_write_direct(struct target *target, unsigned number,
+ uint64_t value)
+{
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ riscv_addr_t input = riscv_program_alloc_d(&program);
+ riscv_program_write_ram(&program, input + 4, value >> 32);
+ riscv_program_write_ram(&program, input, value);
+
+ assert(GDB_REGNO_XPR0 == 0);
+ if (number <= GDB_REGNO_XPR31) {
+ riscv_program_lx(&program, number, input);
+ } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ riscv_program_fld(&program, number, input);
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ enum gdb_regno temp = riscv_program_gettemp(&program);
+ riscv_program_lx(&program, temp, input);
+ riscv_program_csrw(&program, temp, number);
+ } else {
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ abort();
+ }
+
+ int exec_out = riscv_program_exec(&program, target);
+ if (exec_out != ERROR_OK) {
+ LOG_ERROR("Unable to execute program");
+ return exec_out;
+ }
+
+ return ERROR_OK;
+}
+
+/** Actually read registers from the target right now. */
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
+{
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t output = riscv_program_alloc_d(&program);
+ riscv_program_write_ram(&program, output + 4, 0);
+ riscv_program_write_ram(&program, output, 0);
+
+ assert(GDB_REGNO_XPR0 == 0);
+ if (number <= GDB_REGNO_XPR31) {
+ riscv_program_sx(&program, number, output);
+ } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ riscv_program_fsd(&program, number, output);
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ LOG_DEBUG("reading CSR index=0x%03x", number - GDB_REGNO_CSR0);
+ enum gdb_regno temp = riscv_program_gettemp(&program);
+ riscv_program_csrr(&program, temp, number);
+ riscv_program_sx(&program, temp, output);
+ } else {
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ abort();
+ }
+
+ int exec_out = riscv_program_exec(&program, target);
+ if (exec_out != ERROR_OK) {
+ LOG_ERROR("Unable to execute program");
+ return exec_out;
+ }
+
+ *value = 0;
+ *value |= ((uint64_t)(riscv_program_read_ram(&program, output + 4))) << 32;
+ *value |= riscv_program_read_ram(&program, output);
+ LOG_DEBUG("register 0x%x = 0x%" PRIx64, number, *value);
+ return ERROR_OK;
+}
+
+static int maybe_read_tselect(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+
+ if (info->tselect_dirty) {
+ int result = register_read_direct(target, &info->tselect, GDB_REGNO_TSELECT);
+ if (result != ERROR_OK)
+ return result;
+ info->tselect_dirty = false;
+ }
+
+ return ERROR_OK;
+}
+
+/*** OpenOCD target functions. ***/
+
+static int register_get(struct reg *reg)
+{
+ struct target *target = (struct target *) reg->arch_info;
+ uint64_t value = riscv_get_register(target, reg->number);
+ buf_set_u64(reg->value, 0, 64, value);
+ return ERROR_OK;
+}
+
+static int register_write(struct target *target, unsigned int number,
+ uint64_t value)
+{
+ riscv_set_register(target, number, value);
+ return ERROR_OK;
+}
+
+static int register_set(struct reg *reg, uint8_t *buf)
+{
+ struct target *target = (struct target *) reg->arch_info;
+
+ uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
+
+ LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
+ struct reg *r = &target->reg_cache->reg_list[reg->number];
+ r->valid = true;
+ memcpy(r->value, buf, (r->size + 7) / 8);
+
+ return register_write(target, reg->number, value);
+}
+
+static struct reg_arch_type riscv_reg_arch_type = {
+ .get = register_get,
+ .set = register_set
+};
+
+static int init_target(struct command_context *cmd_ctx,
+ struct target *target)
+{
+ LOG_DEBUG("init");
+ riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+
+ riscv_info_init(generic_info);
+ generic_info->get_register = &riscv013_get_register;
+ generic_info->set_register = &riscv013_set_register;
+ generic_info->select_current_hart = &riscv013_select_current_hart;
+ generic_info->is_halted = &riscv013_is_halted;
+ generic_info->halt_current_hart = &riscv013_halt_current_hart;
+ generic_info->resume_current_hart = &riscv013_resume_current_hart;
+ generic_info->step_current_hart = &riscv013_step_current_hart;
+ generic_info->on_halt = &riscv013_on_halt;
+ generic_info->on_resume = &riscv013_on_resume;
+ generic_info->on_step = &riscv013_on_step;
+ generic_info->halt_reason = &riscv013_halt_reason;
+ generic_info->debug_buffer_enter = &riscv013_debug_buffer_enter;
+ generic_info->debug_buffer_leave = &riscv013_debug_buffer_leave;
+ generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
+ generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
+ generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
+ generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
+ generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
+ generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
+ generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
+ generic_info->reset_current_hart = &riscv013_reset_current_hart;
+
+ generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
+ if (!generic_info->version_specific)
+ return ERROR_FAIL;
+ riscv013_info_t *info = get_info(target);
+
+ info->progbuf_size = -1;
+ info->progbuf_addr = -1;
+ info->data_size = -1;
+ info->data_addr = -1;
+
+ info->dmi_busy_delay = 0;
+ info->ac_busy_delay = 0;
+
+ target->reg_cache = calloc(1, sizeof(*target->reg_cache));
+ target->reg_cache->name = "RISC-V registers";
+ target->reg_cache->num_regs = GDB_REGNO_COUNT;
+
+ target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
+
+ const unsigned int max_reg_name_len = 12;
+ info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
+ char *reg_name = info->reg_names;
+ info->reg_values = NULL;
+
+ for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
+ struct reg *r = &target->reg_cache->reg_list[i];
+ r->number = i;
+ r->caller_save = true;
+ r->dirty = false;
+ r->valid = false;
+ r->exist = true;
+ r->type = &riscv_reg_arch_type;
+ r->arch_info = target;
+ if (i <= GDB_REGNO_XPR31) {
+ sprintf(reg_name, "x%d", i);
+ } else if (i == GDB_REGNO_PC) {
+ sprintf(reg_name, "pc");
+ } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
+ sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
+ } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
+ sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
+ } else if (i == GDB_REGNO_PRIV) {
+ sprintf(reg_name, "priv");
+ }
+ if (reg_name[0]) {
+ r->name = reg_name;
+ }
+ reg_name += strlen(reg_name) + 1;
+ assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
+ }
+#if 0
+ update_reg_list(target);
+#endif
+
+ memset(info->trigger_unique_id, 0xff, sizeof(info->trigger_unique_id));
+
+ return ERROR_OK;
+}
+
+static void deinit_target(struct target *target)
+{
+ LOG_DEBUG("riscv_deinit_target()");
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ free(info->version_specific);
+ info->version_specific = NULL;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+ riscv013_info_t *info = get_info(target);
+ maybe_read_tselect(target);
+
+ int i;
+ for (i = 0; i < riscv_count_triggers(target); i++) {
+ if (info->trigger_unique_id[i] != -1) {
+ continue;
+ }
+
+ register_write_direct(target, GDB_REGNO_TSELECT, i);
+
+ uint64_t tdata1;
+ register_read_direct(target, &tdata1, GDB_REGNO_TDATA1);
+ int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+
+ if (type != 2) {
+ continue;
+ }
+
+ if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
+ // Trigger is already in use, presumably by user code.
+ continue;
+ }
+
+ // address/data match trigger
+ tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+ tdata1 = set_field(tdata1, MCONTROL_ACTION,
+ MCONTROL_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
+ tdata1 |= MCONTROL_M;
+ if (info->misa & (1 << ('H' - 'A')))
+ tdata1 |= MCONTROL_H;
+ if (info->misa & (1 << ('S' - 'A')))
+ tdata1 |= MCONTROL_S;
+ if (info->misa & (1 << ('U' - 'A')))
+ tdata1 |= MCONTROL_U;
+
+ if (trigger->execute)
+ tdata1 |= MCONTROL_EXECUTE;
+ if (trigger->read)
+ tdata1 |= MCONTROL_LOAD;
+ if (trigger->write)
+ tdata1 |= MCONTROL_STORE;
+
+ register_write_direct(target, GDB_REGNO_TDATA1, tdata1);
+
+ uint64_t tdata1_rb;
+ register_read_direct(target, &tdata1_rb, GDB_REGNO_TDATA1);
+ LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+ if (tdata1 != tdata1_rb) {
+ LOG_DEBUG("Trigger %d doesn't support what we need; After writing 0x%"
+ PRIx64 " to tdata1 it contains 0x%" PRIx64,
+ i, tdata1, tdata1_rb);
+ register_write_direct(target, GDB_REGNO_TDATA1, 0);
+ continue;
+ }
+
+ register_write_direct(target, GDB_REGNO_TDATA2, trigger->address);
+
+ LOG_DEBUG("Using resource %d for bp %d", i,
+ trigger->unique_id);
+ info->trigger_unique_id[i] = trigger->unique_id;
+ break;
+ }
+ if (i >= riscv_count_triggers(target)) {
+ LOG_ERROR("Couldn't find an available hardware trigger.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ return ERROR_OK;
+}
+
+static int remove_trigger(struct target *target, struct trigger *trigger)
+{
+ riscv013_info_t *info = get_info(target);
+
+ maybe_read_tselect(target);
+
+ int i;
+ for (i = 0; i < riscv_count_triggers(target); i++) {
+ if (info->trigger_unique_id[i] == trigger->unique_id) {
+ break;
+ }
+ }
+ if (i >= riscv_count_triggers(target)) {
+ LOG_ERROR("Couldn't find the hardware resources used by hardware "
+ "trigger.");
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id);
+ register_write_direct(target, GDB_REGNO_TSELECT, i);
+ register_write_direct(target, GDB_REGNO_TDATA1, 0);
+ info->trigger_unique_id[i] = -1;
+
+ return ERROR_OK;
+}
+
+static void trigger_from_breakpoint(struct trigger *trigger,
+ const struct breakpoint *breakpoint)
+{
+ trigger->address = breakpoint->address;
+ trigger->length = breakpoint->length;
+ trigger->mask = ~0LL;
+ trigger->read = false;
+ trigger->write = false;
+ trigger->execute = true;
+ // unique_id is unique across both breakpoints and watchpoints.
+ trigger->unique_id = breakpoint->unique_id;
+}
+
+static void trigger_from_watchpoint(struct trigger *trigger,
+ const struct watchpoint *watchpoint)
+{
+ trigger->address = watchpoint->address;
+ trigger->length = watchpoint->length;
+ trigger->mask = watchpoint->mask;
+ trigger->value = watchpoint->value;
+ trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
+ trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
+ trigger->execute = false;
+ // unique_id is unique across both breakpoints and watchpoints.
+ trigger->unique_id = watchpoint->unique_id;
+}
+
+static int add_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (breakpoint->type == BKPT_SOFT) {
+ if (target_read_memory(target, breakpoint->address, breakpoint->length, 1,
+ breakpoint->orig_instr) != ERROR_OK) {
+ LOG_ERROR("Failed to read original instruction at 0x%x",
+ breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ int retval;
+ if (breakpoint->length == 4) {
+ retval = target_write_u32(target, breakpoint->address, ebreak());
+ } else {
+ retval = target_write_u16(target, breakpoint->address, ebreak_c());
+ }
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%x",
+ breakpoint->length, breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ } else if (breakpoint->type == BKPT_HARD) {
+ struct trigger trigger;
+ trigger_from_breakpoint(&trigger, breakpoint);
+ int result = add_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ } else {
+ LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ breakpoint->set = true;
+
+ return ERROR_OK;
+}
+
+static int remove_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ if (breakpoint->type == BKPT_SOFT) {
+ if (target_write_memory(target, breakpoint->address, breakpoint->length, 1,
+ breakpoint->orig_instr) != ERROR_OK) {
+ LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
+ "0x%x", breakpoint->length, breakpoint->address);
+ return ERROR_FAIL;
+ }
+
+ } else if (breakpoint->type == BKPT_HARD) {
+ struct trigger trigger;
+ trigger_from_breakpoint(&trigger, breakpoint);
+ int result = remove_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ } else {
+ LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ breakpoint->set = false;
+
+ return ERROR_OK;
+}
+
+static int add_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct trigger trigger;
+ trigger_from_watchpoint(&trigger, watchpoint);
+
+ int result = add_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ watchpoint->set = true;
+
+ return ERROR_OK;
+}
+
+static int remove_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct trigger trigger;
+ trigger_from_watchpoint(&trigger, watchpoint);
+
+ int result = remove_trigger(target, &trigger);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ watchpoint->set = false;
+
+ return ERROR_OK;
+}
+
+static int examine(struct target *target)
+{
+ // Don't need to select dbus, since the first thing we do is read dtmcontrol.
+
+ uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+ LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ LOG_DEBUG(" dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET));
+ LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE));
+ LOG_DEBUG(" dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT));
+ LOG_DEBUG(" abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS));
+ LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION));
+ if (dtmcontrol == 0) {
+ LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+ return ERROR_FAIL;
+ }
+ if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
+ LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
+ get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
+ return ERROR_FAIL;
+ }
+
+ riscv013_info_t *info = get_info(target);
+ info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
+ info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
+
+ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(dmstatus, DMI_DMSTATUS_VERSIONLO) != 2) {
+ LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d "
+ "(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSIONLO), dmstatus);
+ return ERROR_FAIL;
+ }
+
+ // Reset the Debug Module.
+ dmi_write(target, DMI_DMCONTROL, 0);
+ dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
+ dmcontrol = dmi_read(target, DMI_DMCONTROL);
+
+ LOG_DEBUG("dmcontrol: 0x%08x", dmcontrol);
+ LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
+
+ if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) {
+ LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
+ dmcontrol);
+ return ERROR_FAIL;
+ }
+
+ if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
+ LOG_ERROR("Authentication required by RISC-V core but not "
+ "supported by OpenOCD. dmcontrol=0x%x", dmcontrol);
+ return ERROR_FAIL;
+ }
+
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
+ LOG_ERROR("The hart is unavailable.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) {
+ LOG_ERROR("The hart doesn't exist.");
+ return ERROR_FAIL;
+ }
+
+ // Check that abstract data registers are accessible.
+ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
+ info->progsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGSIZE);
+
+ /* Before doing anything else we must first enumerate the harts. */
+ RISCV_INFO(r);
+ if (riscv_rtos_enabled(target)) {
+ for (int i = 0; i < RISCV_MAX_HARTS; ++i) {
+ riscv_set_current_hartid(target, i);
+ uint32_t s = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
+ break;
+ r->hart_count = i + 1;
+ }
+ } else {
+ r->hart_count = 1;
+ }
+
+ LOG_DEBUG("Enumerated %d harts", r->hart_count);
+
+ /* Halt every hart so we can probe them. */
+ riscv_halt_all_harts(target);
+
+ /* Find the address of the program buffer, which must be done without
+ * knowing anything about the target. */
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ riscv_set_current_hartid(target, i);
+
+ /* Without knowing anything else we can at least mess with the
+ * program buffer. */
+ r->debug_buffer_size[i] = riscv013_progbuf_size(target);
+
+ /* Guess this is a 32-bit system, we're probing it. */
+ r->xlen[i] = 32;
+
+ /* First find the low 32 bits of the program buffer. This is
+ * used to check for alignment. */
+ struct riscv_program program32;
+ riscv_program_init(&program32, target);
+ riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_insert(&program32, auipc(GDB_REGNO_S0));
+ riscv_program_insert(&program32, sw(GDB_REGNO_S0, GDB_REGNO_S0, -4));
+ riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_fence(&program32);
+ riscv_program_exec(&program32, target);
+
+ riscv_addr_t progbuf_addr = dmi_read(target, DMI_PROGBUF0) - 4;
+ if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) != 0) {
+ LOG_ERROR("Unable to find the address of the program buffer on hart %d", i);
+ r->xlen[i] = -1;
+ continue;
+ }
+ r->debug_buffer_addr[i] = progbuf_addr;
+
+ /* Check to see if the core can execute 64 bit instructions.
+ * In order to make this work we first need to */
+ int offset = (progbuf_addr % 8 == 0) ? -4 : 0;
+
+ struct riscv_program program64;
+ riscv_program_init(&program64, target);
+ riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_insert(&program64, auipc(GDB_REGNO_S0));
+ riscv_program_insert(&program64, sd(GDB_REGNO_S0, GDB_REGNO_S0, offset));
+ riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_fence(&program64);
+ riscv_program_exec(&program64, target);
+
+ if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) == 0) {
+ r->debug_buffer_addr[i] =
+ (dmi_read(target, DMI_PROGBUF0 + (8 + offset) / 4) << 32)
+ + dmi_read(target, DMI_PROGBUF0 + (4 + offset) / 4)
+ - 4;
+ r->xlen[i] = 64;
+ }
+
+ LOG_DEBUG("hart %d has XLEN=%d", i, r->xlen[i]);
+ LOG_DEBUG("found program buffer at 0x%08lx", (long)(r->debug_buffer_addr[i]));
+
+ if (riscv_program_gah(&program64, r->debug_buffer_addr[i])) {
+ LOG_ERROR("This implementation will not work with hart %d with debug_buffer_addr of 0x%lx\n", i,
+ (long)r->debug_buffer_addr[i]);
+ abort();
+ }
+
+ /* Check to see if we can use the data words as an extended
+ * program buffer or not. */
+ if (r->debug_buffer_addr[i] + (4 * r->debug_buffer_size[i]) == riscv013_data_addr(target)) {
+ r->debug_buffer_size[i] += riscv013_data_size(target);
+ LOG_DEBUG("extending the debug buffer using data words, total size %d", r->debug_buffer_size[i]);
+ }
+ }
+
+ /* Then we check the number of triggers availiable to each hart. */
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ for (uint32_t t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+ riscv_set_current_hartid(target, i);
+
+ r->trigger_count[i] = t;
+ register_write_direct(target, GDB_REGNO_TSELECT, t);
+ uint64_t tselect = t+1;
+ register_read_direct(target, &tselect, GDB_REGNO_TSELECT);
+ if (tselect != t)
+ break;
+ }
+ }
+
+ /* Resumes all the harts, so the debugger can later pause them. */
+ riscv_resume_all_harts(target);
+ target_set_examined(target);
+
+ // This print is used by some regression suites to know when
+ // they can connect with gdb/telnet.
+ // We will need to update those suites if we want to remove this line.
+ LOG_INFO("Examined RISC-V core");
+ return ERROR_OK;
+}
+
+static int assert_reset(struct target *target)
+{
+ /*FIXME -- this only works for single-hart.*/
+ RISCV_INFO(r);
+ assert(r->current_hartid == 0);
+
+ select_dmi(target);
+ LOG_DEBUG("ASSERTING NDRESET");
+ uint32_t control = dmi_read(target, DMI_DMCONTROL);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+ if (target->reset_halt) {
+ LOG_DEBUG("TARGET RESET HALT SET, ensuring halt is set during reset.");
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 1);
+ } else {
+ LOG_DEBUG("TARGET RESET HALT NOT SET");
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
+ }
+
+ dmi_write(target, DMI_DMCONTROL,
+ control);
+
+ return ERROR_OK;
+}
+
+static int deassert_reset(struct target *target)
+{
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
+
+ select_dmi(target);
+
+ /*FIXME -- this only works for Single Hart*/
+ assert(r->current_hartid == 0);
+
+ /*FIXME -- is there bookkeeping we need to do here*/
+
+ uint32_t control = dmi_read(target, DMI_DMCONTROL);
+
+ // Clear the reset, but make sure haltreq is still set
+ if (target->reset_halt) {
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 1);
+ }
+
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 0);
+ dmi_write(target, DMI_DMCONTROL, control);
+
+ uint32_t status;
+ int dmi_busy_delay = info->dmi_busy_delay;
+ if (target->reset_halt) {
+ LOG_DEBUG("DEASSERTING RESET, waiting for hart to be halted.");
+ do {
+ status = dmi_read(target, DMI_DMSTATUS);
+ } while (get_field(status, DMI_DMSTATUS_ALLHALTED) == 0);
+ } else {
+ LOG_DEBUG("DEASSERTING RESET, waiting for hart to be running.");
+ do {
+ status = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(status, DMI_DMSTATUS_ANYHALTED) ||
+ get_field(status, DMI_DMSTATUS_ANYUNAVAIL)) {
+ LOG_ERROR("Unexpected hart status during reset.");
+ abort();
+ }
+ } while (get_field(status, DMI_DMSTATUS_ALLRUNNING) == 0);
+ }
+ info->dmi_busy_delay = dmi_busy_delay;
+ return ERROR_OK;
+}
+
+static int read_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+
+ LOG_DEBUG("reading %d words of %d bytes from 0x%08lx", count, size, (long)address);
+
+ select_dmi(target);
+ riscv_set_current_hartid(target, 0);
+
+ /* This program uses two temporary registers. A word of data and the
+ * associated address are stored at some location in memory. The
+ * program loads the word from that address and then increments the
+ * address. The debugger is expected to pull the memory word-by-word
+ * from the chip with AUTOEXEC set in order to trigger program
+ * execution on every word. */
+ uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0);
+ uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t r_data = riscv_program_alloc_w(&program);
+ riscv_addr_t r_addr = riscv_program_alloc_x(&program);
+ riscv_program_fence(&program);
+ riscv_program_lx(&program, GDB_REGNO_S0, r_addr);
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+ switch (size) {
+ case 1:
+ riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 2:
+ riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 4:
+ riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
+ riscv_program_sw(&program, GDB_REGNO_S1, r_data);
+ riscv_program_sx(&program, GDB_REGNO_S0, r_addr);
+
+ /* The first round through the program's execution we use the regular
+ * program execution mechanism. */
+ switch (riscv_xlen(target)) {
+ case 64:
+ riscv_program_write_ram(&program, r_addr + 4, (((riscv_addr_t) address) - size) >> 32);
+ case 32:
+ riscv_program_write_ram(&program, r_addr, ((riscv_addr_t) address) - size);
+ break;
+ default:
+ LOG_ERROR("unknown XLEN %d", riscv_xlen(target));
+ return ERROR_FAIL;
+ }
+
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ LOG_ERROR(" exiting with ERROR_FAIL");
+ return ERROR_FAIL;
+ }
+
+ uint32_t value = riscv_program_read_ram(&program, r_data);
+ LOG_DEBUG("M[0x%08lx] reads 0x%08lx", (long)address, (long)value);
+ switch (size) {
+ case 1:
+ buffer[0] = value;
+ break;
+ case 2:
+ buffer[0] = value;
+ buffer[1] = value >> 8;
+ break;
+ case 4:
+ buffer[0] = value;
+ buffer[1] = value >> 8;
+ buffer[2] = value >> 16;
+ buffer[3] = value >> 24;
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
+
+ /* The rest of this program is designed to be fast so it reads various
+ * DMI registers directly. */
+ int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4;
+ int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4;
+
+ riscv013_set_autoexec(target, d_data, 1);
+
+ /* Copying memory might fail because we're going too quickly, in which
+ * case we need to back off a bit and try again. There's two
+ * termination conditions to this loop: a non-BUSY error message, or
+ * the data was all copied. */
+ riscv_addr_t cur_addr = 0xbadbeef;
+ riscv_addr_t fin_addr = address + (count * size);
+ riscv_addr_t prev_addr = ((riscv_addr_t) address) - size;
+ LOG_DEBUG("writing until final address 0x%016lx", fin_addr);
+ while (count > 1 && (cur_addr = riscv_read_debug_buffer_x(target, d_addr)) < fin_addr) {
+ LOG_DEBUG("transferring burst starting at address 0x%016lx (previous burst was 0x%016lx)", cur_addr, prev_addr);
+ assert(prev_addr < cur_addr);
+ prev_addr = cur_addr;
+ riscv_addr_t start = (cur_addr - address) / size;
+ assert (cur_addr >= address);
+ struct riscv_batch *batch = riscv_batch_alloc(
+ target,
+ 32,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+ size_t reads = 0;
+ size_t rereads = reads;
+ for (riscv_addr_t i = start; i < count; ++i) {
+ size_t index =
+ riscv_batch_add_dmi_read(
+ batch,
+ riscv013_debug_buffer_register(target, r_data));
+ assert(index == reads);
+ reads++;
+ if (riscv_batch_full(batch))
+ break;
+ }
+
+ riscv_batch_run(batch);
+
+ for (size_t i = start; i < start + reads; ++i) {
+ riscv_addr_t offset = size*i;
+ riscv_addr_t t_addr = address + offset;
+ uint8_t *t_buffer = buffer + offset;
+
+ uint64_t dmi_out = riscv_batch_get_dmi_read(batch, rereads);
+ value = get_field(dmi_out, DTM_DMI_DATA);
+ rereads++;
+
+ switch (size) {
+ case 1:
+ t_buffer[0] = value;
+ break;
+ case 2:
+ t_buffer[0] = value;
+ t_buffer[1] = value >> 8;
+ break;
+ case 4:
+ t_buffer[0] = value;
+ t_buffer[1] = value >> 8;
+ t_buffer[2] = value >> 16;
+ t_buffer[3] = value >> 24;
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("M[0x%08lx] reads 0x%08lx", (long)t_addr, (long)value);
+ }
+
+ riscv_batch_free(batch);
+
+ // Note that if the scan resulted in a Busy DMI response, it
+ // is this read to abstractcs that will cause the dmi_busy_delay
+ // to be incremented if necessary. The loop condition above
+ // catches the case where no writes went through at all.
+
+ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+ abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ switch (get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)) {
+ case CMDERR_NONE:
+ LOG_DEBUG("successful (partial?) memory write");
+ break;
+ case CMDERR_BUSY:
+ LOG_DEBUG("memory write resulted in busy response");
+ riscv013_clear_abstract_error(target);
+ increase_ac_busy_delay(target);
+ break;
+ default:
+ LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ riscv013_clear_abstract_error(target);
+ return ERROR_FAIL;
+ }
+ }
+
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_OK;
+}
+
+static int write_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+
+ LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
+
+ select_dmi(target);
+ riscv_set_current_hartid(target, 0);
+
+ /* This program uses two temporary registers. A word of data and the
+ * associated address are stored at some location in memory. The
+ * program stores the word to that address and then increments the
+ * address. The debugger is expected to feed the memory word-by-word
+ * into the chip with AUTOEXEC set in order to trigger program
+ * execution on every word. */
+ uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0);
+ uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t r_data = riscv_program_alloc_w(&program);
+ riscv_addr_t r_addr = riscv_program_alloc_x(&program);
+ riscv_program_fence(&program);
+ riscv_program_lx(&program, GDB_REGNO_S0, r_addr);
+ riscv_program_lw(&program, GDB_REGNO_S1, r_data);
+
+ switch (size) {
+ case 1:
+ riscv_program_sbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 2:
+ riscv_program_shr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 4:
+ riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
+
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+ riscv_program_sx(&program, GDB_REGNO_S0, r_addr);
+
+ /* The first round through the program's execution we use the regular
+ * program execution mechanism. */
+ uint32_t value;
+ switch (size) {
+ case 1:
+ value = buffer[0];
+ break;
+ case 2:
+ value = buffer[0]
+ | ((uint32_t) buffer[1] << 8);
+ break;
+ case 4:
+ value = buffer[0]
+ | ((uint32_t) buffer[1] << 8)
+ | ((uint32_t) buffer[2] << 16)
+ | ((uint32_t) buffer[3] << 24);
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
+
+ switch (riscv_xlen(target)) {
+ case 64:
+ riscv_program_write_ram(&program, r_addr + 4, (uint64_t)address >> 32);
+ case 32:
+ riscv_program_write_ram(&program, r_addr, address);
+ break;
+ default:
+ LOG_ERROR("unknown XLEN %d", riscv_xlen(target));
+ return ERROR_FAIL;
+ }
+ riscv_program_write_ram(&program, r_data, value);
+
+ LOG_DEBUG("M[0x%08lx] writes 0x%08lx", (long)address, (long)value);
+
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ LOG_ERROR(" exiting with ERROR_FAIL");
+ return ERROR_FAIL;
+ }
+
+ /* The rest of this program is designed to be fast so it reads various
+ * DMI registers directly. */
+ int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4;
+ int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4;
+
+ riscv013_set_autoexec(target, d_data, 1);
+
+ /* Copying memory might fail because we're going too quickly, in which
+ * case we need to back off a bit and try again. There's two
+ * termination conditions to this loop: a non-BUSY error message, or
+ * the data was all copied. */
+ riscv_addr_t cur_addr = 0xbadbeef;
+ riscv_addr_t fin_addr = address + (count * size);
+ LOG_DEBUG("writing until final address 0x%016lx", fin_addr);
+ while ((cur_addr = riscv_read_debug_buffer_x(target, d_addr)) < fin_addr) {
+ LOG_DEBUG("transferring burst starting at address 0x%016lx", cur_addr);
+ riscv_addr_t start = (cur_addr - address) / size;
+ assert (cur_addr > address);
+ struct riscv_batch *batch = riscv_batch_alloc(
+ target,
+ 32,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+ for (riscv_addr_t i = start; i < count; ++i) {
+ riscv_addr_t offset = size*i;
+ riscv_addr_t t_addr = address + offset;
+ const uint8_t *t_buffer = buffer + offset;
+
+ switch (size) {
+ case 1:
+ value = t_buffer[0];
+ break;
+ case 2:
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8);
+ break;
+ case 4:
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8)
+ | ((uint32_t) t_buffer[2] << 16)
+ | ((uint32_t) t_buffer[3] << 24);
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("M[0x%08lx] writes 0x%08lx", (long)t_addr, (long)value);
+
+ riscv_batch_add_dmi_write(
+ batch,
+ riscv013_debug_buffer_register(target, r_data),
+ value);
+ if (riscv_batch_full(batch))
+ break;
+ }
+
+ riscv_batch_run(batch);
+ riscv_batch_free(batch);
+
+ // Note that if the scan resulted in a Busy DMI response, it
+ // is this read to abstractcs that will cause the dmi_busy_delay
+ // to be incremented if necessary. The loop condition above
+ // catches the case where no writes went through at all.
+
+ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+ abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ switch (get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)) {
+ case CMDERR_NONE:
+ LOG_DEBUG("successful (partial?) memory write");
+ break;
+ case CMDERR_BUSY:
+ LOG_DEBUG("memory write resulted in busy response");
+ riscv013_clear_abstract_error(target);
+ increase_ac_busy_delay(target);
+ break;
+ default:
+ LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_FAIL;
+ }
+ }
+
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_OK;
+}
+
+static int arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+struct target_type riscv013_target =
+{
+ .name = "riscv",
+
+ .init_target = init_target,
+ .deinit_target = deinit_target,
+ .examine = examine,
+
+ .poll = &riscv_openocd_poll,
+ .halt = &riscv_openocd_halt,
+ .resume = &riscv_openocd_resume,
+ .step = &riscv_openocd_step,
+
+ .assert_reset = assert_reset,
+ .deassert_reset = deassert_reset,
+
+ .read_memory = read_memory,
+ .write_memory = write_memory,
+
+ .add_breakpoint = add_breakpoint,
+ .remove_breakpoint = remove_breakpoint,
+
+ .add_watchpoint = add_watchpoint,
+ .remove_watchpoint = remove_watchpoint,
+
+ .arch_state = arch_state,
+};
+
+/*** 0.13-specific implementations of various RISC-V hepler functions. ***/
+static riscv_reg_t riscv013_get_register(struct target *target, int hid, int rid)
+{
+ LOG_DEBUG("reading register 0x%08x on hart %d", rid, hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ uint64_t out;
+ riscv013_info_t *info = get_info(target);
+
+ if (rid <= GDB_REGNO_XPR31) {
+ register_read_direct(target, &out, rid);
+ } else if (rid == GDB_REGNO_PC) {
+ register_read_direct(target, &out, GDB_REGNO_DPC);
+ LOG_DEBUG("read PC from DPC: 0x%016lx", out);
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ register_read_direct(target, &dcsr, CSR_DCSR);
+ buf_set_u64((unsigned char *)&out, 0, 8, get_field(dcsr, CSR_DCSR_PRV));
+ } else {
+ int result = register_read_direct(target, &out, rid);
+ if (result != ERROR_OK) {
+ LOG_ERROR("Unable to read register %d", rid);
+ out = -1;
+ }
+
+ if (rid == GDB_REGNO_MSTATUS)
+ info->mstatus_actual = out;
+ }
+
+ return out;
+}
+
+static void riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+{
+ LOG_DEBUG("writing register 0x%08x on hart %d", rid, hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ if (rid <= GDB_REGNO_XPR31) {
+ register_write_direct(target, rid, value);
+ } else if (rid == GDB_REGNO_PC) {
+ LOG_DEBUG("writing PC to DPC: 0x%016lx", value);
+ register_write_direct(target, GDB_REGNO_DPC, value);
+ uint64_t actual_value;
+ register_read_direct(target, &actual_value, GDB_REGNO_DPC);
+ LOG_DEBUG(" actual DPC written: 0x%016lx", actual_value);
+ assert(value == actual_value);
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ register_read_direct(target, &dcsr, CSR_DCSR);
+ dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+ register_write_direct(target, CSR_DCSR, dcsr);
+ } else {
+ register_write_direct(target, rid, value);
+ }
+}
+
+static void riscv013_select_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+
+ uint64_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, r->current_hartid);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+}
+
+static void riscv013_halt_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("halting hart %d", r->current_hartid);
+ assert(!riscv_is_halted(target));
+
+ /* Issue the halt command, and then wait for the current hart to halt. */
+ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ for (size_t i = 0; i < 256; ++i)
+ if (riscv_is_halted(target))
+ break;
+
+ if (!riscv_is_halted(target)) {
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ dmcontrol = dmi_read(target, DMI_DMCONTROL);
+
+ LOG_ERROR("unable to halt hart %d", r->current_hartid);
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ abort();
+ }
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+}
+
+static void riscv013_resume_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, false);
+}
+
+static void riscv013_step_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, true);
+}
+
+static void riscv013_on_resume(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, false);
+}
+
+static void riscv013_on_step(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, true);
+}
+
+static void riscv013_on_halt(struct target *target)
+{
+}
+
+static bool riscv013_is_halted(struct target *target)
+{
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
+ LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target));
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
+ LOG_ERROR("hart %d doesn't exist", riscv_current_hartid(target));
+ return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
+}
+
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
+{
+ uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
+ switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
+ case CSR_DCSR_CAUSE_SWBP:
+ case CSR_DCSR_CAUSE_TRIGGER:
+ return RISCV_HALT_BREAKPOINT;
+ case CSR_DCSR_CAUSE_STEP:
+ return RISCV_HALT_SINGLESTEP;
+ case CSR_DCSR_CAUSE_DEBUGINT:
+ case CSR_DCSR_CAUSE_HALT:
+ return RISCV_HALT_INTERRUPT;
+ }
+
+ LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
+ LOG_ERROR(" dcsr=0x%016lx", (long)dcsr);
+ abort();
+}
+
+void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *program)
+{
+}
+
+void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *program)
+{
+}
+
+void riscv013_write_debug_buffer(struct target *target, int index, riscv_insn_t data)
+{
+ if (index >= riscv013_progbuf_size(target))
+ return dmi_write(target, DMI_DATA0 + index - riscv013_progbuf_size(target), data);
+ return dmi_write(target, DMI_PROGBUF0 + index, data);
+}
+
+riscv_insn_t riscv013_read_debug_buffer(struct target *target, int index)
+{
+ if (index >= riscv013_progbuf_size(target))
+ return dmi_read(target, DMI_DATA0 + index - riscv013_progbuf_size(target));
+ return dmi_read(target, DMI_PROGBUF0 + index);
+}
+
+int riscv013_execute_debug_buffer(struct target *target)
+{
+ riscv013_clear_abstract_error(target);
+
+ uint32_t run_program = 0;
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
+ dmi_write(target, DMI_COMMAND, run_program);
+
+ {
+ uint32_t dmstatus = 0;
+ wait_for_idle(target, &dmstatus);
+ }
+
+ uint32_t cs = dmi_read(target, DMI_ABSTRACTCS);
+ if (get_field(cs, DMI_ABSTRACTCS_CMDERR) != 0) {
+ LOG_ERROR("unable to execute program: (abstractcs=0x%08x)", cs);
+ dmi_read(target, DMI_DMSTATUS);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, d);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0);
+}
+
+int riscv013_dmi_write_u64_bits(struct target *target)
+{
+ RISCV013_INFO(info);
+ return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
+}
+
+void riscv013_reset_current_hart(struct target *target)
+{
+ select_dmi(target);
+ uint32_t control = dmi_read(target, DMI_DMCONTROL);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 1);
+ dmi_write(target, DMI_DMCONTROL, control);
+
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 0);
+ dmi_write(target, DMI_DMCONTROL, control);
+
+ while (get_field(dmi_read(target, DMI_DMSTATUS), DMI_DMSTATUS_ALLHALTED) == 0);
+
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, control);
+}
+
+/* Helper Functions. */
+static void riscv013_on_step_or_resume(struct target *target, bool step)
+{
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ LOG_ERROR("Unable to execute fence.i");
+
+ /* We want to twiddle some bits in the debug CSR so debugging works. */
+ uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
+ dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKH, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
+ riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+}
+
+static void riscv013_step_or_resume_current_hart(struct target *target, bool step)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
+ assert(riscv_is_halted(target));
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ abort();
+
+ /* Issue the halt command, and then wait for the current hart to halt. */
+ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
+ for (size_t i = 0; i < 256; ++i) {
+ usleep(10);
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
+ continue;
+ if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
+ continue;
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ return;
+ }
+
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ LOG_ERROR("unable to resume hart %d", r->current_hartid);
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+
+ if (step) {
+ LOG_ERROR(" was stepping, halting");
+ riscv013_halt_current_hart(target);
+ return;
+ }
+
+ abort();
+}
+
+riscv_addr_t riscv013_progbuf_addr(struct target *target)
+{
+ RISCV013_INFO(info);
+ assert(info->progbuf_addr != -1);
+ return info->progbuf_addr;
+}
+
+riscv_addr_t riscv013_progbuf_size(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->progbuf_size == -1) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ info->progbuf_size = get_field(acs, DMI_ABSTRACTCS_PROGSIZE);
+ }
+ return info->progbuf_size;
+}
+
+riscv_addr_t riscv013_data_size(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->data_size == -1) {
+ uint32_t acs = dmi_read(target, DMI_HARTINFO);
+ info->data_size = get_field(acs, DMI_HARTINFO_DATASIZE);
+ }
+ return info->data_size;
+}
+
+riscv_addr_t riscv013_data_addr(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->data_addr == -1) {
+ uint32_t acs = dmi_read(target, DMI_HARTINFO);
+ info->data_addr = get_field(acs, DMI_HARTINFO_DATAACCESS) ? get_field(acs, DMI_HARTINFO_DATAADDR) : 0;
+ }
+ return info->data_addr;
+}
+
+void riscv013_set_autoexec(struct target *target, int offset, bool enabled)
+{
+ if (offset >= riscv013_progbuf_size(target)) {
+ LOG_DEBUG("setting bit %d in AUTOEXECDATA to %d", offset, enabled);
+ uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO);
+ uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA);
+ aa_aed &= ~(1 << (offset - riscv013_progbuf_size(target)));
+ aa_aed |= (enabled << (offset - riscv013_progbuf_size(target)));
+ aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA, aa_aed);
+ dmi_write(target, DMI_ABSTRACTAUTO, aa);
+ } else {
+ LOG_DEBUG("setting bit %d in AUTOEXECPROGBUF to %d", offset, enabled);
+ uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO);
+ uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
+ aa_aed &= ~(1 << offset);
+ aa_aed |= (enabled << offset);
+ aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, aa_aed);
+ dmi_write(target, DMI_ABSTRACTAUTO, aa);
+ }
+}
+
+int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr)
+{
+ if (addr >= riscv013_data_addr(target))
+ return DMI_DATA0 + (addr - riscv013_data_addr(target)) / 4;
+ else
+ return DMI_PROGBUF0 + (addr - riscv013_progbuf_addr(target)) / 4;
+}
+
+void riscv013_clear_abstract_error(struct target *target)
+{
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ dmi_write(target, DMI_ABSTRACTCS, acs);
+}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
new file mode 100644
index 0000000..c232204
--- /dev/null
+++ b/src/target/riscv/riscv.c
@@ -0,0 +1,1227 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target.h"
+#include "target/algorithm.h"
+#include "target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "register.h"
+#include "breakpoints.h"
+#include "helper/time_support.h"
+#include "riscv.h"
+#include "gdb_regs.h"
+#include "rtos/rtos.h"
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+/**
+ * Code structure
+ *
+ * At the bottom of the stack are the OpenOCD JTAG functions:
+ * jtag_add_[id]r_scan
+ * jtag_execute_query
+ * jtag_add_runtest
+ *
+ * There are a few functions to just instantly shift a register and get its
+ * value:
+ * dtmcontrol_scan
+ * idcode_scan
+ * dbus_scan
+ *
+ * Because doing one scan and waiting for the result is slow, most functions
+ * batch up a bunch of dbus writes and then execute them all at once. They use
+ * the scans "class" for this:
+ * scans_new
+ * scans_delete
+ * scans_execute
+ * scans_add_...
+ * Usually you new(), call a bunch of add functions, then execute() and look
+ * at the results by calling scans_get...()
+ *
+ * Optimized functions will directly use the scans class above, but slightly
+ * lazier code will use the cache functions that in turn use the scans
+ * functions:
+ * cache_get...
+ * cache_set...
+ * cache_write
+ * cache_set... update a local structure, which is then synced to the target
+ * with cache_write(). Only Debug RAM words that are actually changed are sent
+ * to the target. Afterwards use cache_get... to read results.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x) (sizeof(x)/sizeof(*x))
+
+// Constants for legacy SiFive hardware breakpoints.
+#define CSR_BPCONTROL_X (1<<0)
+#define CSR_BPCONTROL_W (1<<1)
+#define CSR_BPCONTROL_R (1<<2)
+#define CSR_BPCONTROL_U (1<<3)
+#define CSR_BPCONTROL_S (1<<4)
+#define CSR_BPCONTROL_H (1<<5)
+#define CSR_BPCONTROL_M (1<<6)
+#define CSR_BPCONTROL_BPMATCH (0xf<<7)
+#define CSR_BPCONTROL_BPACTION (0xff<<11)
+
+#define DEBUG_ROM_START 0x800
+#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
+#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
+#define DEBUG_RAM_START 0x400
+
+#define SETHALTNOT 0x10c
+
+/*** JTAG registers. ***/
+
+#define DTMCONTROL 0x10
+#define DTMCONTROL_DBUS_RESET (1<<16)
+#define DTMCONTROL_IDLE (7<<10)
+#define DTMCONTROL_ADDRBITS (0xf<<4)
+#define DTMCONTROL_VERSION (0xf)
+
+#define DBUS 0x11
+#define DBUS_OP_START 0
+#define DBUS_OP_SIZE 2
+typedef enum {
+ DBUS_OP_NOP = 0,
+ DBUS_OP_READ = 1,
+ DBUS_OP_WRITE = 2
+} dbus_op_t;
+typedef enum {
+ DBUS_STATUS_SUCCESS = 0,
+ DBUS_STATUS_FAILED = 2,
+ DBUS_STATUS_BUSY = 3
+} dbus_status_t;
+#define DBUS_DATA_START 2
+#define DBUS_DATA_SIZE 34
+#define DBUS_ADDRESS_START 36
+
+typedef enum {
+ RE_OK,
+ RE_FAIL,
+ RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+ SLOT0,
+ SLOT1,
+ SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define DMCONTROL 0x10
+#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33)
+#define DMCONTROL_HALTNOT (((uint64_t)1)<<32)
+#define DMCONTROL_BUSERROR (7<<19)
+#define DMCONTROL_SERIAL (3<<16)
+#define DMCONTROL_AUTOINCREMENT (1<<15)
+#define DMCONTROL_ACCESS (7<<12)
+#define DMCONTROL_HARTID (0x3ff<<2)
+#define DMCONTROL_NDRESET (1<<1)
+#define DMCONTROL_FULLRESET 1
+
+#define DMINFO 0x11
+#define DMINFO_ABUSSIZE (0x7fU<<25)
+#define DMINFO_SERIALCOUNT (0xf<<21)
+#define DMINFO_ACCESS128 (1<<20)
+#define DMINFO_ACCESS64 (1<<19)
+#define DMINFO_ACCESS32 (1<<18)
+#define DMINFO_ACCESS16 (1<<17)
+#define DMINFO_ACCESS8 (1<<16)
+#define DMINFO_DRAMSIZE (0x3f<<10)
+#define DMINFO_AUTHENTICATED (1<<5)
+#define DMINFO_AUTHBUSY (1<<4)
+#define DMINFO_AUTHTYPE (3<<2)
+#define DMINFO_VERSION 3
+
+/*** Info about the core being debugged. ***/
+
+#define DBUS_ADDRESS_UNKNOWN 0xffff
+#define WALL_CLOCK_TIMEOUT 2
+
+// 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 {
+ REG_XPR0 = 0,
+ REG_XPR31 = 31,
+ REG_PC = 32,
+ REG_FPR0 = 33,
+ REG_FPR31 = 64,
+ REG_CSR0 = 65,
+ REG_MSTATUS = CSR_MSTATUS + REG_CSR0,
+ REG_CSR4095 = 4160,
+ REG_PRIV = 4161,
+ REG_COUNT
+};
+
+#define MAX_HWBPS 16
+#define DRAM_CACHE_SIZE 16
+
+uint8_t ir_dtmcontrol[1] = {DTMCONTROL};
+struct scan_field select_dtmcontrol = {
+ .in_value = NULL,
+ .out_value = ir_dtmcontrol
+};
+uint8_t ir_dbus[1] = {DBUS};
+struct scan_field select_dbus = {
+ .in_value = NULL,
+ .out_value = ir_dbus
+};
+uint8_t ir_idcode[1] = {0x1};
+struct scan_field select_idcode = {
+ .in_value = NULL,
+ .out_value = ir_idcode
+};
+
+struct trigger {
+ uint64_t address;
+ uint32_t length;
+ uint64_t mask;
+ uint64_t value;
+ bool read, write, execute;
+ int unique_id;
+};
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+ struct scan_field field;
+ uint8_t in_value[4];
+ uint8_t out_value[4];
+
+ buf_set_u32(out_value, 0, 32, out);
+
+ jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+ field.num_bits = 32;
+ field.out_value = out_value;
+ field.in_value = in_value;
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ /* Always return to dbus. */
+ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ uint32_t in = buf_get_u32(field.in_value, 0, 32);
+ LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
+
+ return in;
+}
+
+static struct target_type *get_target_type(struct target *target)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+
+ switch (info->dtm_version) {
+ case 0:
+ return &riscv011_target;
+ case 1:
+ return &riscv013_target;
+ default:
+ LOG_ERROR("Unsupported DTM version: %d", info->dtm_version);
+ return NULL;
+ }
+}
+
+static int riscv_init_target(struct command_context *cmd_ctx,
+ struct target *target)
+{
+ LOG_DEBUG("riscv_init_target()");
+ target->arch_info = calloc(1, sizeof(riscv_info_t));
+ if (!target->arch_info)
+ return ERROR_FAIL;
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ info->cmd_ctx = cmd_ctx;
+
+ select_dtmcontrol.num_bits = target->tap->ir_length;
+ select_dbus.num_bits = target->tap->ir_length;
+ select_idcode.num_bits = target->tap->ir_length;
+
+ return ERROR_OK;
+}
+
+static void riscv_deinit_target(struct target *target)
+{
+ LOG_DEBUG("riscv_deinit_target()");
+ struct target_type *tt = get_target_type(target);
+ tt->deinit_target(target);
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ free(info);
+ target->arch_info = NULL;
+}
+
+static int oldriscv_halt(struct target *target)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->halt(target);
+}
+
+static int riscv_add_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->add_breakpoint(target, breakpoint);
+}
+
+static int riscv_remove_breakpoint(struct target *target,
+ struct breakpoint *breakpoint)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->remove_breakpoint(target, breakpoint);
+}
+
+static int riscv_add_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->add_watchpoint(target, watchpoint);
+}
+
+static int riscv_remove_watchpoint(struct target *target,
+ struct watchpoint *watchpoint)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->remove_watchpoint(target, watchpoint);
+}
+
+static int oldriscv_step(struct target *target, int current, uint32_t address,
+ int handle_breakpoints)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->step(target, current, address, handle_breakpoints);
+}
+
+static int old_or_new_riscv_step(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints
+){
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_step(target, current, address, handle_breakpoints);
+ else
+ return riscv_openocd_step(target, current, address, handle_breakpoints);
+}
+
+
+static int riscv_examine(struct target *target)
+{
+ LOG_DEBUG("riscv_examine()");
+ if (target_was_examined(target)) {
+ LOG_DEBUG("Target was already examined.\n");
+ return ERROR_OK;
+ }
+
+ // Don't need to select dbus, since the first thing we do is read dtmcontrol.
+
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+ LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
+ LOG_DEBUG(" version=0x%x", info->dtm_version);
+
+ struct target_type *tt = get_target_type(target);
+ if (tt == NULL)
+ return ERROR_FAIL;
+
+ int result = tt->init_target(info->cmd_ctx, target);
+ if (result != ERROR_OK)
+ return result;
+
+ return tt->examine(target);
+}
+
+static int oldriscv_poll(struct target *target)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->poll(target);
+}
+
+static int old_or_new_riscv_poll(struct target *target)
+{
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_poll(target);
+ else
+ return riscv_openocd_poll(target);
+}
+
+static int old_or_new_riscv_halt(struct target *target)
+{
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_halt(target);
+ else
+ return riscv_openocd_halt(target);
+}
+
+static int oldriscv_assert_reset(struct target *target)
+{
+ LOG_DEBUG("RISCV ASSERT RESET");
+ struct target_type *tt = get_target_type(target);
+ return tt->assert_reset(target);
+}
+
+static int oldriscv_deassert_reset(struct target *target)
+{
+ LOG_DEBUG("RISCV DEASSERT RESET");
+ struct target_type *tt = get_target_type(target);
+ return tt->deassert_reset(target);
+}
+
+
+static int old_or_new_riscv_assert_reset(struct target *target)
+{
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_assert_reset(target);
+ else
+ return riscv_openocd_assert_reset(target);
+}
+
+static int old_or_new_riscv_deassert_reset(struct target *target)
+{
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_deassert_reset(target);
+ else
+ return riscv_openocd_deassert_reset(target);
+}
+
+static int oldriscv_resume(struct target *target, int current, uint32_t address,
+ int handle_breakpoints, int debug_execution)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->resume(target, current, address, handle_breakpoints,
+ debug_execution);
+}
+
+static int old_or_new_riscv_resume(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints,
+ int debug_execution
+){
+ RISCV_INFO(r);
+ if (r->is_halted == NULL)
+ return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
+ else
+ return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
+}
+
+static int riscv_read_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->read_memory(target, address, size, count, buffer);
+}
+
+static int riscv_write_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->write_memory(target, address, size, count, buffer);
+}
+
+static int riscv_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("reg_class=%d", reg_class);
+ LOG_DEBUG("riscv_get_gdb_reg_list: rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
+ if (r->rtos_hartid != -1)
+ riscv_set_current_hartid(target, r->rtos_hartid);
+ else
+ riscv_set_current_hartid(target, 0);
+
+ switch (reg_class) {
+ case REG_CLASS_GENERAL:
+ *reg_list_size = 32;
+ break;
+ case REG_CLASS_ALL:
+ *reg_list_size = REG_COUNT;
+ break;
+ default:
+ LOG_ERROR("Unsupported reg_class: %d", reg_class);
+ return ERROR_FAIL;
+ }
+
+ *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
+ if (!*reg_list) {
+ return ERROR_FAIL;
+ }
+
+ if (!target->reg_cache) {
+ LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
+ return ERROR_FAIL;
+ }
+
+ for (int i = 0; i < *reg_list_size; i++) {
+ assert(target->reg_cache->reg_list[i].size > 0);
+ (*reg_list)[i] = &target->reg_cache->reg_list[i];
+ }
+
+ return ERROR_OK;
+}
+
+static int riscv_arch_state(struct target *target)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->arch_state(target);
+}
+
+// Algorithm must end with a software breakpoint instruction.
+static int riscv_run_algorithm(struct target *target, int num_mem_params,
+ struct mem_param *mem_params, int num_reg_params,
+ struct reg_param *reg_params, uint32_t entry_point,
+ uint32_t exit_point, int timeout_ms, void *arch_info)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+
+ if (num_mem_params > 0) {
+ LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /// Save registers
+ struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1);
+ if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
+
+ uint64_t saved_regs[32];
+ for (int i = 0; i < num_reg_params; i++) {
+ LOG_DEBUG("save %s", reg_params[i].reg_name);
+ struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+ if (!r) {
+ LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
+ return ERROR_FAIL;
+ }
+
+ if (r->size != reg_params[i].size) {
+ LOG_ERROR("Register %s is %d bits instead of %d bits.",
+ reg_params[i].reg_name, r->size, reg_params[i].size);
+ return ERROR_FAIL;
+ }
+
+ if (r->number > REG_XPR31) {
+ LOG_ERROR("Only GPRs can be use as argument registers.");
+ return ERROR_FAIL;
+ }
+
+ if (r->type->get(r) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ saved_regs[r->number] = buf_get_u64(r->value, 0, r->size);
+ if (r->type->set(r, reg_params[i].value) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ }
+
+
+ // Disable Interrupts before attempting to run the algorithm.
+ uint64_t current_mstatus;
+ uint8_t mstatus_bytes[8];
+
+ LOG_DEBUG("Disabling Interrupts");
+ char mstatus_name[20];
+ sprintf(mstatus_name, "csr%d", CSR_MSTATUS);
+ struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+ mstatus_name, 1);
+ reg_mstatus->type->get(reg_mstatus);
+ current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
+ uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+ buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
+ ie_mask, 0));
+
+ reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+
+ /// Run algorithm
+ LOG_DEBUG("resume at 0x%x", entry_point);
+ if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ int64_t start = timeval_ms();
+ while (target->state != TARGET_HALTED) {
+ LOG_DEBUG("poll()");
+ int64_t now = timeval_ms();
+ if (now - start > timeout_ms) {
+ LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
+ LOG_ERROR(" now = 0x%08x", (uint32_t) now);
+ LOG_ERROR(" start = 0x%08x", (uint32_t) start);
+ oldriscv_halt(target);
+ old_or_new_riscv_poll(target);
+ return ERROR_TARGET_TIMEOUT;
+ }
+
+ int result = old_or_new_riscv_poll(target);
+ if (result != ERROR_OK) {
+ return result;
+ }
+ }
+
+ if (reg_pc->type->get(reg_pc) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
+ if (final_pc != exit_point) {
+ LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" PRIx32,
+ final_pc, exit_point);
+ return ERROR_FAIL;
+ }
+
+ // Restore Interrupts
+ LOG_DEBUG("Restoring Interrupts");
+ buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
+ reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+
+ /// Restore registers
+ uint8_t buf[8];
+ buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+ if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+
+ for (int i = 0; i < num_reg_params; i++) {
+ LOG_DEBUG("restore %s", reg_params[i].reg_name);
+ struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+ buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
+ if (r->type->set(r, buf) != ERROR_OK) {
+ return ERROR_FAIL;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/* Should run code on the target to perform CRC of
+memory. Not yet implemented.
+*/
+
+static int riscv_checksum_memory(struct target *target,
+ uint32_t address, uint32_t count,
+ uint32_t* checksum)
+{
+ *checksum = 0xFFFFFFFF;
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+}
+
+/* Should run code on the target to check whether a memory
+block holds all-ones (because this is generally called on
+NOR flash which is 1 when "blank")
+Not yet implemented.
+*/
+int riscv_blank_check_memory(struct target * target,
+ uint32_t address,
+ uint32_t count,
+ uint32_t * blank)
+{
+ *blank = 0;
+
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+}
+
+/*** OpenOCD Helper Functions ***/
+
+/* 0 means nothing happened, 1 means the hart's state changed (and thus the
+ * poll should terminate), and -1 means there was an error. */
+static int riscv_poll_hart(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ riscv_set_current_hartid(target, hartid);
+
+ LOG_DEBUG("polling hart %d, target->state=%d (TARGET_HALTED=%d)", hartid, target->state, TARGET_HALTED);
+
+ /* If OpenOCD this we're running but this hart is halted then it's time
+ * to raise an event. */
+ if (target->state != TARGET_HALTED && riscv_is_halted(target)) {
+ LOG_DEBUG(" triggered a halt");
+ r->on_halt(target);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
+{
+ LOG_DEBUG("polling all harts");
+ int triggered_hart = -1;
+ if (riscv_rtos_enabled(target)) {
+ /* Check every hart for an event. */
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ int out = riscv_poll_hart(target, i);
+ switch (out) {
+ case 0:
+ continue;
+ case 1:
+ triggered_hart = i;
+ break;
+ case -1:
+ return ERROR_FAIL;
+ }
+ }
+ if (triggered_hart == -1) {
+ LOG_DEBUG(" no harts just halted, target->state=%d", target->state);
+ return ERROR_OK;
+ }
+ LOG_DEBUG(" hart %d halted", triggered_hart);
+
+ /* If we're here then at least one hart triggered. That means
+ * we want to go and halt _every_ hart in the system, as that's
+ * the invariant we hold here. Some harts might have already
+ * halted (as we're either in single-step mode or they also
+ * triggered a breakpoint), so don't attempt to halt those
+ * harts. */
+ for (int i = 0; i < riscv_count_harts(target); ++i)
+ riscv_halt_one_hart(target, i);
+ } else {
+ if (riscv_poll_hart(target, riscv_current_hartid(target)) == 0)
+ return ERROR_OK;
+
+ triggered_hart = riscv_current_hartid(target);
+ LOG_DEBUG(" hart %d halted", triggered_hart);
+ }
+
+ target->state = TARGET_HALTED;
+ switch (riscv_halt_reason(target, triggered_hart)) {
+ case RISCV_HALT_BREAKPOINT:
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case RISCV_HALT_INTERRUPT:
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case RISCV_HALT_SINGLESTEP:
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ break;
+ }
+
+ if (riscv_rtos_enabled(target)) {
+ target->rtos->current_threadid = triggered_hart + 1;
+ target->rtos->current_thread = triggered_hart + 1;
+ }
+
+ target->state = TARGET_HALTED;
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ return ERROR_OK;
+}
+
+int riscv_openocd_halt(struct target *target)
+{
+ LOG_DEBUG("halting all harts");
+
+ int out = riscv_halt_all_harts(target);
+ if (out != ERROR_OK) {
+ LOG_ERROR("Unable to halt all harts");
+ return out;
+ }
+
+ register_cache_invalidate(target->reg_cache);
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_DBGRQ;
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ return out;
+}
+
+int riscv_openocd_resume(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints,
+ int debug_execution
+) {
+ LOG_DEBUG("resuming all harts");
+
+ if (!current) {
+ riscv_set_register(target, GDB_REGNO_PC, address);
+ }
+
+ int out = riscv_resume_all_harts(target);
+ if (out != ERROR_OK) {
+ LOG_ERROR("unable to resume all harts");
+ return out;
+ }
+
+ register_cache_invalidate(target->reg_cache);
+ target->state = TARGET_RUNNING;
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ return out;
+}
+
+int riscv_openocd_step(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints
+) {
+ LOG_DEBUG("stepping rtos hart");
+
+ if (!current) {
+ riscv_set_register(target, GDB_REGNO_PC, address);
+ }
+
+ int out = riscv_step_rtos_hart(target);
+ if (out != ERROR_OK) {
+ LOG_ERROR("unable to step rtos hart");
+ return out;
+ }
+
+ register_cache_invalidate(target->reg_cache);
+ target->state = TARGET_RUNNING;
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ return out;
+}
+
+int riscv_openocd_assert_reset(struct target *target)
+{
+ LOG_DEBUG("asserting reset for all harts");
+ int out = riscv_reset_all_harts(target);
+ if (out != ERROR_OK) {
+ LOG_ERROR("unable to reset all harts");
+ return out;
+ }
+
+ return out;
+}
+
+int riscv_openocd_deassert_reset(struct target *target)
+{
+ LOG_DEBUG("deasserting reset for all harts");
+ if (target->reset_halt)
+ riscv_halt_all_harts(target);
+ else
+ riscv_resume_all_harts(target);
+ return ERROR_OK;
+}
+
+struct target_type riscv_target =
+{
+ .name = "riscv",
+
+ .init_target = riscv_init_target,
+ .deinit_target = riscv_deinit_target,
+ .examine = riscv_examine,
+
+ /* poll current target status */
+ .poll = old_or_new_riscv_poll,
+
+ .halt = old_or_new_riscv_halt,
+ .resume = old_or_new_riscv_resume,
+ .step = old_or_new_riscv_step,
+
+ .assert_reset = old_or_new_riscv_assert_reset,
+ .deassert_reset = old_or_new_riscv_deassert_reset,
+
+ .read_memory = riscv_read_memory,
+ .write_memory = riscv_write_memory,
+
+ .blank_check_memory = riscv_blank_check_memory,
+ .checksum_memory = riscv_checksum_memory,
+
+ .get_gdb_reg_list = riscv_get_gdb_reg_list,
+
+ .add_breakpoint = riscv_add_breakpoint,
+ .remove_breakpoint = riscv_remove_breakpoint,
+
+ .add_watchpoint = riscv_add_watchpoint,
+ .remove_watchpoint = riscv_remove_watchpoint,
+
+ .arch_state = riscv_arch_state,
+
+ .run_algorithm = riscv_run_algorithm,
+};
+
+/*** RISC-V Interface ***/
+
+void riscv_info_init(riscv_info_t *r)
+{
+ memset(r, 0, sizeof(*r));
+ r->dtm_version = 1;
+ r->registers_initialized = false;
+
+ for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
+ r->xlen[h] = -1;
+ r->debug_buffer_addr[h] = -1;
+
+ for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
+ r->valid_saved_registers[h][e] = false;
+ }
+}
+
+int riscv_halt_all_harts(struct target *target)
+{
+ if (riscv_rtos_enabled(target)) {
+ for (int i = 0; i < riscv_count_harts(target); ++i)
+ riscv_halt_one_hart(target, i);
+ } else {
+ riscv_halt_one_hart(target, riscv_current_hartid(target));
+ }
+
+ return ERROR_OK;
+}
+
+int riscv_halt_one_hart(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("halting hart %d", hartid);
+ riscv_set_current_hartid(target, hartid);
+ if (riscv_is_halted(target)) {
+ LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
+ return ERROR_OK;
+ }
+
+ r->halt_current_hart(target);
+ return ERROR_OK;
+}
+
+int riscv_resume_all_harts(struct target *target)
+{
+ if (riscv_rtos_enabled(target)) {
+ for (int i = 0; i < riscv_count_harts(target); ++i)
+ riscv_resume_one_hart(target, i);
+ } else {
+ riscv_resume_one_hart(target, riscv_current_hartid(target));
+ }
+
+ riscv_invalidate_register_cache(target);
+ return ERROR_OK;
+}
+
+int riscv_resume_one_hart(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("resuming hart %d", hartid);
+ riscv_set_current_hartid(target, hartid);
+ if (!riscv_is_halted(target)) {
+ LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
+ return ERROR_OK;
+ }
+
+ r->on_resume(target);
+ r->resume_current_hart(target);
+ return ERROR_OK;
+}
+
+int riscv_reset_all_harts(struct target *target)
+{
+ if (riscv_rtos_enabled(target)) {
+ for (int i = 0; i < riscv_count_harts(target); ++i)
+ riscv_reset_one_hart(target, i);
+ } else {
+ riscv_reset_one_hart(target, riscv_current_hartid(target));
+ }
+
+ riscv_invalidate_register_cache(target);
+ return ERROR_OK;
+}
+
+int riscv_reset_one_hart(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("resetting hart %d", hartid);
+ riscv_halt_one_hart(target, hartid);
+ riscv_set_current_hartid(target, hartid);
+ r->reset_current_hart(target);
+ /* At this point the hart must be halted. On platforms that support
+ * "reset halt" exactly we expect the hart to have been halted before
+ * executing any instructions, while on older cores it'll just have
+ * halted quickly. */
+ return ERROR_OK;
+}
+
+int riscv_step_rtos_hart(struct target *target)
+{
+ RISCV_INFO(r);
+ int hartid = r->current_hartid;
+ if (riscv_rtos_enabled(target)) {
+ hartid = r->rtos_hartid;
+ if (hartid == -1) {
+ LOG_USER("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
+ hartid = 0;
+ }
+ }
+ riscv_set_current_hartid(target, hartid);
+ LOG_DEBUG("stepping hart %d", hartid);
+
+ assert(riscv_is_halted(target));
+ riscv_invalidate_register_cache(target);
+ r->on_step(target);
+ r->step_current_hart(target);
+ riscv_invalidate_register_cache(target);
+ r->on_halt(target);
+ assert(riscv_is_halted(target));
+ return ERROR_OK;
+}
+
+int riscv_xlen(const struct target *target)
+{
+ return riscv_xlen_of_hart(target, riscv_current_hartid(target));
+}
+
+int riscv_xlen_of_hart(const struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ assert(r->xlen[hartid] != -1);
+ return r->xlen[hartid];
+}
+
+bool riscv_rtos_enabled(const struct target *target)
+{
+ return target->rtos != NULL;
+}
+
+void riscv_set_current_hartid(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ int previous_hartid = riscv_current_hartid(target);
+ r->current_hartid = hartid;
+ assert(riscv_rtos_enabled(target) || target->coreid == hartid);
+ LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
+ if (riscv_rtos_enabled(target))
+ r->select_current_hart(target);
+
+ /* This might get called during init, in which case we shouldn't be
+ * setting up the register cache. */
+ if (!target_was_examined(target))
+ return;
+
+ /* Avoid invalidating the register cache all the time. */
+ if (r->registers_initialized
+ && (!riscv_rtos_enabled(target) || (previous_hartid == hartid))
+ && target->reg_cache->reg_list[GDB_REGNO_XPR0].size == (long)riscv_xlen(target)
+ && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
+ LOG_DEBUG("registers already initialized, skipping");
+ return;
+ } else
+ LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
+
+ riscv_invalidate_register_cache(target);
+}
+
+void riscv_invalidate_register_cache(struct target *target)
+{
+ RISCV_INFO(r);
+
+ /* Update the register list's widths. */
+ register_cache_invalidate(target->reg_cache);
+ for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
+ struct reg *reg = &target->reg_cache->reg_list[i];
+
+ reg->value = &r->reg_cache_values[i];
+ reg->valid = false;
+
+ switch (i) {
+ case GDB_REGNO_PRIV:
+ reg->size = 8;
+ break;
+ default:
+ reg->size = riscv_xlen(target);
+ break;
+ }
+ }
+
+ r->registers_initialized = true;
+}
+
+int riscv_current_hartid(const struct target *target)
+{
+ RISCV_INFO(r);
+ if (riscv_rtos_enabled(target))
+ return r->current_hartid;
+ else
+ return target->coreid;
+}
+
+void riscv_set_all_rtos_harts(struct target *target)
+{
+ RISCV_INFO(r);
+ r->rtos_hartid = -1;
+}
+
+void riscv_set_rtos_hartid(struct target *target, int hartid)
+{
+ LOG_DEBUG("setting RTOS hartid %d", hartid);
+ RISCV_INFO(r);
+ r->rtos_hartid = hartid;
+}
+
+int riscv_count_harts(struct target *target)
+{
+ if (target == NULL) return 1;
+ RISCV_INFO(r);
+ if (r == NULL) return 1;
+ return r->hart_count;
+}
+
+bool riscv_has_register(struct target *target, int hartid, int regid)
+{
+ return 1;
+}
+
+void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
+{
+ return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
+}
+
+void riscv_set_register_on_hart(struct target *target, int hartid, enum gdb_regno regid, uint64_t value)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("writing register %d on hart %d", regid, hartid);
+ return r->set_register(target, hartid, regid, value);
+}
+
+riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno r)
+{
+ return riscv_get_register_on_hart(target, riscv_current_hartid(target), r);
+}
+
+uint64_t riscv_get_register_on_hart(struct target *target, int hartid, enum gdb_regno regid)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("reading register %d on hart %d", regid, hartid);
+ return r->get_register(target, hartid, regid);
+}
+
+bool riscv_is_halted(struct target *target)
+{
+ RISCV_INFO(r);
+ return r->is_halted(target);
+}
+
+enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ riscv_set_current_hartid(target, hartid);
+ assert(riscv_is_halted(target));
+ return r->halt_reason(target);
+}
+
+int riscv_count_triggers(struct target *target)
+{
+ return riscv_count_triggers_of_hart(target, riscv_current_hartid(target));
+}
+
+int riscv_count_triggers_of_hart(struct target *target, int hartid)
+{
+ RISCV_INFO(r);
+ assert(hartid < riscv_count_harts(target));
+ return r->trigger_count[hartid];
+}
+
+size_t riscv_debug_buffer_size(struct target *target)
+{
+ RISCV_INFO(r);
+ return r->debug_buffer_size[riscv_current_hartid(target)];
+}
+
+riscv_addr_t riscv_debug_buffer_addr(struct target *target)
+{
+ RISCV_INFO(r);
+ riscv_addr_t out = r->debug_buffer_addr[riscv_current_hartid(target)];
+ assert(out != -1);
+ return out;
+}
+
+int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program)
+{
+ RISCV_INFO(r);
+ r->debug_buffer_enter(target, program);
+ return ERROR_OK;
+}
+
+int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program)
+{
+ RISCV_INFO(r);
+ r->debug_buffer_leave(target, program);
+ return ERROR_OK;
+}
+
+int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
+{
+ RISCV_INFO(r);
+ r->write_debug_buffer(target, index, insn);
+ return ERROR_OK;
+}
+
+riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
+{
+ RISCV_INFO(r);
+ return r->read_debug_buffer(target, index);
+}
+
+riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index)
+{
+ riscv_addr_t out = 0;
+ switch (riscv_xlen(target)) {
+ case 64:
+ out |= (uint64_t)riscv_read_debug_buffer(target, index + 1) << 32;
+ case 32:
+ out |= riscv_read_debug_buffer(target, index + 0) << 0;
+ break;
+ default:
+ LOG_ERROR("unsupported XLEN %d", riscv_xlen(target));
+ abort();
+ }
+ return out;
+}
+
+int riscv_execute_debug_buffer(struct target *target)
+{
+ RISCV_INFO(r);
+ return r->execute_debug_buffer(target);
+}
+
+void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+{
+ RISCV_INFO(r);
+ r->fill_dmi_write_u64(target, buf, a, d);
+}
+
+void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a)
+{
+ RISCV_INFO(r);
+ r->fill_dmi_read_u64(target, buf, a);
+}
+
+void riscv_fill_dmi_nop_u64(struct target *target, char *buf)
+{
+ RISCV_INFO(r);
+ r->fill_dmi_nop_u64(target, buf);
+}
+
+int riscv_dmi_write_u64_bits(struct target *target)
+{
+ RISCV_INFO(r);
+ return r->dmi_write_u64_bits(target);
+}
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
new file mode 100644
index 0000000..7b27b35
--- /dev/null
+++ b/src/target/riscv/riscv.h
@@ -0,0 +1,217 @@
+#ifndef RISCV_H
+#define RISCV_H
+
+struct riscv_program;
+
+#include <stdint.h>
+#include "opcodes.h"
+#include "gdb_regs.h"
+
+/* The register cache is staticly allocated. */
+#define RISCV_MAX_HARTS 32
+#define RISCV_MAX_REGISTERS 5000
+#define RISCV_MAX_TRIGGERS 32
+
+extern struct target_type riscv011_target;
+extern struct target_type riscv013_target;
+
+/*
+ * Definitions shared by code supporting all RISC-V versions.
+ */
+typedef uint64_t riscv_reg_t;
+typedef uint32_t riscv_insn_t;
+typedef int64_t riscv_addr_t;
+
+enum riscv_halt_reason {
+ RISCV_HALT_INTERRUPT,
+ RISCV_HALT_BREAKPOINT,
+ RISCV_HALT_SINGLESTEP,
+};
+
+typedef struct {
+ unsigned dtm_version;
+ struct command_context *cmd_ctx;
+ void *version_specific;
+
+ /* The number of harts on this system. */
+ int hart_count;
+
+ /* The hart that the RTOS thinks is currently being debugged. */
+ int rtos_hartid;
+
+ /* The hart that is currently being debugged. Note that this is
+ * different than the hartid that the RTOS is expected to use. This
+ * one will change all the time, it's more of a global argument to
+ * every function than an actual */
+ int current_hartid;
+
+ /* Enough space to store all the registers we might need to save. */
+ /* FIXME: This should probably be a bunch of register caches. */
+ uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
+ bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
+
+ /* The register cache points into here. */
+ uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
+
+ /* It's possible that each core has a different supported ISA set. */
+ int xlen[RISCV_MAX_HARTS];
+
+ /* The number of triggers per hart. */
+ int trigger_count[RISCV_MAX_HARTS];
+
+ /* The address of the debug RAM buffer. */
+ riscv_addr_t debug_buffer_addr[RISCV_MAX_HARTS];
+
+ /* The number of entries in the debug buffer. */
+ int debug_buffer_size[RISCV_MAX_HARTS];
+
+ /* This avoids invalidating the register cache too often. */
+ bool registers_initialized;
+
+ /* Helper functions that target the various RISC-V debug spec
+ * implementations. */
+ riscv_reg_t (*get_register)(struct target *, int, int);
+ void (*set_register)(struct target *, int, int, uint64_t);
+ void (*select_current_hart)(struct target *);
+ bool (*is_halted)(struct target *target);
+ void (*halt_current_hart)(struct target *);
+ void (*resume_current_hart)(struct target *target);
+ void (*step_current_hart)(struct target *target);
+ void (*on_halt)(struct target *target);
+ void (*on_resume)(struct target *target);
+ void (*on_step)(struct target *target);
+ enum riscv_halt_reason (*halt_reason)(struct target *target);
+ void (*debug_buffer_enter)(struct target *target, struct riscv_program *program);
+ void (*debug_buffer_leave)(struct target *target, struct riscv_program *program);
+ void (*write_debug_buffer)(struct target *target, int i, riscv_insn_t d);
+ riscv_insn_t (*read_debug_buffer)(struct target *target, int i);
+ int (*execute_debug_buffer)(struct target *target);
+ int (*dmi_write_u64_bits)(struct target *target);
+ void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
+ void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
+ void (*fill_dmi_nop_u64)(struct target *target, char *buf);
+ void (*reset_current_hart)(struct target *target);
+} riscv_info_t;
+
+/* Everything needs the RISC-V specific info structure, so here's a nice macro
+ * that provides that. */
+static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
+static inline riscv_info_t *riscv_info(const struct target *target)
+{ return target->arch_info; }
+#define RISCV_INFO(R) riscv_info_t *R = riscv_info(target);
+
+extern uint8_t ir_dtmcontrol[1];
+extern struct scan_field select_dtmcontrol;
+extern uint8_t ir_dbus[1];
+extern struct scan_field select_dbus;
+extern uint8_t ir_idcode[1];
+extern struct scan_field select_idcode;
+
+/*** OpenOCD Interface */
+int riscv_openocd_poll(struct target *target);
+
+int riscv_openocd_halt(struct target *target);
+
+int riscv_openocd_resume(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints,
+ int debug_execution
+);
+
+int riscv_openocd_step(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints
+);
+
+int riscv_openocd_assert_reset(struct target *target);
+int riscv_openocd_deassert_reset(struct target *target);
+
+/*** RISC-V Interface ***/
+
+/* Initializes the shared RISC-V structure. */
+void riscv_info_init(riscv_info_t *r);
+
+/* Run control, possibly for multiple harts. The _all_harts versions resume
+ * all the enabled harts, which when running in RTOS mode is all the harts on
+ * the system. */
+int riscv_halt_all_harts(struct target *target);
+int riscv_halt_one_hart(struct target *target, int hartid);
+int riscv_resume_all_harts(struct target *target);
+int riscv_resume_one_hart(struct target *target, int hartid);
+int riscv_reset_all_harts(struct target *target);
+int riscv_reset_one_hart(struct target *target, int hartid);
+
+/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
+ * then the only hart. */
+int riscv_step_rtos_hart(struct target *target);
+
+/* Returns XLEN for the given (or current) hart. */
+int riscv_xlen(const struct target *target);
+int riscv_xlen_of_hart(const struct target *target, int hartid);
+
+bool riscv_rtos_enabled(const struct target *target);
+
+/* Sets the current hart, which is the hart that will actually be used when
+ * issuing debug commands. */
+void riscv_set_current_hartid(struct target *target, int hartid);
+int riscv_current_hartid(const struct target *target);
+
+/*** Support functions for the RISC-V 'RTOS', which provides multihart support
+ * without requiring multiple targets. */
+
+/* When using the RTOS to debug, this selects the hart that is currently being
+ * debugged. This doesn't propogate to the hardware. */
+void riscv_set_all_rtos_harts(struct target *target);
+void riscv_set_rtos_hartid(struct target *target, int hartid);
+
+/* Lists the number of harts in the system, which are assumed to be
+ * concecutive and start with mhartid=0. */
+int riscv_count_harts(struct target *target);
+
+/* Returns TRUE if the target has the given register on the given hart. */
+bool riscv_has_register(struct target *target, int hartid, int regid);
+
+/* Returns the value of the given register on the given hart. 32-bit registers
+ * are zero extended to 64 bits. */
+void riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
+void riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
+riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno i);
+riscv_reg_t riscv_get_register_on_hart(struct target *target, int hid, enum gdb_regno rid);
+
+/* Checks the state of the current hart -- "is_halted" checks the actual
+ * on-device register. */
+bool riscv_is_halted(struct target *target);
+enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
+
+/* Returns the number of triggers availiable to either the current hart or to
+ * the given hart. */
+int riscv_count_triggers(struct target *target);
+int riscv_count_triggers_of_hart(struct target *target, int hartid);
+
+/* These helper functions let the generic program interface get target-specific
+ * information. */
+size_t riscv_debug_buffer_size(struct target *target);
+riscv_addr_t riscv_debug_buffer_addr(struct target *target);
+
+int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program);
+int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program);
+
+riscv_insn_t riscv_read_debug_buffer(struct target *target, int index);
+riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index);
+int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
+int riscv_write_debug_buffer_x(struct target *target, int index, riscv_addr_t data);
+int riscv_execute_debug_buffer(struct target *target);
+
+void riscv_fill_dmi_nop_u64(struct target *target, char *buf);
+void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
+void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a);
+int riscv_dmi_write_u64_bits(struct target *target);
+
+/* Invalidates the register cache. */
+void riscv_invalidate_register_cache(struct target *target);
+
+#endif
diff --git a/src/target/target.c b/src/target/target.c
index e04ecc4..eb45faf 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -105,6 +105,7 @@ extern struct target_type nds32_v3m_target;
extern struct target_type or1k_target;
extern struct target_type quark_x10xx_target;
extern struct target_type quark_d20xx_target;
+extern struct target_type riscv_target;
static struct target_type *target_types[] = {
&arm7tdmi_target,
@@ -136,6 +137,7 @@ static struct target_type *target_types[] = {
&or1k_target,
&quark_x10xx_target,
&quark_d20xx_target,
+ &riscv_target,
#if BUILD_TARGET64
&aarch64_target,
#endif
@@ -1098,7 +1100,7 @@ int target_add_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if ((target->state != TARGET_HALTED) && (breakpoint->type != BKPT_HARD)) {
- LOG_WARNING("target %s is not halted", target_name(target));
+ LOG_WARNING("target %s is not halted (add breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_breakpoint(target, breakpoint);
@@ -1108,7 +1110,7 @@ int target_add_context_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target_name(target));
+ LOG_WARNING("target %s is not halted (add context breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_context_breakpoint(target, breakpoint);
@@ -1118,7 +1120,7 @@ int target_add_hybrid_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target_name(target));
+ LOG_WARNING("target %s is not halted (add hybrid breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_hybrid_breakpoint(target, breakpoint);
@@ -1134,7 +1136,7 @@ int target_add_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target_name(target));
+ LOG_WARNING("target %s is not halted (add watchpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_watchpoint(target, watchpoint);
@@ -1148,7 +1150,7 @@ int target_hit_watchpoint(struct target *target,
struct watchpoint **hit_watchpoint)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target->cmd_name);
+ LOG_WARNING("target %s is not halted (hit watchpoint)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
@@ -1177,7 +1179,7 @@ int target_step(struct target *target,
int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target->cmd_name);
+ LOG_WARNING("target %s is not halted (gdb fileio)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->get_gdb_fileio_info(target, fileio_info);
@@ -1186,7 +1188,7 @@ int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fi
int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target->cmd_name);
+ LOG_WARNING("target %s is not halted (gdb fileio end)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
@@ -1196,7 +1198,7 @@ int target_profiling(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
{
if (target->state != TARGET_HALTED) {
- LOG_WARNING("target %s is not halted", target->cmd_name);
+ LOG_WARNING("target %s is not halted (profiling)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->profiling(target, samples, max_num_samples,
@@ -2720,6 +2722,7 @@ COMMAND_HANDLER(handle_reg_command)
struct reg *reg = NULL;
unsigned count = 0;
char *value;
+ int retval;
LOG_DEBUG("-");
@@ -2805,8 +2808,13 @@ COMMAND_HANDLER(handle_reg_command)
if ((CMD_ARGC == 2) && (strcmp(CMD_ARGV[1], "force") == 0))
reg->valid = 0;
- if (reg->valid == 0)
- reg->type->get(reg);
+ if (reg->valid == 0) {
+ retval = reg->type->get(reg);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't get register %s.", reg->name);
+ return retval;
+ }
+ }
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
free(value);
@@ -2820,7 +2828,12 @@ COMMAND_HANDLER(handle_reg_command)
return ERROR_FAIL;
str_to_buf(CMD_ARGV[1], strlen(CMD_ARGV[1]), buf, reg->size, 0);
- reg->type->set(reg, buf);
+ retval = reg->type->set(reg, buf);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't set register %s.", reg->name);
+ free (buf);
+ return retval;
+ }
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
diff --git a/src/target/target_type.h b/src/target/target_type.h
index 34e2778..0960e6d 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -53,6 +53,7 @@ struct target_type {
/* halt will log a warning, but return ERROR_OK if the target is already halted. */
int (*halt)(struct target *target);
+ /* See target.c target_resume() for documentation. */
int (*resume)(struct target *target, int current, target_addr_t address,
int handle_breakpoints, int debug_execution);
int (*step)(struct target *target, int current, target_addr_t address,