Compare commits

...

15 Commits

Author SHA1 Message Date
Andrew Leech 264bdafab1
Merge 0fe810bcaf into bd21820b4c 2024-01-15 17:20:18 +01:00
stijn bd21820b4c tests/run-tests.py: Fix path-based special test detection.
Compare the full absolute path instead of relying on the path form
passed by the user.

For instance, this will make

python3 run-tests.py -d basics
python3 run-tests.py -d ./basics
python3 run-tests.py -d ../tests/basics
python3 run-tests.py -d /full/path/to/basics

all behave the same by correctly treating the bytes_compare3 and
builtin_help tests as special, whereas previously only the first
invocation would do that and hence result in these tests to fail
when called with a different path form.

Signed-off-by: stijn <stijn@ignitron.net>
2024-01-10 11:34:09 +01:00
stijn 88d21f186b tests/run-tests.py: Make repl test detection more correct.
Avoid unrelated tests which happen to have "repl_" anywhere
in their path to be treated as repl tests.

Signed-off-by: stijn <stijn@ignitron.net>
2024-01-10 11:34:09 +01:00
stijn ba4330ba10 tests/run-tests.py: Remove unneeded argument from run_feature_check().
In 405893af this was likely left as-is to minimize the diff,
but it just complicates things.

Signed-off-by: stijn <stijn@ignitron.net>
2024-01-10 11:34:09 +01:00
Andrew Leech 0fe810bcaf stm32/main: Catch and report corrupted lfs filesystem at startup.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-30 10:47:55 +11:00
Andrew Leech 80037049f4 stm32/PYBD_SF2: Specify spiflash chip used on board.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 8dfb228208 memory/spiflash: Support reading SFDP.
The Serial Flash Discoverable Parameters (SFDP) is a jedec standard
interface on (most/newer) spiflash chips that allows querying the
flash size (amongst other things).

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 7cdc235949 memory/spiflash: Support read in 1 and 4 line modes.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 9867f7c3ad stm32/ospi: Support read in 1 and 4 line modes.
Fixes support for 4 line communication with chip, but
maintains fallback to 2 or 1 line if hardware needs it.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 5b5f6dbc89 stm32/qspi: Support read in 1 and 4 line modes.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech ce8d3e5356 bus/softqspi: Support read in 1 and 4 line modes.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 08a4c8479e bus/qspi: Support read in 1 and 4 line modes.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 1fba0fe1a7 stm32/spibdev: Support defining exact flash chip(s) connected.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech b58ca13583 drivers/memory/spiflash: Support defining exact flash chip(s) connected.
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
Andrew Leech 5cc37a4d63 drivers/external_flash_device: Add AT25SF161B/641B & MX25L25673G.
Add some more details to the struct definition.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
2023-11-24 09:13:34 +11:00
13 changed files with 369 additions and 97 deletions

View File

@ -39,12 +39,17 @@ enum {
MP_QSPI_IOCTL_BUS_RELEASE,
};
enum qspi_tranfer_mode {
MP_QSPI_TRANSFER_CMD_ADDR_DATA,
MP_QSPI_TRANSFER_CMD_QADDR_QDATA,
};
typedef struct _mp_qspi_proto_t {
int (*ioctl)(void *self, uint32_t cmd);
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_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode);
} mp_qspi_proto_t;
typedef struct _mp_soft_qspi_obj_t {

View File

@ -188,16 +188,28 @@ 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_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
int ret = 0;
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
uint8_t cmd_buf[7] = {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_qread(self, len, dest);
if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
// cmd, address and data on 1 line.
// 3 addr bytes, 1 dummy byte (8 dummy cycles x 1 line)
mp_soft_qspi_transfer(self, addr_len + 1, &cmd_buf[1], NULL);
mp_soft_qspi_transfer(self, len, NULL, dest);
} else if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
// cmd 1 line, address and data on 4 lines.
// 3/4 addr bytes, 1 extra byte (mode: 0), 2 dummy bytes (4 dummy cycles x 4 lines)
mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]);
mp_soft_qspi_qread(self, len, dest);
} else {
ret = -1;
}
CS_HIGH(self);
return 0;
return ret;
}
const mp_qspi_proto_t mp_soft_qspi_proto = {
@ -205,5 +217,5 @@ const mp_qspi_proto_t mp_soft_qspi_proto = {
.write_cmd_data = mp_soft_qspi_write_cmd_data,
.write_cmd_addr_data = mp_soft_qspi_write_cmd_addr_data,
.read_cmd = mp_soft_qspi_read_cmd,
.read_cmd_qaddr_qdata = mp_soft_qspi_read_cmd_qaddr_qdata,
.read_cmd_addr_data = mp_soft_qspi_read_cmd_addr_data,
};

View File

@ -29,11 +29,16 @@
#include <stdbool.h>
#include <stdint.h>
typedef struct {
typedef struct _external_flash_device {
// Flash size in bytes.
uint32_t total_size;
uint16_t start_up_time_us;
// Three response bytes to 0x9f JEDEC ID command.
// The first field is always a manufacturer_id, however the other two are used
// differently be each manufacturer.
// All three together will always uniquely identify a chip model.
uint8_t manufacturer_id;
uint8_t memory_type;
uint8_t capacity;
@ -66,9 +71,12 @@ typedef struct {
bool single_status_byte : 1;
} external_flash_device;
// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21
// Xplained board.
// typedef struct _external_flash_device ;
// Settings for the Adesto Tech / Renesas AT25DF081A 1MiB SPI flash.
// Its on the SAMD21 Xplained board.
// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf
// https://www.renesas.com/eu/en/document/dst/at25df081a-datasheet
#define AT25DF081A { \
.total_size = (1 << 20), /* 1 MiB */ \
.start_up_time_us = 10000, \
@ -85,6 +93,42 @@ typedef struct {
.single_status_byte = false, \
}
// Settings for the Renesas AT25SF161B 2MiB SPI flash.
// Datasheet: https://www.renesas.com/us/en/document/dst/at25sf161b-datasheet?r=1608796
#define AT25SF161B { \
.total_size = (1 << 21), /* 2 MiB */ \
.start_up_time_us = 5000, \
.manufacturer_id = 0x1f, \
.memory_type = 0x86, \
.capacity = 0x01, \
.max_clock_speed_mhz = 133, \
.quad_enable_bit_mask = 0x02, \
.has_sector_protection = true, \
.supports_fast_read = true, \
.supports_qspi = true, \
.supports_qspi_writes = true, \
.write_status_register_split = true, \
.single_status_byte = false, \
}
// Settings for the Renesas AT25SF641B 8MiB SPI flash.
// Datasheet: https://www.renesas.com/us/en/document/dst/at25sf641b-datasheet?r=1608816
#define AT25SF641B { \
.total_size = (1 << 23), /* 8 MiB */ \
.start_up_time_us = 5000, \
.manufacturer_id = 0x1f, \
.memory_type = 0x88, \
.capacity = 0x01, \
.max_clock_speed_mhz = 133, \
.quad_enable_bit_mask = 0x02, \
.has_sector_protection = true, \
.supports_fast_read = true, \
.supports_qspi = true, \
.supports_qspi_writes = true, \
.write_status_register_split = true, \
.single_status_byte = false, \
}
// Settings for the Gigadevice GD25Q16C 2MiB SPI flash.
// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/
#define GD25Q16C { \
@ -388,6 +432,24 @@ typedef struct {
.single_status_byte = true, \
}
// Settings for the Macronix MX25L25673G 32MiB SPI flash.
// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/8761/MX25L25673G,%203V,%20256Mb,%20v1.7.pdf
#define MX25L25673G { \
.total_size = (1 << 25), /* 32 MiB */ \
.start_up_time_us = 5000, \
.manufacturer_id = 0xc2, \
.memory_type = 0x20, \
.capacity = 0x19, \
.max_clock_speed_mhz = 133, \
.quad_enable_bit_mask = 0x40, \
.has_sector_protection = false, \
.supports_fast_read = true, \
.supports_qspi = true, \
.supports_qspi_writes = true, \
.write_status_register_split = false, \
.single_status_byte = true, \
}
// Settings for the Macronix MX25R6435F 8MiB SPI flash.
// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf
// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz.

View File

@ -30,17 +30,19 @@
#include "py/mperrno.h"
#include "py/mphal.h"
#include "drivers/memory/spiflash.h"
#include "drivers/memory/external_flash_device.h"
#define QSPI_QE_MASK (0x02)
#define USE_WR_DELAY (1)
#define CMD_WRSR (0x01)
#define CMD_WRCR (0x31)
#define CMD_WRITE (0x02)
#define CMD_READ (0x03)
#define CMD_RDSR (0x05)
#define CMD_WREN (0x06)
#define CMD_SEC_ERASE (0x20)
#define CMD_RDCR (0x35)
#define CMD_RD_SFDP (0x5a)
#define CMD_RD_DEVID (0x9f)
#define CMD_CHIP_ERASE (0xc7)
#define CMD_C4READ (0xeb)
@ -56,6 +58,25 @@
#define PAGE_SIZE (256) // maximum bytes we can write in one SPI transfer
#define SECTOR_SIZE MP_SPIFLASH_ERASE_BLOCK_SIZE
#ifndef MICROPY_HW_SPIFLASH_DEVICES
#define MICROPY_HW_SPIFLASH_DEVICES
#endif
#if !BUILDING_MBOOT
#define diag_printf(...) mp_printf(MICROPY_ERROR_PRINTER, __VA_ARGS__)
#else
#define diag_printf(...)
#endif
// List of all possible flash devices used by device.
// MICROPY_HW_SPIFLASH_DEVICES can be set to a comma separated list in mpconfigboard.h
static const external_flash_device possible_devices[] = {
MICROPY_HW_SPIFLASH_DEVICES
};
#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
static external_flash_device generic_config = GENERIC;
STATIC void mp_spiflash_acquire_bus(mp_spiflash_t *self) {
const mp_spiflash_config_t *c = self->config;
if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) {
@ -84,14 +105,15 @@ STATIC int mp_spiflash_write_cmd_data(mp_spiflash_t *self, uint8_t cmd, size_t l
return ret;
}
STATIC int mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src, uint8_t *dest) {
STATIC int mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src, uint8_t *dest, uint8_t mode) {
int ret = 0;
const mp_spiflash_config_t *c = self->config;
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
uint8_t buf[5] = {cmd, 0};
uint8_t buf[6] = {cmd, 0};
uint8_t buff_len = 1 + mp_spi_set_addr_buff(&buf[1], addr);
uint8_t dummy = (cmd == CMD_RD_SFDP)? 1 : 0;
mp_hal_pin_write(c->bus.u_spi.cs, 0);
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, buff_len, buf, NULL);
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, buff_len + dummy, buf, NULL);
if (len && (src != NULL)) {
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL);
} else if (len && (dest != NULL)) {
@ -101,7 +123,7 @@ 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);
ret = c->bus.u_qspi.proto->read_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, dest, mode);
} else {
ret = c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src);
}
@ -114,7 +136,7 @@ STATIC int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, ui
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
mp_hal_pin_write(c->bus.u_spi.cs, 0);
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL);
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void*)dest, (void*)dest);
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void *)dest, (void *)dest);
mp_hal_pin_write(c->bus.u_spi.cs, 1);
return 0;
} else {
@ -125,12 +147,15 @@ STATIC int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, ui
STATIC int mp_spiflash_read_data(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest) {
const mp_spiflash_config_t *c = self->config;
uint8_t cmd;
uint8_t mode;
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_READ_32 : CMD_READ;
mode = MP_QSPI_TRANSFER_CMD_ADDR_DATA;
} else {
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_C4READ_32 : CMD_C4READ;
mode = MP_QSPI_TRANSFER_CMD_QADDR_QDATA;
}
return mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, NULL, dest);
return mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, NULL, dest, mode);
}
STATIC int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) {
@ -164,7 +189,8 @@ static inline void mp_spiflash_deepsleep_internal(mp_spiflash_t *self, int value
mp_spiflash_write_cmd(self, value ? 0xb9 : 0xab); // sleep/wake
}
void mp_spiflash_init(mp_spiflash_t *self) {
int mp_spiflash_init(mp_spiflash_t *self) {
int ret = 0;
self->flags = 0;
if (self->config->bus_kind == MP_SPIFLASH_BUS_SPI) {
@ -183,30 +209,126 @@ void mp_spiflash_init(mp_spiflash_t *self) {
#if defined(CHECK_DEVID)
// Validate device id
uint32_t devid;
int ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid);
ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid);
if (ret != 0 || devid != CHECK_DEVID) {
mp_spiflash_release_bus(self);
return;
return -2;
}
#endif
if (self->config->bus_kind == MP_SPIFLASH_BUS_QSPI) {
// Set QE bit
uint32_t sr = 0, cr = 0;
int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
if (ret == 0) {
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
// Start with generic configuration, update with exact if found.
self->device = &generic_config;
uint8_t jedec_ids[3];
ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, (uint32_t *)jedec_ids);
if (ret != 0) {
mp_spiflash_release_bus(self);
return -2;
}
for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
const external_flash_device *possible_device = &possible_devices[i];
if (jedec_ids[0] == possible_device->manufacturer_id &&
jedec_ids[1] == possible_device->memory_type &&
jedec_ids[2] == possible_device->capacity) {
self->device = possible_device;
break;
}
uint32_t data = (sr & 0xff) | (cr & 0xff) << 8;
if (ret == 0 && !(data & (QSPI_QE_MASK << 8))) {
data |= QSPI_QE_MASK << 8;
mp_spiflash_write_cmd(self, CMD_WREN);
mp_spiflash_write_cmd_data(self, CMD_WRSR, 2, data);
mp_spiflash_wait_wip0(self);
}
// If the flash device is not known, try to autodetect suitable settings.
if (self->device == &generic_config) {
if (jedec_ids[0] == 0xc2) { // Macronix devices
generic_config.quad_enable_bit_mask = 0x04;
generic_config.single_status_byte = true;
}
#if MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES
generic_config.total_size = MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES;
#else
// Try to read "Serial Flash Discoverable Parameters"
// JEDEC Standard No. 216, 9 x 32bit dwords of data.
// Start be reading the headers to confirm sfdp is supported and find the parameter table address.
uint32_t sfdp[4] = {0};
ret = mp_spiflash_transfer_cmd_addr_data(self, CMD_RD_SFDP, 0, sizeof(sfdp), NULL, (uint8_t *)sfdp, MP_QSPI_TRANSFER_CMD_ADDR_DATA);
const char sfdp_header[] = {'S', 'F', 'D', 'P'};
if (ret != 0 || sfdp[0] != *(uint32_t *)sfdp_header) {
diag_printf("mp_spiflash: sfdp not supported\n");
diag_printf("Set MICROPY_HW_SPIFLASH_DEVICES or MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES");
diag_printf("jedec ids: 0x%x 0x%x 0x%x\n", jedec_ids[0], jedec_ids[1], jedec_ids[2]);
mp_spiflash_release_bus(self);
return -2;
} else {
// Read the first few SFDP parameter tables.
uint32_t sfdp_param_table_addr = sfdp[3] & 0xFFFFFF;
ret = mp_spiflash_transfer_cmd_addr_data(self, CMD_RD_SFDP, sfdp_param_table_addr, sizeof(sfdp), NULL, (uint8_t *)sfdp, MP_QSPI_TRANSFER_CMD_ADDR_DATA);
// Flash Memory Density
uint32_t size = sfdp[1] & ~(1 << 31);
if (size != 0) {
if (sfdp[1] & (1 << 31)) {
// When bit-31 is set to 1, the total bits is 2^size.
generic_config.total_size = 1 << size;
} else {
// When bit-31 is set to 0, the total bits is size + 1.
generic_config.total_size = (size + 1) / 8;
}
}
uint8_t opcode_sec_erase = (sfdp[0] >> 8 & 0xFF);
if (opcode_sec_erase != 0x20) {
diag_printf("mp_spiflash_sec_erase: opcode not supported\n");
}
if (sfdp[0] & (1 << 21)) {
// Supports (1-4-4) Fast Read: Device supports single line opcode,
// quad line address, and quad output Fast Read.
generic_config.supports_fast_read = true;
generic_config.supports_qspi = true;
}
}
#endif
}
if (self->config->bus_kind == MP_SPIFLASH_BUS_QSPI) {
// Set quad enable (QE) bit.
uint32_t sr = 0, cr = 0;
ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
if (ret == 0 && self->device->single_status_byte) {
// QE bit is in status byte 1
if ((sr & self->device->quad_enable_bit_mask) == 0) {
mp_spiflash_write_cmd(self, CMD_WREN);
sr |= self->device->quad_enable_bit_mask;
mp_spiflash_write_cmd_data(self, CMD_WRSR, 1, sr);
}
// Verify it's written correctly
ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
if (ret == 0 && (sr & self->device->quad_enable_bit_mask) == 0) {
// QE bit could not be set
ret = -1;
}
}
if (ret == 0 && (!self->device->single_status_byte)) {
// QE bit is in command register / status byte 2
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
if ((cr & self->device->quad_enable_bit_mask) == 0) {
mp_spiflash_write_cmd(self, CMD_WREN);
cr |= self->device->quad_enable_bit_mask;
if (self->device->write_status_register_split) {
// Some devices have a separate command to write CR
mp_spiflash_write_cmd_data(self, CMD_WRCR, 1, cr);
} else {
// Other devices expect both SR and CR to be written in one operation
uint32_t data = (sr & 0xff) | (cr & 0xff) << 8;
mp_spiflash_write_cmd_data(self, CMD_WRSR, 2, data);
}
}
// Verify it's written correctly
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
if (ret == 0 && (cr & self->device->quad_enable_bit_mask) == 0) {
// QE bit could not be set
ret = -1;
}
}
}
mp_spiflash_release_bus(self);
return ret;
}
void mp_spiflash_deepsleep(mp_spiflash_t *self, int value) {
@ -235,7 +357,7 @@ STATIC int mp_spiflash_erase_block_internal(mp_spiflash_t *self, uint32_t addr)
// erase the sector
uint8_t cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_SEC_ERASE_32 : CMD_SEC_ERASE;
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, 0, NULL, NULL);
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, 0, NULL, NULL, 0);
if (ret != 0) {
return ret;
}
@ -260,7 +382,7 @@ STATIC int mp_spiflash_write_page(mp_spiflash_t *self, uint32_t addr, size_t len
// write the page
uint8_t cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_WRITE_32 : CMD_WRITE;
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, src, NULL);
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, src, NULL, 0);
if (ret != 0) {
return ret;
}
@ -404,7 +526,7 @@ STATIC int mp_spiflash_cached_write_part(mp_spiflash_t *self, uint32_t addr, siz
// Restriction for now, so we don't need to erase multiple pages
if (offset + len > SECTOR_SIZE) {
printf("mp_spiflash_cached_write_part: len is too large\n");
diag_printf("mp_spiflash_cached_write_part: len is too large\n");
return -MP_EIO;
}

View File

@ -37,6 +37,7 @@ enum {
};
struct _mp_spiflash_t;
struct _external_flash_device;
#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
// A cache must be provided by the user in the config struct. The same cache
@ -69,9 +70,10 @@ typedef struct _mp_spiflash_config_t {
typedef struct _mp_spiflash_t {
const mp_spiflash_config_t *config;
volatile uint32_t flags;
const struct _external_flash_device *device;
} mp_spiflash_t;
void mp_spiflash_init(mp_spiflash_t *self);
int mp_spiflash_init(mp_spiflash_t *self);
void mp_spiflash_deepsleep(mp_spiflash_t *self, int value);
// These functions go direct to the SPI flash device

View File

@ -65,10 +65,10 @@ void board_sleep(int value);
#define MICROPY_HW_RTC_USE_CALOUT (1)
// SPI flash #1, for R/W storage
#define MICROPY_HW_SPIFLASH_DEVICES AT25SF161B
#define MICROPY_HW_SOFTQSPI_SCK_LOW(self) (GPIOE->BSRR = (0x10000 << 11))
#define MICROPY_HW_SOFTQSPI_SCK_HIGH(self) (GPIOE->BSRR = (1 << 11))
#define MICROPY_HW_SOFTQSPI_NIBBLE_READ(self) ((GPIOE->IDR >> 7) & 0xf)
#define MICROPY_HW_SPIFLASH_SIZE_BITS (16 * 1024 * 1024)
#define MICROPY_HW_SPIFLASH_CS (pyb_pin_QSPI1_CS)
#define MICROPY_HW_SPIFLASH_SCK (pyb_pin_QSPI1_CLK)
#define MICROPY_HW_SPIFLASH_IO0 (pyb_pin_QSPI1_D0)
@ -84,7 +84,6 @@ extern struct _spi_bdev_t spi_bdev;
#endif
#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (MICROPY_HW_SPIFLASH_SIZE_BITS / 8)
#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol
// SPI flash #2, to be memory mapped

View File

@ -186,7 +186,15 @@ MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) {
if (len != -1) {
// Detected a littlefs filesystem so create correct block device for it
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR_len), MP_OBJ_NEW_SMALL_INT(len) };
bdev = MP_OBJ_TYPE_GET_SLOT(&pyb_flash_type, make_new)(&pyb_flash_type, 0, 1, args);
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
bdev = MP_OBJ_TYPE_GET_SLOT(&pyb_flash_type, make_new)(&pyb_flash_type, 0, 1, args);
nlr_pop();
} else {
// Uncaught exception; len must be an invalid length.
mp_printf(&mp_plat_print, "MPY: corrupted filesystem\n");
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
}
}
#endif

View File

@ -539,11 +539,9 @@
// - MICROPY_HW_BDEV_SPIFLASH - pointer to a spi_bdev_t
// - MICROPY_HW_BDEV_SPIFLASH_CONFIG - pointer to an mp_spiflash_config_t
// - MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES - size in bytes of the SPI flash
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES / FLASH_BLOCK_SIZE) : \
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (uint32_t)MICROPY_HW_BDEV_SPIFLASH_CONFIG) : \
spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)) \
)
// The board can specify the SPI flash chip(s) being used as comma separated list in:
// - MICROPY_HW_SPIFLASH_DEVICES
#define MICROPY_HW_BDEV_IOCTL(op, arg) (spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)))
#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), (bl), (n))
#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), (bl), (n))
#endif

View File

@ -269,37 +269,59 @@ STATIC int octospi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *de
return 0;
}
STATIC int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
STATIC int octospi_read_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
// Note this only support use with 1 or 4 line commands.
// Full 8-line mode support is not included.
// Some commands will auto-downgrade to support 2-line mode if needed by hardware.
(void)self_in;
#if defined(MICROPY_HW_OSPIFLASH_IO1) && !defined(MICROPY_HW_OSPIFLASH_IO2) && !defined(MICROPY_HW_OSPIFLASH_IO4)
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
uint32_t dmode = 0;
uint32_t admode = 0;
uint32_t dcyc = 0;
uint32_t abmode = 0;
if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
dmode = 3; // 4 data lines used
admode = 3; // 4 address lines used
dcyc = 4; // 4 dummy cycles (2 bytes)
abmode = 3; // alternate-byte bytes sent on 4 lines
} else if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
dmode = 1; // 1 data lines used
admode = 1; // 1 address lines used
dcyc = 8; // 8 dummy cycles (1 byte)
abmode = 0; // No alternate-byte bytes sent
} else {
return -1;
}
#if !defined(MICROPY_HW_OSPIFLASH_IO2) && !defined(MICROPY_HW_OSPIFLASH_IO4)
// Use 2-line address, 2-line data.
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
uint32_t dmode = 2; // data on 2-lines
uint32_t admode = 2; // address on 2-lines
uint32_t dcyc = 4; // 4 dummy cycles
dmode = 2; // data on 2-lines
admode = 2; // address on 2-lines
dcyc = 4; // 4 dummy cycles
if (cmd == 0xeb || cmd == 0xec) {
// Convert to 2-line command.
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 0xbc : 0xbb;
}
#endif
#else
#if !defined(MICROPY_HW_OSPIFLASH_IO1)
// Fallback to use 1-line address, 1-line data.
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
uint32_t dmode = 1; // data on 1-line
uint32_t admode = 1; // address on 1-line
uint32_t dcyc = 0; // 0 dummy cycles
dmode = 1; // data on 1-line
admode = 1; // address on 1-line
dcyc = 0; // 0 dummy cycles
if (cmd == 0xeb || cmd == 0xec) {
// Convert to 1-line command.
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 0x13 : 0x03;
}
#endif
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
@ -311,7 +333,7 @@ STATIC int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t add
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
| dmode << OCTOSPI_CCR_DMODE_Pos // data on n lines
| 0 << OCTOSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
| abmode << OCTOSPI_CCR_ABMODE_Pos // alternate byte
| adsize << OCTOSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
| admode << OCTOSPI_CCR_ADMODE_Pos // address on n lines
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
@ -357,7 +379,7 @@ const mp_qspi_proto_t octospi_proto = {
.write_cmd_data = octospi_write_cmd_data,
.write_cmd_addr_data = octospi_write_cmd_addr_data,
.read_cmd = octospi_read_cmd,
.read_cmd_qaddr_qdata = octospi_read_cmd_qaddr_qdata,
.read_cmd_addr_data = octospi_read_cmd_addr_data,
};
#endif // defined(MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2)

View File

@ -350,11 +350,29 @@ STATIC int qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest)
return 0;
}
STATIC int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
STATIC int qspi_read_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
(void)self_in;
uint8_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
uint32_t dmode = 0;
uint32_t admode = 0;
uint32_t dcyc = 0;
uint32_t abmode = 0;
if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
dmode = 3; // 4 data lines used
admode = 3; // 4 address lines used
dcyc = 4; // 4 dummy cycles (2 bytes)
abmode = 3; // alternate-byte bytes sent on 4 lines
} else if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
dmode = 1; // 1 data lines used
admode = 1; // 1 address lines used
dcyc = 8; // 8 dummy cycles (1 byte)
abmode = 0; // No alternate-byte bytes sent
} else {
return -1;
}
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
QUADSPI->DLR = len - 1; // number of bytes to read
@ -363,12 +381,12 @@ STATIC int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr,
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
| 1 << QUADSPI_CCR_FMODE_Pos // indirect read mode
| 3 << QUADSPI_CCR_DMODE_Pos // data on 4 lines
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
| dmode << QUADSPI_CCR_DMODE_Pos // data lines
| dcyc << QUADSPI_CCR_DCYC_Pos // dummy cycles
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
| abmode << QUADSPI_CCR_ABMODE_Pos // alternate byte count / lines
| adsize << QUADSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
| admode << QUADSPI_CCR_ADMODE_Pos // address lines
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
;
@ -419,7 +437,7 @@ const mp_qspi_proto_t qspi_proto = {
.write_cmd_data = qspi_write_cmd_data,
.write_cmd_addr_data = qspi_write_cmd_addr_data,
.read_cmd = qspi_read_cmd,
.read_cmd_qaddr_qdata = qspi_read_cmd_qaddr_qdata,
.read_cmd_addr_data = qspi_read_cmd_addr_data,
};
#endif // defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2)

View File

@ -29,16 +29,33 @@
#include "irq.h"
#include "led.h"
#include "storage.h"
#include "drivers/memory/external_flash_device.h"
#if MICROPY_HW_ENABLE_STORAGE
int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
switch (op) {
case BDEV_IOCTL_INIT:
#ifdef MICROPY_HW_BDEV_SPIFLASH_CONFIG
if (!arg) {
arg = (uint32_t)(MICROPY_HW_BDEV_SPIFLASH_CONFIG);
}
#endif
bdev->spiflash.config = (const mp_spiflash_config_t *)arg;
mp_spiflash_init(&bdev->spiflash);
int ret = mp_spiflash_init(&bdev->spiflash);
bdev->flash_tick_counter_last_write = 0;
return 0;
return ret;
case BDEV_IOCTL_NUM_BLOCKS:
#if MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES
return MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES / FLASH_BLOCK_SIZE;
#else
if (bdev->spiflash.device != NULL) {
external_flash_device *flash = (external_flash_device *)bdev->spiflash.device;
return flash->total_size / FLASH_BLOCK_SIZE;
}
return -1;
#endif
case BDEV_IOCTL_IRQ_HANDLER: {
int ret = 0;

View File

@ -48,11 +48,11 @@ static void storage_systick_callback(uint32_t ticks_ms);
void storage_init(void) {
if (!storage_is_initialised) {
storage_is_initialised = true;
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
if (MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0) == 0) {
storage_is_initialised = true;
}
#if defined(MICROPY_HW_BDEV2_IOCTL)
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0);

View File

@ -176,20 +176,25 @@ def run_script_on_remote_target(pyb, args, test_file, is_special):
return had_crash, output_mupy
def run_micropython(pyb, args, test_file, is_special=False):
special_tests = (
special_tests = [
base_path(file)
for file in (
"micropython/meminfo.py",
"basics/bytes_compare3.py",
"basics/builtin_help.py",
"thread/thread_exc2.py",
"esp32/partition_ota.py",
)
]
def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False):
had_crash = False
if pyb is None:
# run on PC
if (
test_file.startswith(("cmdline/", base_path("feature_check/")))
or test_file in special_tests
test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/")))
or test_file_abspath in special_tests
):
# special handling for tests of the unix cmdline program
is_special = True
@ -205,7 +210,7 @@ def run_micropython(pyb, args, test_file, is_special=False):
# run the test, possibly with redirected input
try:
if "repl_" in test_file:
if os.path.basename(test_file).startswith("repl_"):
# Need to use a PTY to test command line editing
try:
import pty
@ -283,7 +288,7 @@ def run_micropython(pyb, args, test_file, is_special=False):
mpy_modname = os.path.splitext(os.path.basename(mpy_filename))[0]
cmdlist.extend(["-m", mpy_modname])
else:
cmdlist.append(os.path.abspath(test_file))
cmdlist.append(test_file_abspath)
# run the actual test
try:
@ -316,7 +321,7 @@ def run_micropython(pyb, args, test_file, is_special=False):
if is_special and not had_crash and b"\nSKIP\n" in output_mupy:
return b"SKIP\n"
if is_special or test_file in special_tests:
if is_special or test_file_abspath in special_tests:
# convert parts of the output that are not stable across runs
with open(test_file + ".exp", "rb") as f:
lines_exp = []
@ -360,11 +365,12 @@ def run_micropython(pyb, args, test_file, is_special=False):
return output_mupy
def run_feature_check(pyb, args, base_path, test_file):
def run_feature_check(pyb, args, test_file):
if pyb is not None and test_file.startswith("repl_"):
# REPL feature tests will not run via pyboard because they require prompt interactivity
return b""
return run_micropython(pyb, args, base_path("feature_check", test_file), is_special=True)
test_file_path = base_path("feature_check", test_file)
return run_micropython(pyb, args, test_file_path, test_file_path, is_special=True)
class ThreadSafeCounter:
@ -419,57 +425,57 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# run-tests.py script itself so use base_path.
# Check if micropython.native is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "native_check.py")
output = run_feature_check(pyb, args, "native_check.py")
if output != b"native\n":
skip_native = True
# Check if arbitrary-precision integers are supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "int_big.py")
output = run_feature_check(pyb, args, "int_big.py")
if output != b"1000000000000000000000000000000000000000000000\n":
skip_int_big = True
# Check if bytearray is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "bytearray.py")
output = run_feature_check(pyb, args, "bytearray.py")
if output != b"bytearray\n":
skip_bytearray = True
# Check if set type (and set literals) is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "set_check.py")
output = run_feature_check(pyb, args, "set_check.py")
if output != b"{1}\n":
skip_set_type = True
# Check if slice is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "slice.py")
output = run_feature_check(pyb, args, "slice.py")
if output != b"slice\n":
skip_slice = True
# Check if async/await keywords are supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "async_check.py")
output = run_feature_check(pyb, args, "async_check.py")
if output != b"async\n":
skip_async = True
# Check if const keyword (MicroPython extension) is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "const.py")
output = run_feature_check(pyb, args, "const.py")
if output != b"1\n":
skip_const = True
# Check if __rOP__ special methods are supported, and skip such tests if it's not
output = run_feature_check(pyb, args, base_path, "reverse_ops.py")
output = run_feature_check(pyb, args, "reverse_ops.py")
if output == b"TypeError\n":
skip_revops = True
# Check if io module exists, and skip such tests if it doesn't
output = run_feature_check(pyb, args, base_path, "io_module.py")
output = run_feature_check(pyb, args, "io_module.py")
if output != b"io\n":
skip_io_module = True
# Check if fstring feature is enabled, and skip such tests if it doesn't
output = run_feature_check(pyb, args, base_path, "fstring.py")
output = run_feature_check(pyb, args, "fstring.py")
if output != b"a=1\n":
skip_fstring = True
# Check if @micropython.asm_thumb supports Thumb2 instructions, and skip such tests if it doesn't
output = run_feature_check(pyb, args, base_path, "inlineasm_thumb2.py")
output = run_feature_check(pyb, args, "inlineasm_thumb2.py")
if output != b"thumb2\n":
skip_tests.add("inlineasm/asmbcc.py")
skip_tests.add("inlineasm/asmbitops.py")
@ -484,23 +490,23 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
skip_tests.add("inlineasm/asmspecialregs.py")
# Check if emacs repl is supported, and skip such tests if it's not
t = run_feature_check(pyb, args, base_path, "repl_emacs_check.py")
t = run_feature_check(pyb, args, "repl_emacs_check.py")
if "True" not in str(t, "ascii"):
skip_tests.add("cmdline/repl_emacs_keys.py")
# Check if words movement in repl is supported, and skip such tests if it's not
t = run_feature_check(pyb, args, base_path, "repl_words_move_check.py")
t = run_feature_check(pyb, args, "repl_words_move_check.py")
if "True" not in str(t, "ascii"):
skip_tests.add("cmdline/repl_words_move.py")
upy_byteorder = run_feature_check(pyb, args, base_path, "byteorder.py")
upy_float_precision = run_feature_check(pyb, args, base_path, "float.py")
upy_byteorder = run_feature_check(pyb, args, "byteorder.py")
upy_float_precision = run_feature_check(pyb, args, "float.py")
try:
upy_float_precision = int(upy_float_precision)
except ValueError:
upy_float_precision = 0
has_complex = run_feature_check(pyb, args, base_path, "complex.py") == b"complex\n"
has_coverage = run_feature_check(pyb, args, base_path, "coverage.py") == b"coverage\n"
has_complex = run_feature_check(pyb, args, "complex.py") == b"complex\n"
has_coverage = run_feature_check(pyb, args, "coverage.py") == b"coverage\n"
cpy_byteorder = subprocess.check_output(
CPYTHON3_CMD + [base_path("feature_check/byteorder.py")]
)
@ -673,6 +679,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
def run_one_test(test_file):
test_file = test_file.replace("\\", "/")
test_file_abspath = os.path.abspath(test_file).replace("\\", "/")
if args.filters:
# Default verdict is the opposit of the first action
@ -733,7 +740,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# run CPython to work out expected output
try:
output_expected = subprocess.check_output(
CPYTHON3_CMD + [os.path.abspath(test_file)],
CPYTHON3_CMD + [test_file_abspath],
cwd=os.path.dirname(test_file),
stderr=subprocess.STDOUT,
)
@ -750,7 +757,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
return
# run MicroPython
output_mupy = run_micropython(pyb, args, test_file)
output_mupy = run_micropython(pyb, args, test_file, test_file_abspath)
if output_mupy == b"SKIP\n":
print("skip ", test_file)