From 15af5d07455287ad4d956c686e804780da1f8711 Mon Sep 17 00:00:00 2001 From: ianst Date: Thu, 2 Nov 2023 09:59:05 -0700 Subject: [PATCH] xtensa flash: minimal KC705 support - dummy flash driver for KC705/ML605 - experimental updates to board .cfg file - flash erase, write support w/ 32-byte buffering (LX) - algo support manually verified (NX) Signed-off-by: ianst --- contrib/loaders/flash/xtensa/Makefile | 31 ++ contrib/loaders/flash/xtensa/xtkcml705fl.S | 71 ++++ contrib/loaders/flash/xtensa/xtkcml705fl.inc | 18 + src/flash/nor/Makefile.am | 3 +- src/flash/nor/driver.h | 1 + src/flash/nor/drivers.c | 1 + src/flash/nor/xtkcml705fl.c | 344 +++++++++++++++++++ tcl/board/xtensa-kc705_flash-ext.cfg | 43 +++ 8 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 contrib/loaders/flash/xtensa/Makefile create mode 100644 contrib/loaders/flash/xtensa/xtkcml705fl.S create mode 100644 contrib/loaders/flash/xtensa/xtkcml705fl.inc create mode 100644 src/flash/nor/xtkcml705fl.c create mode 100644 tcl/board/xtensa-kc705_flash-ext.cfg diff --git a/contrib/loaders/flash/xtensa/Makefile b/contrib/loaders/flash/xtensa/Makefile new file mode 100644 index 0000000000..99c5388be6 --- /dev/null +++ b/contrib/loaders/flash/xtensa/Makefile @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= xt- + +CC=$(CROSS_COMPILE)clang +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +AFLAGS = -c -static -nostartfiles -mlittle-endian -mtext-section-literals +CFLAGS = -c -nostdlib -nostartfiles -Os -g -fPIC + +all: xtkcml705fl.inc + +.PHONY: clean + +%.elf: %.S + $(CC) $(AFLAGS) $< -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/xtensa/xtkcml705fl.S b/contrib/loaders/flash/xtensa/xtkcml705fl.S new file mode 100644 index 0000000000..e9596cd125 --- /dev/null +++ b/contrib/loaders/flash/xtensa/xtkcml705fl.S @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Xtensa flasher for KC705/ML605 platforms + * Copyright (C) 2023 Cadence Design Systems + ***************************************************************************/ + +/* + * Parameters: + * a10 = flash base (in), status (out) + * a11 = count (halfword-16bit) + * a12 = buffer start + * a13 = buffer end / scratch register + * a14 = target address + * + * NOTE: if count does not indicate a multiple of 32 bytes, this logic + * will round up to and program the next multiple of 32 bytes. + */ + .text + + .align 4 +_flash_prog_start: + j _flash_prog + + .align 4 + .literal_position + +_flash_prog: + movi a13, 0x50 /* clear flash status register */ + s16i a13, a10, 0 + +_flash_prog_loop: + movi a13, 0xe8 /* set flash into buffer program mode */ + s16i a13, a14, 0 + movi a13, 31 /* always program chunks of N=32 bytes (write N-1) */ + s16i a13, a14, 0 + .set i, 0 + .rept 32 + l16ui a13, a12, i + s16i a13, a14, i + .set i, i+2 + .endr + movi a13, 32 + sub a11, a11, a13 /* increment half-word count */ + add a13, a13, a13 /* double a13 for incrementing buffer/target addresses */ + add a12, a12, a13 + add a14, a14, a13 + movi a13, 0xd0 /* end buffer program mode */ + s16i a13, a14, 0 +_flash_prog_status_check: + l16ui a13, a10, 0 + bbci a13, 7, _flash_prog_status_check + extui a13, a13, 0, 7 + bnez a13, _flash_prog_error + bgei a11, 1, _flash_prog_loop + +_flash_prog_done: + /* TODO: set program mode size of -1? */ + movi a13, 0x50 /* clear status register */ + s16i a13, a10, 0 + movi a13, 0xff /* restore read array (normal) mode */ + s16i a13, a10, 0 + movi a10, 0 + break 0, 0 /* Coded breakpoint at end of algo */ +_flash_prog_error: + movi a10, -4 + break 0, 0 /* Coded breakpoint at end of algo */ + +_idle_loop: + j _idle_loop + diff --git a/contrib/loaders/flash/xtensa/xtkcml705fl.inc b/contrib/loaders/flash/xtensa/xtkcml705fl.inc new file mode 100644 index 0000000000..2a81b89934 --- /dev/null +++ b/contrib/loaders/flash/xtensa/xtkcml705fl.inc @@ -0,0 +1,18 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x06,0x00,0x00,0x00,0x5c,0x0d,0xd2,0x5a,0x00,0xd2,0xa0,0xe8,0xd2,0x5e,0x00,0x1c, +0xfd,0xd2,0x5e,0x00,0xd2,0x1c,0x00,0xd2,0x5e,0x00,0xd2,0x1c,0x01,0xd2,0x5e,0x01, +0xd2,0x1c,0x02,0xd2,0x5e,0x02,0xd2,0x1c,0x03,0xd2,0x5e,0x03,0xd2,0x1c,0x04,0xd2, +0x5e,0x04,0xd2,0x1c,0x05,0xd2,0x5e,0x05,0xd2,0x1c,0x06,0xd2,0x5e,0x06,0xd2,0x1c, +0x07,0xd2,0x5e,0x07,0xd2,0x1c,0x08,0xd2,0x5e,0x08,0xd2,0x1c,0x09,0xd2,0x5e,0x09, +0xd2,0x1c,0x0a,0xd2,0x5e,0x0a,0xd2,0x1c,0x0b,0xd2,0x5e,0x0b,0xd2,0x1c,0x0c,0xd2, +0x5e,0x0c,0xd2,0x1c,0x0d,0xd2,0x5e,0x0d,0xd2,0x1c,0x0e,0xd2,0x5e,0x0e,0xd2,0x1c, +0x0f,0xd2,0x5e,0x0f,0xd2,0x1c,0x10,0xd2,0x5e,0x10,0xd2,0x1c,0x11,0xd2,0x5e,0x11, +0xd2,0x1c,0x12,0xd2,0x5e,0x12,0xd2,0x1c,0x13,0xd2,0x5e,0x13,0xd2,0x1c,0x14,0xd2, +0x5e,0x14,0xd2,0x1c,0x15,0xd2,0x5e,0x15,0xd2,0x1c,0x16,0xd2,0x5e,0x16,0xd2,0x1c, +0x17,0xd2,0x5e,0x17,0xd2,0x1c,0x18,0xd2,0x5e,0x18,0xd2,0x1c,0x19,0xd2,0x5e,0x19, +0xd2,0x1c,0x1a,0xd2,0x5e,0x1a,0xd2,0x1c,0x1b,0xd2,0x5e,0x1b,0xd2,0x1c,0x1c,0xd2, +0x5e,0x1c,0xd2,0x1c,0x1d,0xd2,0x5e,0x1d,0xd2,0x1c,0x1e,0xd2,0x5e,0x1e,0xd2,0x1c, +0x1f,0xd2,0x5e,0x1f,0x2c,0x0d,0xd0,0xbb,0xc0,0xda,0xdd,0xda,0xcc,0xda,0xee,0xd2, +0xa0,0xd0,0xd2,0x5e,0x00,0xd2,0x1a,0x00,0x77,0x6d,0xf9,0xd0,0xd0,0x64,0xdc,0x4d, +0xa6,0x1b,0x02,0x86,0xc4,0xff,0x5c,0x0d,0xd2,0x5a,0x00,0xd2,0xa0,0xff,0xd2,0x5a, +0x00,0x0c,0x0a,0x00,0x40,0x00,0x7c,0xca,0x00,0x40,0x00,0x06,0xff,0xff, diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 534a7a804e..7038bcdbfa 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -79,7 +79,8 @@ NOR_DRIVERS = \ %D%/w600.c \ %D%/xcf.c \ %D%/xmc1xxx.c \ - %D%/xmc4xxx.c + %D%/xmc4xxx.c \ + %D%/xtkcml705fl.c NORHEADERS = \ %D%/core.h \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index a63b72c8fa..dfee3537b7 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -308,5 +308,6 @@ extern const struct flash_driver w600_flash; extern const struct flash_driver xcf_flash; extern const struct flash_driver xmc1xxx_flash; extern const struct flash_driver xmc4xxx_flash; +extern const struct flash_driver xtkcml705fl_flash; #endif /* OPENOCD_FLASH_NOR_DRIVER_H */ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 3157bd3292..36841e1cc4 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -85,6 +85,7 @@ static const struct flash_driver * const flash_drivers[] = { &xmc4xxx_flash, &w600_flash, &rsl10_flash, + &xtkcml705fl_flash, NULL, }; diff --git a/src/flash/nor/xtkcml705fl.c b/src/flash/nor/xtkcml705fl.c new file mode 100644 index 0000000000..307ba4975c --- /dev/null +++ b/src/flash/nor/xtkcml705fl.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Generic Xtensa KC705 / ML605 flash driver * + * Copyright (C) 2020-2023 Cadence Design Systems, Inc. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "imp.h" +#include +#include +#include + + +#define FLASHADDR 0x90000000 /* Defines for KC705 */ +#define FLASHSIZE 0x08000000 /* 128MB */ +#define FLASHTOP (FLASHADDR + FLASHSIZE) +#define BLOCKSIZE 0x20000 /* size of regular block (bytes) */ + +#define PROGRAM_SUSPEND_CMD 0xB0 /* Program suspend command */ +#define PROGRAM_RESUME_CMD 0xD0 /* Program resume command */ + + +struct xtkcml705fl_options { + uint8_t rdp; + uint8_t user; + uint16_t data; + uint32_t protection; +}; + +struct xtkcml705fl_flash_bank { + struct xtkcml705fl_options option_bytes; + int ppage_size; + bool probed; + + bool has_dual_banks; + /* used to access dual flash bank xtkcml705fll */ + bool can_load_options; + uint32_t register_base; + uint8_t default_rdp; + int user_data_offset; + int option_offset; + uint32_t user_bank_size; +}; + + +static inline uint16_t flashread(struct flash_bank *bank, target_addr_t addr) +{ + struct target *target = bank->target; + uint16_t val; + int status = target_read_u16(target, addr, &val); + if (status != ERROR_OK) { + LOG_ERROR("Read flash 0x%lx failed: %d", addr, status); + return ERROR_FLASH_OPERATION_FAILED; + } + return val; +} + +static inline void flashwrite(struct flash_bank *bank, target_addr_t addr, uint16_t data) +{ + struct target *target = bank->target; + int status = target_write_u16(target, addr, data); + if (status != ERROR_OK) + LOG_ERROR("Write flash 0x%lx failed: %d", addr, status); +} + + +/* flash bank xtkcml705fl 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(xtkcml705fl_flash_bank_command) +{ + struct xtkcml705fl_flash_bank *xtkcml705fl_info; + + LOG_DEBUG("Flash bank starting..."); + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + xtkcml705fl_info = malloc(sizeof(struct xtkcml705fl_flash_bank)); + + bank->driver_priv = xtkcml705fl_info; + xtkcml705fl_info->probed = false; + xtkcml705fl_info->has_dual_banks = false; + xtkcml705fl_info->can_load_options = false; +// xtkcml705fl_info->register_base = FLASH_REG_BASE_B0; + xtkcml705fl_info->register_base = 0; + xtkcml705fl_info->user_bank_size = bank->size; + + /* The flash write must be aligned to a halfword boundary */ + bank->write_start_alignment = 2; + bank->write_end_alignment = 2; + + LOG_DEBUG("status: OK"); + return ERROR_OK; +} + +static int xtkcml705fl_probe(struct flash_bank *bank) +{ + struct xtkcml705fl_flash_bank *xtkcml705fl_info = bank->driver_priv; + uint16_t flash_size_in_kb; + //uint16_t max_flash_size_in_kb; + //uint32_t dbgmcu_idcode; + int page_size; + uint32_t base_address = 0x90000000; + + xtkcml705fl_info->probed = false; + xtkcml705fl_info->register_base = 0; + xtkcml705fl_info->user_data_offset = 10; + xtkcml705fl_info->option_offset = 0; + + //LOG_INFO("flash size = %d KiB", flash_size_in_kb); + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + bank->base = base_address; + bank->size = 0x01000000; // 16MB fixed + + /* calculate numbers of pages */ + page_size = 0x20000; + bank->num_sectors = bank->size / page_size; + + /* check that calculation result makes sense */ + assert(bank->num_sectors > 0); + + bank->sectors = alloc_block_array(0, page_size, bank->num_sectors); + if (!bank->sectors) + return ERROR_FAIL; + + + bank->num_prot_blocks = 0; + //bank->prot_blocks = alloc_block_array(0, stm32x_info->ppage_size * page_size, num_prot_blocks); + //if (!bank->prot_blocks) + // return ERROR_FAIL; + + xtkcml705fl_info->probed = true; + return ERROR_OK; +} + +static int xtkcml705fl_auto_probe(struct flash_bank *bank) +{ + struct xtkcml705fl_flash_bank *xtkcml705fl_info = bank->driver_priv; + if (xtkcml705fl_info->probed) + return ERROR_OK; + return xtkcml705fl_probe(bank); +} + +static int xtkcml705fl_erase_sector(struct flash_bank *bank, int sector) +{ + unsigned int flashpos = FLASHADDR + sector * BLOCKSIZE; + unsigned char stat; + + flashwrite(bank, FLASHADDR, 0x50); /* clear status register */ + /* unlock block */ + flashwrite(bank, flashpos, 0x60); /* clear block lock bits */ + flashwrite(bank, flashpos, 0xD0); /* clear block lock confirm */ + while (((stat = flashread(bank, FLASHADDR)) & 0x80) == 0) + ; + if (stat & ~0x80) { + LOG_ERROR("Unlock flash block %d (0x%x) failed: %d", sector, flashpos, stat); + return ERROR_FLASH_OPERATION_FAILED; + } + /* erase block */ + flashwrite(bank, flashpos, 0x20); /* block erase mode */ + flashwrite(bank, flashpos, 0xD0); /* block erase confirm */ + while (((stat = flashread(bank, FLASHADDR)) & 0x80) == 0) + ; + if (stat & ~0x80) { + LOG_ERROR("Erase flash block %d (0x%x) failed: %d", sector, flashpos, stat); + return ERROR_FLASH_OPERATION_FAILED; + } + flashwrite(bank, FLASHADDR, 0xFF); /* restore read array (normal) mode */ + return ERROR_OK; +} + +static int xtkcml705fl_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + int retval; + + /* + * It could be possible to do a mass erase if all sectors must be + * erased, but it is not implemented yet. + */ + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* + * Loop over the selected sectors and erase them + */ + for (unsigned int i = first; i <= last; i++) { + retval = xtkcml705fl_erase_sector(bank, i); + if (retval != ERROR_OK) + return retval; + bank->sectors[i].is_erased = 1; + } + return ERROR_OK; +} + +static int xtkcml705fl_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t address, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size; + struct working_area *write_algorithm; + struct working_area *source; + struct xtensa_algorithm xtensa_info; + uint32_t hwords_count = count / 2; + int retval; + + // TODO: Xtensa stub loader.... + static const uint8_t xtkcml705fl_flash_write_code[] = { +#include "../../../contrib/loaders/flash/xtensa/xtkcml705fl.inc" + }; + + /* The flash write must be aligned to a halfword boundary. + * The flash infrastructure ensures it, do just a security check + */ + assert(address % 2 == 0); + assert(count % 2 == 0); + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(xtkcml705fl_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(xtkcml705fl_flash_write_code), xtkcml705fl_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 + 8, MAX(buffer_size, 256)); + /* Normally we allocate all available working area. + * MIN shrinks buffer_size if the size of the written block is smaller. + * MAX prevents using async algo if the available working area is smaller + * than 256, the following allocation fails with + * ERROR_TARGET_RESOURCE_NOT_AVAILABLE and slow flashing takes place. + */ + + retval = target_alloc_working_area(target, buffer_size, &source); + /* Allocated size is always 32-bit 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; + } + + /* data to program */ + retval = target_write_buffer(target, source->address, source->size, buffer); + if (retval != ERROR_OK) { + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + return retval; + } + + struct reg_param reg_params[5]; + + // TODO: FIXME: ARs appropriate for both windowed/call0 configs? + init_reg_param(®_params[0], "a10", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ + init_reg_param(®_params[1], "a11", 32, PARAM_OUT); /* count (halfword-16bit) */ + init_reg_param(®_params[2], "a12", 32, PARAM_OUT); /* buffer start */ + init_reg_param(®_params[3], "a13", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[4], "a14", 32, PARAM_IN_OUT); /* target address */ + + buf_set_u32(reg_params[0].value, 0, 32, FLASHADDR); + buf_set_u32(reg_params[1].value, 0, 32, hwords_count); + 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, FLASHADDR + address); + + xtensa_info.core_mode = XT_MODE_RING0; + + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + write_algorithm->address, 0, + 0, &xtensa_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) + LOG_ERROR("flash write failed just before address 0x%" PRIx32, + buf_get_u32(reg_params[4].value, 0, 32)); + + 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; +} + + +static const struct command_registration xtkcml705fl_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration xtkcml705fl_command_handlers[] = { + { + .name = "xtkcml705fl", + .mode = COMMAND_ANY, + .help = "xtkcml705fl flash command group", + .usage = "", + .chain = xtkcml705fl_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver xtkcml705fl_flash = { + .name = "xtkcml705fl", + .commands = xtkcml705fl_command_handlers, + .flash_bank_command = xtkcml705fl_flash_bank_command, + .erase = xtkcml705fl_erase, + .protect = NULL, + .write = xtkcml705fl_write, + .read = default_flash_read, + .probe = xtkcml705fl_probe, + .auto_probe = xtkcml705fl_auto_probe, + .erase_check = NULL, + .protect_check = NULL, + .info = NULL, + .free_driver_priv = NULL, +}; diff --git a/tcl/board/xtensa-kc705_flash-ext.cfg b/tcl/board/xtensa-kc705_flash-ext.cfg new file mode 100644 index 0000000000..f5407c8967 --- /dev/null +++ b/tcl/board/xtensa-kc705_flash-ext.cfg @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence KC705 FPGA Development Platform for Xtensa targets +# Can be used with various external adapters, e.g. Flyswatter2 or JLink +# + +adapter speed 10000 + +# KC705 supports JTAG only +transport select jtag + +# Create Xtensa target first +source [find target/xtensa.cfg] + +# Work-area is a space in RAM used for flash programming +# By default use 4kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x1000 +} + +# Work-are location depends on configuration system memory location +if { [info exists WORKAREAADDR] } { + set _WORKAREAADDR $WORKAREAADDR +} else { + set _WORKAREAADDR 0x61000000 +} + +# Allow overriding the Flash bank size +if { [info exists FLASH_SIZE] } { + set _FLASH_SIZE $FLASH_SIZE +} else { + # Set 16MB of flash by default for compatibility with ML605/KC705 + set _FLASH_SIZE 0x01000000 +} + +# Work area location +$_TARGETNAME configure -work-area-phys $_WORKAREAADDR -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# flash size will be probed +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME xtkcml705fl 0x90000000 $_FLASH_SIZE 0 0 $_TARGETNAME +