stm32/mboot: Add support for erase/read/write of external SPI flash.

This patch adds support to mboot for programming external SPI flash.  It
allows SPI flash to be programmed via a USB DFU utility in the same way
that internal MCU flash is programmed.
This commit is contained in:
Damien George 2018-06-22 15:30:34 +10:00
parent 7f41f73f0f
commit ec7982ec6d
2 changed files with 130 additions and 24 deletions

View File

@ -31,6 +31,32 @@ How to use
#define MBOOT_BOOTPIN_PULL (MP_HAL_PIN_PULL_UP)
#define MBOOT_BOOTPIN_ACTIVE (0)
Mboot supports programming external SPI flash via the DFU and I2C
interfaces. SPI flash will be mapped to an address range. To
configure it use the following options (edit as needed):
#define MBOOT_SPIFLASH_ADDR (0x80000000)
#define MBOOT_SPIFLASH_BYTE_SIZE (2 * 1024 * 1024)
#define MBOOT_SPIFLASH_LAYOUT "/0x80000000/64*32Kg"
#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (32 / 4)
#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash)
#define MBOOT_SPIFLASH_CONFIG (&spiflash_config)
This assumes that the board declares and defines the relevant SPI flash
configuration structs, eg in the board-specific bdev.c file. The
`MBOOT_SPIFLASH2_LAYOUT` string will be seen by the USB DFU utility and
must describe the SPI flash layout. Note that the number of pages in
this layout description (the `64` above) cannot be larger than 99 (it
must fit in two digits) so the reported page size (the `32Kg` above)
must be made large enough so the number of pages fits in two digits.
Alternatively the layout can specify multiple sections like
`32*16Kg,32*16Kg`, in which case `MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE`
must be changed to `16 / 4` to match tho `16Kg` value.
Mboot supports up to two external SPI flash devices. To configure the
second one use the same configuration names as above but with
`SPIFLASH2`, ie `MBOOT_SPIFLASH2_ADDR` etc.
2. Build the board's main application firmware as usual.
3. Build mboot via:

View File

@ -81,7 +81,7 @@ static uint32_t get_le32(const uint8_t *b) {
void mp_hal_delay_us(mp_uint_t usec) {
// use a busy loop for the delay
// sys freq is always a multiple of 2MHz, so division here won't lose precision
const uint32_t ucount = HAL_RCC_GetSysClockFreq() / 2000000 * usec / 2;
const uint32_t ucount = CORE_PLL_FREQ / 2000000 * usec / 2;
for (uint32_t count = 0; ++count <= ucount;) {
}
}
@ -314,6 +314,14 @@ static int usrbtn_state(void) {
/******************************************************************************/
// FLASH
#ifndef MBOOT_SPIFLASH_LAYOUT
#define MBOOT_SPIFLASH_LAYOUT ""
#endif
#ifndef MBOOT_SPIFLASH2_LAYOUT
#define MBOOT_SPIFLASH2_LAYOUT ""
#endif
typedef struct {
uint32_t base_address;
uint32_t sector_size;
@ -332,7 +340,7 @@ typedef struct {
|| defined(STM32F732xx) \
|| defined(STM32F733xx)
#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg"
#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT
static const flash_layout_t flash_layout[] = {
{ 0x08000000, 0x04000, 4 },
@ -350,7 +358,7 @@ static const flash_layout_t flash_layout[] = {
#elif defined(STM32F767xx)
#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg"
#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT
// This is for dual-bank mode disabled
static const flash_layout_t flash_layout[] = {
@ -378,20 +386,18 @@ static uint32_t flash_get_sector_index(uint32_t addr) {
return 0;
}
static int do_mass_erase(void) {
static int flash_mass_erase(void) {
// TODO
return -1;
}
static int do_page_erase(uint32_t addr) {
static int flash_page_erase(uint32_t addr) {
uint32_t sector = flash_get_sector_index(addr);
if (sector == 0) {
// Don't allow to erase the sector with this bootloader in it
return -1;
}
led_state(LED0, 1);
HAL_FLASH_Unlock();
// Clear pending flags (if any)
@ -411,8 +417,6 @@ static int do_page_erase(uint32_t addr) {
return -1;
}
led_state(LED0, 0);
// Check the erase set bits to 1, at least for the first 256 bytes
for (int i = 0; i < 64; ++i) {
if (((volatile uint32_t*)addr)[i] != 0xffffffff) {
@ -423,15 +427,12 @@ static int do_page_erase(uint32_t addr) {
return 0;
}
static int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
static int flash_write(uint32_t addr, const uint8_t *src8, size_t len) {
if (addr >= flash_layout[0].base_address && addr < flash_layout[0].base_address + flash_layout[0].sector_size) {
// Don't allow to write the sector with this bootloader in it
return -1;
}
static uint32_t led_tog = 0;
led_state(LED0, (led_tog++) & 16);
const uint32_t *src = (const uint32_t*)src8;
size_t num_word32 = (len + 3) / 4;
HAL_FLASH_Unlock();
@ -449,6 +450,84 @@ static int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
return 0;
}
/******************************************************************************/
// Writable address space interface
static int do_mass_erase(void) {
// TODO
return flash_mass_erase();
}
#if defined(MBOOT_SPIFLASH_ADDR) || defined(MBOOT_SPIFLASH2_ADDR)
static int spiflash_page_erase(mp_spiflash_t *spif, uint32_t addr, uint32_t n_blocks) {
for (int i = 0; i < n_blocks; ++i) {
int ret = mp_spiflash_erase_block(spif, addr);
if (ret != 0) {
return ret;
}
addr += MP_SPIFLASH_ERASE_BLOCK_SIZE;
}
return 0;
}
#endif
static int do_page_erase(uint32_t addr) {
led_state(LED0, 1);
#if defined(MBOOT_SPIFLASH_ADDR)
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
return spiflash_page_erase(MBOOT_SPIFLASH_SPIFLASH,
addr - MBOOT_SPIFLASH_ADDR, MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE);
}
#endif
#if defined(MBOOT_SPIFLASH2_ADDR)
if (MBOOT_SPIFLASH2_ADDR <= addr && addr < MBOOT_SPIFLASH2_ADDR + MBOOT_SPIFLASH2_BYTE_SIZE) {
return spiflash_page_erase(MBOOT_SPIFLASH2_SPIFLASH,
addr - MBOOT_SPIFLASH2_ADDR, MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE);
}
#endif
return flash_page_erase(addr);
}
static void do_read(uint32_t addr, int len, uint8_t *buf) {
#if defined(MBOOT_SPIFLASH_ADDR)
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, buf);
return;
}
#endif
#if defined(MBOOT_SPIFLASH2_ADDR)
if (MBOOT_SPIFLASH2_ADDR <= addr && addr < MBOOT_SPIFLASH2_ADDR + MBOOT_SPIFLASH2_BYTE_SIZE) {
mp_spiflash_read(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, buf);
return;
}
#endif
// Other addresses, just read directly from memory
memcpy(buf, (void*)addr, len);
}
static int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
static uint32_t led_tog = 0;
led_state(LED0, (led_tog++) & 4);
#if defined(MBOOT_SPIFLASH_ADDR)
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
return mp_spiflash_write(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, src8);
}
#endif
#if defined(MBOOT_SPIFLASH2_ADDR)
if (MBOOT_SPIFLASH2_ADDR <= addr && addr < MBOOT_SPIFLASH2_ADDR + MBOOT_SPIFLASH2_BYTE_SIZE) {
return mp_spiflash_write(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, src8);
}
#endif
return flash_write(addr, src8, len);
}
/******************************************************************************/
// I2C slave interface
@ -554,7 +633,7 @@ void i2c_slave_process_rx_end(void) {
if (len > I2C_CMD_BUF_LEN) {
len = I2C_CMD_BUF_LEN;
}
memcpy(buf, (void*)i2c_obj.cmd_rdaddr, len);
do_read(i2c_obj.cmd_rdaddr, len, buf);
i2c_obj.cmd_rdaddr += len;
} else if (buf[0] == I2C_CMD_WRITE) {
if (i2c_obj.cmd_wraddr == APPLICATION_ADDR) {
@ -732,7 +811,8 @@ static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) {
if (cmd == DFU_UPLOAD) {
if (arg >= 2) {
dfu_state.cmd = DFU_CMD_UPLOAD;
memcpy(buf, (void*)((arg - 2) * max_len + dfu_state.addr), len);
uint32_t addr = (arg - 2) * max_len + dfu_state.addr;
do_read(addr, len, buf);
return len;
}
} else if (cmd == DFU_GETSTATUS && len == 6) {
@ -773,14 +853,14 @@ enum {
typedef struct _pyb_usbdd_obj_t {
bool started;
bool tx_pending;
USBD_HandleTypeDef hUSBDDevice;
uint8_t bRequest;
uint16_t wValue;
uint16_t wLength;
uint8_t rx_buf[USB_XFER_SIZE];
uint8_t tx_buf[USB_XFER_SIZE];
bool tx_pending;
__ALIGN_BEGIN uint8_t rx_buf[USB_XFER_SIZE] __ALIGN_END;
__ALIGN_BEGIN uint8_t tx_buf[USB_XFER_SIZE] __ALIGN_END;
// RAM to hold the current descriptors, which we configure on the fly
__ALIGN_BEGIN uint8_t usbd_device_desc[USB_LEN_DEV_DESC] __ALIGN_END;
@ -1135,14 +1215,14 @@ enter_bootloader:
__ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory");
#endif
#if 0
#if defined(MICROPY_HW_BDEV_IOCTL)
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
#if defined(MBOOT_SPIFLASH_ADDR)
MBOOT_SPIFLASH_SPIFLASH->config = MBOOT_SPIFLASH_CONFIG;
mp_spiflash_init(MBOOT_SPIFLASH_SPIFLASH);
#endif
#if defined(MICROPY_HW_BDEV2_IOCTL)
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0);
#endif
#if defined(MBOOT_SPIFLASH2_ADDR)
MBOOT_SPIFLASH2_SPIFLASH->config = MBOOT_SPIFLASH2_CONFIG;
mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH);
#endif
dfu_init();