Add support for Cypress PSoC6 family of devices
* Tested on CY8CKIT-001 kit with PSoC6 daughter board. * Tested with several J-Link adapters (Ultra+, Basic) Change-Id: I0a818c231e5f0b270c7774037b38d23221d59417 Signed-off-by: Bohdan Tymkiv <bhdt@cypress.com> Reviewed-on: http://openocd.zylin.com/4233 Tested-by: jenkins Reviewed-by: Tomas Vanek <vanekt@fbl.cz>riscv-compliance-dev
parent
445dc23eb5
commit
e9f54db003
|
@ -5891,6 +5891,62 @@ The @var{num} parameter is a value shown by @command{flash banks}.
|
||||||
@end deffn
|
@end deffn
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Flash Driver} psoc6
|
||||||
|
Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers.
|
||||||
|
PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
|
||||||
|
the same Flash/RAM/MMIO address space.
|
||||||
|
|
||||||
|
Flash in PSoC6 is split into three regions:
|
||||||
|
@itemize @bullet
|
||||||
|
@item Main Flash - this is the main storage for user application.
|
||||||
|
Total size varies among devices, sector size: 256 kBytes, row size:
|
||||||
|
512 bytes. Supports erase operation on individual rows.
|
||||||
|
@item Work Flash - intended to be used as storage for user data
|
||||||
|
(e.g. EEPROM emulation). Total size: 32 KBytes, sector size: 32 KBytes,
|
||||||
|
row size: 512 bytes.
|
||||||
|
@item Supervisory Flash - special region which contains device-specific
|
||||||
|
service data. This region does not support erase operation. Only few rows can
|
||||||
|
be programmed by the user, most of the rows are read only. Programming
|
||||||
|
operation will erase row automatically.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
All three flash regions are supported by the driver. Flash geometry is detected
|
||||||
|
automatically by parsing data in SPCIF_GEOMETRY register.
|
||||||
|
|
||||||
|
PSoC6 is equipped with NOR Flash so erased Flash reads as 0x00.
|
||||||
|
|
||||||
|
@example
|
||||||
|
flash bank main_flash_cm0 psoc6 0x10000000 0 0 0 $@{TARGET@}.cm0
|
||||||
|
flash bank work_flash_cm0 psoc6 0x14000000 0 0 0 $@{TARGET@}.cm0
|
||||||
|
flash bank super_flash_user_cm0 psoc6 0x16000800 0 0 0 $@{TARGET@}.cm0
|
||||||
|
flash bank super_flash_nar_cm0 psoc6 0x16001A00 0 0 0 $@{TARGET@}.cm0
|
||||||
|
flash bank super_flash_key_cm0 psoc6 0x16005A00 0 0 0 $@{TARGET@}.cm0
|
||||||
|
flash bank super_flash_toc2_cm0 psoc6 0x16007C00 0 0 0 $@{TARGET@}.cm0
|
||||||
|
|
||||||
|
flash bank main_flash_cm4 psoc6 0x10000000 0 0 0 $@{TARGET@}.cm4
|
||||||
|
flash bank work_flash_cm4 psoc6 0x14000000 0 0 0 $@{TARGET@}.cm4
|
||||||
|
flash bank super_flash_user_cm4 psoc6 0x16000800 0 0 0 $@{TARGET@}.cm4
|
||||||
|
flash bank super_flash_nar_cm4 psoc6 0x16001A00 0 0 0 $@{TARGET@}.cm4
|
||||||
|
flash bank super_flash_key_cm4 psoc6 0x16005A00 0 0 0 $@{TARGET@}.cm4
|
||||||
|
flash bank super_flash_toc2_cm4 psoc6 0x16007C00 0 0 0 $@{TARGET@}.cm4
|
||||||
|
@end example
|
||||||
|
|
||||||
|
psoc6-specific commands
|
||||||
|
@deffn Command {psoc6 reset_halt}
|
||||||
|
Command can be used to simulate broken Vector Catch from gdbinit or tcl scripts.
|
||||||
|
When invoked for CM0+ target, it will set break point at application entry point
|
||||||
|
and issue SYSRESETREQ. This will reset both cores and all peripherals. CM0+ will
|
||||||
|
reset CM4 during boot anyway so this is safe. On CM4 target, VECTRESET is used
|
||||||
|
instead of SYSRESETREQ to avoid unwanted reset of CM0+;
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {psoc6 mass_erase} num
|
||||||
|
Erases the contents given flash bank. The @var{num} parameter is a value shown
|
||||||
|
by @command{flash banks}.
|
||||||
|
Note: only Main and Work flash regions support Erase operation.
|
||||||
|
@end deffn
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn {Flash Driver} sim3x
|
@deffn {Flash Driver} sim3x
|
||||||
All members of the SiM3 microcontroller family from Silicon Laboratories
|
All members of the SiM3 microcontroller family from Silicon Laboratories
|
||||||
include internal flash and use ARM Cortex-M3 cores. It supports both JTAG
|
include internal flash and use ARM Cortex-M3 cores. It supports both JTAG
|
||||||
|
|
|
@ -41,6 +41,7 @@ NOR_DRIVERS = \
|
||||||
%D%/ocl.c \
|
%D%/ocl.c \
|
||||||
%D%/pic32mx.c \
|
%D%/pic32mx.c \
|
||||||
%D%/psoc4.c \
|
%D%/psoc4.c \
|
||||||
|
%D%/psoc6.c \
|
||||||
%D%/sim3x.c \
|
%D%/sim3x.c \
|
||||||
%D%/spi.c \
|
%D%/spi.c \
|
||||||
%D%/stmsmi.c \
|
%D%/stmsmi.c \
|
||||||
|
|
|
@ -54,6 +54,7 @@ extern struct flash_driver numicro_flash;
|
||||||
extern struct flash_driver ocl_flash;
|
extern struct flash_driver ocl_flash;
|
||||||
extern struct flash_driver pic32mx_flash;
|
extern struct flash_driver pic32mx_flash;
|
||||||
extern struct flash_driver psoc4_flash;
|
extern struct flash_driver psoc4_flash;
|
||||||
|
extern struct flash_driver psoc6_flash;
|
||||||
extern struct flash_driver sim3x_flash;
|
extern struct flash_driver sim3x_flash;
|
||||||
extern struct flash_driver stellaris_flash;
|
extern struct flash_driver stellaris_flash;
|
||||||
extern struct flash_driver stm32f1x_flash;
|
extern struct flash_driver stm32f1x_flash;
|
||||||
|
@ -110,6 +111,7 @@ static struct flash_driver *flash_drivers[] = {
|
||||||
&ocl_flash,
|
&ocl_flash,
|
||||||
&pic32mx_flash,
|
&pic32mx_flash,
|
||||||
&psoc4_flash,
|
&psoc4_flash,
|
||||||
|
&psoc6_flash,
|
||||||
&sim3x_flash,
|
&sim3x_flash,
|
||||||
&stellaris_flash,
|
&stellaris_flash,
|
||||||
&stm32f1x_flash,
|
&stm32f1x_flash,
|
||||||
|
|
|
@ -0,0 +1,985 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2017 by Bohdan Tymkiv *
|
||||||
|
* bohdan.tymkiv@cypress.com bohdan200@gmail.com *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "imp.h"
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/cortex_m.h"
|
||||||
|
#include "target/breakpoints.h"
|
||||||
|
#include "target/target_type.h"
|
||||||
|
#include "time_support.h"
|
||||||
|
#include "target/algorithm.h"
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* PSoC6 device definitions
|
||||||
|
*************************************************************************************************/
|
||||||
|
#define MFLASH_SECTOR_SIZE (256u * 1024u)
|
||||||
|
#define WFLASH_SECTOR_SIZE (32u * 1024u)
|
||||||
|
|
||||||
|
#define MEM_BASE_MFLASH 0x10000000u
|
||||||
|
#define MEM_BASE_WFLASH 0x14000000u
|
||||||
|
#define MEM_WFLASH_SIZE 32768u
|
||||||
|
#define MEM_BASE_SFLASH 0x16000000u
|
||||||
|
#define RAM_STACK_WA_SIZE 2048u
|
||||||
|
#define PSOC6_SPCIF_GEOMETRY 0x4025F00Cu
|
||||||
|
|
||||||
|
#define PROTECTION_UNKNOWN 0x00u
|
||||||
|
#define PROTECTION_VIRGIN 0x01u
|
||||||
|
#define PROTECTION_NORMAL 0x02u
|
||||||
|
#define PROTECTION_SECURE 0x03u
|
||||||
|
#define PROTECTION_DEAD 0x04u
|
||||||
|
|
||||||
|
#define MEM_BASE_IPC 0x40230000u
|
||||||
|
#define IPC_STRUCT_SIZE 0x20u
|
||||||
|
#define MEM_IPC(n) (MEM_BASE_IPC + (n) * IPC_STRUCT_SIZE)
|
||||||
|
#define MEM_IPC_ACQUIRE(n) (MEM_IPC(n) + 0x00u)
|
||||||
|
#define MEM_IPC_NOTIFY(n) (MEM_IPC(n) + 0x08u)
|
||||||
|
#define MEM_IPC_DATA(n) (MEM_IPC(n) + 0x0Cu)
|
||||||
|
#define MEM_IPC_LOCK_STATUS(n) (MEM_IPC(n) + 0x10u)
|
||||||
|
|
||||||
|
#define MEM_BASE_IPC_INTR 0x40231000u
|
||||||
|
#define IPC_INTR_STRUCT_SIZE 0x20u
|
||||||
|
#define MEM_IPC_INTR(n) (MEM_BASE_IPC_INTR + (n) * IPC_INTR_STRUCT_SIZE)
|
||||||
|
#define MEM_IPC_INTR_MASK(n) (MEM_IPC_INTR(n) + 0x08u)
|
||||||
|
#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u
|
||||||
|
#define IPC_LOCK_ACQUIRED_MSK 0x80000000u
|
||||||
|
|
||||||
|
#define IPC_ID 2u
|
||||||
|
#define IPC_INTR_ID 0u
|
||||||
|
#define IPC_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
#define SROMAPI_SIID_REQ 0x00000001u
|
||||||
|
#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u)
|
||||||
|
#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u)
|
||||||
|
#define SROMAPI_WRITEROW_REQ 0x05000100u
|
||||||
|
#define SROMAPI_PROGRAMROW_REQ 0x06000100u
|
||||||
|
#define SROMAPI_ERASESECTOR_REQ 0x14000100u
|
||||||
|
#define SROMAPI_ERASEALL_REQ 0x0A000100u
|
||||||
|
#define SROMAPI_ERASEROW_REQ 0x1C000100u
|
||||||
|
|
||||||
|
#define SROMAPI_STATUS_MSK 0xF0000000u
|
||||||
|
#define SROMAPI_STAT_SUCCESS 0xA0000000u
|
||||||
|
#define SROMAPI_DATA_LOCATION_MSK 0x00000001u
|
||||||
|
#define SROMAPI_CALL_TIMEOUT_MS 1500
|
||||||
|
|
||||||
|
struct psoc6_target_info {
|
||||||
|
uint32_t silicon_id;
|
||||||
|
uint8_t protection;
|
||||||
|
uint32_t main_flash_sz;
|
||||||
|
uint32_t row_sz;
|
||||||
|
bool is_probed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct timeout {
|
||||||
|
int64_t start_time;
|
||||||
|
long timeout_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct row_region {
|
||||||
|
uint32_t addr;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct row_region safe_sflash_regions[] = {
|
||||||
|
{0x16000800, 0x800}, /* SFLASH: User Data */
|
||||||
|
{0x16001A00, 0x200}, /* SFLASH: NAR */
|
||||||
|
{0x16005A00, 0xC00}, /* SFLASH: Public Key */
|
||||||
|
{0x16007C00, 0x400}, /* SFLASH: TOC2 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SFLASH_NUM_REGIONS (sizeof(safe_sflash_regions) / sizeof(safe_sflash_regions[0]))
|
||||||
|
|
||||||
|
static struct working_area *g_stack_area;
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Initializes timeout_s structure with given timeout in milliseconds
|
||||||
|
*************************************************************************************************/
|
||||||
|
static void timeout_init(struct timeout *to, long timeout_ms)
|
||||||
|
{
|
||||||
|
to->start_time = timeval_ms();
|
||||||
|
to->timeout_ms = timeout_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Returns true if given timeout_s object has expired
|
||||||
|
*************************************************************************************************/
|
||||||
|
static bool timeout_expired(struct timeout *to)
|
||||||
|
{
|
||||||
|
return (timeval_ms() - to->start_time) > to->timeout_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Prepares PSoC6 for running pseudo flash algorithm. This function allocates Working Area for
|
||||||
|
* the algorithm and for CPU Stack.
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int sromalgo_prepare(struct target *target)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
/* Initialize Vector Table Offset register (in case FW modified it) */
|
||||||
|
hr = target_write_u32(target, 0xE000ED08, 0x00000000);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Allocate Working Area for Stack and Flash algorithm */
|
||||||
|
hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &g_stack_area);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Restore THUMB bit in xPSR register */
|
||||||
|
const struct armv7m_common *cm = target_to_armv7m(target);
|
||||||
|
hr = cm->store_core_reg_u32(target, ARMV7M_xPSR, 0x01000000);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
exit_free_wa:
|
||||||
|
/* Something went wrong, free allocated area */
|
||||||
|
if (g_stack_area) {
|
||||||
|
target_free_working_area(target, g_stack_area);
|
||||||
|
g_stack_area = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Releases working area
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int sromalgo_release(struct target *target)
|
||||||
|
{
|
||||||
|
int hr = ERROR_OK;
|
||||||
|
|
||||||
|
/* Free Stack/Flash algorithm working area */
|
||||||
|
if (g_stack_area) {
|
||||||
|
hr = target_free_working_area(target, g_stack_area);
|
||||||
|
g_stack_area = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Runs pseudo flash algorithm. Algorithm itself consist of couple of NOPs followed by BKPT
|
||||||
|
* instruction. The trick here is that NMI has already been posted to CM0 via IPC structure
|
||||||
|
* prior to calling this function. CM0 will immediately jump to NMI handler and execute
|
||||||
|
* SROM API code.
|
||||||
|
* This approach is borrowed from PSoC4 Flash Driver.
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int sromalgo_run(struct target *target)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
struct armv7m_algorithm armv7m_info;
|
||||||
|
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||||
|
armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||||
|
|
||||||
|
struct reg_param reg_params;
|
||||||
|
init_reg_param(®_params, "sp", 32, PARAM_OUT);
|
||||||
|
buf_set_u32(reg_params.value, 0, 32, g_stack_area->address + g_stack_area->size);
|
||||||
|
|
||||||
|
/* mov r8, r8; mov r8, r8 */
|
||||||
|
hr = target_write_u32(target, g_stack_area->address + 0, 0x46C046C0);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* mov r8, r8; bkpt #0 */
|
||||||
|
hr = target_write_u32(target, g_stack_area->address + 4, 0xBE0046C0);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = target_run_algorithm(target, 0, NULL, 1, ®_params, g_stack_area->address,
|
||||||
|
0, SROMAPI_CALL_TIMEOUT_MS, &armv7m_info);
|
||||||
|
|
||||||
|
destroy_reg_param(®_params);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Waits for expected IPC lock status.
|
||||||
|
* PSoC6 uses IPC structures for inter-core communication. Same IPCs are used to invoke SROM API.
|
||||||
|
* IPC structure must be locked prior to invoking any SROM API. This ensures nothing else in the
|
||||||
|
* system will use same IPC thus corrupting our data. Locking is performed by ipc_acquire(), this
|
||||||
|
* function ensures that IPC is actually in expected state
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_expected)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
uint32_t reg_val;
|
||||||
|
|
||||||
|
struct timeout to;
|
||||||
|
timeout_init(&to, IPC_TIMEOUT_MS);
|
||||||
|
|
||||||
|
while (!timeout_expired(&to)) {
|
||||||
|
/* Process any server requests */
|
||||||
|
keep_alive();
|
||||||
|
|
||||||
|
/* Read IPC Lock status */
|
||||||
|
hr = target_read_u32(target, MEM_IPC_LOCK_STATUS(ipc_id), ®_val);
|
||||||
|
if (hr != ERROR_OK) {
|
||||||
|
LOG_ERROR("Unable to read IPC Lock Status register");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_locked = (reg_val & IPC_LOCK_ACQUIRED_MSK) != 0;
|
||||||
|
|
||||||
|
if (lock_expected == is_locked)
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target->coreid) {
|
||||||
|
LOG_WARNING("SROM API calls via CM4 target are supported on single-core PSoC6 devices only. "
|
||||||
|
"Please perform all Flash-related operations via CM0+ target on dual-core devices.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR("Timeout polling IPC Lock Status");
|
||||||
|
return ERROR_TARGET_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Acquires IPC structure
|
||||||
|
* PSoC6 uses IPC structures for inter-core communication. Same IPCs are used to invoke SROM API.
|
||||||
|
* IPC structure must be locked prior to invoking any SROM API. This ensures nothing else in the
|
||||||
|
* system will use same IPC thus corrupting our data. This function locks the IPC.
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int ipc_acquire(struct target *target, char ipc_id)
|
||||||
|
{
|
||||||
|
int hr = ERROR_OK;
|
||||||
|
bool is_acquired = false;
|
||||||
|
uint32_t reg_val;
|
||||||
|
|
||||||
|
struct timeout to;
|
||||||
|
timeout_init(&to, IPC_TIMEOUT_MS);
|
||||||
|
|
||||||
|
while (!timeout_expired(&to)) {
|
||||||
|
keep_alive();
|
||||||
|
|
||||||
|
hr = target_write_u32(target, MEM_IPC_ACQUIRE(ipc_id), IPC_ACQUIRE_SUCCESS_MSK);
|
||||||
|
if (hr != ERROR_OK) {
|
||||||
|
LOG_ERROR("Unable to write to IPC Acquire register");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if data is written on first step */
|
||||||
|
hr = target_read_u32(target, MEM_IPC_ACQUIRE(ipc_id), ®_val);
|
||||||
|
if (hr != ERROR_OK) {
|
||||||
|
LOG_ERROR("Unable to read IPC Acquire register");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_acquired = (reg_val & IPC_ACQUIRE_SUCCESS_MSK) != 0;
|
||||||
|
if (is_acquired) {
|
||||||
|
/* If IPC structure is acquired, the lock status should be set */
|
||||||
|
hr = ipc_poll_lock_stat(target, ipc_id, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_acquired)
|
||||||
|
LOG_ERROR("Timeout acquiring IPC structure");
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Invokes SROM API functions which are responsible for Flash operations
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int call_sromapi(struct target *target,
|
||||||
|
uint32_t req_and_params,
|
||||||
|
uint32_t working_area,
|
||||||
|
uint32_t *data_out)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
bool is_data_in_ram = (req_and_params & SROMAPI_DATA_LOCATION_MSK) == 0;
|
||||||
|
|
||||||
|
hr = ipc_acquire(target, IPC_ID);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (is_data_in_ram)
|
||||||
|
hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), working_area);
|
||||||
|
else
|
||||||
|
hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), req_and_params);
|
||||||
|
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Enable notification interrupt of IPC_INTR_STRUCT0(CM0+) for IPC_STRUCT2 */
|
||||||
|
hr = target_write_u32(target, MEM_IPC_INTR_MASK(IPC_INTR_ID), 1u << (16 + IPC_ID));
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, MEM_IPC_NOTIFY(IPC_ID), 1);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = sromalgo_run(target);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Poll lock status */
|
||||||
|
hr = ipc_poll_lock_stat(target, IPC_ID, false);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Poll Data byte */
|
||||||
|
if (is_data_in_ram)
|
||||||
|
hr = target_read_u32(target, working_area, data_out);
|
||||||
|
else
|
||||||
|
hr = target_read_u32(target, MEM_IPC_DATA(IPC_ID), data_out);
|
||||||
|
|
||||||
|
if (hr != ERROR_OK) {
|
||||||
|
LOG_ERROR("Error reading SROM API Status location");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_success = (*data_out & SROMAPI_STATUS_MSK) == SROMAPI_STAT_SUCCESS;
|
||||||
|
if (!is_success) {
|
||||||
|
LOG_ERROR("SROM API execution failed. Status: 0x%08X", (uint32_t)*data_out);
|
||||||
|
return ERROR_TARGET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Retrieves SiliconID and Protection status of the target device
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int get_silicon_id(struct target *target, uint32_t *si_id, uint8_t *protection)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
uint32_t family_rev, siid_prot;
|
||||||
|
|
||||||
|
hr = sromalgo_prepare(target);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Read FamilyID and Revision */
|
||||||
|
hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Read SiliconID and Protection */
|
||||||
|
hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
*si_id = (siid_prot & 0x0000FFFF) << 16;
|
||||||
|
*si_id |= (family_rev & 0x00FF0000) >> 8;
|
||||||
|
*si_id |= (family_rev & 0x000000FF) >> 0;
|
||||||
|
|
||||||
|
*protection = (siid_prot & 0x000F0000) >> 0x10;
|
||||||
|
|
||||||
|
hr = sromalgo_release(target);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Translates Protection status to openocd-friendly boolean value
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_protect_check(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
int is_protected;
|
||||||
|
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
switch (psoc6_info->protection) {
|
||||||
|
case PROTECTION_VIRGIN:
|
||||||
|
case PROTECTION_NORMAL:
|
||||||
|
is_protected = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROTECTION_UNKNOWN:
|
||||||
|
case PROTECTION_SECURE:
|
||||||
|
case PROTECTION_DEAD:
|
||||||
|
default:
|
||||||
|
is_protected = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < bank->num_sectors; i++)
|
||||||
|
bank->sectors[i].is_protected = is_protected;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Life Cycle transition is not currently supported
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_protect(struct flash_bank *bank, int set, int first, int last)
|
||||||
|
{
|
||||||
|
(void)bank;
|
||||||
|
(void)set;
|
||||||
|
(void)first;
|
||||||
|
(void)last;
|
||||||
|
|
||||||
|
LOG_WARNING("Life Cycle transition for PSoC6 is not supported");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Translates Protection status to string
|
||||||
|
*************************************************************************************************/
|
||||||
|
static const char *protection_to_str(uint8_t protection)
|
||||||
|
{
|
||||||
|
switch (protection) {
|
||||||
|
case PROTECTION_VIRGIN:
|
||||||
|
return "VIRGIN";
|
||||||
|
break;
|
||||||
|
case PROTECTION_NORMAL:
|
||||||
|
return "NORMAL";
|
||||||
|
break;
|
||||||
|
case PROTECTION_SECURE:
|
||||||
|
return "SECURE";
|
||||||
|
break;
|
||||||
|
case PROTECTION_DEAD:
|
||||||
|
return "DEAD";
|
||||||
|
break;
|
||||||
|
case PROTECTION_UNKNOWN:
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Displays human-readable information about acquired device
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||||
|
{
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
|
||||||
|
if (psoc6_info->is_probed == false)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
snprintf(buf, buf_size,
|
||||||
|
"PSoC6 Silicon ID: 0x%08X\n"
|
||||||
|
"Protection: %s\n"
|
||||||
|
"Main Flash size: %d kB\n"
|
||||||
|
"Work Flash size: 32 kB\n",
|
||||||
|
psoc6_info->silicon_id,
|
||||||
|
protection_to_str(psoc6_info->protection),
|
||||||
|
psoc6_info->main_flash_sz / 1024);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Returns true if flash bank name represents Supervisory Flash
|
||||||
|
*************************************************************************************************/
|
||||||
|
static bool is_sflash_bank(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
|
||||||
|
if (bank->base == safe_sflash_regions[i].addr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Returns true if flash bank name represents Work Flash
|
||||||
|
*************************************************************************************************/
|
||||||
|
static inline bool is_wflash_bank(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
return (bank->base == MEM_BASE_WFLASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Returns true if flash bank name represents Main Flash
|
||||||
|
*************************************************************************************************/
|
||||||
|
static inline bool is_mflash_bank(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
return (bank->base == MEM_BASE_MFLASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Probes the device and populates related data structures with target flash geometry data.
|
||||||
|
* This is done in non-intrusive way, no SROM API calls are involved so GDB can safely attach to a
|
||||||
|
* running target.
|
||||||
|
* Function assumes that size of Work Flash is 32kB (true for all current part numbers)
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_probe(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
|
||||||
|
int hr = ERROR_OK;
|
||||||
|
|
||||||
|
/* Retrieve data from SPCIF_GEOMATRY */
|
||||||
|
uint32_t geom;
|
||||||
|
target_read_u32(target, PSOC6_SPCIF_GEOMETRY, &geom);
|
||||||
|
uint32_t row_sz_lg2 = (geom & 0xF0) >> 4;
|
||||||
|
uint32_t row_sz = (0x01 << row_sz_lg2);
|
||||||
|
uint32_t row_cnt = 1 + ((geom & 0x00FFFF00) >> 8);
|
||||||
|
uint32_t bank_cnt = 1 + ((geom & 0xFF000000) >> 24);
|
||||||
|
|
||||||
|
/* Calculate size of Main Flash*/
|
||||||
|
uint32_t flash_sz_bytes = bank_cnt * row_cnt * row_sz;
|
||||||
|
|
||||||
|
if (bank->sectors) {
|
||||||
|
free(bank->sectors);
|
||||||
|
bank->sectors = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bank_size = 0;
|
||||||
|
|
||||||
|
if (is_mflash_bank(bank))
|
||||||
|
bank_size = flash_sz_bytes;
|
||||||
|
else if (is_wflash_bank(bank))
|
||||||
|
bank_size = MEM_WFLASH_SIZE;
|
||||||
|
else if (is_sflash_bank(bank)) {
|
||||||
|
for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
|
||||||
|
if (safe_sflash_regions[i].addr == bank->base) {
|
||||||
|
bank_size = safe_sflash_regions[i].size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bank_size == 0) {
|
||||||
|
LOG_ERROR("Invalid Flash Bank base address in config file");
|
||||||
|
return ERROR_FLASH_BANK_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_sectors = bank_size / row_sz;
|
||||||
|
bank->size = bank_size;
|
||||||
|
bank->chip_width = 4;
|
||||||
|
bank->bus_width = 4;
|
||||||
|
bank->erased_value = 0;
|
||||||
|
bank->default_padded_value = 0;
|
||||||
|
|
||||||
|
bank->num_sectors = num_sectors;
|
||||||
|
bank->sectors = calloc(num_sectors, sizeof(struct flash_sector));
|
||||||
|
for (size_t i = 0; i < num_sectors; i++) {
|
||||||
|
bank->sectors[i].size = row_sz;
|
||||||
|
bank->sectors[i].offset = i * row_sz;
|
||||||
|
bank->sectors[i].is_erased = -1;
|
||||||
|
bank->sectors[i].is_protected = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
psoc6_info->is_probed = true;
|
||||||
|
psoc6_info->main_flash_sz = flash_sz_bytes;
|
||||||
|
psoc6_info->row_sz = row_sz;
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Probes target device only if it hasn't been probed yet
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_auto_probe(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
if (psoc6_info->is_probed)
|
||||||
|
hr = ERROR_OK;
|
||||||
|
else
|
||||||
|
hr = psoc6_probe(bank);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Erases single sector (256k) on target device
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_erase_sector(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
|
||||||
|
LOG_DEBUG("Erasing SECTOR @%08X", addr);
|
||||||
|
|
||||||
|
int hr = target_write_u32(target, wa->address, SROMAPI_ERASESECTOR_REQ);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, wa->address + 0x04, addr);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
uint32_t data_out;
|
||||||
|
hr = call_sromapi(target, SROMAPI_ERASESECTOR_REQ, wa->address, &data_out);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
LOG_ERROR("SECTOR @%08X not erased!", addr);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Erases single row (512b) on target device
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_erase_row(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
|
||||||
|
LOG_DEBUG("Erasing ROW @%08X", addr);
|
||||||
|
|
||||||
|
int hr = target_write_u32(target, wa->address, SROMAPI_ERASEROW_REQ);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, wa->address + 0x04, addr);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
uint32_t data_out;
|
||||||
|
hr = call_sromapi(target, SROMAPI_ERASEROW_REQ, wa->address, &data_out);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
LOG_ERROR("ROW @%08X not erased!", addr);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Performs Erase operation.
|
||||||
|
* Function will try to use biggest erase block possible to speedup the operation
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_erase(struct flash_bank *bank, int first, int last)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
const uint32_t sector_size = is_wflash_bank(bank) ? WFLASH_SECTOR_SIZE : MFLASH_SECTOR_SIZE;
|
||||||
|
|
||||||
|
int hr;
|
||||||
|
struct working_area *wa;
|
||||||
|
|
||||||
|
if (is_sflash_bank(bank)) {
|
||||||
|
LOG_INFO("Erase operation on Supervisory Flash is not required, skipping");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sromalgo_prepare(target);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* Number of rows in single sector */
|
||||||
|
const int rows_in_sector = sector_size / psoc6_info->row_sz;
|
||||||
|
|
||||||
|
while (last >= first) {
|
||||||
|
/* Erase Sector if we are on sector boundary and erase size covers whole sector */
|
||||||
|
if ((first % rows_in_sector) == 0 &&
|
||||||
|
(last - first + 1) >= rows_in_sector) {
|
||||||
|
hr = psoc6_erase_sector(bank, wa, bank->base + first * psoc6_info->row_sz);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
for (int i = first; i < first + rows_in_sector; i++)
|
||||||
|
bank->sectors[i].is_erased = 1;
|
||||||
|
|
||||||
|
first += rows_in_sector;
|
||||||
|
} else {
|
||||||
|
/* Perform Row Erase otherwise */
|
||||||
|
hr = psoc6_erase_row(bank, wa, bank->base + first * psoc6_info->row_sz);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
bank->sectors[first].is_erased = 1;
|
||||||
|
first += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_free_wa:
|
||||||
|
target_free_working_area(target, wa);
|
||||||
|
exit:
|
||||||
|
sromalgo_release(target);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Programs single Flash Row
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_program_row(struct flash_bank *bank,
|
||||||
|
uint32_t addr,
|
||||||
|
const uint8_t *buffer,
|
||||||
|
bool is_sflash)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
struct working_area *wa;
|
||||||
|
const uint32_t sromapi_req = is_sflash ? SROMAPI_WRITEROW_REQ : SROMAPI_PROGRAMROW_REQ;
|
||||||
|
uint32_t data_out;
|
||||||
|
int hr = ERROR_OK;
|
||||||
|
|
||||||
|
LOG_DEBUG("Programming ROW @%08X", addr);
|
||||||
|
|
||||||
|
hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, wa->address, sromapi_req);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
hr = target_write_u32(target,
|
||||||
|
wa->address + 0x04,
|
||||||
|
0x106);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, wa->address + 0x08, addr);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
hr = target_write_u32(target, wa->address + 0x0C, wa->address + 0x10);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
hr = target_write_buffer(target, wa->address + 0x10, psoc6_info->row_sz, buffer);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
goto exit_free_wa;
|
||||||
|
|
||||||
|
hr = call_sromapi(target, sromapi_req, wa->address, &data_out);
|
||||||
|
|
||||||
|
exit_free_wa:
|
||||||
|
target_free_working_area(target, wa);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Programs set of Rows
|
||||||
|
*************************************************************************************************/
|
||||||
|
static int psoc6_program(struct flash_bank *bank,
|
||||||
|
const uint8_t *buffer,
|
||||||
|
uint32_t offset,
|
||||||
|
uint32_t count)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
struct psoc6_target_info *psoc6_info = bank->driver_priv;
|
||||||
|
const bool is_sflash = is_sflash_bank(bank);
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
hr = sromalgo_prepare(target);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
uint8_t page_buf[psoc6_info->row_sz];
|
||||||
|
|
||||||
|
while (count) {
|
||||||
|
uint32_t row_offset = offset % psoc6_info->row_sz;
|
||||||
|
uint32_t aligned_addr = bank->base + offset - row_offset;
|
||||||
|
uint32_t row_bytes = MIN(psoc6_info->row_sz - row_offset, count);
|
||||||
|
|
||||||
|
memset(page_buf, 0, sizeof(page_buf));
|
||||||
|
memcpy(&page_buf[row_offset], buffer, row_bytes);
|
||||||
|
|
||||||
|
hr = psoc6_program_row(bank, aligned_addr, page_buf, is_sflash);
|
||||||
|
if (hr != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to program Flash at address 0x%08X", aligned_addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += row_bytes;
|
||||||
|
offset += row_bytes;
|
||||||
|
count -= row_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sromalgo_release(target);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Performs Mass Erase of given flash bank
|
||||||
|
* Syntax: psoc6 mass_erase bank_id
|
||||||
|
*************************************************************************************************/
|
||||||
|
COMMAND_HANDLER(psoc6_handle_mass_erase_command)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC != 1)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
struct flash_bank *bank;
|
||||||
|
int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = psoc6_erase(bank, 0, bank->num_sectors - 1);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* Simulates broken Vector Catch
|
||||||
|
* Function will try to determine entry point of user application. If it succeeds it will set HW
|
||||||
|
* breakpoint at that address, issue SW Reset and remove the breakpoint afterwards.
|
||||||
|
* In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will
|
||||||
|
* reset CM4 anyway, so using SYSRESETREQ is safe here.
|
||||||
|
* In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core.
|
||||||
|
*************************************************************************************************/
|
||||||
|
int handle_reset_halt(struct target *target)
|
||||||
|
{
|
||||||
|
int hr;
|
||||||
|
uint32_t reset_addr;
|
||||||
|
bool is_cm0 = (target->coreid == 0);
|
||||||
|
|
||||||
|
/* Halt target device */
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
hr = target_halt(target);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read Vector Offset register */
|
||||||
|
uint32_t vt_base;
|
||||||
|
const uint32_t vt_offset_reg = is_cm0 ? 0x402102B0 : 0x402102C0;
|
||||||
|
hr = target_read_u32(target, vt_offset_reg, &vt_base);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
/* Invalid value means flash is empty */
|
||||||
|
vt_base &= 0xFFFFFF00;
|
||||||
|
if ((vt_base == 0) || (vt_base == 0xFFFFFF00))
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
/* Read Reset Vector value*/
|
||||||
|
hr = target_read_u32(target, vt_base + 4, &reset_addr);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
/* Invalid value means flash is empty */
|
||||||
|
if ((reset_addr == 0) || (reset_addr == 0xFFFFFF00))
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
|
||||||
|
/* Set breakpoint at User Application entry point */
|
||||||
|
hr = breakpoint_add(target, reset_addr, 2, BKPT_HARD);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
const struct armv7m_common *cm = target_to_armv7m(target);
|
||||||
|
|
||||||
|
if (is_cm0) {
|
||||||
|
/* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */
|
||||||
|
LOG_INFO("psoc6.cm0: bkpt @0x%08X, issuing SYSRESETREQ", reset_addr);
|
||||||
|
hr = mem_ap_write_atomic_u32(cm->debug_ap,
|
||||||
|
NVIC_AIRCR,
|
||||||
|
AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
|
||||||
|
|
||||||
|
/* Wait for bootcode and initialize DAP */
|
||||||
|
usleep(3000);
|
||||||
|
dap_dp_init(cm->debug_ap->dap);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("psoc6.cm4: bkpt @0x%08X, issuing VECTRESET", reset_addr);
|
||||||
|
hr = mem_ap_write_atomic_u32(cm->debug_ap,
|
||||||
|
NVIC_AIRCR,
|
||||||
|
AIRCR_VECTKEY | AIRCR_VECTRESET);
|
||||||
|
if (hr != ERROR_OK)
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
|
||||||
|
|
||||||
|
/* Remove the break point */
|
||||||
|
breakpoint_remove(target, reset_addr);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(psoc6_handle_reset_halt)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
return handle_reset_halt(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLASH_BANK_COMMAND_HANDLER(psoc6_flash_bank_command)
|
||||||
|
{
|
||||||
|
struct psoc6_target_info *psoc6_info;
|
||||||
|
int hr = ERROR_OK;
|
||||||
|
|
||||||
|
if (CMD_ARGC < 6)
|
||||||
|
hr = ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
else {
|
||||||
|
psoc6_info = calloc(1, sizeof(struct psoc6_target_info));
|
||||||
|
psoc6_info->is_probed = false;
|
||||||
|
bank->driver_priv = psoc6_info;
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct command_registration psoc6_exec_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "mass_erase",
|
||||||
|
.handler = psoc6_handle_mass_erase_command,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.usage = NULL,
|
||||||
|
.help = "Erases entire Main Flash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "reset_halt",
|
||||||
|
.handler = psoc6_handle_reset_halt,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.usage = NULL,
|
||||||
|
.help = "Tries to simulate broken Vector Catch",
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct command_registration psoc6_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "psoc6",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "PSoC 6 flash command group",
|
||||||
|
.usage = "",
|
||||||
|
.chain = psoc6_exec_command_handlers,
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flash_driver psoc6_flash = {
|
||||||
|
.name = "psoc6",
|
||||||
|
.commands = psoc6_command_handlers,
|
||||||
|
.flash_bank_command = psoc6_flash_bank_command,
|
||||||
|
.erase = psoc6_erase,
|
||||||
|
.protect = psoc6_protect,
|
||||||
|
.write = psoc6_program,
|
||||||
|
.read = default_flash_read,
|
||||||
|
.probe = psoc6_probe,
|
||||||
|
.auto_probe = psoc6_auto_probe,
|
||||||
|
.erase_check = default_flash_blank_check,
|
||||||
|
.protect_check = psoc6_protect_check,
|
||||||
|
.info = psoc6_get_info,
|
||||||
|
};
|
|
@ -0,0 +1,134 @@
|
||||||
|
#
|
||||||
|
# Configuration script for Cypress PSoC6 family of microcontrollers (CY8C6xxx)
|
||||||
|
# PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
|
||||||
|
# the same Flash/RAM/MMIO address space.
|
||||||
|
#
|
||||||
|
|
||||||
|
source [find target/swj-dp.tcl]
|
||||||
|
|
||||||
|
adapter_khz 1000
|
||||||
|
|
||||||
|
global _CHIPNAME
|
||||||
|
if { [info exists CHIPNAME] } {
|
||||||
|
set _CHIPNAME $CHIPNAME
|
||||||
|
} else {
|
||||||
|
set _CHIPNAME psoc6
|
||||||
|
}
|
||||||
|
|
||||||
|
global TARGET
|
||||||
|
set TARGET $_CHIPNAME.cpu
|
||||||
|
|
||||||
|
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf
|
||||||
|
|
||||||
|
# Is CM0 Debugging enabled ?
|
||||||
|
global _ENABLE_CM0
|
||||||
|
if { [info exists ENABLE_CM0] } {
|
||||||
|
set _ENABLE_CM0 $ENABLE_CM0
|
||||||
|
} else {
|
||||||
|
set _ENABLE_CM0 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Is CM4 Debugging enabled ?
|
||||||
|
global _ENABLE_CM4
|
||||||
|
if { [info exists ENABLE_CM4] } {
|
||||||
|
set _ENABLE_CM4 $ENABLE_CM4
|
||||||
|
} else {
|
||||||
|
set _ENABLE_CM4 1
|
||||||
|
}
|
||||||
|
|
||||||
|
global _WORKAREASIZE_CM0
|
||||||
|
if { [info exists WORKAREASIZE_CM0] } {
|
||||||
|
set _WORKAREASIZE_CM0 $WORKAREASIZE_CM0
|
||||||
|
} else {
|
||||||
|
set _WORKAREASIZE_CM0 0x4000
|
||||||
|
}
|
||||||
|
|
||||||
|
global _WORKAREASIZE_CM4
|
||||||
|
if { [info exists WORKAREASIZE_CM4] } {
|
||||||
|
set _WORKAREASIZE_CM4 $WORKAREASIZE_CM4
|
||||||
|
} else {
|
||||||
|
set _WORKAREASIZE_CM4 0x4000
|
||||||
|
}
|
||||||
|
|
||||||
|
global _WORKAREAADDR_CM0
|
||||||
|
if { [info exists WORKAREAADDR_CM0] } {
|
||||||
|
set _WORKAREAADDR_CM0 $WORKAREAADDR_CM0
|
||||||
|
} else {
|
||||||
|
set _WORKAREAADDR_CM0 0x08000000
|
||||||
|
}
|
||||||
|
|
||||||
|
global _WORKAREAADDR_CM4
|
||||||
|
if { [info exists WORKAREAADDR_CM4] } {
|
||||||
|
set _WORKAREAADDR_CM4 $WORKAREAADDR_CM4
|
||||||
|
} else {
|
||||||
|
set _WORKAREAADDR_CM4 0x08000000
|
||||||
|
}
|
||||||
|
|
||||||
|
proc init_reset { mode } {
|
||||||
|
global RESET_MODE
|
||||||
|
set RESET_MODE $mode
|
||||||
|
|
||||||
|
if {[using_jtag]} {
|
||||||
|
jtag arp_init-reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Utility to make 'reset halt' work as reset;halt on a target
|
||||||
|
# It does not prevent running code after reset
|
||||||
|
proc psoc6_deassert_post { target } {
|
||||||
|
# PSoC6 cleared AP registers including TAR during reset
|
||||||
|
# Force examine to synchronize OpenOCD target status
|
||||||
|
$target arp_examine
|
||||||
|
|
||||||
|
global RESET_MODE
|
||||||
|
if { $RESET_MODE ne "run" } {
|
||||||
|
$target arp_poll
|
||||||
|
$target arp_poll
|
||||||
|
set st [$target curstate]
|
||||||
|
if { $st eq "reset" } {
|
||||||
|
# we assume running state follows
|
||||||
|
# if reset accidentally halts, waiting is useless
|
||||||
|
catch { $target arp_waitstate running 100 }
|
||||||
|
set st [$target curstate]
|
||||||
|
}
|
||||||
|
if { $st eq "running" } {
|
||||||
|
echo "$target: Ran after reset and before halt..."
|
||||||
|
$target arp_halt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $_ENABLE_CM0 } {
|
||||||
|
target create ${TARGET}.cm0 cortex_m -chain-position $TARGET -ap-num 1 -coreid 0
|
||||||
|
${TARGET}.cm0 configure -work-area-phys $_WORKAREAADDR_CM0 -work-area-size $_WORKAREASIZE_CM0 -work-area-backup 0
|
||||||
|
|
||||||
|
flash bank main_flash_cm0 psoc6 0x10000000 0 0 0 ${TARGET}.cm0
|
||||||
|
flash bank work_flash_cm0 psoc6 0x14000000 0 0 0 ${TARGET}.cm0
|
||||||
|
flash bank super_flash_user_cm0 psoc6 0x16000800 0 0 0 ${TARGET}.cm0
|
||||||
|
flash bank super_flash_nar_cm0 psoc6 0x16001A00 0 0 0 ${TARGET}.cm0
|
||||||
|
flash bank super_flash_key_cm0 psoc6 0x16005A00 0 0 0 ${TARGET}.cm0
|
||||||
|
flash bank super_flash_toc2_cm0 psoc6 0x16007C00 0 0 0 ${TARGET}.cm0
|
||||||
|
|
||||||
|
${TARGET}.cm0 cortex_m reset_config sysresetreq
|
||||||
|
${TARGET}.cm0 configure -event reset-deassert-post "psoc6_deassert_post ${TARGET}.cm0"
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $_ENABLE_CM4 } {
|
||||||
|
target create ${TARGET}.cm4 cortex_m -chain-position $TARGET -ap-num 2 -coreid 1
|
||||||
|
${TARGET}.cm4 configure -work-area-phys $_WORKAREAADDR_CM4 -work-area-size $_WORKAREASIZE_CM4 -work-area-backup 0
|
||||||
|
|
||||||
|
flash bank main_flash_cm4 psoc6 0x10000000 0 0 0 ${TARGET}.cm4
|
||||||
|
flash bank work_flash_cm4 psoc6 0x14000000 0 0 0 ${TARGET}.cm4
|
||||||
|
flash bank super_flash_user_cm4 psoc6 0x16000800 0 0 0 ${TARGET}.cm4
|
||||||
|
flash bank super_flash_nar_cm4 psoc6 0x16001A00 0 0 0 ${TARGET}.cm4
|
||||||
|
flash bank super_flash_key_cm4 psoc6 0x16005A00 0 0 0 ${TARGET}.cm4
|
||||||
|
flash bank super_flash_toc2_cm4 psoc6 0x16007C00 0 0 0 ${TARGET}.cm4
|
||||||
|
|
||||||
|
${TARGET}.cm4 cortex_m reset_config vectreset
|
||||||
|
${TARGET}.cm4 configure -event reset-deassert-post "psoc6_deassert_post ${TARGET}.cm4"
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $_ENABLE_CM0 } {
|
||||||
|
# Use CM0+ by default on dual-core devices
|
||||||
|
targets ${TARGET}.cm0
|
||||||
|
}
|
Loading…
Reference in New Issue