flash/stm32h7x: support STM32H7R/H7Sx

The STM32H7R/H7Sx has a flash size up to 64 Kb
Change-Id: I2e9d80758d1bc88defdd6bbd1787026373b39fa4
Signed-off-by: HAOUES Ahmed <ahmed.haoues@st.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8890
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins
This commit is contained in:
HAOUES Ahmed
2025-07-23 14:57:11 +01:00
committed by Tomas Vanek
parent b8dc15f43b
commit 03b79387cc
4 changed files with 310 additions and 20 deletions

View File

@@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* Copyright (C) 2017 by STMicroelectronics *
***************************************************************************/
.text
.syntax unified
.cpu cortex-m4
.thumb
/*
* Code limitations:
* The workarea must have size multiple of 4 bytes, since R/W
* operations are all at 32 bits.
* The workarea must be big enough to contain rp, wp and data, thus the minimum
* workarea size is: min_wa_size = sizeof(rp, wp, data) = 4 + 4 + sizeof(data).
* - for 0x450 devices: sizeof(data) = 32 bytes, thus min_wa_size = 40 bytes.
* - for 0x480 devices: sizeof(data) = 16 bytes, thus min_wa_size = 24 bytes.
* To benefit from concurrent host write-to-buffer and target
* write-to-flash, the workarea must be way bigger than the minimum.
*
* To avoid confusions the write word size is got from .block_size member of
* struct stm32h7x_part_info defined in stm32h7x.c
*/
/*
* Params :
* r0 = workarea start, status (out)
* r1 = workarea end
* r2 = target address
* r3 = count (of write words)
* r4 = size of write word
* r5 = flash reg base
*
* Clobbered:
* r6 - rp
* r7 - wp, status, tmp
* r8 - loop index, tmp
*/
#define STM32_FLASH_CR_OFFSET 0x10 /* offset of CR register in FLASH struct */
#define STM32_FLASH_SR_OFFSET 0x14 /* offset of SR register in FLASH struct */
#define STM32_FLASH_ICR_OFFSET 0x28 /* offset of SR register in FLASH struct */
#define STM32_CR_PROG 0x00000002 /* PG */
#define STM32_SR_QW_MASK 0x00000004 /* QW */
#define STM32_SR_ERROR_MASK 0x1F2E0000 /* DBECCERR | SNECCERR | RDSERR | RDPERR | OPERR
| INCERR | STRBERR | PGSERR | WRPERR */
.thumb_func
.global _start
_start:
ldr r6, [r0, #4] /* read rp */
wait_fifo:
ldr r7, [r0, #0] /* read wp */
cbz r7, exit /* abort if wp == 0, status = 0 */
subs r7, r7, r6 /* number of bytes available for read in r7 */
ittt mi /* if wrapped around */
addmi r7, r1 /* add size of buffer */
submi r7, r0
submi r7, #8
cmp r7, r4 /* wait until data buffer is full */
bcc wait_fifo
mov r7, #STM32_CR_PROG
str r7, [r5, #STM32_FLASH_CR_OFFSET]
mov r8, #4
udiv r8, r4, r8 /* number of words is size of write word divided by 4*/
write_flash:
dsb
ldr r7, [r6], #0x04 /* read one word from src, increment ptr */
str r7, [r2], #0x04 /* write one word to dst, increment ptr */
dsb
cmp r6, r1 /* if rp >= end of buffer ... */
it cs
addcs r6, r0, #8 /* ... then wrap at buffer start */
subs r8, r8, #1 /* decrement loop index */
bne write_flash /* loop if not done */
busy:
ldr r7, [r5, #STM32_FLASH_SR_OFFSET]
tst r7, #STM32_SR_QW_MASK
bne busy /* operation in progress, wait ... */
ldr r7, [r5, #STM32_FLASH_ICR_OFFSET]
ldr r8, =STM32_SR_ERROR_MASK
tst r7, r8
bne error /* fail... */
str r6, [r0, #4] /* store rp */
subs r3, r3, #1 /* decrement count */
bne wait_fifo /* loop if not done */
b exit
error:
movs r8, #0
str r8, [r0, #4] /* set rp = 0 on error */
exit:
mov r0, r7 /* return status in r0 */
bkpt #0x00
.pool

View File

@@ -0,0 +1,8 @@
/* Autogenerated with ../../../../src/helper/bin2char.sh */
0x46,0x68,0x07,0x68,0x77,0xb3,0xbf,0x1b,0x42,0xbf,0x7f,0x18,0x3f,0x1a,0x08,0x3f,
0xa7,0x42,0xf6,0xd3,0x4f,0xf0,0x02,0x07,0x2f,0x61,0x4f,0xf0,0x04,0x08,0xb4,0xfb,
0xf8,0xf8,0xbf,0xf3,0x4f,0x8f,0x56,0xf8,0x04,0x7b,0x42,0xf8,0x04,0x7b,0xbf,0xf3,
0x4f,0x8f,0x8e,0x42,0x28,0xbf,0x00,0xf1,0x08,0x06,0xb8,0xf1,0x01,0x08,0xf0,0xd1,
0x6f,0x69,0x17,0xf0,0x04,0x0f,0xfb,0xd1,0xaf,0x6a,0xdf,0xf8,0x1c,0x80,0x17,0xea,
0x08,0x0f,0x03,0xd1,0x46,0x60,0x01,0x3b,0xd3,0xd1,0x03,0xe0,0x5f,0xf0,0x00,0x08,
0xc0,0xf8,0x04,0x80,0x38,0x46,0x00,0xbe,0x00,0x00,0x2e,0x1f,

View File

@@ -18,14 +18,21 @@
#define FLASH_WRITE_TIMEOUT 5
#define MASS_ERASE_TIMEOUT 30000
static const uint8_t stm32h7_flash_write_code[] = {
#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc"
};
static const uint8_t stm32h7rs_flash_write_code[] = {
#include "../../../contrib/loaders/flash/stm32/stm32h7rx.inc"
};
enum stm32h7_flash_reg_index {
STM32_FLASH_ACR_INDEX,
STM32_FLASH_KEYR_INDEX,
STM32_FLASH_OPTKEYR_INDEX,
STM32_FLASH_SR_INDEX,
STM32_FLASH_CR_INDEX,
STM32_FLASH_ICR_INDEX,
STM32_FLASH_CCR_INDEX,
STM32_FLASH_ICR_CCR_INDEX,
STM32_FLASH_OPTCR_INDEX,
STM32_FLASH_OPTSR_INDEX,
STM32_FLASH_OPTSR_CUR_INDEX,
@@ -46,7 +53,7 @@ static const uint32_t stm32h7_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
[STM32_FLASH_OPTKEYR_INDEX] = 0x08,
[STM32_FLASH_SR_INDEX] = 0x10,
[STM32_FLASH_CR_INDEX] = 0x0C,
[STM32_FLASH_CCR_INDEX] = 0x14,
[STM32_FLASH_ICR_CCR_INDEX] = 0x14,
[STM32_FLASH_OPTCR_INDEX] = 0x18,
[STM32_FLASH_OPTSR_CUR_INDEX] = 0x1C,
[STM32_FLASH_OPTSR_PRG_INDEX] = 0x20,
@@ -55,6 +62,18 @@ static const uint32_t stm32h7_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
[STM32_FLASH_WPSN_PRG_INDEX] = 0x3C
};
static const uint32_t stm32h7rs_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
[STM32_FLASH_ACR_INDEX] = 0x00,
[STM32_FLASH_KEYR_INDEX] = 0x04,
[STM32_FLASH_OPTKEYR_INDEX] = 0x100,
[STM32_FLASH_SR_INDEX] = 0x14,
[STM32_FLASH_CR_INDEX] = 0x10,
[STM32_FLASH_ICR_CCR_INDEX] = 0x28,
[STM32_FLASH_OPTCR_INDEX] = 0x104,
[STM32_FLASH_OPTSR_INDEX] = 0x10C,
[STM32_FLASH_ISR_INDEX] = 0x24,
};
/* FLASH_CR register bits */
#define FLASH_LOCK BIT(0)
#define FLASH_PG BIT(1)
@@ -67,6 +86,19 @@ static const uint32_t stm32h7_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
#define FLASH_FW BIT(6)
#define FLASH_START BIT(7)
/* FLASH_ISR register bits for H7RS */
#define FLASH_CRCRDERRF BIT(28) /* CRC read error flag */
#define FLASH_CRCENDF BIT(27) /* CRC end flag */
#define FLASH_DBECCERRF BIT(26) /* ECC double error flag */
#define FLASH_SNECCERRF BIT(25) /* ECC single error flag */
#define FLASH_RDSERRF BIT(24) /* Read security error flag */
#define FLASH_INCERRF BIT(21) /* Inconsistency error flag */
#define FLASH_OBLERRF BIT(20) /* Option byte loading error flag */
#define FLASH_STRBERRF BIT(19) /* Strobe error flag */
#define FLASH_PGSERRF BIT(18) /* Programming sequence error flag */
#define FLASH_WRPERRF BIT(17) /* Write protection error flag */
#define FLASH_EOPF BIT(16) /* End-of-program flag */
/* FLASH_SR register bits */
#define FLASH_BSY BIT(0) /* Operation in progress */
#define FLASH_QW BIT(2) /* Operation queue in progress */
@@ -82,6 +114,9 @@ static const uint32_t stm32h7_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
#define FLASH_ERROR (FLASH_WRPERR | FLASH_PGSERR | FLASH_STRBERR | FLASH_INCERR | FLASH_OPERR | \
FLASH_RDPERR | FLASH_RDSERR | FLASH_SNECCERR | FLASH_DBECCERR)
/* Possible errors for H7RS */
#define FLASH_ERROR_H7RS (FLASH_CRCRDERRF | FLASH_CRCENDF | FLASH_DBECCERRF | FLASH_SNECCERRF | FLASH_RDSERRF | \
FLASH_INCERRF | FLASH_OBLERRF | FLASH_STRBERRF | FLASH_PGSERRF | FLASH_WRPERRF | FLASH_EOPF)
/* FLASH_OPTCR register bits */
#define OPT_LOCK BIT(0)
@@ -114,6 +149,7 @@ static const uint32_t stm32h7_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
#define DEVID_STM32H74_H75XX 0x450
#define DEVID_STM32H7A_H7BXX 0x480
#define DEVID_STM32H72_H73XX 0x483
#define DEVID_STM32H7R_H7SXX 0x485
struct stm32h7_rev {
uint16_t rev;
@@ -139,6 +175,9 @@ struct stm32h7_part_info {
/* function to compute flash_cr register values */
uint32_t (*compute_flash_cr)(uint32_t cmd, int snb);
int (*get_flash_error_status)(struct flash_bank *bank, uint32_t *status);
uint32_t flash_error;
const uint8_t *write_code;
size_t write_code_size;
};
struct stm32h7_flash_bank {
@@ -168,12 +207,16 @@ static const struct stm32h7_rev stm32h72_h73xx_revs[] = {
{ 0x1000, "A" }, { 0x1001, "Z" },
};
static const struct stm32h7_rev stm32h7r_h7sxx_revs[] = {
{ 0x1000, "A" }, { 0x2000, "B" },
};
static uint32_t stm32h74_h75xx_compute_flash_cr(uint32_t cmd, int snb)
{
return cmd | (snb << 8);
}
static uint32_t stm32h7a_h7bxx_compute_flash_cr(uint32_t cmd, int snb)
static uint32_t stm32h7a_h7b_h7r_h7sxx_compute_flash_cr(uint32_t cmd, int snb)
{
/* save FW and START bits, to be right shifted by 2 bits later */
const uint32_t tmp = cmd & (FLASH_FW | FLASH_START);
@@ -185,6 +228,7 @@ static uint32_t stm32h7a_h7bxx_compute_flash_cr(uint32_t cmd, int snb)
}
static int stm32h7_get_flash_status(struct flash_bank *bank, uint32_t *status);
static int stm32h7rs_get_flash_status(struct flash_bank *bank, uint32_t *status);
static const struct stm32h7_part_info stm32h7_parts[] = {
{
@@ -202,6 +246,9 @@ static const struct stm32h7_part_info stm32h7_parts[] = {
.wps_mask = 0xFF,
.compute_flash_cr = stm32h74_h75xx_compute_flash_cr,
.get_flash_error_status = stm32h7_get_flash_status,
.flash_error = FLASH_ERROR,
.write_code = stm32h7_flash_write_code,
.write_code_size = sizeof(stm32h7_flash_write_code),
},
{
.id = DEVID_STM32H7A_H7BXX,
@@ -216,8 +263,11 @@ static const struct stm32h7_part_info stm32h7_parts[] = {
.fsize_addr = 0x08FFF80C,
.wps_group_size = 4,
.wps_mask = 0xFFFFFFFF,
.compute_flash_cr = stm32h7a_h7bxx_compute_flash_cr,
.compute_flash_cr = stm32h7a_h7b_h7r_h7sxx_compute_flash_cr,
.get_flash_error_status = stm32h7_get_flash_status,
.flash_error = FLASH_ERROR,
.write_code = stm32h7_flash_write_code,
.write_code_size = sizeof(stm32h7_flash_write_code),
},
{
.id = DEVID_STM32H72_H73XX,
@@ -234,6 +284,28 @@ static const struct stm32h7_part_info stm32h7_parts[] = {
.wps_mask = 0xFF,
.compute_flash_cr = stm32h74_h75xx_compute_flash_cr,
.get_flash_error_status = stm32h7_get_flash_status,
.flash_error = FLASH_ERROR,
.write_code = stm32h7_flash_write_code,
.write_code_size = sizeof(stm32h7_flash_write_code),
},
{
.id = DEVID_STM32H7R_H7SXX,
.revs = stm32h7r_h7sxx_revs,
.num_revs = ARRAY_SIZE(stm32h7r_h7sxx_revs),
.device_str = "STM32H7Rx/7Sx",
.page_size_kb = 8,
.block_size = 16,
.max_flash_size_kb = 64,
.max_bank_size_kb = 64,
.has_dual_bank = false,
.fsize_addr = 0x08FFF80C,
.wps_group_size = 1,
.wps_mask = 0xFF,
.compute_flash_cr = stm32h7a_h7b_h7r_h7sxx_compute_flash_cr,
.get_flash_error_status = stm32h7rs_get_flash_status,
.flash_error = FLASH_ERROR_H7RS,
.write_code = stm32h7rs_flash_write_code,
.write_code_size = sizeof(stm32h7rs_flash_write_code),
},
};
@@ -302,10 +374,16 @@ static int stm32h7_get_flash_status(struct flash_bank *bank, uint32_t *status)
return stm32h7_read_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, status);
}
static int stm32h7rs_get_flash_status(struct flash_bank *bank, uint32_t *status)
{
return stm32h7_read_flash_reg_by_index(bank, STM32_FLASH_ISR_INDEX, status);
}
static int stm32h7_wait_flash_op_queue(struct flash_bank *bank, int timeout)
{
uint32_t status;
int retval;
struct stm32h7_flash_bank *stm32h7_info = bank->driver_priv;
/* wait for flash operations completion */
for (;;) {
@@ -324,16 +402,16 @@ static int stm32h7_wait_flash_op_queue(struct flash_bank *bank, int timeout)
}
if (status & FLASH_WRPERR) {
LOG_ERROR("wait_flash_op_queue, WRPERR detected");
LOG_ERROR("wait_flash_op_queue, write protection error");
retval = ERROR_FAIL;
}
/* Clear error + EOP flags but report errors */
if (status & FLASH_ERROR) {
if (status & stm32h7_info->part_info->flash_error) {
if (retval == ERROR_OK)
retval = ERROR_FAIL;
/* If this operation fails, we ignore it and report the original retval */
stm32h7_write_flash_reg_by_index(bank, STM32_FLASH_CCR_INDEX, status);
stm32h7_write_flash_reg_by_index(bank, STM32_FLASH_ICR_CCR_INDEX, status);
}
return retval;
}
@@ -611,24 +689,21 @@ static int stm32h7_write_block(struct flash_bank *bank, const uint8_t *buffer,
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
static const uint8_t stm32h7_flash_write_code[] = {
#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc"
};
if (target_alloc_working_area(target, sizeof(stm32h7_flash_write_code),
if (target_alloc_working_area(target, stm32h7_info->part_info->write_code_size,
&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(stm32h7_flash_write_code),
stm32h7_flash_write_code);
stm32h7_info->part_info->write_code_size,
stm32h7_info->part_info->write_code);
if (retval != ERROR_OK) {
target_free_working_area(target, write_algorithm);
return retval;
}
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
data_size /= 2;
@@ -680,10 +755,10 @@ static int stm32h7_write_block(struct flash_bank *bank, const uint8_t *buffer,
if (flash_sr & FLASH_WRPERR)
LOG_ERROR("flash memory write protected");
if ((flash_sr & FLASH_ERROR) != 0) {
LOG_ERROR("flash write failed, FLASH_SR = 0x%08" PRIx32, flash_sr);
if ((flash_sr & stm32h7_info->part_info->flash_error) != 0) {
LOG_ERROR("flash write failed, status = 0x%08" PRIx32, flash_sr);
/* Clear error + EOP flags but report errors */
stm32h7_write_flash_reg_by_index(bank, STM32_FLASH_CCR_INDEX, flash_sr);
stm32h7_write_flash_reg_by_index(bank, STM32_FLASH_ICR_CCR_INDEX, flash_sr);
retval = ERROR_FAIL;
}
}
@@ -810,8 +885,11 @@ static int stm32h7_probe(struct flash_bank *bank)
LOG_DEBUG("device id = 0x%08" PRIx32, stm32h7_info->idcode);
device_id = stm32h7_info->idcode & 0xfff;
stm32h7_info->flash_regs = stm32h7_flash_regs;
if (device_id == DEVID_STM32H7R_H7SXX) {
stm32h7_info->flash_regs = stm32h7rs_flash_regs;
} else {
stm32h7_info->flash_regs = stm32h7_flash_regs;
}
for (unsigned int n = 0; n < ARRAY_SIZE(stm32h7_parts); n++) {
if (device_id == stm32h7_parts[n].id)
@@ -872,6 +950,7 @@ static int stm32h7_probe(struct flash_bank *bank)
flash_size_in_kb /= 2;
break;
case DEVID_STM32H72_H73XX:
case DEVID_STM32H7R_H7SXX:
break;
default:
LOG_ERROR("unsupported device");

98
tcl/target/stm32h7rsx.cfg Normal file
View File

@@ -0,0 +1,98 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# script for stm32h7rsx family
#
# stm32h7rs devices support both JTAG and SWD transports.
#
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME stm32h7rsx
}
# Issue an error when hla is used
if { [using_hla] } {
echo "Error : hla does not support STM32H7RS devices"
}
set _ENDIAN little
# Work-area is a space in RAM used for flash programming
# By default use 64kB
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x10000
}
#jtag scan chain
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
if { [using_jtag] } {
set _CPUTAPID 0x6ba00477
} {
set _CPUTAPID 0x6ba02477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
if {[using_jtag]} {
jtag newtap $_CHIPNAME bs -irlen 5
}
target create $_CHIPNAME.ap0 mem_ap -dap $_CHIPNAME.dap -ap-num 0
target create $_CHIPNAME.cpu0 cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -ap-num 1
# test ram area
$_CHIPNAME.cpu0 configure -work-area-phys 0x24000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
flash bank $_CHIPNAME.flash stm32h7x 0x08000000 0 0 0 $_CHIPNAME.cpu0
# Make sure that cpu0 is selected
targets $_CHIPNAME.cpu0
# Clock after reset is HSI at 64 MHz, no need of PLL
adapter speed 4000
adapter srst delay 100
if {[using_jtag]} {
jtag_ntrst_delay 100
}
# use hardware reset
#
# The STM32H7RS does not support connect_assert_srst mode because the AXI is
# unavailable while SRST is asserted, and that is used to access the DBGMCU
# component at 0x5C001000 in the examine-end event handler.
#
reset_config srst_gates_jtag
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
$_CHIPNAME.cpu0 cortex_m reset_config sysresetreq
# Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal
# HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3
# makes the data access cacheable. This allows reading and writing data in the
# CPU cache from the debugger, which is far more useful than going straight to
# RAM when operating on typical variables, and is generally no worse when
# operating on special memory locations.
$_CHIPNAME.dap apcsw 0x08000000 0x08000000
}
$_CHIPNAME.cpu0 configure -event examine-end {
# Enable debug during low power modes (uses more power)
# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
mmw 0x5C001004 0x00000007 0
# Enable clock for tracing
# DBGMCU_CR |= TRACECLKEN
mmw 0x5C001004 0x00100000 0
}