This is an automated email from Gerrit. Kristof Mulier ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/5839
-- gerrit commit 9f9cf322b2e82a2eff00f9b60c67f6a3349275dc Author: Kristof Mulier <[email protected]> Date: Mon Sep 21 11:47:41 2020 +0200 RISC-V patches: Patches for the GigaDevice RISC-V MCUs Mr. Leo Tzagkarakis from GigaDevice sent me a patch to make OpenOCD work with the GigaDevice RISC-V based microcontrollers. The patch turned didn't match checkpatch.pl standard, so Mr. Leo Tzagkarakis fixed the patch. Now it should be okay. Change-Id: Ia17e1d28649def0e9164e30c2b9163cb57a97029 Signed-off-by: Kristof Mulier <[email protected]> diff --git a/contrib/loaders/flash/gd32vf103/Makefile b/contrib/loaders/flash/gd32vf103/Makefile new file mode 100644 index 0000000..8e4d5c1 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/Makefile @@ -0,0 +1,28 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= riscv-none-embed- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +CFLAGS = -march=rv32i -mabi=ilp32 -static -nostartfiles -nostdlib -W + +all: gd32vf103.inc + +.PHONY: clean + +%.elf: %.S + $(CC) $(CFLAGS) $< -o $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.lst *.bin *.inc diff --git a/contrib/loaders/flash/gd32vf103/gd32vf103.S b/contrib/loaders/flash/gd32vf103/gd32vf103.S new file mode 100644 index 0000000..b7df2c1 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/gd32vf103.S @@ -0,0 +1,48 @@ +.text + /* Params: + * a0 - flash base (in), status (out) + * a1 - count (halfword-16bit) + * a2 - workarea start + * a3 - workarea end + * a4 - target address + * a5 - tmp + * Clobbered: + * s5 - rp + * s6 - wp, tmp + * t7 - tmp +*/ +.global _start +_start: + j wait_fifo +_exit: + ebreak +wait_fifo: + lw s6, 0(a2) /* read wp */ + beqz s6, exit /* abort if wp == 0 */ + lw s5, 4(a2) /* read rp */ + sub a5, s5, s6 + beqz a5, wait_fifo /* wait until rp != wp */ + lhu s6, 0(s5) /* copy data from RAM to flash */ + sh s6, 0(a4) + addi s5, s5, 2 /* "*target_address++ = *rp++" */ + addi a4, a4, 2 +busy: + lw s7, 0x0c(a0) /* wait until BSY flag is reset */ + andi a5, s7, 1 + bnez a5, busy + andi a5, s7, 0x14 /* check the error bits */ + bnez a5, error + bgtu a3, s5, no_wrap /* wrap rp at end of buffer */ + mv s5, a2 + add s5, s5, 8 +no_wrap: + sw s5, 4(a2) /* store rp */ + addi a1, a1, -1 /* decrement halfword count */ + beqz a1, exit /* loop if not done */ + j wait_fifo +error: + mv a0, x0 + sw a0, 4(a2) /* set rp = 0 on error */ +exit: + mv a0, s7 /* return status in a0 */ + j _exit diff --git a/contrib/loaders/flash/gd32vf103/gd32vf103.inc b/contrib/loaders/flash/gd32vf103/gd32vf103.inc new file mode 100644 index 0000000..d68b173 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/gd32vf103.inc @@ -0,0 +1,8 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x6f,0x00,0x80,0x00,0x73,0x00,0x10,0x00,0x03,0x2b,0x06,0x00,0x63,0x0c,0x0b,0x04, +0x83,0x2a,0x46,0x00,0xb3,0x87,0x6a,0x41,0xe3,0x88,0x07,0xfe,0x03,0xdb,0x0a,0x00, +0x23,0x10,0x67,0x01,0x93,0x8a,0x2a,0x00,0x13,0x07,0x27,0x00,0x83,0x2b,0xc5,0x00, +0x93,0xf7,0x1b,0x00,0xe3,0x9c,0x07,0xfe,0x93,0xf7,0x4b,0x01,0x63,0x90,0x07,0x02, +0x63,0xe6,0xda,0x00,0x93,0x0a,0x06,0x00,0x93,0x8a,0x8a,0x00,0x23,0x22,0x56,0x01, +0x93,0x85,0xf5,0xff,0x63,0x88,0x05,0x00,0x6f,0xf0,0x1f,0xfb,0x13,0x05,0x00,0x00, +0x23,0x22,0xa6,0x00,0x13,0x85,0x0b,0x00,0x6f,0xf0,0xdf,0xf9, diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index b95b003..33781dd 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -31,6 +31,7 @@ NOR_DRIVERS = \ %D%/fespi.c \ %D%/fm3.c \ %D%/fm4.c \ + %D%/gd32vf103.c \ %D%/jtagspi.c \ %D%/kinetis.c \ %D%/kinetis_ke.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index d52e072..af35aab 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -44,6 +44,7 @@ extern const struct flash_driver faux_flash; extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; extern const struct flash_driver fespi_flash; +extern const struct flash_driver gd32vf103_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; @@ -116,6 +117,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &gd32vf103_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/gd32vf103.c b/src/flash/nor/gd32vf103.c new file mode 100644 index 0000000..8ac2495 --- /dev/null +++ b/src/flash/nor/gd32vf103.c @@ -0,0 +1,1356 @@ +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/binarybuffer.h> +#include <target/algorithm.h> + +/* gd32vf103 register locations */ + +#define FLASH_REG_BASE_B0 0x40022000 +#define FLASH_REG_BASE_B1 0x40022040 + +#define FMC_WS 0x00 +#define FMC_KEY 0x04 +#define FMC_OBKEY 0x08 +#define FMC_STAT 0x0C +#define FMC_CTL 0x10 +#define FMC_ADDR 0x14 +#define FMC_OBSTAT 0x1C + +/* TODO: Check if code using these really should be hard coded to bank 0. + * There are valid cases, on dual flash devices the protection of the + * second bank is done on the bank0 reg's. */ +#define FMC_WS_B0 0x40022000 +#define FMC_KEY_B0 0x40022004 +#define FMC_OBKEY_B0 0x40022008 +#define FMC_STAT_B0 0x4002200C +#define FMC_CTL_B0 0x40022010 +#define FMC_ADDR_B0 0x40022014 +#define FMC_OBSTAT_B0 0x4002201C +#define FMC_WP_B0 0x40022020 + +/* option byte location */ + +#define FMC_OB_RDP 0x1FFFF800 + +/* FMC_CTL register bits */ + +#define FMC_CTL_PG (1 << 0) +#define FMC_CTL_PER (1 << 1) +#define FMC_CTL_MER (1 << 2) +#define FMC_CTL_OBPG (1 << 4) +#define FMC_CTL_OBER (1 << 5) +#define FMC_CTL_START (1 << 6) +#define FMC_CTL_LK (1 << 7) +#define FMC_CTL_OBWEN (1 << 9) + +/* FMC_STAT register bits */ + +#define FMC_STAT_BUSY (1 << 0) +#define FMC_STAT_PGERR (1 << 2) +#define FMC_STAT_WPERR (1 << 4) +#define FMC_STAT_ENDF (1 << 5) + +/* FMC_OBSTAT bit definitions (reading) */ + +#define FMC_OBSTAT_OBERR 0 +#define FMC_OBSTAT_SPC 1 +#define FMC_OBSTAT_WDG_SW 2 +#define FMC_OBSTAT_RST_DSLEEP 3 +#define FMC_OBSTAT_RST_STDBY 4 +#define FMC_OBSTAT_BB 5 /* dual flash bank only */ + +/* register unlock keys */ + +#define UNLOCK_KEY0 0x45670123 +#define UNLOCK_KEY1 0xCDEF89AB + +/* timeout values */ + +#define FLASH_WRITE_TIMEOUT 500 +#define FLASH_ERASE_TIMEOUT 5000 + +struct gd32vf103_options { + uint16_t RDP; + uint16_t user_options; + uint16_t user_data; + uint16_t protection[4]; +}; + +struct gd32vf103_flash_bank { + struct gd32vf103_options option_bytes; + int ppage_size; + int probed; + + bool has_dual_banks; + /* used to access dual flash bank gd32vf103 */ + uint32_t register_base; + uint16_t default_rdp; + int user_data_offset; + int option_offset; + uint32_t user_bank_size; +}; + +static int gd32vf103_mass_erase(struct flash_bank *bank); +static int get_gd32vf103_info(struct flash_bank *bank, char *buf, int buf_size); +static int gd32vf103_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count); + +/* flash bank gd32vf103 <base> <size> 0 0 <target#> + */ +FLASH_BANK_COMMAND_HANDLER(gd32vf103_flash_bank_command) +{ + struct gd32vf103_flash_bank *gd32vf103_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + gd32vf103_info = malloc(sizeof(struct gd32vf103_flash_bank)); + + bank->driver_priv = gd32vf103_info; + gd32vf103_info->probed = 0; + gd32vf103_info->has_dual_banks = false; + gd32vf103_info->register_base = FLASH_REG_BASE_B0; + gd32vf103_info->user_bank_size = bank->size; + + return ERROR_OK; +} + +static inline int gd32vf103_get_flash_reg(struct flash_bank *bank, uint32_t reg) +{ + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + return reg + gd32vf103_info->register_base; +} + +static inline int gd32vf103_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), status); +} + +static int gd32vf103_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + int retval = ERROR_OK; + + /* wait for busy to clear */ + for (;;) { + retval = gd32vf103_get_flash_status(bank, &status); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("status: 0x%" PRIx32 "", status); + if ((status & FMC_STAT_BUSY) == 0) + break; + if (timeout-- <= 0) { + LOG_ERROR("timed out waiting for flash"); + return ERROR_FAIL; + } + alive_sleep(1); + } + + if (status & FMC_STAT_WPERR) { + LOG_ERROR("gd32vf103 device protected"); + retval = ERROR_FAIL; + } + + if (status & FMC_STAT_PGERR) { + LOG_ERROR("gd32vf103 device programming failed"); + retval = ERROR_FAIL; + } + + /* Clear but report errors */ + if (status & (FMC_STAT_WPERR | FMC_STAT_PGERR)) { + /* If this operation fails, we ignore it and report the original + * retval + */ + target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), + FMC_STAT_WPERR | FMC_STAT_PGERR); + } + return retval; +} + +static int gd32vf103_check_operation_supported(struct flash_bank *bank) +{ + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + + /* if we have a dual flash bank device then + * we need to perform option byte stuff on bank0 only */ + if (gd32vf103_info->register_base != FLASH_REG_BASE_B0) { + LOG_ERROR("Option Byte Operation's must use bank0"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int gd32vf103_read_options(struct flash_bank *bank) +{ + uint32_t optiondata; + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + struct target *target = bank->target; + + gd32vf103_info = bank->driver_priv; + + /* read current option bytes */ + int retval = target_read_u32(target, FMC_OBSTAT_B0, &optiondata); + if (retval != ERROR_OK) + return retval; + + gd32vf103_info->option_bytes.user_options = (optiondata >> gd32vf103_info->option_offset >> 2) & 0xffff; + gd32vf103_info->option_bytes.user_data = (optiondata >> gd32vf103_info->user_data_offset) & 0xffff; + gd32vf103_info->option_bytes.RDP = (optiondata & (1 << FMC_OBSTAT_SPC)) ? 0xFFFF : 0x5AA5; + + if (optiondata & (1 << FMC_OBSTAT_SPC)) + LOG_INFO("Device Security Bit Set"); + + /* each bit refers to a 4bank protection */ + retval = target_read_u32(target, FMC_WP_B0, &optiondata); + if (retval != ERROR_OK) + return retval; + + gd32vf103_info->option_bytes.protection[0] = (uint16_t)optiondata; + gd32vf103_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); + gd32vf103_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); + gd32vf103_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); + + return ERROR_OK; +} + +static int gd32vf103_erase_options(struct flash_bank *bank) +{ + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + struct target *target = bank->target; + + gd32vf103_info = bank->driver_priv; + + /* read current options */ + gd32vf103_read_options(bank); + + /* unlock flash registers */ + int retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* erase option bytes */ + retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBER | FMC_CTL_OBWEN); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBER | FMC_CTL_START | FMC_CTL_OBWEN); + if (retval != ERROR_OK) + return retval; + + retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* clear readout protection and complementary option bytes + * this will also force a device unlock if set */ + gd32vf103_info->option_bytes.RDP = gd32vf103_info->default_rdp; + + return ERROR_OK; +} + +static int gd32vf103_write_options(struct flash_bank *bank) +{ + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + struct target *target = bank->target; + + gd32vf103_info = bank->driver_priv; + + /* unlock flash registers */ + int retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* program option bytes */ + retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBPG | FMC_CTL_OBWEN); + if (retval != ERROR_OK) + return retval; + + uint8_t opt_bytes[16]; + + /* SPC */ + target_buffer_set_u16(target, opt_bytes, gd32vf103_info->option_bytes.RDP); + /* USER */ + target_buffer_set_u16(target, opt_bytes + 2, gd32vf103_info->option_bytes.user_options); + /* DATA[7:0] */ + target_buffer_set_u16(target, opt_bytes + 4, gd32vf103_info->option_bytes.user_data & 0xff); + /* DATA[15:8] */ + target_buffer_set_u16(target, opt_bytes + 6, (gd32vf103_info->option_bytes.user_data >> 8) & 0xff); + /* WP[7:0] */ + target_buffer_set_u16(target, opt_bytes + 8, gd32vf103_info->option_bytes.protection[0]); + /* WP[15:8] */ + target_buffer_set_u16(target, opt_bytes + 10, gd32vf103_info->option_bytes.protection[1]); + /* WP[23:16]] */ + target_buffer_set_u16(target, opt_bytes + 12, gd32vf103_info->option_bytes.protection[2]); + /* WP[31:24] */ + target_buffer_set_u16(target, opt_bytes + 14, gd32vf103_info->option_bytes.protection[3]); + + uint32_t offset = FMC_OB_RDP - bank->base; + retval = gd32vf103_write_block(bank, opt_bytes, offset, sizeof(opt_bytes) / 2); + if (retval != ERROR_OK) { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + LOG_ERROR("working area required to erase options bytes"); + return retval; + } + + retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_LK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int gd32vf103_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + + uint32_t protection; + int i, s; + int num_bits; + int set; + + int retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + /* medium density - each bit refers to a 4bank protection + * high density - each bit refers to a 2bank protection */ + retval = target_read_u32(target, FMC_WP_B0, &protection); + if (retval != ERROR_OK) + return retval; + + /* medium density - each protection bit is for 4 * 1K pages + * high density - each protection bit is for 2 * 2K pages */ + num_bits = (bank->num_sectors / gd32vf103_info->ppage_size); + + if (gd32vf103_info->ppage_size == 2) { + /* high density flash/connectivity line protection */ + + set = 1; + + if (protection & (1 << 31)) + set = 0; + + /* bit 31 controls sector 62 - 255 protection for high density + * bit 31 controls sector 62 - 127 protection for connectivity line */ + for (s = 62; s < bank->num_sectors; s++) + bank->sectors[s].is_protected = set; + + if (bank->num_sectors > 61) + num_bits = 31; + + for (i = 0; i < num_bits; i++) { + set = 1; + + if (protection & (1 << i)) + set = 0; + + for (s = 0; s < gd32vf103_info->ppage_size; s++) + bank->sectors[(i * gd32vf103_info->ppage_size) + s].is_protected = set; + } + } else { + /* low/medium density flash protection */ + for (i = 0; i < num_bits; i++) { + set = 1; + + if (protection & (1 << i)) + set = 0; + + for (s = 0; s < gd32vf103_info->ppage_size; s++) + bank->sectors[(i * gd32vf103_info->ppage_size) + s].is_protected = set; + } + } + + return ERROR_OK; +} + +static int gd32vf103_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int i; + uint32_t optiondata; + uint32_t obstat; + + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + gd32vf103_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + target_read_u32(target, FMC_WP_B0, &optiondata); + target_read_u32(target, FMC_OBSTAT_B0, &obstat); + if ((0xFFFFFFFF != optiondata) || ((obstat & 0x2) != 0)) { + gd32vf103_erase_options(bank); + optiondata = 0xFFFFFFFF; + gd32vf103_info->option_bytes.RDP = 0x5AA5; + gd32vf103_info->option_bytes.protection[0] = (uint16_t)optiondata; + gd32vf103_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); + gd32vf103_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); + gd32vf103_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); + + gd32vf103_write_options(bank); + LOG_INFO("Unlock flash Sucess !!! Pls Reset Platfrom !!\n"); + return ERROR_FAIL; + } + if ((first == 0) && (last == (bank->num_sectors - 1))) + return gd32vf103_mass_erase(bank); + + /* unlock flash registers */ + int retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + for (i = first; i <= last; i++) { + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_ADDR), + bank->base + bank->sectors[i].offset); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, + gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PER | FMC_CTL_START); + if (retval != ERROR_OK) + return retval; + + retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + bank->sectors[i].is_erased = 1; + } + + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int gd32vf103_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + struct target *target = bank->target; + uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; + int i, reg, bit; + int status; + uint32_t protection; + + gd32vf103_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + if ((first % gd32vf103_info->ppage_size) != 0) { + LOG_WARNING("aligned start protect sector to a %d sector boundary", + gd32vf103_info->ppage_size); + first = first - (first % gd32vf103_info->ppage_size); + } + if (((last + 1) % gd32vf103_info->ppage_size) != 0) { + LOG_WARNING("aligned end protect sector to a %d sector boundary", + gd32vf103_info->ppage_size); + last++; + last = last - (last % gd32vf103_info->ppage_size); + last--; + } + + /* medium density - each bit refers to a 4bank protection + * high density - each bit refers to a 2bank protection */ + retval = target_read_u32(target, FMC_WP_B0, &protection); + if (retval != ERROR_OK) + return retval; + + prot_reg[0] = (uint16_t)protection; + prot_reg[1] = (uint16_t)(protection >> 8); + prot_reg[2] = (uint16_t)(protection >> 16); + prot_reg[3] = (uint16_t)(protection >> 24); + + if (gd32vf103_info->ppage_size == 2) { + /* high density flash */ + + /* bit 7 controls sector 62 - 255 protection */ + if (last > 61) { + if (set) + prot_reg[3] &= ~(1 << 7); + else + prot_reg[3] |= (1 << 7); + } + + if (first > 61) + first = 62; + if (last > 61) + last = 61; + + for (i = first; i <= last; i++) { + reg = (i / gd32vf103_info->ppage_size) / 8; + bit = (i / gd32vf103_info->ppage_size) - (reg * 8); + + if (set) + prot_reg[reg] &= ~(1 << bit); + else + prot_reg[reg] |= (1 << bit); + } + } else { + /* medium density flash */ + for (i = first; i <= last; i++) { + reg = (i / gd32vf103_info->ppage_size) / 8; + bit = (i / gd32vf103_info->ppage_size) - (reg * 8); + + if (set) + prot_reg[reg] &= ~(1 << bit); + else + prot_reg[reg] |= (1 << bit); + } + } + + status = gd32vf103_erase_options(bank); + if (status != ERROR_OK) + return status; + + gd32vf103_info->option_bytes.protection[0] = prot_reg[0]; + gd32vf103_info->option_bytes.protection[1] = prot_reg[1]; + gd32vf103_info->option_bytes.protection[2] = prot_reg[2]; + gd32vf103_info->option_bytes.protection[3] = prot_reg[3]; + + return gd32vf103_write_options(bank); +} + +static int gd32vf103_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *write_algorithm; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + int retval = ERROR_OK; + + static const uint8_t gd32vf103_flash_write_code[] = { +#include "../../../contrib/loaders/flash/gd32vf103/gd32vf103.inc" + }; + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(gd32vf103_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(gd32vf103_flash_write_code), gd32vf103_flash_write_code); + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + return retval; + } + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */ + if (buffer_size <= 256) { + /* we already allocated the writing code, but failed to get a + * buffer, free the algorithm */ + target_free_working_area(target, write_algorithm); + + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + } + + init_reg_param(®_params[0], "a0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ + init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (halfword-16bit) */ + init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */ + init_reg_param(®_params[3], "a3", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[4], "a4", 32, PARAM_IN_OUT); /* target address */ + + + uint32_t wp_addr = source->address; + uint32_t rp_addr = source->address + 4; + uint32_t fifo_start_addr = source->address + 8; + uint32_t fifo_end_addr = source->address + source->size; + + uint32_t wp = fifo_start_addr; + uint32_t rp = fifo_start_addr; + + /* (2:block size) */ + uint32_t thisrun_bytes = fifo_end_addr-fifo_start_addr-2; + + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + while (count > 0) { + retval = target_read_u32(target, rp_addr, &rp); + if (retval != ERROR_OK) { + LOG_ERROR("failed to get read pointer"); + break; + } + + if (wp != rp) { + LOG_ERROR("Failed to write flash ;; rp = 0x%x ;;; wp = 0x%x", rp, wp); + break; + } + wp = fifo_start_addr; + rp = fifo_start_addr; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + break; + /* Limit to the amount of data we actually want to write */ + if (thisrun_bytes > count * 2) + thisrun_bytes = count * 2; + + /* Write data to fifo */ + retval = target_write_buffer(target, wp, thisrun_bytes, buffer); + if (retval != ERROR_OK) + break; + + /* Update counters and wrap write pointer */ + buffer += thisrun_bytes; + count -= thisrun_bytes / 2; + rp = fifo_start_addr; + wp = fifo_start_addr+thisrun_bytes; + + /* Store updated write pointer to target */ + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + break; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + buf_set_u32(reg_params[0].value, 0, 32, gd32vf103_info->register_base); + buf_set_u32(reg_params[1].value, 0, 32, thisrun_bytes/2); + buf_set_u32(reg_params[2].value, 0, 32, source->address); + buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[4].value, 0, 32, address); + + retval = target_run_algorithm(target, 0, NULL, 5, reg_params, + write_algorithm->address, write_algorithm->address+4, + 10000, NULL); + + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", + write_algorithm->address, retval); + return retval; + } + address += thisrun_bytes; + } + + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_ERROR("flash write failed at address 0x%"PRIx32, + buf_get_u32(reg_params[4].value, 0, 32)); + + if (buf_get_u32(reg_params[0].value, 0, 32) & FMC_STAT_PGERR) { + LOG_ERROR("flash memory not erased before writing"); + /* Clear but report errors */ + target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), FMC_STAT_PGERR); + } + + if (buf_get_u32(reg_params[0].value, 0, 32) & FMC_STAT_WPERR) { + LOG_ERROR("flash memory write protected"); + /* Clear but report errors */ + target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), FMC_STAT_WPERR); + } + } + + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return retval; +} + +static int gd32vf103_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint8_t *new_buffer = NULL; + + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* If there's an odd number of bytes, the data has to be padded. Duplicate + * the buffer and use the normal code path with a single block write since + * it's probably cheaper than to special case the last odd write using + * discrete accesses. */ + if (count & 1) { + new_buffer = malloc(count + 1); + if (new_buffer == NULL) { + LOG_ERROR("odd number of bytes to write and no memory for padding buffer"); + return ERROR_FAIL; + } + LOG_INFO("odd number of bytes to write, padding with 0xff"); + buffer = memcpy(new_buffer, buffer, count); + new_buffer[count++] = 0xff; + } + + uint32_t words_remaining = count / 2; + int retval, retval2; + + /* unlock flash registers */ + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + goto cleanup; + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + goto cleanup; + + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PG); + if (retval != ERROR_OK) + goto cleanup; + + /* try using a block write */ + retval = gd32vf103_write_block(bank, buffer, offset, words_remaining); + + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + /* if block write failed (no sufficient working area), + * we use normal (slow) single halfword accesses */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + + while (words_remaining > 0) { + uint16_t value; + memcpy(&value, buffer, sizeof(uint16_t)); + + retval = target_write_u16(target, bank->base + offset, value); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + retval = gd32vf103_wait_status_busy(bank, 5); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + words_remaining--; + buffer += 2; + offset += 2; + } + } + +reset_pg_and_lock: + retval2 = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); + if (retval == ERROR_OK) + retval = retval2; + +cleanup: + if (new_buffer) + free(new_buffer); + + return retval; +} + +static int gd32vf103_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + + struct target *target = bank->target; + uint32_t device_id_register = 0xE0042000; + /* read GD32VF103 device id register */ + int retval = target_read_u32(target, device_id_register, device_id); + if (retval != ERROR_OK) + return retval; + + return retval; +} + +static int gd32vf103_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb) +{ + struct target *target = bank->target; + uint32_t flash_size_reg = 0x1FFFF7E0; + + int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb); + if (retval != ERROR_OK) + return retval; + + return retval; +} + +static int gd32vf103_probe(struct flash_bank *bank) +{ + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + int i; + uint16_t flash_size_in_kb; + uint16_t max_flash_size_in_kb; + uint32_t device_id; + int page_size; + uint32_t base_address = 0x08000000; + + gd32vf103_info->probed = 0; + gd32vf103_info->register_base = FLASH_REG_BASE_B0; + gd32vf103_info->user_data_offset = 10; + gd32vf103_info->option_offset = 0; + + /* default factory protection level */ + gd32vf103_info->default_rdp = 0x5AA5; + + /* read gd32vf103 device id register */ + int retval = gd32vf103_get_device_id(bank, &device_id); + if (retval != ERROR_OK) + return retval; + + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + + /* set page size, protection granularity and max flash size depending on family */ + switch (device_id & 0xfff) { + case 0x410: + case 0x418: /* connectivity line density */ + page_size = 1024; + gd32vf103_info->ppage_size = 4; + max_flash_size_in_kb = 128; + break; + default: + LOG_WARNING("Cannot identify target as a gd32vf103 family."); + return ERROR_FAIL; + } + + /* get flash size from target. */ + retval = gd32vf103_get_flash_size(bank, &flash_size_in_kb); + LOG_INFO("flash_size_in_kb = 0x%08" PRIx32 "", flash_size_in_kb); + /* failed reading flash size or flash size invalid, default to max target family */ + if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) { + LOG_WARNING("gd32vf103 flash size failed, probe inaccurate - assuming %dk flash", + max_flash_size_in_kb); + flash_size_in_kb = max_flash_size_in_kb; + } + + if (gd32vf103_info->has_dual_banks) { + /* split reported size into matching bank */ + if (bank->base != 0x08080000) { + /* bank 0 will be fixed 512k */ + flash_size_in_kb = 512; + } else { + flash_size_in_kb -= 512; + /* bank1 also uses a register offset */ + gd32vf103_info->register_base = FLASH_REG_BASE_B1; + base_address = 0x08080000; + } + } + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (gd32vf103_info->user_bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + flash_size_in_kb = gd32vf103_info->user_bank_size / 1024; + } + + LOG_INFO("flash size = %dkbytes", flash_size_in_kb); + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + /* calculate numbers of pages */ + int num_pages = flash_size_in_kb * 1024 / page_size; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->base = base_address; + bank->size = (num_pages * page_size); + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (i = 0; i < num_pages; i++) { + bank->sectors[i].offset = i * page_size; + bank->sectors[i].size = page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + gd32vf103_info->probed = 1; + + return ERROR_OK; +} + +static int gd32vf103_auto_probe(struct flash_bank *bank) +{ + struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; + if (gd32vf103_info->probed) + return ERROR_OK; + return gd32vf103_probe(bank); +} + +static int get_gd32vf103_info(struct flash_bank *bank, char *buf, int buf_size) +{ + uint32_t dbgmcu_idcode; + + /* read gd32vf103 device id register */ + int retval = gd32vf103_get_device_id(bank, &dbgmcu_idcode); + if (retval != ERROR_OK) + return retval; + + uint16_t device_id = dbgmcu_idcode & 0xfff; + uint16_t rev_id = dbgmcu_idcode >> 16; + const char *device_str; + const char *rev_str = NULL; + + switch (device_id) { + + case 0x418: + device_str = "gd32vf103 (gdm32501)"; + + switch (rev_id) { + case 0x1000: + rev_str = "A"; + break; + + case 0x1001: + rev_str = "B"; + break; + } + break; + default: + snprintf(buf, buf_size, "Cannot identify target as a GD32VF103 x\n"); + return ERROR_FAIL; + } + + if (rev_str != NULL) + snprintf(buf, buf_size, "%s - Rev: %s", device_str, rev_str); + else + snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)", device_str, rev_id); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vf103_handle_lock_command) +{ + struct target *target = NULL; + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vf103_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + if (gd32vf103_erase_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to erase options"); + return ERROR_OK; + } + + /* set readout protection */ + gd32vf103_info->option_bytes.RDP = 0; + + if (gd32vf103_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to lock device"); + return ERROR_OK; + } + + command_print(CMD_CTX, "gd32vf103 locked"); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vf103_handle_unlock_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + if (gd32vf103_erase_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to unlock device"); + return ERROR_OK; + } + + if (gd32vf103_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to lock device"); + return ERROR_OK; + } + + command_print(CMD_CTX, "gd32vf103 unlocked.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect."); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vf103_handle_options_read_command) +{ + uint32_t optionbyte; + struct target *target = NULL; + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vf103_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + retval = target_read_u32(target, FMC_OBSTAT_B0, &optionbyte); + if (retval != ERROR_OK) + return retval; + command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte); + + int user_data = optionbyte; + + if (optionbyte >> FMC_OBSTAT_OBERR & 1) + command_print(CMD_CTX, "Option Byte Complement Error"); + + if (optionbyte >> FMC_OBSTAT_SPC & 1) + command_print(CMD_CTX, "Readout Protection On"); + else + command_print(CMD_CTX, "Readout Protection Off"); + + /* user option bytes are offset depending on variant */ + optionbyte >>= gd32vf103_info->option_offset; + + if (optionbyte >> FMC_OBSTAT_WDG_SW & 1) + command_print(CMD_CTX, "Software Watchdog"); + else + command_print(CMD_CTX, "Hardware Watchdog"); + + if (optionbyte >> FMC_OBSTAT_RST_DSLEEP & 1) + command_print(CMD_CTX, "Stop: No reset generated"); + else + command_print(CMD_CTX, "Stop: Reset generated"); + + if (optionbyte >> FMC_OBSTAT_RST_STDBY & 1) + command_print(CMD_CTX, "Standby: No reset generated"); + else + command_print(CMD_CTX, "Standby: Reset generated"); + + if (gd32vf103_info->has_dual_banks) { + if (optionbyte >> FMC_OBSTAT_BB & 1) + command_print(CMD_CTX, "Boot: Bank 0"); + else + command_print(CMD_CTX, "Boot: Bank 1"); + } + + command_print(CMD_CTX, "User Option0: 0x%02" PRIx8, + (uint8_t)((user_data >> gd32vf103_info->user_data_offset) & 0xff)); + command_print(CMD_CTX, "User Option1: 0x%02" PRIx8, + (uint8_t)((user_data >> (gd32vf103_info->user_data_offset + 8)) & 0xff)); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vf103_handle_options_write_command) +{ + struct target *target = NULL; + struct gd32vf103_flash_bank *gd32vf103_info = NULL; + uint16_t optionbyte; + + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vf103_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = gd32vf103_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + retval = gd32vf103_read_options(bank); + if (ERROR_OK != retval) + return retval; + + /* start with current options */ + optionbyte = gd32vf103_info->option_bytes.user_options; + + /* skip over flash bank */ + CMD_ARGC--; + CMD_ARGV++; + + while (CMD_ARGC) { + if (strcmp("SWWDG", CMD_ARGV[0]) == 0) + optionbyte |= (1 << 0); + else if (strcmp("HWWDG", CMD_ARGV[0]) == 0) + optionbyte &= ~(1 << 0); + else if (strcmp("NORSTSTOP", CMD_ARGV[0]) == 0) + optionbyte |= (1 << 1); + else if (strcmp("RSTSTOP", CMD_ARGV[0]) == 0) + optionbyte &= ~(1 << 1); + else if (strcmp("NORSTSTNDBY", CMD_ARGV[0]) == 0) + optionbyte |= (1 << 2); + else if (strcmp("RSTSTNDBY", CMD_ARGV[0]) == 0) + optionbyte &= ~(1 << 2); + else if (gd32vf103_info->has_dual_banks) { + if (strcmp("BOOT0", CMD_ARGV[0]) == 0) + optionbyte |= (1 << 3); + else if (strcmp("BOOT1", CMD_ARGV[0]) == 0) + optionbyte &= ~(1 << 3); + else + return ERROR_COMMAND_SYNTAX_ERROR; + } else + return ERROR_COMMAND_SYNTAX_ERROR; + CMD_ARGC--; + CMD_ARGV++; + } + + if (gd32vf103_erase_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to erase options"); + return ERROR_OK; + } + + gd32vf103_info->option_bytes.user_options = optionbyte; + + if (gd32vf103_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "gd32vf103 failed to write options"); + return ERROR_OK; + } + + command_print(CMD_CTX, "gd32vf103 write options complete.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect."); + + return ERROR_OK; +} + +static int gd32vf103_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* unlock option flash registers */ + int retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* mass erase flash memory */ + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_MER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), + FMC_CTL_MER | FMC_CTL_START); + if (retval != ERROR_OK) + return retval; + + retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vf103_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = gd32vf103_mass_erase(bank); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "gd32vf103 mass erase complete"); + } else + command_print(CMD_CTX, "gd32vf103 mass erase failed"); + + return retval; +} + +static const struct command_registration gd32vf103_exec_command_handlers[] = { + { + .name = "lock", + .handler = gd32vf103_handle_lock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Lock entire flash device.", + }, + { + .name = "unlock", + .handler = gd32vf103_handle_unlock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Unlock entire protected flash device.", + }, + { + .name = "mass_erase", + .handler = gd32vf103_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + { + .name = "options_read", + .handler = gd32vf103_handle_options_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Read and display device option byte.", + }, + { + .name = "options_write", + .handler = gd32vf103_handle_options_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id ('SWWDG'|'HWWDG') " + "('RSTSTNDBY'|'NORSTSTNDBY') " + "('RSTSTOP'|'NORSTSTOP')", + .help = "Replace bits in device option byte.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration gd32vf103_command_handlers[] = { + { + .name = "gd32vf103", + .mode = COMMAND_ANY, + .help = "gd32vf103 flash command group", + .usage = "", + .chain = gd32vf103_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver gd32vf103_flash = { + .name = "gd32vf103", + .commands = gd32vf103_command_handlers, + .flash_bank_command = gd32vf103_flash_bank_command, + .erase = gd32vf103_erase, + .protect = gd32vf103_protect, + .write = gd32vf103_write, + .read = default_flash_read, + .probe = gd32vf103_probe, + .auto_probe = gd32vf103_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = gd32vf103_protect_check, + .info = get_gd32vf103_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 2f8da5b..5bc3edc 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -21,6 +21,7 @@ #include "helper/time_support.h" #include "helper/list.h" #include "riscv.h" +#include "rtos/riscv_debug.h" #include "debug_defines.h" #include "rtos/rtos.h" #include "program.h" @@ -31,7 +32,8 @@ #define DMI_PROGBUF1 (DMI_PROGBUF0 + 1) static int riscv013_on_step_or_resume(struct target *target, bool step); -static int riscv013_step_or_resume_current_hart(struct target *target, bool step); +static int riscv013_step_or_resume_current_hart(struct target *target, + bool step, bool use_hasel); static void riscv013_clear_abstract_error(struct target *target); /* Implementations of the functions in riscv_info_t. */ @@ -39,12 +41,13 @@ static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid); static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); static int riscv013_select_current_hart(struct target *target); -static int riscv013_halt_current_hart(struct target *target); -static int riscv013_resume_current_hart(struct target *target); +static int riscv013_halt_prep(struct target *target); +static int riscv013_halt_go(struct target *target); +static int riscv013_resume_go(struct target *target); static int riscv013_step_current_hart(struct target *target); static int riscv013_on_halt(struct target *target); static int riscv013_on_step(struct target *target); -static int riscv013_on_resume(struct target *target); +static int riscv013_resume_prep(struct target *target); static bool riscv013_is_halted(struct target *target); static enum riscv_halt_reason riscv013_halt_reason(struct target *target); static int riscv013_write_debug_buffer(struct target *target, unsigned index, @@ -146,12 +149,20 @@ typedef enum { typedef struct { struct list_head list; int abs_chain_position; + + /* The number of harts connected to this DM. */ + int hart_count; /* Indicates we already reset this DM, so don't need to do it again. */ bool was_reset; /* Targets that are connected to this DM. */ struct list_head target_list; /* The currently selected hartid on this DM. */ int current_hartid; + bool hasel_supported; + + /* The program buffer stores executable code. 0 is an illegal instruction, + * so we use 0 to mean the cached value is invalid. */ + uint32_t progbuf_cache[16]; } dm013_info_t; typedef struct { @@ -160,6 +171,8 @@ typedef struct { } target_list_t; typedef struct { + /* The indexed used to address this hart in its DM. */ + unsigned index; /* Number of address bits in the dbus register. */ unsigned abits; /* Number of abstract command data registers. */ @@ -229,7 +242,7 @@ static riscv013_info_t *get_info(const struct target *target) * global list of DMs. If it's not in there, then create one and initialize it * to 0. */ -static dm013_info_t *get_dm(struct target *target) +dm013_info_t *get_dm(struct target *target) { RISCV013_INFO(info); if (info->dm) @@ -247,9 +260,11 @@ static dm013_info_t *get_dm(struct target *target) } if (!dm) { + LOG_DEBUG("[%d] Allocating new DM", target->coreid); dm = calloc(1, sizeof(dm013_info_t)); dm->abs_chain_position = abs_chain_position; dm->current_hartid = -1; + dm->hart_count = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); } @@ -313,7 +328,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_DMSTATUS, DMI_DMSTATUS_ANYHALTED, "anyhalted" }, { DMI_DMSTATUS, DMI_DMSTATUS_AUTHENTICATED, "authenticated" }, { DMI_DMSTATUS, DMI_DMSTATUS_AUTHBUSY, "authbusy" }, - { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" }, + { DMI_DMSTATUS, DMI_DMSTATUS_HASRESETHALTREQ, "hasresethaltreq" }, + { DMI_DMSTATUS, DMI_DMSTATUS_CONFSTRPTRVALID, "confstrptrvalid" }, { DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" }, { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" }, @@ -323,6 +339,9 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" }, + { DMI_SBCS, DMI_SBCS_SBVERSION, "sbversion" }, + { DMI_SBCS, DMI_SBCS_SBBUSYERROR, "sbbusyerror" }, + { DMI_SBCS, DMI_SBCS_SBBUSY, "sbbusy" }, { DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" }, { DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" }, { DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" }, @@ -395,6 +414,10 @@ static void dump_field(int idle, const struct scan_field *field) static void select_dmi(struct target *target) { + if (bscan_tunnel_ir_width != 0) { + select_dmi_via_bscan(target); + return; + } jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); } @@ -402,7 +425,10 @@ 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] = { 0 }; + uint8_t out_value[4]; + + if (bscan_tunnel_ir_width != 0) + return dtmcontrol_scan_via_bscan(target, out); buf_set_u32(out_value, 0, 32, out); @@ -458,6 +484,8 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, .out_value = out, .in_value = in }; + uint8_t tunneled_dr_width; + struct scan_field tunneled_dr[4]; if (r->reset_delays_wait >= 0) { r->reset_delays_wait--; @@ -468,7 +496,6 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, } memset(in, 0, num_bytes); - memset(out, 0, num_bytes); assert(info->abits != 0); @@ -476,8 +503,59 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out); buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out); - /* Assume dbus is already selected. */ - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); + /* I wanted to place this code in a different function, but the way JTAG command + queueing works in the jtag handling functions, the scan fields either have to be + heap allocated, global/static, or else they need to stay on the stack until + the jtag_execute_queue() call. Heap or static fields in this case doesn't seem + the best fit. Declaring stack based field values in a subsidiary function call wouldn't + work. */ + if (bscan_tunnel_ir_width != 0) { + jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + + /* I wanted to use struct initialization syntax, but that would involve either + declaring the variable within this scope (which would go out of scope at runtime + before the JTAG queue gets executed, which is an error waiting to happen), or + initializing outside of the check for whether a BSCAN tunnel was active (which + would be a waste of CPU time when BSCAN tunnel is not being used. So I declared the + struct at the function's top-level, so its lifetime exceeds the point at which + the queue is executed, and initializing with assignments here. */ + memset(tunneled_dr, 0, sizeof(tunneled_dr)); + if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) { + tunneled_dr[3].num_bits = 1; + tunneled_dr[3].out_value = bscan_one; + tunneled_dr[2].num_bits = 7; + tunneled_dr_width = num_bits; + tunneled_dr[2].out_value = &tunneled_dr_width; + /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so + scanning num_bits + 1, and then will right shift the input field after executing the queues */ + + tunneled_dr[1].num_bits = num_bits+1; + tunneled_dr[1].out_value = out; + tunneled_dr[1].in_value = in; + + + tunneled_dr[0].num_bits = 3; + tunneled_dr[0].out_value = bscan_zero; + } else { + /* BSCAN_TUNNEL_NESTED_TAP */ + tunneled_dr[0].num_bits = 1; + tunneled_dr[0].out_value = bscan_one; + tunneled_dr[1].num_bits = 7; + tunneled_dr_width = num_bits; + tunneled_dr[1].out_value = &tunneled_dr_width; + /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so + scanning num_bits + 1, and then will right shift the input field after executing the queues */ + tunneled_dr[2].num_bits = num_bits+1; + tunneled_dr[2].out_value = out; + tunneled_dr[2].in_value = in; + tunneled_dr[3].num_bits = 3; + tunneled_dr[3].out_value = bscan_zero; + } + jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE); + } else { + /* Assume dbus is already selected. */ + jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); + } int idle_count = info->dmi_busy_delay; if (exec) @@ -492,6 +570,11 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, return DMI_STATUS_FAILED; } + if (bscan_tunnel_ir_width != 0) { + /* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */ + buffer_shr(in, num_bytes, 1); + } + if (data_in) *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); @@ -507,7 +590,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, * caller whether DMI was ever busy during this call. */ static int dmi_op_timeout(struct target *target, uint32_t *data_in, bool *dmi_busy_encountered, int dmi_op, uint32_t address, - uint32_t data_out, int timeout_sec, bool exec) + uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) { select_dmi(target); @@ -558,34 +641,36 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, return ERROR_FAIL; } - /* This second loop ensures the request succeeded, and gets back data. - * Note that NOP can result in a 'busy' result as well, but that would be - * noticed on the next DMI access we do. */ - while (1) { - status = dmi_scan(target, &address_in, data_in, 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 %s (NOP) at 0x%x, status=%d", op_name, address, - status); - return ERROR_FAIL; + if (ensure_success) { + /* This second loop ensures the request succeeded, and gets back data. + * Note that NOP can result in a 'busy' result as well, but that would be + * noticed on the next DMI access we do. */ + while (1) { + status = dmi_scan(target, &address_in, data_in, 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 %s (NOP) at 0x%x, status=%d", op_name, address, + status); + return ERROR_FAIL; + } + if (time(NULL) - start > timeout_sec) + return ERROR_TIMEOUT_REACHED; } - if (time(NULL) - start > timeout_sec) - return ERROR_TIMEOUT_REACHED; - } - if (status != DMI_STATUS_SUCCESS) { - if (status == DMI_STATUS_FAILED || !data_in) { - LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address, - status); - } else { - LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d", - op_name, address, *data_in, status); + if (status != DMI_STATUS_SUCCESS) { + if (status == DMI_STATUS_FAILED || !data_in) { + LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address, + status); + } else { + LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d", + op_name, address, *data_in, status); + } + return ERROR_FAIL; } - return ERROR_FAIL; } return ERROR_OK; @@ -593,10 +678,10 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, static int dmi_op(struct target *target, uint32_t *data_in, bool *dmi_busy_encountered, int dmi_op, uint32_t address, - uint32_t data_out, bool exec) + uint32_t data_out, bool exec, bool ensure_success) { int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op, - address, data_out, riscv_command_timeout_sec, exec); + address, data_out, riscv_command_timeout_sec, exec, ensure_success); if (result == ERROR_TIMEOUT_REACHED) { LOG_ERROR("DMI operation didn't complete in %d seconds. The target is " "either really slow or broken. You could increase the " @@ -609,29 +694,30 @@ static int dmi_op(struct target *target, uint32_t *data_in, static int dmi_read(struct target *target, uint32_t *value, uint32_t address) { - return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false); + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false, true); } static int dmi_read_exec(struct target *target, uint32_t *value, uint32_t address) { - return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true); + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true, true); } static int dmi_write(struct target *target, uint32_t address, uint32_t value) { - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false, true); } -static int dmi_write_exec(struct target *target, uint32_t address, uint32_t value) +static int dmi_write_exec(struct target *target, uint32_t address, + uint32_t value, bool ensure_success) { - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success); } int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, bool authenticated, unsigned timeout_sec) { int result = dmi_op_timeout(target, dmstatus, NULL, DMI_OP_READ, - DMI_DMSTATUS, 0, timeout_sec, false); + DMI_DMSTATUS, 0, timeout_sec, false, true); if (result != ERROR_OK) return result; if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { @@ -663,11 +749,13 @@ uint32_t abstract_register_size(unsigned width) { switch (width) { case 32: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 2); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 2); case 64: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 3); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 3); + break; case 128: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 4); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 4); + break; default: LOG_ERROR("Unsupported register width: %d", width); return 0; @@ -720,7 +808,7 @@ static int execute_abstract_command(struct target *target, uint32_t command) LOG_DEBUG("command=0x%x; access register, size=%d, postexec=%d, " "transfer=%d, write=%d, regno=0x%x", command, - 8 << get_field(command, AC_ACCESS_REGISTER_SIZE), + 8 << get_field(command, AC_ACCESS_REGISTER_AARSIZE), get_field(command, AC_ACCESS_REGISTER_POSTEXEC), get_field(command, AC_ACCESS_REGISTER_TRANSFER), get_field(command, AC_ACCESS_REGISTER_WRITE), @@ -732,17 +820,17 @@ static int execute_abstract_command(struct target *target, uint32_t command) } } - dmi_write_exec(target, DMI_COMMAND, command); + if (dmi_write_exec(target, DMI_COMMAND, command, false) != ERROR_OK) + return ERROR_FAIL; uint32_t abstractcs = 0; - wait_for_idle(target, &abstractcs); + int result = wait_for_idle(target, &abstractcs); info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); - if (info->cmderr != 0) { + if (info->cmderr != 0 || result != ERROR_OK) { LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs); /* Clear the error. */ - dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR, - info->cmderr)); + dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); return ERROR_FAIL; } @@ -757,7 +845,7 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ~0; case 64: dmi_read(target, &v, DMI_DATA0 + offset + 1); @@ -776,7 +864,7 @@ static int write_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ERROR_FAIL; case 64: dmi_write(target, DMI_DATA0 + offset + 1, value >> 32); @@ -796,10 +884,10 @@ static uint32_t access_register_command(struct target *target, uint32_t number, uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0); switch (size) { case 32: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2); + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 2); break; case 64: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3); + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3); break; default: assert(0); @@ -899,6 +987,45 @@ static int register_write_abstract(struct target *target, uint32_t number, return ERROR_OK; } +/* + * Sets the AAMSIZE field of a memory access abstract command based on + * the width (bits). + */ +static uint32_t abstract_memory_size(unsigned width) +{ + switch (width) { + case 8: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 0); + case 16: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 1); + case 32: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 2); + case 64: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 3); + case 128: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 4); + default: + LOG_ERROR("Unsupported memory width: %d", width); + return 0; + } +} + +/* + * Creates a memory access abstract command. + */ +static uint32_t access_memory_command(struct target *target, bool virtual, + unsigned width, bool postincrement, bool write) +{ + uint32_t command = set_field(0, AC_ACCESS_MEMORY_CMDTYPE, 2); + command = set_field(command, AC_ACCESS_MEMORY_AAMVIRTUAL, virtual); + command |= abstract_memory_size(width); + command = set_field(command, AC_ACCESS_MEMORY_AAMPOSTINCREMENT, + postincrement); + command = set_field(command, AC_ACCESS_MEMORY_WRITE, write); + + return command; +} + static int examine_progbuf(struct target *target) { riscv013_info_t *info = get_info(target); @@ -1141,10 +1268,6 @@ static int register_write_direct(struct target *target, unsigned number, int result = register_write_abstract(target, number, value, register_size(target, number)); - if (result == ERROR_OK && target->reg_cache) { - struct reg *reg = &target->reg_cache->reg_list[number]; - buf_set_u64(reg->value, 0, reg->size, value); - } if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 || !riscv_is_halted(target)) return result; @@ -1231,6 +1354,14 @@ static int register_read(struct target *target, uint64_t *value, uint32_t number return ERROR_OK; } +static int is_fpu_reg(uint32_t gdb_regno) +{ + return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR); +} + /** Actually read registers from the target right now. */ static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) { @@ -1256,14 +1387,16 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t /* Write program to move data into s0. */ uint64_t mstatus; - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (is_fpu_reg(number)) { if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) return ERROR_FAIL; if ((mstatus & MSTATUS_FS) == 0) if (register_write_direct(target, GDB_REGNO_MSTATUS, set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK) return ERROR_FAIL; + } + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { if (riscv_supports_extension(target, riscv_current_hartid(target), 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a @@ -1308,8 +1441,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t return ERROR_FAIL; } - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - (mstatus & MSTATUS_FS) == 0) + if (is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0) if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK) return ERROR_FAIL; @@ -1360,6 +1492,18 @@ static void deinit_target(struct target *target) info->version_specific = NULL; } +static int set_haltgroup(struct target *target, bool *supported) +{ + uint32_t write = set_field(DMI_DMCS2_HGWRITE, DMI_DMCS2_HALTGROUP, target->smp); + if (dmi_write(target, DMI_DMCS2, write) != ERROR_OK) + return ERROR_FAIL; + uint32_t read; + if (dmi_read(target, &read, DMI_DMCS2) != ERROR_OK) + return ERROR_FAIL; + *supported = get_field(read, DMI_DMCS2_HALTGROUP) == (unsigned) target->smp; + return ERROR_OK; +} + static int examine(struct target *target) { /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ @@ -1382,6 +1526,8 @@ static int examine(struct target *target) } riscv013_info_t *info = get_info(target); + /* TODO: This won't be true if there are multiple DMs. */ + info->index = target->coreid; info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); @@ -1394,7 +1540,8 @@ static int examine(struct target *target) } dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO | - DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE); + DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE | + DMI_DMCONTROL_HASEL); uint32_t dmcontrol; if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) return ERROR_FAIL; @@ -1405,6 +1552,8 @@ static int examine(struct target *target) return ERROR_FAIL; } + dm->hasel_supported = get_field(dmcontrol, DMI_DMCONTROL_HASEL); + uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, false) != ERROR_OK) return ERROR_FAIL; @@ -1467,11 +1616,43 @@ static int examine(struct target *target) r->impebreak); } + if (info->progbufsize < 4 && riscv_enable_virtual) { + LOG_ERROR("set_enable_virtual is not available on this target. It " + "requires a program buffer size of at least 4. (progbufsize=%d) " + "Use `riscv set_enable_virtual off` to continue." + , info->progbufsize); + } + /* Before doing anything else we must first enumerate the harts. */ + if (dm->hart_count < 0) { + for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { + r->current_hartid = i; + if (riscv013_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + + uint32_t s; + if (dmstatus_read(target, &s, true) != ERROR_OK) + return ERROR_FAIL; + if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) + break; + dm->hart_count = i + 1; + + if (get_field(s, DMI_DMSTATUS_ANYHAVERESET)) + dmi_write(target, DMI_DMCONTROL, + set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i)); + } + + LOG_DEBUG("Detected %d harts.", dm->hart_count); + } + + if (dm->hart_count == 0) { + LOG_ERROR("No harts found!"); + return ERROR_FAIL; + } /* Don't call any riscv_* functions until after we've counted the number of * cores and initialized registers. */ - for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { + for (int i = 0; i < dm->hart_count; ++i) { if (!riscv_rtos_enabled(target) && i != target->coreid) continue; @@ -1479,20 +1660,9 @@ static int examine(struct target *target) if (riscv013_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - uint32_t s; - if (dmstatus_read(target, &s, true) != ERROR_OK) - return ERROR_FAIL; - if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) - break; - r->hart_count = i + 1; - - if (get_field(s, DMI_DMSTATUS_ANYHAVERESET)) - dmi_write(target, DMI_DMCONTROL, - set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i)); - bool halted = riscv_is_halted(target); if (!halted) { - if (riscv013_halt_current_hart(target) != ERROR_OK) { + if (riscv013_halt_go(target) != ERROR_OK) { LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); return ERROR_FAIL; } @@ -1523,18 +1693,23 @@ static int examine(struct target *target) r->misa[i]); if (!halted) - riscv013_resume_current_hart(target); + riscv013_step_or_resume_current_hart(target, false, false); } - LOG_DEBUG("Enumerated %d harts", r->hart_count); + target_set_examined(target); - if (r->hart_count == 0) { - LOG_ERROR("No harts found!"); - return ERROR_FAIL; + if (target->smp) { + bool haltgroup_supported; + if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK) + return ERROR_FAIL; + if (haltgroup_supported) + LOG_INFO("Core %d made part of halt group %d.", target->coreid, + target->smp); + else + LOG_INFO("Core %d could not be made part of halt group %d.", + target->coreid, target->smp); } - target_set_examined(target); - /* Some regression suites rely on seeing 'Examined RISC-V core' to know * when they can connect with gdb/telnet. * We will need to update those suites if we want to change that text. */ @@ -1586,6 +1761,12 @@ int riscv013_authdata_write(struct target *target, uint32_t value) return ERROR_OK; } +static int riscv013_hart_count(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + return dm->hart_count; +} + static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -1596,11 +1777,12 @@ static int init_target(struct command_context *cmd_ctx, 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->resume_go = &riscv013_resume_go; 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->resume_prep = &riscv013_resume_prep; + generic_info->halt_prep = &riscv013_halt_prep; + generic_info->halt_go = &riscv013_halt_go; generic_info->on_step = &riscv013_on_step; generic_info->halt_reason = &riscv013_halt_reason; generic_info->read_debug_buffer = &riscv013_read_debug_buffer; @@ -1616,6 +1798,7 @@ static int init_target(struct command_context *cmd_ctx, generic_info->dmi_write = &dmi_write; generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg; generic_info->test_compliance = &riscv013_test_compliance; + generic_info->hart_count = &riscv013_hart_count; generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; @@ -1681,6 +1864,13 @@ static int assert_reset(struct target *target) target->state = TARGET_RESET; + dm013_info_t *dm = get_dm(target); + + /* The DM might have gotten reset if OpenOCD called us in some reset that + * involves SRST being toggled. So clear our cache which may be out of + * date. */ + memset(dm->progbuf_cache, 0, sizeof(dm->progbuf_cache)); + return ERROR_OK; } @@ -1761,6 +1951,30 @@ static int deassert_reset(struct target *target) /** * @par size in bytes */ +static void read_from_buf(uint64_t *value, const uint8_t *buffer, unsigned size) +{ + switch (size) { + case 1: + *value = buffer[0]; + break; + case 2: + *value = buffer[0] + | ((uint64_t) buffer[1] << 8); + break; + case 4: + *value = buffer[0] + | ((uint64_t) buffer[1] << 8) + | ((uint64_t) buffer[2] << 16) + | ((uint64_t) buffer[3] << 24); + break; + default: + assert(false); + } +} + +/** + * @par size in bytes + */ static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size) { switch (size) { @@ -1890,9 +2104,11 @@ static target_addr_t sb_read_address(struct target *target) target_addr_t address = 0; uint32_t v; if (sbasize > 32) { +#if BUILD_TARGET64 dmi_read(target, &v, DMI_SBADDRESS1); address |= v; address <<= 32; +#endif } dmi_read(target, &v, DMI_SBADDRESS0); address |= v; @@ -1909,7 +2125,11 @@ static int sb_write_address(struct target *target, target_addr_t address) if (sbasize > 64) dmi_write(target, DMI_SBADDRESS2, 0); if (sbasize > 32) +#if BUILD_TARGET64 dmi_write(target, DMI_SBADDRESS1, address >> 32); +#else + dmi_write(target, DMI_SBADDRESS1, 0); +#endif return dmi_write(target, DMI_SBADDRESS0, address); } @@ -1930,6 +2150,39 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) } } +static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_old) +{ + RISCV013_INFO(info); + + if (riscv_enable_virtual && info->progbufsize >= 4) { + /* Read DCSR */ + uint64_t dcsr; + if (register_read(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + + /* Read and save MSTATUS */ + if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + return ERROR_FAIL; + *mstatus_old = *mstatus; + + /* If we come from m-mode with mprv set, we want to keep mpp */ + if (get_field(dcsr, DCSR_PRV) < 3) { + /* MPP = PRIV */ + *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, DCSR_PRV)); + + /* MPRV = 1 */ + *mstatus = set_field(*mstatus, MSTATUS_MPRV, 1); + + /* Write MSTATUS */ + if (*mstatus != *mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, *mstatus) != ERROR_OK) + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + static int read_memory_bus_v0(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { @@ -2018,14 +2271,17 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, target_addr_t end_address = address + count * size; while (next_address < end_address) { - uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1); - sbcs |= sb_sbaccess(size); - sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1); - sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1); - dmi_write(target, DMI_SBCS, sbcs); + uint32_t sbcs_write = set_field(0, DMI_SBCS_SBREADONADDR, 1); + sbcs_write |= sb_sbaccess(size); + sbcs_write = set_field(sbcs_write, DMI_SBCS_SBAUTOINCREMENT, 1); + if (count > 1) + sbcs_write = set_field(sbcs_write, DMI_SBCS_SBREADONDATA, count > 1); + if (dmi_write(target, DMI_SBCS, sbcs_write) != ERROR_OK) + return ERROR_FAIL; /* This address write will trigger the first read. */ - sb_write_address(target, next_address); + if (sb_write_address(target, next_address) != ERROR_OK) + return ERROR_FAIL; if (info->bus_master_read_delay) { jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE); @@ -2036,34 +2292,50 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, } for (uint32_t i = (next_address - address) / size; i < count - 1; i++) { - read_memory_bus_word(target, address + i * size, size, - buffer + i * size); + if (read_memory_bus_word(target, address + i * size, size, + buffer + i * size) != ERROR_OK) + return ERROR_FAIL; } - sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0); - dmi_write(target, DMI_SBCS, sbcs); + uint32_t sbcs_read = 0; + if (count > 1) { + /* "Writes to sbcs while sbbusy is high result in undefined behavior. + * A debugger must not write to sbcs until it reads sbbusy as 0." */ + if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK) + return ERROR_FAIL; - read_memory_bus_word(target, address + (count - 1) * size, size, - buffer + (count - 1) * size); + sbcs_write = set_field(sbcs_write, DMI_SBCS_SBREADONDATA, 0); + if (dmi_write(target, DMI_SBCS, sbcs_write) != ERROR_OK) + return ERROR_FAIL; + } - if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK) - return ERROR_FAIL; + if (!get_field(sbcs_read, DMI_SBCS_SBERROR) && + !get_field(sbcs_read, DMI_SBCS_SBBUSYERROR)) { + if (read_memory_bus_word(target, address + (count - 1) * size, size, + buffer + (count - 1) * size) != ERROR_OK) + return ERROR_FAIL; - if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) { + if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK) + return ERROR_FAIL; + } + + if (get_field(sbcs_read, DMI_SBCS_SBBUSYERROR)) { /* We read while the target was busy. Slow down and try again. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); + if (dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR) != ERROR_OK) + return ERROR_FAIL; next_address = sb_read_address(target); info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; continue; } - unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); + unsigned error = get_field(sbcs_read, DMI_SBCS_SBERROR); if (error == 0) { next_address = end_address; } else { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR); + if (dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR) != ERROR_OK) + return ERROR_FAIL; return ERROR_FAIL; } } @@ -2086,6 +2358,128 @@ static int batch_run(const struct target *target, struct riscv_batch *batch) return riscv_batch_run(batch); } +/* + * Performs a memory read using memory access abstract commands. The read sizes + * supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 byte + * aamsize fields in the memory access abstract command. + */ +static int read_memory_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + int result = ERROR_OK; + + LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + memset(buffer, 0, count*size); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + if (width > 64) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_ERROR("Unsupported size: %d bits", size); + return ERROR_FAIL; + } + + /* Create the command (physical address, postincrement, read) */ + uint32_t command = access_memory_command(target, false, width, true, false); + + /* Execute the reads */ + uint8_t *p = buffer; + bool updateaddr = true; + unsigned width32 = (width + 31) / 32 * 32; + for (uint32_t c = 0; c < count; c++) { + /* Only update the addres initially and let postincrement update it */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + LOG_ERROR("Failed to execute command read_memory_abstract()."); + return result; + } + + /* Copy arg0 to buffer (rounded width up to nearest 32) */ + riscv_reg_t value = read_abstract_arg(target, 0, width32); + write_to_buf(p, value, size); + + updateaddr = false; + p += size; + } + + return result; +} + +/* + * Performs a memory write using memory access abstract commands. The write + * sizes supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 + * byte aamsize fields in the memory access abstract command. + */ +static int write_memory_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + int result = ERROR_OK; + + LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + if (width > 64) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_ERROR("Unsupported size: %d bits", width); + return ERROR_FAIL; + } + + /* Create the command (physical address, postincrement, write) */ + uint32_t command = access_memory_command(target, false, width, true, true); + + /* Execute the writes */ + const uint8_t *p = buffer; + bool updateaddr = true; + for (uint32_t c = 0; c < count; c++) { + /* Move data to arg0 */ + riscv_reg_t value = 0; + read_from_buf(&value, p, size); + result = write_abstract_arg(target, 0, value, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg0 during write_memory_abstract()."); + return result; + } + + /* Only update the addres initially and let postincrement update it */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + LOG_ERROR("Failed to execute command write_memory_abstract()."); + return result; + } + + updateaddr = false; + p += size; + } + + return result; +} + /** * Read the requested memory, taking care to execute every read exactly once, * even if cmderr=busy is encountered. @@ -2207,7 +2601,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres /* Restore the command, and execute it. * Now DMI_DATA0 contains the next value just as it would if no * error had occurred. */ - dmi_write_exec(target, DMI_COMMAND, command); + dmi_write_exec(target, DMI_COMMAND, command, true); next_read_addr += size; dmi_write(target, DMI_ABSTRACTAUTO, @@ -2291,12 +2685,83 @@ error: return result; } +/* Only need to save/restore one GPR to read a single word, and the progbuf + * program doesn't need to increment. */ +static int read_memory_progbuf_one(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer) +{ + RISCV013_INFO(info); + + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + + uint64_t s0; + + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + /* Write the program (load, increment) */ + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + switch (size) { + case 1: + riscv_program_lbr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); + break; + case 2: + riscv_program_lhr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); + break; + case 4: + riscv_program_lwr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); + break; + default: + LOG_ERROR("Unsupported size: %d", size); + return ERROR_FAIL; + } + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + /* Write address to S0, and execute buffer. */ + if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK) + return ERROR_FAIL; + uint32_t command = access_register_command(target, GDB_REGNO_S0, + riscv_xlen(target), AC_ACCESS_REGISTER_WRITE | + AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); + if (execute_abstract_command(target, command) != ERROR_OK) + return ERROR_FAIL; + + uint64_t value; + if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + write_to_buf(buffer, value, size); + + if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK) + return ERROR_FAIL; + + /* Restore MSTATUS */ + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + return ERROR_FAIL; + + return ERROR_OK; +} + /** * Read the requested memory, silently handling memory access errors. */ static int read_memory_progbuf(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { + RISCV013_INFO(info); + int result = ERROR_OK; LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, @@ -2306,6 +2771,17 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, memset(buffer, 0, count*size); + if (execute_fence(target) != ERROR_OK) + return ERROR_FAIL; + + if (count == 1) + return read_memory_progbuf_one(target, address, size, buffer); + + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + /* s0 holds the next address to write to * s1 holds the next data value to write */ @@ -2315,12 +2791,12 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; - if (execute_fence(target) != ERROR_OK) - return ERROR_FAIL; - /* Write the program (load, increment) */ struct riscv_program program; riscv_program_init(&program, target); + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + switch (size) { case 1: riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); @@ -2335,11 +2811,14 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, LOG_ERROR("Unsupported size: %d", size); return ERROR_FAIL; } + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); if (riscv_program_ebreak(&program) != ERROR_OK) return ERROR_FAIL; - riscv_program_write(&program); + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; result = read_memory_progbuf_inner(target, address, size, count, buffer); @@ -2371,6 +2850,12 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, riscv_set_register(target, GDB_REGNO_S0, s0); riscv_set_register(target, GDB_REGNO_S1, s1); + + /* Restore MSTATUS */ + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + return ERROR_FAIL; + return result; } @@ -2395,8 +2880,7 @@ static int read_memory(struct target *target, target_addr_t address, if (info->progbufsize >= 2) return read_memory_progbuf(target, address, size, count, buffer); - LOG_ERROR("Don't know how to read memory on this target."); - return ERROR_FAIL; + return read_memory_abstract(target, address, size, count, buffer); } static int write_memory_bus_v0(struct target *target, target_addr_t address, @@ -2414,6 +2898,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, /* B.8 Writing Memory, single write check if we write in one go */ if (count == 1) { /* count is in bytes here */ + /* TODO: Test with read_from_buf(&value, t_buffer, size) */ /* check the size */ switch (size) { case 1: @@ -2458,6 +2943,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, t_addr = address + offset; t_buffer = buffer + offset; + /* TODO: Test with read_from_buf(&value, t_buffer, size) */ switch (size) { case 1: value = t_buffer[0]; @@ -2498,24 +2984,39 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, target_addr_t next_address = address; target_addr_t end_address = address + count * size; + int result; + sb_write_address(target, next_address); while (next_address < end_address) { + LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR, + next_address); + + struct riscv_batch *batch = riscv_batch_alloc( + target, + 32, + info->dmi_busy_delay + info->bus_master_write_delay); + for (uint32_t i = (next_address - address) / size; i < count; i++) { const uint8_t *p = buffer + i * size; + + if (riscv_batch_available_scans(batch) < (size + 3) / 4) + break; + if (size > 12) - dmi_write(target, DMI_SBDATA3, + riscv_batch_add_dmi_write(batch, DMI_SBDATA3, ((uint32_t) p[12]) | (((uint32_t) p[13]) << 8) | (((uint32_t) p[14]) << 16) | (((uint32_t) p[15]) << 24)); + if (size > 8) - dmi_write(target, DMI_SBDATA2, + riscv_batch_add_dmi_write(batch, DMI_SBDATA2, ((uint32_t) p[8]) | (((uint32_t) p[9]) << 8) | (((uint32_t) p[10]) << 16) | (((uint32_t) p[11]) << 24)); if (size > 4) - dmi_write(target, DMI_SBDATA1, + riscv_batch_add_dmi_write(batch, DMI_SBDATA1, ((uint32_t) p[4]) | (((uint32_t) p[5]) << 8) | (((uint32_t) p[6]) << 16) | @@ -2527,34 +3028,53 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, } if (size > 1) value |= ((uint32_t) p[1]) << 8; - dmi_write(target, DMI_SBDATA0, value); + riscv_batch_add_dmi_write(batch, DMI_SBDATA0, value); log_memory_access(address + i * size, value, size, false); - - if (info->bus_master_write_delay) { - jtag_add_runtest(info->bus_master_write_delay, TAP_IDLE); - if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Failed to scan idle sequence"); - return ERROR_FAIL; - } - } + next_address += size; } - if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK) + result = batch_run(target, batch); + riscv_batch_free(batch); + if (result != ERROR_OK) + return result; + + bool dmi_busy_encountered; + if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, + DMI_SBCS, 0, false, false) != ERROR_OK) return ERROR_FAIL; - if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) { + time_t start = time(NULL); + while (get_field(sbcs, DMI_SBCS_SBBUSY)) { + if (time(NULL) - start > riscv_command_timeout_sec) { + LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec, sbcs); + return ERROR_FAIL; + } + + if (dmi_read(target, &sbcs, DMI_SBCS) != ERROR_OK) + return ERROR_FAIL; + } + + if (get_field(sbcs, DMI_SBCS_SBBUSYERROR) || dmi_busy_encountered) { /* We wrote while the target was busy. Slow down and try again. */ dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); - next_address = sb_read_address(target); info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; + + next_address = sb_read_address(target); + if (next_address < address) { + /* This should never happen, probably buggy hardware. */ + LOG_DEBUG("unexpected system bus address 0x%" TARGET_PRIxADDR, + next_address); + return ERROR_FAIL; + } + continue; } unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); - if (error == 0) { - next_address = end_address; - } else { + if (error != 0) { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR); @@ -2574,6 +3094,11 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, select_dmi(target); + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + /* s0 holds the next address to write to * s1 holds the next data value to write */ @@ -2588,6 +3113,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, /* Write the program (store, increment) */ struct riscv_program program; riscv_program_init(&program, target); + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); switch (size) { case 1: @@ -2605,6 +3132,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, goto error; } + if (riscv_enable_virtual && info->progbufsize >= 4 && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); result = riscv_program_ebreak(&program); @@ -2631,6 +3160,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, unsigned offset = size*i; const uint8_t *t_buffer = buffer + offset; + /* TODO: Test with read_from_buf(&value, t_buffer, size)*/ uint32_t value; switch (size) { case 1: @@ -2704,7 +3234,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, uint32_t abstractcs; bool dmi_busy_encountered; if (dmi_op(target, &abstractcs, &dmi_busy_encountered, DMI_OP_READ, - DMI_ABSTRACTCS, 0, false) != ERROR_OK) + DMI_ABSTRACTCS, 0, false, true) != ERROR_OK) goto error; while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK) @@ -2741,6 +3271,11 @@ error: if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) return ERROR_FAIL; + /* Restore MSTATUS */ + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + return ERROR_FAIL; + if (execute_fence(target) != ERROR_OK) return ERROR_FAIL; @@ -2768,8 +3303,7 @@ static int write_memory(struct target *target, target_addr_t address, if (info->progbufsize >= 2) return write_memory_progbuf(target, address, size, count, buffer); - LOG_ERROR("Don't know how to write memory on this target."); - return ERROR_FAIL; + return write_memory_abstract(target, address, size, count, buffer); } static int arch_state(struct target *target) @@ -2785,8 +3319,8 @@ struct target_type riscv013_target = { .examine = examine, .poll = &riscv_openocd_poll, - .halt = &riscv_openocd_halt, - .resume = &riscv_openocd_resume, + .halt = &riscv_halt, + .resume = &riscv_resume, .step = &riscv_openocd_step, .assert_reset = assert_reset, @@ -2802,14 +3336,15 @@ struct target_type riscv013_target = { static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid) { - LOG_DEBUG("reading register %s on hart %d", gdb_regno_name(rid), hid); + LOG_DEBUG("[%d] reading register %s on hart %d", target->coreid, + gdb_regno_name(rid), hid); riscv_set_current_hartid(target, hid); int result = ERROR_OK; if (rid == GDB_REGNO_PC) { result = register_read(target, value, GDB_REGNO_DPC); - LOG_DEBUG("read PC from DPC: 0x%" PRIx64, *value); + LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value); } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; result = register_read(target, &dcsr, GDB_REGNO_DCSR); @@ -2825,19 +3360,19 @@ static int riscv013_get_register(struct target *target, static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value) { - LOG_DEBUG("writing 0x%" PRIx64 " to register %s on hart %d", value, - gdb_regno_name(rid), hid); + LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s on hart %d", + target->coreid, value, gdb_regno_name(rid), hid); riscv_set_current_hartid(target, hid); if (rid <= GDB_REGNO_XPR31) { return register_write_direct(target, rid, value); } else if (rid == GDB_REGNO_PC) { - LOG_DEBUG("writing PC to DPC: 0x%" PRIx64, value); + LOG_DEBUG("[%d] writing PC to DPC: 0x%" PRIx64, target->coreid, 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%016" PRIx64, actual_value); + LOG_DEBUG("[%d] actual DPC written: 0x%016" PRIx64, target->coreid, actual_value); if (value != actual_value) { LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back " "value (0x%" PRIx64 ")", value, actual_value); @@ -2873,18 +3408,79 @@ static int riscv013_select_current_hart(struct target *target) return result; } -static int riscv013_halt_current_hart(struct target *target) +/* Select all harts that were prepped and that are selectable, clearing the + * prepped flag on the harts that actually were selected. */ +static int select_prepped_harts(struct target *target, bool *use_hasel) { + dm013_info_t *dm = get_dm(target); + if (!dm->hasel_supported) { + RISCV_INFO(r); + r->prepped = false; + *use_hasel = false; + return ERROR_OK; + } + + assert(dm->hart_count); + unsigned hawindow_count = (dm->hart_count + 31) / 32; + uint32_t hawindow[hawindow_count]; + + memset(hawindow, 0, sizeof(uint32_t) * hawindow_count); + + target_list_t *entry; + unsigned total_selected = 0; + list_for_each_entry(entry, &dm->target_list, list) { + struct target *t = entry->target; + riscv_info_t *r = riscv_info(t); + riscv013_info_t *info = get_info(t); + unsigned index = info->index; + LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped); + r->selected = r->prepped; + if (r->prepped) { + hawindow[index / 32] |= 1 << (index % 32); + r->prepped = false; + total_selected++; + } + index++; + } + + /* Don't use hasel if we only need to talk to one hart. */ + if (total_selected <= 1) { + *use_hasel = false; + return ERROR_OK; + } + + for (unsigned i = 0; i < hawindow_count; i++) { + if (dmi_write(target, DMI_HAWINDOWSEL, i) != ERROR_OK) + return ERROR_FAIL; + if (dmi_write(target, DMI_HAWINDOW, hawindow[i]) != ERROR_OK) + return ERROR_FAIL; + } + + *use_hasel = true; + return ERROR_OK; +} + +static int riscv013_halt_prep(struct target *target) +{ + return ERROR_OK; +} + +static int riscv013_halt_go(struct target *target) +{ + bool use_hasel = false; + if (!riscv_rtos_enabled(target)) { + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; + } + RISCV_INFO(r); LOG_DEBUG("halting hart %d", r->current_hartid); - if (riscv_is_halted(target)) - LOG_ERROR("Hart %d is already halted!", r->current_hartid); /* Issue the halt command, and then wait for the current hart to halt. */ - uint32_t dmcontrol; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1); + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_HALTREQ; + if (use_hasel) + dmcontrol |= DMI_DMCONTROL_HASEL; + dmcontrol = set_hartsel(dmcontrol, r->current_hartid); dmi_write(target, DMI_DMCONTROL, dmcontrol); for (size_t i = 0; i < 256; ++i) if (riscv_is_halted(target)) @@ -2906,20 +3502,38 @@ static int riscv013_halt_current_hart(struct target *target) dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0); dmi_write(target, DMI_DMCONTROL, dmcontrol); + if (use_hasel) { + target_list_t *entry; + dm013_info_t *dm = get_dm(target); + list_for_each_entry(entry, &dm->target_list, list) { + struct target *t = entry->target; + t->state = TARGET_HALTED; + if (t->debug_reason == DBG_REASON_NOTHALTED) + t->debug_reason = DBG_REASON_DBGRQ; + } + } + /* The "else" case is handled in halt_go(). */ + return ERROR_OK; } -static int riscv013_resume_current_hart(struct target *target) +static int riscv013_resume_go(struct target *target) { - return riscv013_step_or_resume_current_hart(target, false); + bool use_hasel = false; + if (!riscv_rtos_enabled(target)) { + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; + } + + return riscv013_step_or_resume_current_hart(target, false, use_hasel); } static int riscv013_step_current_hart(struct target *target) { - return riscv013_step_or_resume_current_hart(target, true); + return riscv013_step_or_resume_current_hart(target, true, false); } -static int riscv013_on_resume(struct target *target) +static int riscv013_resume_prep(struct target *target) { return riscv013_on_step_or_resume(target, false); } @@ -2936,6 +3550,12 @@ static int riscv013_on_halt(struct target *target) static bool riscv013_is_halted(struct target *target) { + int hartid = riscv_current_hartid(target); + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE; + dmcontrol = set_hartsel(dmcontrol, hartid); + dmcontrol |= DMI_DMCONTROL_SETRESETHALTREQ; + dmi_write(target, DMI_DMCONTROL, dmcontrol); + uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return false; @@ -2944,10 +3564,11 @@ static bool riscv013_is_halted(struct target *target) if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target)); if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) { - int hartid = riscv_current_hartid(target); + hartid = riscv_current_hartid(target); LOG_INFO("Hart %d unexpectedly reset!", hartid); + LOG_INFO("Note: Hart is halted due to the halt-on-reset bit is set,please continue your program by appropriate debugger commands or operations!!"); /* TODO: Can we make this more obvious to eg. a gdb user? */ - uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | + dmcontrol = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET; dmcontrol = set_hartsel(dmcontrol, hartid); /* If we had been halted when we reset, request another halt. If we @@ -2993,7 +3614,15 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) { - return dmi_write(target, DMI_PROGBUF0 + index, data); + dm013_info_t *dm = get_dm(target); + if (dm->progbuf_cache[index] != data) { + if (dmi_write(target, DMI_PROGBUF0 + index, data) != ERROR_OK) + return ERROR_FAIL; + dm->progbuf_cache[index] = data; + } else { + LOG_DEBUG("cache hit for 0x%x @%d", data, index); + } + return ERROR_OK; } riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) @@ -3006,7 +3635,7 @@ riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) int riscv013_execute_debug_buffer(struct target *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_AARSIZE, 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); @@ -3422,7 +4051,8 @@ static int riscv013_on_step_or_resume(struct target *target, bool step) return riscv_set_register(target, GDB_REGNO_DCSR, dcsr); } -static int riscv013_step_or_resume_current_hart(struct target *target, bool step) +static int riscv013_step_or_resume_current_hart(struct target *target, + bool step, bool use_hasel) { RISCV_INFO(r); LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step); @@ -3431,13 +4061,15 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step return ERROR_FAIL; } - if (maybe_execute_fence_i(target) != ERROR_OK) - return ERROR_FAIL; - /* Issue the resume command, and then wait for the current hart to resume. */ - uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE; + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_RESUMEREQ; + if (use_hasel) + dmcontrol |= DMI_DMCONTROL_HASEL; dmcontrol = set_hartsel(dmcontrol, r->current_hartid); - dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ); + dmi_write(target, DMI_DMCONTROL, dmcontrol); + + dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HASEL, 0); + dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0); uint32_t dmstatus; for (size_t i = 0; i < 256; ++i) { @@ -3453,17 +4085,16 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step return ERROR_OK; } + dmi_write(target, DMI_DMCONTROL, dmcontrol); + LOG_ERROR("unable to resume hart %d", r->current_hartid); - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; LOG_ERROR(" dmstatus =0x%08x", dmstatus); if (step) { LOG_ERROR(" was stepping, halting"); - riscv013_halt_current_hart(target); + riscv_halt(target); return ERROR_OK; } @@ -3489,17 +4120,25 @@ void riscv013_clear_abstract_error(struct target *target) } } /* Clear the error status. */ - dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR); + dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); } +#ifdef _WIN32 +#define FILE_SEP '\\' +#else +#define FILE_SEP '/' +#endif #define COMPLIANCE_TEST(b, message) \ -{ \ +{ \ + const char *last_sep = strrchr(__FILE__, FILE_SEP); \ + const char *fname = (last_sep == NULL ? __FILE__ : last_sep + 1); \ + LOG_INFO("Executing test %d (%s:%d): %s", total_tests, fname, __LINE__, message); \ int pass = 0; \ if (b) { \ pass = 1; \ passed_tests++; \ } \ - LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \ + LOG_INFO(" %s", (pass) ? "PASSED" : "FAILED"); \ assert(pass); \ total_tests++; \ } @@ -3528,6 +4167,12 @@ int riscv013_test_compliance(struct target *target) return ERROR_FAIL; } + if (!target_was_examined(target)) { + LOG_ERROR("Cannot run compliance test, because target has not yet " + "been examined, or the examination failed.\n"); + return ERROR_FAIL; + } + int total_tests = 0; int passed_tests = 0; @@ -3560,7 +4205,7 @@ int riscv013_test_compliance(struct target *target) /* TODO: test that hamask registers exist if hasel does. */ /* haltreq */ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* This bit is not actually readable according to the spec, so nothing to check.*/ /* DMSTATUS */ @@ -3568,10 +4213,10 @@ int riscv013_test_compliance(struct target *target) /* resumereq */ /* This bit is not actually readable according to the spec, so nothing to check.*/ - COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false)); /* Halt all harts again so the test can continue.*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */ uint32_t hartinfo; @@ -3712,13 +4357,13 @@ int riscv013_test_compliance(struct target *target) But at any rate, this is not legal and should cause an error. */ COMPLIANCE_WRITE(target, DMI_COMMAND, 0xAAAAAAAA); COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \ "Illegal COMMAND should result in UNSUPPORTED"); COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); COMPLIANCE_WRITE(target, DMI_COMMAND, 0x55555555); COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \ "Illegal COMMAND should result in UNSUPPORTED"); COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); @@ -3732,10 +4377,10 @@ int riscv013_test_compliance(struct target *target) COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i), "GPR Reads should be supported."); if (riscv_xlen(target) > 32) { - /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */ + /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */ COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported."); } else { - /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */ + /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */ COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported."); } } @@ -3936,7 +4581,7 @@ int riscv013_test_compliance(struct target *target) */ /* Halt every hart for any follow-up tests*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); uint32_t failed_tests = total_tests - passed_tests; if (total_tests == passed_tests) { -- _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
