Skip to content

stm32: add support to PYBD_SF6 for boards with larger flash #17067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 9, 2025
2 changes: 1 addition & 1 deletion drivers/bus/qspi.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ typedef struct _mp_qspi_proto_t {
int (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data);
int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src);
int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest);
int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest);
int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest);
} mp_qspi_proto_t;

typedef struct _mp_soft_qspi_obj_t {
Expand Down
6 changes: 3 additions & 3 deletions drivers/bus/softqspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,13 @@ static int mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_
return 0;
}

static int mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
static int mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest) {
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
uint8_t cmd_buf[7] = {cmd};
uint8_t cmd_buf[16] = {cmd};
uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
CS_LOW(self);
mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles)
mp_soft_qspi_qwrite(self, addr_len + 1 + num_dummy, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), N dummy bytes (2*N dummy cycles)
mp_soft_qspi_qread(self, len, dest);
CS_HIGH(self);
return 0;
Expand Down
26 changes: 21 additions & 5 deletions drivers/memory/spiflash.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@
#include "py/mphal.h"
#include "drivers/memory/spiflash.h"

#if defined(CHECK_DEVID)
#error "CHECK_DEVID no longer supported, use MICROPY_HW_SPIFLASH_DETECT_DEVICE instead"
#endif

// The default number of dummy bytes for quad-read is 2. This can be changed by enabling
// MICROPY_HW_SPIFLASH_CHIP_PARAMS and configuring the value in mp_spiflash_chip_params_t.
#if MICROPY_HW_SPIFLASH_CHIP_PARAMS
#define MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(spiflash) (spiflash->chip_params->qread_num_dummy)
#else
#define MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(spiflash) (2)
#endif

#define QSPI_QE_MASK (0x02)
#define USE_WR_DELAY (1)

Expand Down Expand Up @@ -111,7 +123,8 @@ static int mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd,
mp_hal_pin_write(c->bus.u_spi.cs, 1);
} else {
if (dest != NULL) {
ret = c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, len, dest);
uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self);
ret = c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, num_dummy, len, dest);
} else {
ret = c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src);
}
Expand Down Expand Up @@ -182,7 +195,8 @@ void mp_spiflash_init(mp_spiflash_t *self) {
mp_hal_pin_output(self->config->bus.u_spi.cs);
self->config->bus.u_spi.proto->ioctl(self->config->bus.u_spi.data, MP_SPI_IOCTL_INIT);
} else {
self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, 0);
uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self);
self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy);
}

mp_spiflash_acquire_bus(self);
Expand All @@ -198,11 +212,13 @@ void mp_spiflash_init(mp_spiflash_t *self) {
mp_hal_delay_ms(1);
#endif

#if defined(CHECK_DEVID)
// Validate device id
#if MICROPY_HW_SPIFLASH_DETECT_DEVICE
// Attempt to detect SPI flash based on its JEDEC id.
uint32_t devid;
int ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid);
if (ret != 0 || devid != CHECK_DEVID) {
ret = mp_spiflash_detect(self, ret, devid);
if (ret != 0) {
// Could not read device id.
mp_spiflash_release_bus(self);
return;
}
Expand Down
27 changes: 27 additions & 0 deletions drivers/memory/spiflash.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
#include "drivers/bus/spi.h"
#include "drivers/bus/qspi.h"

// Whether to enable dynamic configuration of SPI flash through mp_spiflash_chip_params_t.
#ifndef MICROPY_HW_SPIFLASH_CHIP_PARAMS
#define MICROPY_HW_SPIFLASH_CHIP_PARAMS (0)
#endif

// Whether to enable detection of SPI flash during initialisation.
#ifndef MICROPY_HW_SPIFLASH_DETECT_DEVICE
#define MICROPY_HW_SPIFLASH_DETECT_DEVICE (0)
#endif

#define MP_SPIFLASH_ERASE_BLOCK_SIZE (4096) // must be a power of 2

enum {
Expand Down Expand Up @@ -66,14 +76,31 @@ typedef struct _mp_spiflash_config_t {
#endif
} mp_spiflash_config_t;

#if MICROPY_HW_SPIFLASH_CHIP_PARAMS
typedef struct _mp_spiflash_chip_params_t {
uint32_t jedec_id;
uint8_t memory_size_bytes_log2;
uint8_t qspi_prescaler;
uint8_t qread_num_dummy;
} mp_spiflash_chip_params_t;
#endif

typedef struct _mp_spiflash_t {
const mp_spiflash_config_t *config;
#if MICROPY_HW_SPIFLASH_CHIP_PARAMS
const mp_spiflash_chip_params_t *chip_params;
#endif
volatile uint32_t flags;
} mp_spiflash_t;

void mp_spiflash_init(mp_spiflash_t *self);
void mp_spiflash_deepsleep(mp_spiflash_t *self, int value);

#if MICROPY_HW_SPIFLASH_DETECT_DEVICE
// A board/port should define this function to perform actions based on the JEDEC id.
int mp_spiflash_detect(mp_spiflash_t *spiflash, int ret, uint32_t devid);
#endif

// These functions go direct to the SPI flash device
int mp_spiflash_erase_block(mp_spiflash_t *self, uint32_t addr);
int mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest);
Expand Down
88 changes: 88 additions & 0 deletions ports/stm32/boards/PYBD_SF6/board_init.c
Original file line number Diff line number Diff line change
@@ -1 +1,89 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2025 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <stdbool.h>
#include "boardctrl.h"
#include "qspi.h"

#if BUILDING_MBOOT
#include "mboot/mboot.h"
#endif

// Use PYBD_SF2 as base configuration.
#include "boards/PYBD_SF2/board_init.c"

// Adesto AT25SF161 16-MBit.
static const mp_spiflash_chip_params_t chip_params_at25sf161 = {
.jedec_id = 0x01861f,
.memory_size_bytes_log2 = 21,
.qspi_prescaler = 3, // maximum frequency 104MHz
.qread_num_dummy = 2,
};

// Infineon S25FL064 64-MBit.
static const mp_spiflash_chip_params_t chip_params_s25fl064 = {
.jedec_id = 0x176001,
.memory_size_bytes_log2 = 23,
.qspi_prescaler = 2, // maximum frequency 108MHz
.qread_num_dummy = 4,
};

// Selection of possible SPI flash chips.
static const mp_spiflash_chip_params_t *const chip_params_table[] = {
&chip_params_at25sf161,
&chip_params_s25fl064,
};

void board_early_init_sf6(void) {
// Initialise default SPI flash parameters.
MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0 = &chip_params_at25sf161;
MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1 = &chip_params_at25sf161;

// Continue with standard board early init.
board_early_init();
}

int mp_spiflash_detect(mp_spiflash_t *spiflash, int ret, uint32_t devid) {
if (ret != 0) {
// Could not identify flash. Succeed anyway using default chip parameters.
return 0;
}

// Try to detect the SPI flash based on the JEDEC id.
for (size_t i = 0; i < MP_ARRAY_SIZE(chip_params_table); ++i) {
if (devid == chip_params_table[i]->jedec_id) {
spiflash->chip_params = chip_params_table[i];
if (spiflash->config->bus_kind == MP_SPIFLASH_BUS_QSPI) {
// Reinitialise the QSPI but to set new size, prescaler and dummy bytes.
uint8_t num_dummy = spiflash->chip_params->qread_num_dummy;
spiflash->config->bus.u_qspi.proto->ioctl(spiflash->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy);
}
break;
}
}

return 0;
}
3 changes: 1 addition & 2 deletions ports/stm32/boards/PYBD_SF6/f767.ld
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */
FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */
FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI, at least 2MiB */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* DTCM=128k, SRAM1=368K, SRAM2=16K */
}

Expand All @@ -39,6 +39,5 @@ _heap_end = _sstack;

/* ROMFS location */
_micropy_hw_romfs_part0_start = ORIGIN(FLASH_ROMFS);
_micropy_hw_romfs_part0_size = LENGTH(FLASH_ROMFS);

INCLUDE common_bl.ld
50 changes: 50 additions & 0 deletions ports/stm32/boards/PYBD_SF6/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,53 @@
#define MICROPY_HW_ETH_RMII_TX_EN (pyb_pin_W8)
#define MICROPY_HW_ETH_RMII_TXD0 (pyb_pin_W45)
#define MICROPY_HW_ETH_RMII_TXD1 (pyb_pin_W49)

// The below code reconfigures SPI flash for dynamic size detection.

#undef MICROPY_BOARD_EARLY_INIT
#undef MICROPY_HW_SPIFLASH_SIZE_BITS
#undef MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2
#undef MBOOT_SPIFLASH_BYTE_SIZE
#undef MBOOT_SPIFLASH_LAYOUT
#undef MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE
#undef MBOOT_SPIFLASH2_BYTE_SIZE
#undef MBOOT_SPIFLASH2_LAYOUT
#undef MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE

// These are convenience macros to refer to the SPI flash chip parameters for the external SPI flash.
#define MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0 (spi_bdev.spiflash.chip_params) // SPI flash #1, R/W storage
#define MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1 (spi_bdev2.spiflash.chip_params) // SPI flash #2, memory mapped

// Early init is needed to initialise the default SPI flash chip parameters.
#define MICROPY_BOARD_EARLY_INIT board_early_init_sf6

// Enable dynamic detection of SPI flash.
#define MICROPY_HW_SPIFLASH_CHIP_PARAMS (1)
#define MICROPY_HW_SPIFLASH_DETECT_DEVICE (1)

// Settings for SPI flash #1.
#define MICROPY_HW_SPIFLASH_SIZE_BITS (1 << (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2 + 3))

// Settings for SPI flash #2.
#define MICROPY_HW_QSPI_PRESCALER (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->qspi_prescaler)
#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2 + 3)

// ROMFS partition 0 is dynamically sized (SPI flash #2).
#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start)
#define MICROPY_HW_ROMFS_PART0_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2)

// Mboot SPI flash #1 configuration.
#define MBOOT_SPIFLASH_LAYOUT_DYNAMIC_MAX_LEN (20)
#define MBOOT_SPIFLASH_LAYOUT (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2 == 21 ? "/0x80000000/512*4Kg" : "/0x80000000/2048*4Kg")
#define MBOOT_SPIFLASH_BYTE_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS0->memory_size_bytes_log2)
#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1)

// Mboot SPI flash #2 configuration.
#define MBOOT_SPIFLASH2_LAYOUT_DYNAMIC_MAX_LEN (20)
#define MBOOT_SPIFLASH2_LAYOUT (MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2 == 21 ? "/0x90000000/512*4Kg" : "/0x90000000/2048*4Kg")
#define MBOOT_SPIFLASH2_BYTE_SIZE (1 << MICROPY_BOARD_SPIFLASH_CHIP_PARAMS1->memory_size_bytes_log2)
#define MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE (1)

extern unsigned char _micropy_hw_romfs_part0_start;

void board_early_init_sf6(void);
4 changes: 3 additions & 1 deletion ports/stm32/boards/STM32F769DISC/board_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

// This configuration is needed for mboot to be able to write to the external QSPI flash

#define QSPI_QREAD_NUM_DUMMY (2)

#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
static mp_spiflash_cache_t spi_bdev_cache;
#endif
Expand All @@ -21,6 +23,6 @@ spi_bdev_t spi_bdev;
// This init function is needed to memory map the QSPI flash early in the boot process

void board_early_init(void) {
qspi_init();
qspi_init(QSPI_QREAD_NUM_DUMMY);
qspi_memory_map();
}
Loading
Loading