diff --git a/ports/atmel-samd/external_flash/qspi_flash.c b/ports/atmel-samd/external_flash/qspi_flash.c index f4d5ec9041..3fc9d31be4 100644 --- a/ports/atmel-samd/external_flash/qspi_flash.c +++ b/ports/atmel-samd/external_flash/qspi_flash.c @@ -32,6 +32,7 @@ #include "mpconfigboard.h" // for EXTERNAL_FLASH_QSPI_DUAL #include "external_flash/common_commands.h" +#include "peripherals.h" #include "shared_dma.h" #include "atmel_start_pins.h" @@ -55,6 +56,8 @@ bool spi_flash_command(uint8_t command) { } bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + QSPI->INSTRCTRL.bit.INSTR = command; QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | @@ -63,6 +66,11 @@ bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_DATAEN; + // Dummy read of INSTRFRAME needed to synchronize. + // See Instruction Transmission Flow Diagram, figure 37.9, page 995 + // and Example 4, page 998, section 37.6.8.5. + (volatile uint32_t) QSPI->INSTRFRAME.reg; + memcpy(response, (uint8_t *) QSPI_AHB, length); QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; @@ -71,20 +79,28 @@ bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + samd_peripherals_enable_cache(); + return true; } bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + QSPI->INSTRCTRL.bit.INSTR = command; QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_TFRTYPE_WRITE | - QSPI_INSTRFRAME_INSTREN; + QSPI_INSTRFRAME_INSTREN | + (data != NULL ? QSPI_INSTRFRAME_DATAEN : 0); + + // Dummy read of INSTRFRAME needed to synchronize. + // See Instruction Transmission Flow Diagram, figure 37.9, page 995 + // and Example 4, page 998, section 37.6.8.5. + (volatile uint32_t) QSPI->INSTRFRAME.reg; if (data != NULL) { - QSPI->INSTRFRAME.bit.DATAEN = true; - memcpy((uint8_t *) QSPI_AHB, data, length); } @@ -94,6 +110,8 @@ bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + samd_peripherals_enable_cache(); + return true; } @@ -117,6 +135,8 @@ bool spi_flash_sector_command(uint8_t command, uint32_t address) { } bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + QSPI->INSTRCTRL.bit.INSTR = CMD_PAGE_PROGRAM; uint32_t mode = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI; @@ -137,10 +157,14 @@ bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + samd_peripherals_enable_cache(); + return true; } bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + #ifdef EXTERNAL_FLASH_QSPI_DUAL QSPI->INSTRCTRL.bit.INSTR = CMD_DUAL_READ; uint32_t mode = QSPI_INSTRFRAME_WIDTH_DUAL_OUTPUT; @@ -167,6 +191,8 @@ bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + samd_peripherals_enable_cache(); + return true; } @@ -183,7 +209,7 @@ void spi_flash_init(void) { // QSPI->BAUD.bit.BAUD = 32; // Super fast, may be unreliable when Saleae is connected to high speed lines. QSPI->BAUD.bit.BAUD = 2; - QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY | + QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY | // Serial memory mode (map to QSPI_AHB) QSPI_CTRLB_DATALEN_8BITS | QSPI_CTRLB_CSMODE_LASTXFER; diff --git a/ports/atmel-samd/samd51_peripherals.c b/ports/atmel-samd/samd51_peripherals.c index 49ee1270d2..5772b8e0ab 100644 --- a/ports/atmel-samd/samd51_peripherals.c +++ b/ports/atmel-samd/samd51_peripherals.c @@ -163,3 +163,15 @@ void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance) hri_adc_write_CALIB_BIASR2R_bf(instance, biasr2r); hri_adc_write_CALIB_BIASCOMP_bf(instance, biascomp); } + +// Turn off cache and invalidate all data in it. +void samd_peripherals_disable_and_clear_cache(void) { + CMCC->CTRL.bit.CEN = 0; + while (CMCC->SR.bit.CSTS) {} + CMCC->MAINT0.bit.INVALL = 1; +} + +// Enable cache +void samd_peripherals_enable_cache(void) { + CMCC->CTRL.bit.CEN = 1; +} diff --git a/ports/atmel-samd/samd51_peripherals.h b/ports/atmel-samd/samd51_peripherals.h index 0523bf9601..6643379a5c 100644 --- a/ports/atmel-samd/samd51_peripherals.h +++ b/ports/atmel-samd/samd51_peripherals.h @@ -35,4 +35,7 @@ uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad); void samd_peripherals_adc_setup(struct adc_sync_descriptor *adc, Adc *instance); +void samd_peripherals_disable_and_clear_cache(void); +void samd_peripherals_enable_cache(void); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD51_PERIPHERALS_H diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 0e2d937634..103f39c753 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -57,6 +57,7 @@ #include "shared-bindings/rtc/__init__.h" #include "clocks.h" #include "events.h" +#include "peripherals.h" #include "shared_dma.h" #include "tick.h" @@ -105,9 +106,47 @@ safe_mode_t port_init(void) { SUPC->BOD33.bit.ENABLE = 0; SUPC->BOD33.bit.LEVEL = 200; // 2.7V: 1.5V + LEVEL * 6mV. SUPC->BOD33.bit.ENABLE = 1; + + // MPU (Memory Protection Unit) setup. + // We hoped we could make the QSPI region be non-cachable with the MPU, + // but the CMCC doesn't seem to pay attention to the MPU settings. + // Leaving this code here disabled, + // because it was hard enough to figure out, and maybe there's + // a mistake that could make it work in the future. +#if 0 + // Designate QSPI memory mapped region as not cachable. + + // Turn off MPU in case it is on. + MPU->CTRL = 0; + // Configure region 0. + MPU->RNR = 0; + // Region base: start of QSPI mapping area. + // QSPI region runs from 0x04000000 up to and not including 0x05000000: 16 megabytes + MPU->RBAR = QSPI_AHB; + MPU->RASR = + 0b011 << MPU_RASR_AP_Pos | // full read/write access for privileged and user mode + 0b000 << MPU_RASR_TEX_Pos | // caching not allowed, strongly ordered + 1 << MPU_RASR_S_Pos | // sharable + 0 << MPU_RASR_C_Pos | // not cachable + 0 << MPU_RASR_B_Pos | // not bufferable + 0b10111 << MPU_RASR_SIZE_Pos | // 16MB region size + 1 << MPU_RASR_ENABLE_Pos // enable this region + ; + // Turn off regions 1-7. + for (uint32_t i = 1; i < 8; i ++) { + MPU->RNR = i; + MPU->RBAR = 0; + MPU->RASR = 0; + } + + // Turn on MPU. Turn on PRIVDEFENA, which defines a default memory + // map for all privileged access, so we don't have to set up other regions + // besides QSPI. + MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; #endif - + samd_peripherals_enable_cache(); +#endif // On power on start or external reset, set _ezero to the canary word. If it // gets killed, we boot in safe mode. _ezero is the boundary between statically