flash/stm32f1x: add support for RISC-V GigaDevice GD32VF103
The device has compatible flash macro with STM32F1 family, reuse stm32f1x driver code. Detect non-ARM target - for simplicy test target type name 'riscv' and the address has 32 bits. In case of RISC-V CPU use simple chunked write algo - async algo cannot be used as the core implemented in this device doesn't allow memory access while running. Change-Id: Ie3886fbd8573652691f91a02335812a7300689f7 Signed-off-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-on: https://review.openocd.org/c/openocd/+/6704 Tested-by: jenkins Reviewed-by: Tim Newsome <tim@sifive.com>
This commit is contained in:
parent
a26ee5344c
commit
f2b4897773
|
@ -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 -Os -g -fPIC
|
||||
|
||||
all: gd32vf103.inc
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
%.elf: %.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -S $< > $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -Obinary $< $@
|
||||
|
||||
%.inc: %.bin
|
||||
$(BIN2C) < $< > $@
|
||||
|
||||
clean:
|
||||
-rm -f *.elf *.lst *.bin *.inc
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define FLASH_BSY (1 << 0)
|
||||
#define FLASH_PGERR (1 << 2)
|
||||
#define FLASH_WRPRTERR (1 << 4)
|
||||
|
||||
void flash_write(volatile uint32_t *flash_sr,
|
||||
uint32_t hwords_count,
|
||||
uint16_t *buffer,
|
||||
uint16_t *target_addr) __attribute__((naked));
|
||||
|
||||
void flash_write(volatile uint32_t *flash_sr,
|
||||
uint32_t hwords_count,
|
||||
uint16_t *buffer,
|
||||
uint16_t *target_addr)
|
||||
{
|
||||
do {
|
||||
*target_addr = *buffer++;
|
||||
|
||||
register uint32_t sr;
|
||||
do {
|
||||
sr = *flash_sr;
|
||||
} while (sr & FLASH_BSY);
|
||||
|
||||
if (sr & (FLASH_PGERR | FLASH_WRPRTERR))
|
||||
break;
|
||||
|
||||
target_addr++;
|
||||
} while (--hwords_count);
|
||||
asm("ebreak");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/* Autogenerated with ../../../../src/helper/bin2char.sh */
|
||||
0x83,0x57,0x06,0x00,0x13,0x06,0x26,0x00,0x23,0x90,0xf6,0x00,0x83,0x27,0x05,0x00,
|
||||
0x13,0xf7,0x17,0x00,0xe3,0x1c,0x07,0xfe,0x93,0xf7,0x47,0x01,0x63,0x98,0x07,0x00,
|
||||
0x93,0x85,0xf5,0xff,0x93,0x86,0x26,0x00,0xe3,0x9c,0x05,0xfc,0x73,0x00,0x10,0x00,
|
|
@ -7293,6 +7293,7 @@ applied to all of them.
|
|||
All members of the STM32F0, STM32F1 and STM32F3 microcontroller families
|
||||
from STMicroelectronics and all members of the GD32F1x0, GD32F3x0 and GD32E23x microcontroller
|
||||
families from GigaDevice include internal flash and use ARM Cortex-M0/M3/M4/M23 cores.
|
||||
The driver also works with GD32VF103 powered by RISC-V core.
|
||||
The driver automatically recognizes a number of these chips using
|
||||
the chip identification register, and autoconfigures itself.
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "imp.h"
|
||||
#include <helper/binarybuffer.h>
|
||||
#include <target/algorithm.h>
|
||||
|
@ -129,7 +131,6 @@ struct stm32x_flash_bank {
|
|||
};
|
||||
|
||||
static int stm32x_mass_erase(struct flash_bank *bank);
|
||||
static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id);
|
||||
static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t address, uint32_t hwords_count);
|
||||
|
||||
|
@ -550,6 +551,109 @@ static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buff
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int stm32x_write_block_riscv(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t address, uint32_t hwords_count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
uint32_t buffer_size;
|
||||
struct working_area *write_algorithm;
|
||||
struct working_area *source;
|
||||
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;
|
||||
}
|
||||
|
||||
int 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 */
|
||||
buffer_size = target_get_working_area_avail(target);
|
||||
buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256));
|
||||
|
||||
retval = target_alloc_working_area(target, buffer_size, &source);
|
||||
/* Allocated size is always word aligned */
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
LOG_WARNING("no large enough working area available, can't do block memory writes");
|
||||
/* target_alloc_working_area() may return ERROR_FAIL if area backup fails:
|
||||
* convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE
|
||||
*/
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
struct reg_param reg_params[4];
|
||||
|
||||
init_reg_param(®_params[0], "a0", 32, PARAM_OUT); /* poiner to FLASH_SR */
|
||||
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_IN_OUT); /* target address */
|
||||
|
||||
while (hwords_count > 0) {
|
||||
uint32_t thisrun_hwords = source->size / 2;
|
||||
|
||||
/* Limit to the amount of data we actually want to write */
|
||||
if (thisrun_hwords > hwords_count)
|
||||
thisrun_hwords = hwords_count;
|
||||
|
||||
/* Write data to buffer */
|
||||
retval = target_write_buffer(target, source->address,
|
||||
thisrun_hwords * 2, buffer);
|
||||
if (retval != ERROR_OK)
|
||||
break;
|
||||
|
||||
buf_set_u32(reg_params[0].value, 0, 32, stm32x_get_flash_reg(bank, STM32_FLASH_SR));
|
||||
buf_set_u32(reg_params[1].value, 0, 32, thisrun_hwords);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, source->address);
|
||||
buf_set_u32(reg_params[3].value, 0, 32, address);
|
||||
|
||||
retval = target_run_algorithm(target,
|
||||
0, NULL,
|
||||
ARRAY_SIZE(reg_params), reg_params,
|
||||
write_algorithm->address,
|
||||
write_algorithm->address + sizeof(gd32vf103_flash_write_code) - 4,
|
||||
10000, NULL);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d",
|
||||
write_algorithm->address, retval);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Actually we just need to check for programming errors
|
||||
* stm32x_wait_status_busy also reports error and clears status bits
|
||||
*/
|
||||
retval = stm32x_wait_status_busy(bank, 5);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("flash write failed at address 0x%"PRIx32,
|
||||
buf_get_u32(reg_params[3].value, 0, 32));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update counters */
|
||||
buffer += thisrun_hwords * 2;
|
||||
address += thisrun_hwords * 2;
|
||||
hwords_count -= thisrun_hwords;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
|
||||
destroy_reg_param(®_params[i]);
|
||||
|
||||
target_free_working_area(target, source);
|
||||
target_free_working_area(target, write_algorithm);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** Writes a block to flash either using target algorithm
|
||||
* or use fallback, host controlled halfword-by-halfword access.
|
||||
* Flash controller must be unlocked before this call.
|
||||
|
@ -564,8 +668,15 @@ static int stm32x_write_block(struct flash_bank *bank,
|
|||
*/
|
||||
assert(address % 2 == 0);
|
||||
|
||||
int retval;
|
||||
struct arm *arm = target_to_arm(target);
|
||||
if (is_arm(arm)) {
|
||||
/* try using a block write - on ARM architecture or... */
|
||||
int retval = stm32x_write_block_async(bank, buffer, address, hwords_count);
|
||||
retval = stm32x_write_block_async(bank, buffer, address, hwords_count);
|
||||
} else {
|
||||
/* ... RISC-V architecture */
|
||||
retval = stm32x_write_block_riscv(bank, buffer, address, hwords_count);
|
||||
}
|
||||
|
||||
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
|
||||
/* if block write failed (no sufficient working area),
|
||||
|
@ -631,11 +742,13 @@ reset_pg_and_lock:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
uint32_t device_id_register = 0;
|
||||
struct stm32x_property_addr {
|
||||
uint32_t device_id;
|
||||
uint32_t flash_size;
|
||||
};
|
||||
|
||||
static int stm32x_get_property_addr(struct target *target, struct stm32x_property_addr *addr)
|
||||
{
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("Target not examined yet");
|
||||
return ERROR_TARGET_NOT_EXAMINED;
|
||||
|
@ -643,63 +756,61 @@ static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
|
|||
|
||||
switch (cortex_m_get_partno_safe(target)) {
|
||||
case CORTEX_M0_PARTNO: /* STM32F0x devices */
|
||||
device_id_register = 0x40015800;
|
||||
break;
|
||||
addr->device_id = 0x40015800;
|
||||
addr->flash_size = 0x1FFFF7CC;
|
||||
return ERROR_OK;
|
||||
case CORTEX_M3_PARTNO: /* STM32F1x devices */
|
||||
device_id_register = 0xE0042000;
|
||||
break;
|
||||
addr->device_id = 0xE0042000;
|
||||
addr->flash_size = 0x1FFFF7E0;
|
||||
return ERROR_OK;
|
||||
case CORTEX_M4_PARTNO: /* STM32F3x devices */
|
||||
device_id_register = 0xE0042000;
|
||||
break;
|
||||
addr->device_id = 0xE0042000;
|
||||
addr->flash_size = 0x1FFFF7CC;
|
||||
return ERROR_OK;
|
||||
case CORTEX_M23_PARTNO: /* GD32E23x devices */
|
||||
device_id_register = 0x40015800;
|
||||
break;
|
||||
addr->device_id = 0x40015800;
|
||||
addr->flash_size = 0x1FFFF7E0;
|
||||
return ERROR_OK;
|
||||
case CORTEX_M_PARTNO_INVALID:
|
||||
/* Check for GD32VF103 with RISC-V CPU */
|
||||
if (strcmp(target_type_name(target), "riscv") == 0
|
||||
&& target_address_bits(target) == 32) {
|
||||
/* There is nothing like arm common_magic in riscv_info_t
|
||||
* check text name of target and if target is 32-bit
|
||||
*/
|
||||
addr->device_id = 0xE0042000;
|
||||
addr->flash_size = 0x1FFFF7E0;
|
||||
return ERROR_OK;
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
LOG_ERROR("Cannot identify target as a stm32x");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* read stm32 device id register */
|
||||
int retval = target_read_u32(target, device_id_register, device_id);
|
||||
static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct stm32x_property_addr addr;
|
||||
|
||||
int retval = stm32x_get_property_addr(target, &addr);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
return retval;
|
||||
return target_read_u32(target, addr.device_id, device_id);
|
||||
}
|
||||
|
||||
static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
uint32_t flash_size_reg;
|
||||
struct stm32x_property_addr addr;
|
||||
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("Target not examined yet");
|
||||
return ERROR_TARGET_NOT_EXAMINED;
|
||||
}
|
||||
|
||||
switch (cortex_m_get_partno_safe(target)) {
|
||||
case CORTEX_M0_PARTNO: /* STM32F0x devices */
|
||||
flash_size_reg = 0x1FFFF7CC;
|
||||
break;
|
||||
case CORTEX_M3_PARTNO: /* STM32F1x devices */
|
||||
flash_size_reg = 0x1FFFF7E0;
|
||||
break;
|
||||
case CORTEX_M4_PARTNO: /* STM32F3x devices */
|
||||
flash_size_reg = 0x1FFFF7CC;
|
||||
break;
|
||||
case CORTEX_M23_PARTNO: /* GD32E23x devices */
|
||||
flash_size_reg = 0x1FFFF7E0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Cannot identify target as a stm32x");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb);
|
||||
int retval = stm32x_get_property_addr(target, &addr);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
return retval;
|
||||
return target_read_u16(target, addr.flash_size, flash_size_in_kb);
|
||||
}
|
||||
|
||||
static int stm32x_probe(struct flash_bank *bank)
|
||||
|
@ -790,6 +901,8 @@ static int stm32x_probe(struct flash_bank *bank)
|
|||
stm32x_info->user_data_offset = 16;
|
||||
stm32x_info->option_offset = 6;
|
||||
break;
|
||||
case 0x1906: /* gd32vf103 */
|
||||
break;
|
||||
case 0x1909: /* gd32e23x */
|
||||
stm32x_info->user_data_offset = 16;
|
||||
stm32x_info->option_offset = 6;
|
||||
|
@ -1005,6 +1118,10 @@ static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *c
|
|||
device_str = "GD32F3x0";
|
||||
break;
|
||||
|
||||
case 0x1906:
|
||||
device_str = "GD32VF103";
|
||||
break;
|
||||
|
||||
case 0x1909: /* gd32e23x */
|
||||
device_str = "GD32E23x";
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue