930 lines
29 KiB
C
930 lines
29 KiB
C
/*
|
|
* This file is part of the Black Magic Debug project.
|
|
*
|
|
* Copyright (C) 2022 1BitSquared <info@1bitsquared.com>
|
|
* Written by Rafael Silva <perigoso@riseup.net>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* Support for Renesas RA family of microcontrollers (Arm Core) */
|
|
|
|
#include "general.h"
|
|
#include "target.h"
|
|
#include "target_internal.h"
|
|
#include "cortexm.h"
|
|
#include "adiv5.h"
|
|
|
|
#define RENESAS_PARTID_RA6M2 0x0150
|
|
#define RENESAS_PARTID_RA4M3 0x0310
|
|
|
|
/*
|
|
* Part numbering scheme
|
|
*
|
|
* R7 F A xx x x x x x xx
|
|
* \__/ \_/ \_/ \__/ \_/ \_/ \_/ \_/ \_/ \__/
|
|
* | | | | | | | | | |
|
|
* | | | | | | | | | \_ Package type
|
|
* | | | | | | | | \_____ Quality Grade
|
|
* | | | | | | | \_________ Operating temperature
|
|
* | | | | | | \_____________ Code flash memory size
|
|
* | | | | | \_________________ Feature set
|
|
* | | | | \_____________________ Group number
|
|
* | | | \__________________________ Series name
|
|
* | | \______________________________ family (A: RA)
|
|
* | \__________________________________ Flash memory
|
|
* \_______________________________________ Renesas microcontroller (always 'R7')
|
|
*
|
|
* Renesas Flash MCUs have an internal 16 byte read only register that stores
|
|
* the part number, the code is stored ascii encoded, starting from the lowest memory address
|
|
* except for pnrs stored in 'FIXED_PNR1', where the code is stored in reverse order (but the last 3 bytes are still 0x20 aka ' ')
|
|
*/
|
|
|
|
/* family + series + group no */
|
|
#define PNR_FAMILY_INDEX 3
|
|
#define PNR_SERIES(pnr3, pnr4, pnr5, pnr6) (((pnr3) << 24) | ((pnr4) << 16) | ((pnr5) << 8) | (pnr6))
|
|
typedef enum {
|
|
PNR_SERIES_RA2L1 = PNR_SERIES('A', '2', 'L', '1'),
|
|
PNR_SERIES_RA2E1 = PNR_SERIES('A', '2', 'E', '1'),
|
|
PNR_SERIES_RA2E2 = PNR_SERIES('A', '2', 'E', '2'),
|
|
PNR_SERIES_RA2A1 = PNR_SERIES('A', '2', 'A', '1'),
|
|
PNR_SERIES_RA4M1 = PNR_SERIES('A', '4', 'M', '1'),
|
|
PNR_SERIES_RA4M2 = PNR_SERIES('A', '4', 'M', '2'),
|
|
PNR_SERIES_RA4M3 = PNR_SERIES('A', '4', 'M', '3'),
|
|
PNR_SERIES_RA4E1 = PNR_SERIES('A', '4', 'E', '1'),
|
|
PNR_SERIES_RA4W1 = PNR_SERIES('A', '4', 'W', '1'),
|
|
PNR_SERIES_RA6M1 = PNR_SERIES('A', '6', 'M', '1'),
|
|
PNR_SERIES_RA6M2 = PNR_SERIES('A', '6', 'M', '2'),
|
|
PNR_SERIES_RA6M3 = PNR_SERIES('A', '6', 'M', '3'),
|
|
PNR_SERIES_RA6M4 = PNR_SERIES('A', '6', 'M', '4'),
|
|
PNR_SERIES_RA6M5 = PNR_SERIES('A', '6', 'M', '5'),
|
|
PNR_SERIES_RA6E1 = PNR_SERIES('A', '6', 'E', '1'),
|
|
PNR_SERIES_RA6T1 = PNR_SERIES('A', '6', 'T', '1'),
|
|
PNR_SERIES_RA6T2 = PNR_SERIES('A', '6', 'T', '2'),
|
|
} pnr_series_t;
|
|
|
|
/* Code flash memory size */
|
|
#define PNR_MEMSIZE_INDEX 8
|
|
typedef enum {
|
|
PNR_MEMSIZE_16KB = '3',
|
|
PNR_MEMSIZE_32KB = '5',
|
|
PNR_MEMSIZE_64KB = '7',
|
|
PNR_MEMSIZE_128KB = '9',
|
|
PNR_MEMSIZE_256KB = 'B',
|
|
PNR_MEMSIZE_384KB = 'C',
|
|
PNR_MEMSIZE_512KB = 'D',
|
|
PNR_MEMSIZE_768KB = 'E',
|
|
PNR_MEMSIZE_1MB = 'F',
|
|
PNR_MEMSIZE_1_5MB = 'G',
|
|
PNR_MEMSIZE_2MB = 'H',
|
|
} pnr_memsize_t;
|
|
|
|
/* For future reference, if we want to add an info command
|
|
*
|
|
* Package type
|
|
* FP: LQFP 100 pins 0.5 mm pitch
|
|
* FN: LQFP 80 pins 0.5 mm pitch
|
|
* FM: LQFP 64 pins 0.5 mm pitch
|
|
* FL: LQFP 48 pins 0.5 mm pitch
|
|
* NE: HWQFN 48 pins 0.5 mm pitch
|
|
* FK: LQFP 64 pins 0.8 mm pitch
|
|
* BU: BGA 64 pins 0.4 mm pitch
|
|
* LM: LGA 36 pins 0.5 mm pitch
|
|
* FJ: LQFP 32 pins 0.8 mm pitch
|
|
* NH: HWQFN 32 pins 0.5 mm pitch
|
|
* BV: WLCSP 25 pins 0.4 mm pitch
|
|
* BT: BGA 36 pins
|
|
* NK: HWQFN 24 pins 0.5 mm pitch
|
|
* NJ: HWQFN 20 pins 0.5 mm pitch
|
|
* BY: WLCSP 16 pins 0.4 mm pitch
|
|
* NF: QFN 40 pins
|
|
* LJ: LGA 100 pins
|
|
* NB: QFN 64 pins
|
|
* FB: LQFP 144 pins
|
|
* NG: QFN 56 pins
|
|
* LK: LGA 145 pins
|
|
* BG: BGA 176 pins
|
|
* FC: LQFP 176 pins
|
|
*
|
|
* Quality ID
|
|
* C: Industrial applications
|
|
* D: Consumer applications
|
|
*
|
|
* Operating temperature
|
|
* 2: -40°C to +85°C
|
|
* 3: -40°C to +105°C
|
|
* 4: -40°C to +125°C
|
|
*/
|
|
|
|
/* PNR/UID location by series
|
|
* newer series have a 'Flash Root Table'
|
|
* older series have a fixed location in the flash memory
|
|
*
|
|
* ra2l1 - Fixed location 1
|
|
* ra2e1 - Fixed location 1
|
|
* ra2e2 - Fixed location 1
|
|
* ra2a1 - *undocummented
|
|
* ra4m1 - *undocummented
|
|
* ra4m2 - *undocummented
|
|
* ra4m3 - Fixed location 2 *undocummented
|
|
* ra4e1 - Fixed location 2
|
|
* ra4w1 - *undocummented
|
|
* ra6m1 - Flash Root Table
|
|
* ra6m2 - Flash Root Table
|
|
* ra6m3 - Flash Root Table
|
|
* ra6m4 - Fixed location 2
|
|
* ra6m5 - Fixed location 2
|
|
* ra6e1 - Fixed location 2
|
|
* ra6t1 - Flash Root Table
|
|
* ra6t2 - Fixed location 2
|
|
*/
|
|
#define RENESAS_FIXED1_UID 0x01001C00UL /* Unique ID Register */
|
|
#define RENESAS_FIXED1_PNR 0x01001C10UL /* Part Numbering Register */
|
|
#define RENESAS_FIXED1_MCUVER 0x01001C20UL /* MCU Version Register */
|
|
|
|
#define RENESAS_FIXED2_UID 0x01008190UL /* Unique ID Register */
|
|
#define RENESAS_FIXED2_PNR 0x010080F0UL /* Part Numbering Register */
|
|
#define RENESAS_FIXED2_MCUVER 0x010081B0UL /* MCU Version Register */
|
|
|
|
/* The FMIFRT is a read-only register that stores the Flash Root Table address */
|
|
#define RENESAS_FMIFRT 0x407FB19CUL
|
|
#define RENESAS_FMIFRT_UID(frt) (frt + 0x14UL) /* UID Register offset from Flash Root Table */
|
|
#define RENESAS_FMIFRT_PNR(frt) (frt + 0x24UL) /* PNR Register offset from Flash Root Table */
|
|
#define RENESAS_FMIFRT_MCUVER(frt) (frt + 0x44UL) /* MCUVER Register offset from Flash Root Table */
|
|
|
|
/* System Control OCD Control */
|
|
#define SYSC_BASE 0x4001E000UL
|
|
|
|
#define SYSC_SYOCDCR (SYSC_BASE + 0x40EU) /* System Control OCD Control Register */
|
|
#define SYOCDCR_DBGEN (1U << 7U) /* Debug Enable */
|
|
|
|
#define SYSC_FWEPROR (SYSC_BASE + 0x416U) /* Flash P/E Protect Register */
|
|
#define SYSC_FWEPROR_PERMIT (0x01U)
|
|
#define SYSC_FWEPROR_PROHIBIT (0x10U)
|
|
|
|
/* Flash Memory Control */
|
|
|
|
#define RENESAS_CF_END (0x00200000UL)
|
|
|
|
#define FENTRYR_KEY_OFFSET 8U
|
|
#define FENTRYR_KEY (0xAAU << FENTRYR_KEY_OFFSET)
|
|
#define FENTRYR_PE_CF (1U)
|
|
#define FENTRYR_PE_DF (1U << 7U)
|
|
|
|
/* Renesas RA MCUs can have one of two kinds of flash memory, MF3/4 and RV40 */
|
|
|
|
/* MF3/4 Flash */
|
|
/*
|
|
* MF3/4 Flash Memory Specifications
|
|
* Block Size: Code area: 2 KB (exept RA2A1 is 1KB), Data area: 1 KB
|
|
* Program/Erase unit Program: Code area: 64 bits, Data area: 8 bits
|
|
* Erase: 1 block
|
|
*/
|
|
#define MF3_CF_BLOCK_SIZE (0x800U)
|
|
#define MF3_RA2A1_CF_BLOCK_SIZE (0x400U)
|
|
#define MF3_DF_BLOCK_SIZE (0x400U)
|
|
#define MF3_CF_WRITE_SIZE (0x40U)
|
|
#define MF3_DF_WRITE_SIZE (0x1U)
|
|
|
|
/* RV40 Flash */
|
|
/*
|
|
* RV40F Flash Memory Specifications
|
|
* Block Size: Code area: 8 KB/32KB Data area: 64 Bytes
|
|
* Program/Erase unit Program: Code area: 128 Bytes, Data area: 4/8/16 Bytes
|
|
* Erase: 1 block
|
|
*/
|
|
#define RV40_CF_REGION0_SIZE (0x10000UL)
|
|
#define RV40_CF_REGION0_BLOCK_SIZE (0x2000U)
|
|
#define RV40_CF_REGION1_BLOCK_SIZE (0x8000U)
|
|
#define RV40_DF_BLOCK_SIZE (0x40U)
|
|
#define RV40_CF_WRITE_SIZE (0x80U)
|
|
#define RV40_DF_WRITE_SIZE (0x4U)
|
|
|
|
/* RV40 Flash Commands */
|
|
#define RV40_CMD 0x407E0000UL
|
|
#define RV40_CMD_PROGRAM (0xE8U)
|
|
#define RV40_CMD_PROGRAM_CF (0x80U)
|
|
#define RV40_CMD_PROGRAM_DF (0x02U)
|
|
#define RV40_CMD_BLOCK_ERASE (0x20U)
|
|
#define RV40_CMD_PE_SUSPEND (0xB0U)
|
|
#define RV40_CMD_PE_RESUME (0xD0U)
|
|
#define RV40_CMD_STATUS_CLEAR (0x50U)
|
|
#define RV40_CMD_FORCED_STOP (0xB3U)
|
|
#define RV40_CMD_BLANK_CHECK (0x71U)
|
|
#define RV40_CMD_CONFIG_SET_1 (0x40U)
|
|
#define RV40_CMD_CONFIG_SET_2 (0x08U)
|
|
#define RV40_CMD_LOCK_BIT_PGM (0x77U)
|
|
#define RV40_CMD_LOCK_BIT_READ (0x71U)
|
|
#define RV40_CMD_FINAL (0xD0U)
|
|
|
|
#define RV40_BASE 0x407FE000UL
|
|
|
|
#define RV40_FASTAT (RV40_BASE + 0x10U) /* Flash Access Status */
|
|
#define RV40_FASTAT_CMDLK (1U << 4U) /* Command Lock */
|
|
|
|
#define RV40_FSTATR (RV40_BASE + 0x80U) /* Flash Status */
|
|
|
|
#define RV40_FSTATR_DBFULL (1U << 10U) /* Data Buffer Full */
|
|
#define RV40_FSTATR_RDY (1U << 15U) /* Flash Ready */
|
|
|
|
#define RV40_FSTATR_PRGERR (1U << 12U) /* Programming Error */
|
|
#define RV40_FSTATR_ERSERR (1U << 13U) /* Erasure Error */
|
|
#define RV40_FSTATR_ILGLERR (1U << 14U) /* Illegal Command Error */
|
|
#define RV40_FSTATR_OTERR (1U << 20U) /* Other Error */
|
|
#define RV40_FSTATR_SECERR (1U << 21U) /* Security Error */
|
|
#define RV40_FSTATR_FESETERR (1U << 22U) /* FENTRY Setting Error */
|
|
#define RV40_FSTATR_ILGCOMERR (1U << 23U) /* Illegal Command Error */
|
|
|
|
#define RV40_FSADDR (RV40_BASE + 0x30U)
|
|
|
|
#define RV40_FMEPROT (RV40_BASE + 0x44U)
|
|
#define RV40_FMEPROT_LOCK (0xD901U)
|
|
#define RV40_FMEPROT_UNLOCK (0xD900U)
|
|
|
|
#define RV40_FENTRYR (RV40_BASE + 0x84U)
|
|
#define RV40_FENTRYR_KEY_OFFSET 8U
|
|
#define RV40_FENTRYR_KEY (0xAAU << RV40_FENTRYR_KEY_OFFSET)
|
|
#define RV40_FENTRYR_PE_CF (1U)
|
|
#define RV40_FENTRYR_PE_DF (1U << 7U)
|
|
|
|
#define RV40_FCPSR (RV40_BASE + 0xE0U)
|
|
#define RV40_FCPSR_ESUSPMD 1U
|
|
|
|
#define RENESAS_CF_END (0x00200000UL) /* End of Flash (maximum possible accross families) */
|
|
|
|
static bool renesas_uid(target *t, int argc, const char **argv);
|
|
|
|
const struct command_s renesas_cmd_list[] = {
|
|
{"uid", renesas_uid, "Prints unique number"},
|
|
{NULL, NULL, NULL},
|
|
};
|
|
|
|
typedef struct renesas_priv {
|
|
uint8_t pnr[17]; /* 16-byte PNR + 1-byte null termination */
|
|
pnr_series_t series;
|
|
uint32_t flash_root_table; /* if applicable */
|
|
} renesas_priv_s;
|
|
|
|
static uint32_t renesas_fmifrt_read(target *t)
|
|
{
|
|
return target_mem_read32(t, RENESAS_FMIFRT);
|
|
}
|
|
|
|
static void renesas_uid_read(target *t, const uint32_t base, uint8_t *uid)
|
|
{
|
|
uint32_t uidr[4];
|
|
for (size_t i = 0U; i < 4U; i++)
|
|
uidr[i] = target_mem_read32(t, base + i * 4U);
|
|
|
|
for (size_t i = 0U; i < 16U; i++)
|
|
uid[i] = uidr[i / 4U] >> (i & 3U) * 8U; /* & 3U == % 4U */
|
|
}
|
|
|
|
static bool renesas_pnr_read(target *t, const uint32_t base, uint8_t *pnr)
|
|
{
|
|
uint32_t pnrr[4];
|
|
for (size_t i = 0U; i < 4U; i++)
|
|
pnrr[i] = target_mem_read32(t, base + i * 4U);
|
|
|
|
if (base == RENESAS_FIXED1_PNR) {
|
|
/* Renesas... look what you made me do... */
|
|
/* reverse order, see 'Part numbering scheme' note for context */
|
|
for (size_t i = 0U; i < 13U; i++)
|
|
pnr[i] = pnrr[3U - (i + 3U) / 4U] >> (24U - ((i + 3U) & 3U) * 8U); /* & 3U == % 4U */
|
|
memset(pnr + 13, 0x20, 3);
|
|
} else {
|
|
for (size_t i = 0; i < 16; i++)
|
|
pnr[i] = pnrr[i / 4U] >> (i & 3U) * 8U; /* & 3U == % 4U */
|
|
}
|
|
|
|
/* all Renesas mcus start with 'R7', sanity check */
|
|
return pnr[0] == 'R' && pnr[1] == '7';
|
|
}
|
|
|
|
static pnr_series_t renesas_series(const uint8_t *pnr)
|
|
{
|
|
uint32_t series = 0;
|
|
for (size_t i = 0; i < 4; i++)
|
|
series = (series << 8) | pnr[PNR_FAMILY_INDEX + i];
|
|
|
|
return (pnr_series_t)series;
|
|
}
|
|
|
|
static uint32_t renesas_flash_size(const uint8_t *pnr)
|
|
{
|
|
switch (pnr[PNR_MEMSIZE_INDEX]) {
|
|
case PNR_MEMSIZE_16KB:
|
|
return UINT32_C(16 * 1024);
|
|
case PNR_MEMSIZE_32KB:
|
|
return UINT32_C(32 * 1024);
|
|
case PNR_MEMSIZE_64KB:
|
|
return UINT32_C(64 * 1024);
|
|
case PNR_MEMSIZE_128KB:
|
|
return UINT32_C(128 * 1024);
|
|
case PNR_MEMSIZE_256KB:
|
|
return UINT32_C(256 * 1024);
|
|
case PNR_MEMSIZE_384KB:
|
|
return UINT32_C(384 * 1024);
|
|
case PNR_MEMSIZE_512KB:
|
|
return UINT32_C(512 * 1024);
|
|
case PNR_MEMSIZE_768KB:
|
|
return UINT32_C(768 * 1024);
|
|
case PNR_MEMSIZE_1MB:
|
|
return UINT32_C(1024 * 1024);
|
|
case PNR_MEMSIZE_1_5MB:
|
|
return UINT32_C(1536 * 1024);
|
|
case PNR_MEMSIZE_2MB:
|
|
return UINT32_C(2048 * 1024);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool renesas_enter_flash_mode(target *t)
|
|
{
|
|
target_reset(t);
|
|
|
|
/* permit flash operations */
|
|
target_mem_write8(t, SYSC_FWEPROR, SYSC_FWEPROR_PERMIT);
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef enum pe_mode {
|
|
PE_MODE_READ,
|
|
PE_MODE_CF,
|
|
PE_MODE_DF,
|
|
} pe_mode_e;
|
|
|
|
static bool renesas_rv40_pe_mode(target *t, pe_mode_e pe_mode)
|
|
{
|
|
/* See "Transition to Code Flash P/E Mode": Section 47.9.3.3 of the RA6M4 manual R01UH0890EJ0100. */
|
|
|
|
renesas_priv_s *priv_storage = (renesas_priv_s *)t->target_storage;
|
|
if (!priv_storage)
|
|
return false;
|
|
|
|
bool has_fmeprot = false; /* Code Flash P/E Mode Entry Protection */
|
|
switch (priv_storage->series) {
|
|
case PNR_SERIES_RA4E1:
|
|
case PNR_SERIES_RA4M2:
|
|
case PNR_SERIES_RA4M3:
|
|
case PNR_SERIES_RA6M4:
|
|
case PNR_SERIES_RA6M5:
|
|
case PNR_SERIES_RA6E1:
|
|
case PNR_SERIES_RA6T2:
|
|
has_fmeprot = true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (has_fmeprot)
|
|
target_mem_write16(t, RV40_FMEPROT, RV40_FMEPROT_UNLOCK);
|
|
|
|
/* Set PE/READ mode */
|
|
uint16_t fentryr = 0;
|
|
switch (pe_mode) {
|
|
case PE_MODE_CF:
|
|
fentryr |= FENTRYR_PE_CF;
|
|
break;
|
|
case PE_MODE_DF:
|
|
fentryr |= FENTRYR_PE_DF;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
target_mem_write16(t, RV40_FENTRYR, FENTRYR_KEY | fentryr);
|
|
|
|
platform_timeout timeout;
|
|
platform_timeout_set(&timeout, 10);
|
|
|
|
/* Wait for the operation to complete or timeout, Read until FENTRYR and FRDY is set */
|
|
while (target_mem_read16(t, RV40_FENTRYR) != fentryr || !(target_mem_read32(t, RV40_FSTATR) & RV40_FSTATR_RDY)) {
|
|
if (target_check_error(t) || platform_timeout_is_expired(&timeout))
|
|
return false;
|
|
}
|
|
|
|
if (has_fmeprot && pe_mode == PE_MODE_READ)
|
|
target_mem_write16(t, RV40_FMEPROT, RV40_FMEPROT_LOCK);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool renesas_rv40_error_check(target *t, uint32_t error_bits)
|
|
{
|
|
bool error = false;
|
|
|
|
uint8_t fstatr = target_mem_read32(t, RV40_FSTATR);
|
|
|
|
/* see "Recovery from the Command-Locked State": Section 47.9.3.6 of the RA6M4 manual R01UH0890EJ0100.*/
|
|
if (target_mem_read8(t, RV40_FASTAT) & RV40_FASTAT_CMDLK) {
|
|
/* if an illegal error occurred read and clear CFAE and DFAE in FASTAT. */
|
|
if (fstatr & RV40_FSTATR_ILGLERR) {
|
|
target_mem_read8(t, RV40_FASTAT);
|
|
target_mem_write8(t, RV40_FASTAT, 0);
|
|
}
|
|
error = true;
|
|
}
|
|
|
|
/* check if status is indicating a programming error */
|
|
if (fstatr & error_bits)
|
|
error = true;
|
|
|
|
if (error) {
|
|
/* Stop the flash */
|
|
target_mem_write8(t, RV40_CMD, RV40_CMD_FORCED_STOP);
|
|
|
|
platform_timeout timeout;
|
|
platform_timeout_set(&timeout, 10);
|
|
|
|
/* Wait until the operation has completed or timeout */
|
|
/* Read FRDY bit until it has been set to 1 indicating that the current operation is complete.*/
|
|
while (!(target_mem_read32(t, RV40_FSTATR) & RV40_FSTATR_RDY)) {
|
|
if (target_check_error(t) || platform_timeout_is_expired(&timeout))
|
|
return error;
|
|
}
|
|
|
|
if (target_mem_read8(t, RV40_FASTAT) & RV40_FASTAT_CMDLK)
|
|
return error;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static bool renesas_rv40_prepare(target_flash_s *f)
|
|
{
|
|
target *t = f->t;
|
|
|
|
if (!(target_mem_read32(t, RV40_FSTATR) & RV40_FSTATR_RDY) || target_mem_read16(t, RV40_FENTRYR) != 0) {
|
|
DEBUG_WARN("flash is not ready, may be hanging mid unfinished command due to something going wrong, "
|
|
"please power on reset the device\n");
|
|
|
|
return false;
|
|
}
|
|
|
|
/* code flash or data flash operation */
|
|
const bool code_flash = f->start < RENESAS_CF_END;
|
|
|
|
/* Transition to PE mode */
|
|
const pe_mode_e pe_mode = code_flash ? PE_MODE_CF : PE_MODE_DF;
|
|
|
|
return renesas_rv40_pe_mode(t, pe_mode) && !renesas_rv40_error_check(t, RV40_FSTATR_ILGLERR);
|
|
}
|
|
|
|
static bool renesas_rv40_done(target_flash_s *f)
|
|
{
|
|
target *t = f->t;
|
|
|
|
/* return to read mode */
|
|
return renesas_rv40_pe_mode(t, PE_MODE_READ);
|
|
}
|
|
|
|
/* !TODO: implement blank check */
|
|
static bool renesas_rv40_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
|
|
{
|
|
target *t = f->t;
|
|
|
|
/* code flash or data flash operation */
|
|
const bool code_flash = addr < RENESAS_CF_END;
|
|
|
|
/* Set Erasure Priority Mode */
|
|
target_mem_write16(t, RV40_FCPSR, RV40_FCPSR_ESUSPMD);
|
|
|
|
while (len) {
|
|
/* Set block start address*/
|
|
target_mem_write32(t, RV40_FSADDR, addr);
|
|
|
|
/* increment block address */
|
|
uint16_t block_size;
|
|
if (code_flash)
|
|
block_size = addr < RV40_CF_REGION0_SIZE ? RV40_CF_REGION0_BLOCK_SIZE : RV40_CF_REGION1_BLOCK_SIZE;
|
|
else
|
|
block_size = RV40_DF_BLOCK_SIZE;
|
|
|
|
addr += block_size;
|
|
len -= block_size;
|
|
|
|
/* Issue two part Block Erase commands */
|
|
target_mem_write8(t, RV40_CMD, RV40_CMD_BLOCK_ERASE);
|
|
target_mem_write8(t, RV40_CMD, RV40_CMD_FINAL);
|
|
|
|
/* according to reference manual the max erase time for a 32K block is around 1040ms
|
|
* this is with a FCLK of 4MHz
|
|
*/
|
|
platform_timeout timeout;
|
|
platform_timeout_set(&timeout, 1100);
|
|
|
|
/* Wait until the operation has completed or timeout */
|
|
/* Read FRDY bit until it has been set to 1 indicating that the current operation is complete.*/
|
|
while (!(target_mem_read32(t, RV40_FSTATR) & RV40_FSTATR_RDY)) {
|
|
if (target_check_error(t) || platform_timeout_is_expired(&timeout))
|
|
return false;
|
|
}
|
|
|
|
if (renesas_rv40_error_check(t, RV40_FSTATR_ERSERR | RV40_FSTATR_ILGLERR))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool renesas_rv40_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len)
|
|
{
|
|
target *t = f->t;
|
|
|
|
/* code flash or data flash operation */
|
|
const bool code_flash = dest < RENESAS_CF_END;
|
|
|
|
/* write size for code flash / data flash */
|
|
const uint8_t write_size = code_flash ? RV40_CF_WRITE_SIZE : RV40_DF_WRITE_SIZE;
|
|
|
|
while (len) {
|
|
/* set block start address */
|
|
target_mem_write32(t, RV40_FSADDR, dest);
|
|
|
|
/* increment destination address */
|
|
dest += write_size;
|
|
len -= write_size;
|
|
|
|
/* issue two part Write commands */
|
|
target_mem_write8(t, RV40_CMD, RV40_CMD_PROGRAM);
|
|
target_mem_write8(t, RV40_CMD, (uint8_t)(write_size / 2U));
|
|
|
|
/* according to reference manual the data buffer full time for 2 bytes is 2 usec.
|
|
* this is with a FCLK of 4MHz
|
|
* a complete should take less than 1 msec.
|
|
*/
|
|
platform_timeout timeout;
|
|
platform_timeout_set(&timeout, 10);
|
|
|
|
/* write one chunk */
|
|
for (size_t i = 0U; i < (write_size / 2U); i++) {
|
|
/* copy data from source address to destination */
|
|
target_mem_write16(t, RV40_CMD, *(uint16_t *)src);
|
|
|
|
/* 2 bytes of data */
|
|
src += 2U;
|
|
}
|
|
|
|
/* issue write end command */
|
|
target_mem_write8(t, RV40_CMD, RV40_CMD_FINAL);
|
|
|
|
/* wait until the operation has completed or timeout */
|
|
/* read FRDY bit until it has been set to 1 indicating that the current operation is complete.*/
|
|
while (!(target_mem_read32(t, RV40_FSTATR) & RV40_FSTATR_RDY)) {
|
|
if (target_check_error(t) || platform_timeout_is_expired(&timeout))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !renesas_rv40_error_check(t, RV40_FSTATR_PRGERR | RV40_FSTATR_ILGLERR);
|
|
}
|
|
|
|
static void renesas_add_rv40_flash(target *t, target_addr_t addr, size_t length)
|
|
{
|
|
target_flash_s *f = calloc(1, sizeof(*f));
|
|
if (!f) /* calloc failed: heap exhaustion */
|
|
return;
|
|
|
|
const bool code_flash = addr < RENESAS_CF_END;
|
|
|
|
f->start = addr;
|
|
f->length = length;
|
|
f->erased = 0xffU;
|
|
f->erase = renesas_rv40_flash_erase;
|
|
f->write = renesas_rv40_flash_write;
|
|
f->prepare = renesas_rv40_prepare;
|
|
f->done = renesas_rv40_done;
|
|
|
|
if (code_flash) {
|
|
f->blocksize = RV40_CF_REGION1_BLOCK_SIZE;
|
|
f->writebufsize = RV40_CF_WRITE_SIZE * 8U;
|
|
f->writesize = RV40_CF_WRITE_SIZE;
|
|
} else {
|
|
f->blocksize = RV40_DF_BLOCK_SIZE;
|
|
f->writebufsize = RV40_DF_BLOCK_SIZE * 8U;
|
|
f->writesize = RV40_DF_WRITE_SIZE;
|
|
}
|
|
|
|
target_add_flash(t, f);
|
|
}
|
|
|
|
static void renesas_add_flash(target *t, target_addr_t addr, size_t length)
|
|
{
|
|
renesas_priv_s *priv_storage = (renesas_priv_s *)t->target_storage;
|
|
if (!priv_storage)
|
|
return;
|
|
|
|
/* Renesas RA MCUs can have one of two kinds of flash memory, MF3/4 and RV40
|
|
* Flash type by series:
|
|
* ra2l1 - MF4
|
|
* ra2e1 - MF4
|
|
* ra2e2 - MF4
|
|
* ra2a1 - MF3
|
|
* ra4m1 - MF3
|
|
* ra4m2 - RV40
|
|
* ra4m3 - RV40
|
|
* ra4e1 - RV40
|
|
* ra4w1 - MF3
|
|
* ra6m1 - RV40
|
|
* ra6m2 - RV40
|
|
* ra6m3 - RV40
|
|
* ra6m4 - RV40
|
|
* ra6m5 - RV40
|
|
* ra6e1 - RV40
|
|
* ra6t1 - RV40
|
|
* ra6t2 - RV40
|
|
*/
|
|
|
|
switch (priv_storage->series) {
|
|
case PNR_SERIES_RA2L1:
|
|
case PNR_SERIES_RA2E1:
|
|
case PNR_SERIES_RA2E2:
|
|
case PNR_SERIES_RA2A1:
|
|
case PNR_SERIES_RA4M1:
|
|
case PNR_SERIES_RA4W1:
|
|
return;
|
|
|
|
case PNR_SERIES_RA4M2:
|
|
case PNR_SERIES_RA4M3:
|
|
case PNR_SERIES_RA4E1:
|
|
case PNR_SERIES_RA6M1:
|
|
case PNR_SERIES_RA6M2:
|
|
case PNR_SERIES_RA6M3:
|
|
case PNR_SERIES_RA6M4:
|
|
case PNR_SERIES_RA6E1:
|
|
case PNR_SERIES_RA6M5:
|
|
case PNR_SERIES_RA6T1:
|
|
case PNR_SERIES_RA6T2:
|
|
t->enter_flash_mode = renesas_enter_flash_mode;
|
|
return renesas_add_rv40_flash(t, addr, length);
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool renesas_probe(target *t)
|
|
{
|
|
uint8_t pnr[16]; /* 16-byte PNR */
|
|
uint32_t flash_root_table = 0;
|
|
|
|
/* Enable debug */
|
|
/* a read back doesn't seem to show the change, tried 32-bit write too */
|
|
/* See "DBGEN": Section 2.13.1 of the RA6M4 manual R01UH0890EJ0100. */
|
|
target_mem_write8(t, SYSC_SYOCDCR, SYOCDCR_DBGEN);
|
|
|
|
/* Read the PNR */
|
|
switch (t->part_id) {
|
|
// case :
|
|
/* mcus with PNR located at 0x01001C10
|
|
* ra2l1 (part_id wanted)
|
|
* ra2e1 (part_id wanted)
|
|
* ra2e2 (part_id wanted)
|
|
*/
|
|
// if (!renesas_pnr_read(t, RENESAS_FIXED1_PNR, pnr))
|
|
// return false;
|
|
// break;
|
|
|
|
case RENESAS_PARTID_RA4M3:
|
|
/* mcus with PNR located at 0x010080F0
|
|
* ra4e1 (part_id wanted)
|
|
* ra6m4 (part_id wanted)
|
|
* ra6m5 (part_id wanted)
|
|
* ra6e1 (part_id wanted)
|
|
* ra6t2 (part_id wanted)
|
|
*/
|
|
if (!renesas_pnr_read(t, RENESAS_FIXED2_PNR, pnr))
|
|
return false;
|
|
break;
|
|
|
|
case RENESAS_PARTID_RA6M2:
|
|
/* mcus with Flash Root Table
|
|
* ra6m1 (part_id wanted)
|
|
* ra6m3 (part_id wanted)
|
|
* ra6t1 (part_id wanted)
|
|
*/
|
|
flash_root_table = renesas_fmifrt_read(t);
|
|
if (!renesas_pnr_read(t, RENESAS_FMIFRT_PNR(flash_root_table), pnr))
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* unknown part_id, we know this AP is from renesas, so Let's try brute forcing
|
|
* unfortunately, this is will lead to illegal memory accesses,
|
|
* but experimentally there doesn't seem to be an issue with these in particular
|
|
*
|
|
* try the fixed address RENESAS_FIXED2_PNR first, as it should lead to less illegal/erroneous
|
|
* memory accesses in case of failure, and is the most common case
|
|
*/
|
|
/*
|
|
* ra2a1 *undocummented (part_id + pnr loc wanted)
|
|
* ra4m1 *undocummented (part_id + pnr loc wanted)
|
|
* ra4m2 *undocummented (part_id + pnr loc wanted)
|
|
* ra4w1 *undocummented (part_id + pnr loc wanted)
|
|
*/
|
|
|
|
if (renesas_pnr_read(t, RENESAS_FIXED2_PNR, pnr)) {
|
|
DEBUG_WARN("Found renesas chip (%.*s) with pnr location RENESAS_FIXED2_PNR and unsupported Part ID %" PRIx16
|
|
" please report it\n",
|
|
sizeof(pnr), pnr, t->part_id);
|
|
break;
|
|
}
|
|
|
|
if (renesas_pnr_read(t, RENESAS_FIXED1_PNR, pnr)) {
|
|
DEBUG_WARN("Found renesas chip (%.*s) with pnr location RENESAS_FIXED1_PNR and unsupported Part ID "
|
|
"0x%" PRIx16 " please report it\n",
|
|
sizeof(pnr), pnr, t->part_id);
|
|
break;
|
|
}
|
|
|
|
flash_root_table = renesas_fmifrt_read(t);
|
|
if (renesas_pnr_read(t, RENESAS_FMIFRT_PNR(flash_root_table), pnr)) {
|
|
DEBUG_WARN("Found renesas chip (%.*s) with Flash Root Table and unsupported Part ID 0x%" PRIx16 " "
|
|
"please report it\n",
|
|
sizeof(pnr), pnr, t->part_id);
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
renesas_priv_s *priv_storage = calloc(1, sizeof(renesas_priv_s));
|
|
if (!priv_storage) /* calloc failed: heap exhaustion */
|
|
return false;
|
|
memcpy(priv_storage->pnr, pnr, sizeof(pnr));
|
|
|
|
priv_storage->series = renesas_series(pnr);
|
|
priv_storage->flash_root_table = flash_root_table;
|
|
|
|
t->target_storage = (void *)priv_storage;
|
|
t->driver = (char *)priv_storage->pnr;
|
|
|
|
switch (priv_storage->series) {
|
|
case PNR_SERIES_RA2L1:
|
|
case PNR_SERIES_RA2A1:
|
|
case PNR_SERIES_RA4M1:
|
|
renesas_add_flash(t, 0x40100000, 8UL * 1024UL); /* Data flash memory 8 KB 0x40100000 */
|
|
target_add_ram(t, 0x20000000, 32UL * 1024UL); /* SRAM 32 KB 0x20000000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA2E1:
|
|
renesas_add_flash(t, 0x40100000, 4UL * 1024UL); /* Data flash memory 4 KB 0x40100000 */
|
|
target_add_ram(t, 0x20004000, 16UL * 1024UL); /* SRAM 16 KB 0x20004000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA2E2:
|
|
renesas_add_flash(t, 0x40100000, 2UL * 1024UL); /* Data flash memory 2 KB 0x40100000 */
|
|
target_add_ram(t, 0x20004000, 8UL * 1024UL); /* SRAM 8 KB 0x20004000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA4M2:
|
|
case PNR_SERIES_RA4M3:
|
|
case PNR_SERIES_RA4E1:
|
|
renesas_add_flash(t, 0x08000000, 8UL * 1024UL); /* Data flash memory 8 KB 0x08000000 */
|
|
target_add_ram(t, 0x20000000, 128UL * 1024UL); /* SRAM 128 KB 0x20000000 */
|
|
target_add_ram(t, 0x28000000, 1024UL); /* Standby SRAM 1 KB 0x28000000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA4W1:
|
|
renesas_add_flash(t, 0x40100000, 8UL * 1024UL); /* Data flash memory 8 KB 0x40100000 */
|
|
target_add_ram(t, 0x20000000, 96UL * 1024UL); /* SRAM 96 KB 0x20000000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M1:
|
|
/* conflicting information in the datasheet, here be dragons */
|
|
renesas_add_flash(t, 0x40100000, 8UL * 1024UL); /* Data flash memory 8 KB 0x40100000 */
|
|
target_add_ram(t, 0x20000000, 128UL * 1024UL); /* SRAM 128 KB 0x20000000 */
|
|
target_add_ram(t, 0x1FFE0000, 128UL * 1024UL); /* SRAMHS 128 KB 0x1FFE0000 */
|
|
target_add_ram(t, 0x200FE000, 8UL * 1024UL); /* Standby SRAM 8 KB 0x200FE000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M2:
|
|
renesas_add_flash(t, 0x40100000, 32UL * 1024UL); /* Data flash memory 32 KB 0x40100000 */
|
|
target_add_ram(t, 0x20000000, 256UL * 1024UL); /* SRAM 256 KB 0x20000000 */
|
|
target_add_ram(t, 0x1FFE0000, 128UL * 1024UL); /* SRAMHS 128 KB 0x1FFE0000 */
|
|
target_add_ram(t, 0x200FE000, 8UL * 1024UL); /* Standby SRAM 8 KB 0x200FE000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M3:
|
|
renesas_add_flash(t, 0x40100000, 64UL * 1024UL); /* Data flash memory 64 KB 0x40100000 */
|
|
target_add_ram(t, 0x20000000, 256UL * 1024UL); /* SRAM0 256 KB 0x20000000 */
|
|
target_add_ram(t, 0x20040000, 256UL * 1024UL); /* SRAM1 256 KB 0x20040000 */
|
|
target_add_ram(t, 0x1FFE0000, 128UL * 1024UL); /* SRAMHS 128 KB 0x1FFE0000 */
|
|
target_add_ram(t, 0x200FE000, 8UL * 1024UL); /* Standby SRAM 8 KB 0x200FE000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M4:
|
|
case PNR_SERIES_RA6E1:
|
|
renesas_add_flash(t, 0x08000000, 8UL * 1024UL); /* Data flash memory 8 KB 0x08000000 */
|
|
target_add_ram(t, 0x20000000, 256UL * 1024UL); /* SRAM 256 KB 0x20000000 */
|
|
target_add_ram(t, 0x28000000, 1024UL); /* Standby SRAM 1 KB 0x28000000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M5:
|
|
renesas_add_flash(t, 0x08000000, 8UL * 1024UL); /* Data flash memory 8 KB 0x08000000 */
|
|
target_add_ram(t, 0x20000000, 512UL * 1024UL); /* SRAM 512 KB 0x20000000 */
|
|
target_add_ram(t, 0x28000000, 1024UL); /* Standby SRAM 1 KB 0x28000000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6T1:
|
|
renesas_add_flash(t, 0x40100000, 8UL * 1024UL); /* Data flash memory 8 KB 0x40100000 */
|
|
target_add_ram(t, 0x1FFE0000, 64UL * 1024UL); /* SRAMHS 64 KB 0x1FFE0000 */
|
|
break;
|
|
|
|
case PNR_SERIES_RA6T2:
|
|
renesas_add_flash(t, 0x08000000, 16UL * 1024UL); /* Data flash memory 16 KB 0x08000000 */
|
|
target_add_ram(t, 0x20000000, 64UL * 1024UL); /* SRAM 64 KB 0x20000000 */
|
|
target_add_ram(t, 0x28000000, 1024UL); /* Standby SRAM 1 KB 0x28000000 */
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
renesas_add_flash(t, 0x00000000, renesas_flash_size(pnr)); /* Code flash memory 0x00000000 */
|
|
|
|
target_add_commands(t, renesas_cmd_list, t->driver);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Reads the 16-byte unique number */
|
|
static bool renesas_uid(target *t, int argc, const char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
renesas_priv_s *priv_storage = (renesas_priv_s *)t->target_storage;
|
|
if (!priv_storage)
|
|
return false;
|
|
|
|
uint8_t uid[16];
|
|
uint32_t uid_addr;
|
|
|
|
switch (priv_storage->series) {
|
|
case PNR_SERIES_RA2L1:
|
|
case PNR_SERIES_RA2E1:
|
|
case PNR_SERIES_RA2E2:
|
|
uid_addr = RENESAS_FIXED1_UID;
|
|
break;
|
|
|
|
case PNR_SERIES_RA2A1:
|
|
case PNR_SERIES_RA4M1:
|
|
case PNR_SERIES_RA4M2:
|
|
case PNR_SERIES_RA4M3:
|
|
case PNR_SERIES_RA4E1:
|
|
case PNR_SERIES_RA4W1:
|
|
case PNR_SERIES_RA6M4:
|
|
case PNR_SERIES_RA6M5:
|
|
case PNR_SERIES_RA6E1:
|
|
case PNR_SERIES_RA6T2:
|
|
uid_addr = RENESAS_FIXED2_UID;
|
|
break;
|
|
|
|
case PNR_SERIES_RA6M1:
|
|
case PNR_SERIES_RA6M2:
|
|
case PNR_SERIES_RA6M3:
|
|
case PNR_SERIES_RA6T1:
|
|
uid_addr = RENESAS_FMIFRT_UID(priv_storage->flash_root_table);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
renesas_uid_read(t, uid_addr, uid);
|
|
|
|
tc_printf(t, "Unique Number: 0x");
|
|
for (size_t i = 0U; i < 16U; i++)
|
|
tc_printf(t, "%02" PRIx8, uid[i]);
|
|
tc_printf(t, "\n");
|
|
|
|
return true;
|
|
}
|