Merge pull request #465 from dhalbert/3.0_spi_flash

Support SPI flash chips for CIRCUITPY, using non-DMA SPI for now.
This commit is contained in:
Dan Halbert 2017-11-28 13:59:16 -05:00 committed by GitHub
commit 93978bcd25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 361 additions and 212 deletions

View File

@ -218,6 +218,7 @@ SRC_C = \
mphalport.c \
reset.c \
$(CHIP_FAMILY)_peripherals.c \
peripherals.c \
$(CHIP_FAMILY)_pins.c \
tick.c \
usb.c \
@ -260,6 +261,7 @@ SRC_COMMON_HAL = \
microcontroller/Processor.c \
neopixel_write/__init__.c \
os/__init__.c \
storage/__init__.c \
time/__init__.c \
# analogio/__init__.c \
analogio/AnalogIn.c \
@ -276,7 +278,6 @@ SRC_COMMON_HAL = \
pulseio/PulseIn.c \
pulseio/PulseOut.c \
pulseio/PWMOut.c \
storage/__init__.c \
touchio/__init__.c \
touchio/TouchIn.c \
usb_hid/__init__.c \

@ -1 +1 @@
Subproject commit 72f76894ba08c9de2ec3ae231fb71daaf3eafb1e
Subproject commit 0efc3407dd97ef617a5655674a3516693897a961

View File

@ -3,25 +3,35 @@
#define MICROPY_HW_NEOPIXEL (&pin_PA06)
// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz.
#define SPI_FLASH_BAUDRATE (8000000)
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_C
#define SPI_FLASH_PAD0_PINMUX PINMUX_PA08D_SERCOM2_PAD0 // MOSI
// Use default pinmux for the chip select since we manage it ourselves.
#define SPI_FLASH_PAD1_PINMUX PINMUX_PA09D_SERCOM2_PAD1 // SCK
#define SPI_FLASH_PAD2_PINMUX PINMUX_PA14C_SERCOM2_PAD2 // MISO
#define SPI_FLASH_PAD3_PINMUX PINMUX_UNUSED // SCK
#define SPI_FLASH_SERCOM SERCOM2
#define SPI_FLASH_MOSI_PIN PIN_PA08
#define SPI_FLASH_MISO_PIN PIN_PA14
#define SPI_FLASH_SCK_PIN PIN_PA09
#define SPI_FLASH_CS_PIN PIN_PA13
#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA08D_SERCOM2_PAD0
#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA14C_SERCOM2_PAD2
#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA09D_SERCOM2_PAD1
#define SPI_FLASH_SERCOM SERCOM2
#define SPI_FLASH_SERCOM_INDEX 2
#define SPI_FLASH_MOSI_PAD 0
#define SPI_FLASH_MISO_PAD 2
#define SPI_FLASH_SCK_PAD 1
// <o> Transmit Data Pinout
// <0x0=>PAD[0,1]_DO_SCK
// <0x1=>PAD[2,3]_DO_SCK
// <0x2=>PAD[3,1]_DO_SCK
// <0x3=>PAD[0,3]_DO_SCK
#define SPI_FLASH_DOPO 0
#define SPI_FLASH_DIPO 2 // same as MISO pad
#define SPI_FLASH_CS PIN_PA13
#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25)
// These are pins not to reset.
#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25)
#define MICROPY_PORT_B ( 0 )
#define MICROPY_PORT_C ( 0 )
#include "internal_flash.h"
//#include "spi_flash.h"
#include "spi_flash.h"
// If you change this, then make sure to update the linker scripts as well to
// make sure you don't overwrite code.

View File

@ -4,8 +4,7 @@ USB_PID = 0x8023
USB_PRODUCT = "Feather M0 Express"
USB_MANUFACTURER = "Adafruit Industries LLC"
#SPI_FLASH_FILESYSTEM = 1
INTERNAL_FLASH_FILESYSTEM = 1
SPI_FLASH_FILESYSTEM = 1
CHIP_VARIANT = SAMD21G18A
CHIP_FAMILY = samd21

View File

@ -1,5 +1,3 @@
#define USB_REPL
#define MICROPY_HW_BOARD_NAME "Adafruit Metro M0 Express"
#define MICROPY_HW_MCU_NAME "samd21g18"
@ -8,24 +6,35 @@
#define MICROPY_HW_NEOPIXEL (&pin_PA30)
// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz.
#define SPI_FLASH_BAUDRATE (8000000)
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_F
#define SPI_FLASH_PAD0_PINMUX PINMUX_UNUSED // CS
// Use default pinmux for the chip select since we manage it ourselves.
#define SPI_FLASH_PAD1_PINMUX PINMUX_PB03D_SERCOM5_PAD1 // MISO
#define SPI_FLASH_PAD2_PINMUX PINMUX_PB22D_SERCOM5_PAD2 // MOSI
#define SPI_FLASH_PAD3_PINMUX PINMUX_PB23D_SERCOM5_PAD3 // SCK
#define SPI_FLASH_CS PIN_PA13
#define SPI_FLASH_SERCOM SERCOM5
#define SPI_FLASH_MOSI_PIN PIN_PB22
#define SPI_FLASH_MISO_PIN PIN_PB03
#define SPI_FLASH_SCK_PIN PIN_PB23
#define SPI_FLASH_CS_PIN PIN_PA13
#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB22D_SERCOM5_PAD2
#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB03D_SERCOM5_PAD1
#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB23D_SERCOM5_PAD3
#define SPI_FLASH_SERCOM SERCOM5
#define SPI_FLASH_SERCOM_INDEX 5
#define SPI_FLASH_MOSI_PAD 2
#define SPI_FLASH_MISO_PAD 1
#define SPI_FLASH_SCK_PAD 3
// <o> Transmit Data Pinout
// <0x0=>PAD[0,1]_DO_SCK
// <0x1=>PAD[2,3]_DO_SCK
// <0x2=>PAD[3,1]_DO_SCK
// <0x3=>PAD[0,3]_DO_SCK
#define SPI_FLASH_DOPO 1
#define SPI_FLASH_DIPO 1 // same as MISO pad
// These are pins not to reset.
#define MICROPY_PORT_A (PORT_PA13 |PORT_PA24 | PORT_PA25 | PORT_PA27 | PORT_PA30 | PORT_PA31)
#define MICROPY_PORT_B (PORT_PB03 | PORT_PB22 | PORT_PB23)
#define MICROPY_PORT_C (0)
#include "internal_flash.h"
//#include "spi_flash.h"
#include "spi_flash.h"
// If you change this, then make sure to update the linker scripts as well to
// make sure you don't overwrite code.

View File

@ -4,8 +4,7 @@ USB_PID = 0x8014
USB_PRODUCT = "Metro M0 Express"
USB_MANUFACTURER = "Adafruit Industries LLC"
#SPI_FLASH_FILESYSTEM = 1
INTERNAL_FLASH_FILESYSTEM = 1
SPI_FLASH_FILESYSTEM = 1
CHIP_VARIANT = SAMD21G18A
CHIP_FAMILY = samd21

View File

@ -29,6 +29,5 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB11) },
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB10) },
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PA12) },
{ MP_ROM_QSTR(MP_QSTR_FLASH_CS), MP_ROM_PTR(&pin_PA13) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);

View File

@ -8,29 +8,46 @@
#define MICROPY_HW_NEOPIXEL (&pin_PB17)
#define SPI_FLASH_BAUDRATE (1000000)
#define SPI_FLASH_BAUDRATE (8000000)
// Rev F
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_F
#define SPI_FLASH_PAD0_PINMUX PINMUX_PB08 // MOSI
// Use default pinmux for the chip select since we manage it ourselves.
#define SPI_FLASH_PAD1_PINMUX PINMUX_PB09 // SCK
#define SPI_FLASH_PAD2_PINMUX PINMUX_DEFAULT // CS
#define SPI_FLASH_PAD3_PINMUX PINMUX_PB11 // MISO
#define SPI_FLASH_CS PIN_PB10
#define SPI_FLASH_SERCOM SERCOM5
// Rev B: single channel SPI
// Rev C will be QSPI
#define SPI_FLASH_MOSI_PIN PIN_PB08
#define SPI_FLASH_MISO_PIN PIN_PB11
#define SPI_FLASH_SCK_PIN PIN_PB09
#define SPI_FLASH_CS_PIN PIN_PB10
#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB08D_SERCOM4_PAD0
#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB11D_SERCOM4_PAD3
#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB09D_SERCOM4_PAD1
#define SPI_FLASH_SERCOM SERCOM4
#define SPI_FLASH_SERCOM_INDEX 4
#define SPI_FLASH_MOSI_PAD 0
#define SPI_FLASH_MISO_PAD 3
#define SPI_FLASH_SCK_PAD 1
// <o> Transmit Data Pinout
// <0x0=>PAD[0,1]_DO_SCK
// <0x1=>PAD[2,3]_DO_SCK
// <0x2=>PAD[3,1]_DO_SCK
// <0x3=>PAD[0,3]_DO_SCK
#define SPI_FLASH_DOPO 0
#define SPI_FLASH_DIPO 3 // same as MISO pad
// These are pins not to reset.
#define MICROPY_PORT_A (PORT_PA27)
#define MICROPY_PORT_B (PORT_PB06 | PORT_PB08 | PORT_PB09 | PORT_PB11 | PORT_PB17)
#define MICROPY_PORT_B (PORT_PB06 | PORT_PB08 | PORT_PB09 | PORT_PB10 | PORT_PB11 | PORT_PB17)
#define MICROPY_PORT_C (0)
#define MICROPY_PORT_D (0)
#define AUTORESET_DELAY_MS 500
#include "internal_flash.h"
#include "spi_flash.h"
// If you change this, then make sure to update the linker scripts as well to
// make sure you don't overwrite code.
// make sure you don't overwrite code
// #define CIRCUITPY_INTERNAL_NVM_SIZE 256
#define CIRCUITPY_INTERNAL_NVM_SIZE 0
#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000)
#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE)
#include "flash_S25FL216K.h"
#include "flash_GD25Q16C.h"

View File

@ -4,8 +4,7 @@ USB_PID = 0x8021
USB_PRODUCT = "Metro M4 Express"
USB_MANUFACTURER = "Adafruit Industries LLC"
#SPI_FLASH_FILESYSTEM = 1
INTERNAL_FLASH_FILESYSTEM = 1
SPI_FLASH_FILESYSTEM = 1
CHIP_VARIANT = SAMD51J19A
CHIP_FAMILY = samd51

View File

@ -72,7 +72,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
// Set up I2C clocks on sercom.
samd_peripheral_sercom_clock_init(sercom, sercom_index);
samd_peripherals_sercom_clock_init(sercom, sercom_index);
if (i2c_m_sync_init(&self->i2c_desc, sercom) != ERR_NONE) {
mp_raise_OSError(MP_EIO);

View File

@ -37,17 +37,7 @@
#include "peripherals.h"
#include "pins.h"
#include "shared_dma.h"
// Convert frequency to clock-speed-dependent value. Return 0 if out of range.
static uint8_t baudrate_to_baud_reg_value(const uint32_t baudrate) {
uint32_t baud_reg_value = (uint32_t) (((float) PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY /
(2 * baudrate)) + 0.5f);
if (baud_reg_value > 0xff) {
return 0;
}
return (uint8_t) baud_reg_value;
}
//#include "shared_dma.h"
void common_hal_busio_spi_construct(busio_spi_obj_t *self,
const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi,
@ -78,7 +68,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
}
clock_pinmux = PINMUX(clock->pin, (i == 0) ? MUX_C : MUX_D);
clock_pad = clock->sercom[i].pad;
if (!samd_peripheral_valid_spi_clock_pad(clock_pad)) {
if (!samd_peripherals_valid_spi_clock_pad(clock_pad)) {
continue;
}
for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
@ -86,7 +76,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
if(potential_sercom == mosi->sercom[j].sercom) {
mosi_pinmux = PINMUX(mosi->pin, (j == 0) ? MUX_C : MUX_D);
mosi_pad = mosi->sercom[j].pad;
dopo = samd_peripheral_get_spi_dopo(clock_pad, mosi_pad);
dopo = samd_peripherals_get_spi_dopo(clock_pad, mosi_pad);
if (dopo > 0x3) {
continue; // pad combination not possible
}
@ -121,7 +111,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
}
// Set up SPI clocks on SERCOM.
samd_peripheral_sercom_clock_init(sercom, sercom_index);
samd_peripherals_sercom_clock_init(sercom, sercom_index);
if (spi_m_sync_init(&self->spi_desc, sercom) != ERR_NONE) {
mp_raise_OSError(MP_EIO);
@ -132,7 +122,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
// Always start at 250khz which is what SD cards need. They are sensitive to
// SPI bus noise before they are put into SPI mode.
uint8_t baud_value = baudrate_to_baud_reg_value(250000);
uint8_t baud_value = samd_peripherals_baudrate_to_baud_reg_value(250000);
if (baud_value == 0) {
mp_raise_RuntimeError("SPI initial baudrate out of range.");
}
@ -142,6 +132,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
mp_raise_OSError(MP_EIO);
}
gpio_set_pin_direction(clock->pin, GPIO_DIRECTION_OUT);
gpio_set_pin_pull_mode(clock->pin, GPIO_PULL_OFF);
gpio_set_pin_function(clock->pin, clock_pinmux);
claim_pin(clock);
@ -150,6 +141,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
if (mosi_none) {
self->MOSI_pin = NO_PIN;
} else {
gpio_set_pin_direction(mosi->pin, GPIO_DIRECTION_OUT);
gpio_set_pin_pull_mode(mosi->pin, GPIO_PULL_OFF);
gpio_set_pin_function(mosi->pin, mosi_pinmux);
self->MOSI_pin = mosi->pin;
@ -159,6 +151,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
if (miso_none) {
self->MISO_pin = NO_PIN;
} else {
gpio_set_pin_direction(miso->pin, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(miso->pin, GPIO_PULL_OFF);
gpio_set_pin_function(miso->pin, miso_pinmux);
self->MISO_pin = miso->pin;
@ -186,7 +179,7 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
bool common_hal_busio_spi_configure(busio_spi_obj_t *self,
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
uint8_t baud_reg_value = baudrate_to_baud_reg_value(baudrate);
uint8_t baud_reg_value = samd_peripherals_baudrate_to_baud_reg_value(baudrate);
if (baud_reg_value == 0) {
mp_raise_ValueError("baudrate out of range");
}
@ -248,7 +241,7 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self,
spi_m_sync_get_io_descriptor(&self->spi_desc, &spi_io);
status = spi_io->write(spi_io, data, len);
// }
return status > 0; // Status is number of chars read or an error code < 0.
return status >= 0; // Status is number of chars read or an error code < 0.
}
bool common_hal_busio_spi_read(busio_spi_obj_t *self,
@ -267,5 +260,5 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self,
status = spi_io->read(spi_io, data, len);
// }
return status > 0; // Status is number of chars read or an error code < 0.
return status >= 0; // Status is number of chars read or an error code < 0.
}

View File

@ -30,6 +30,7 @@
#include "py/mperrno.h"
#include "py/runtime.h"
#include "shared-bindings/storage/__init__.h"
#include "usb.h"
extern volatile bool mp_msc_enabled;
@ -38,7 +39,9 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) {
mp_raise_OSError(MP_EINVAL);
}
if (mp_msc_enabled) {
// TODO(dhalbert): is this is a good enough check? It checks for
// CDC enabled. There is no "MSC enabled" check.
if (usb_connected()) {
mp_raise_RuntimeError("Cannot remount '/' when USB is active.");
}

View File

@ -215,7 +215,6 @@ extern const struct _mp_obj_module_t usb_hid_module;
// { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module },
#define MICROPY_PORT_BUILTIN_MODULES \
@ -226,6 +225,7 @@ extern const struct _mp_obj_module_t usb_hid_module;
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_supervisor), (mp_obj_t)&supervisor_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, \

View File

@ -0,0 +1,43 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Dan Halbert for Adafruit Industries
*
* 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 "peripherals.h"
#include "hpl_sercom_config.h"
// Routines that are the same across all samd variants.
// Convert frequency to clock-speed-dependent value. Return 0 if out of range.
uint8_t samd_peripherals_baudrate_to_baud_reg_value(const uint32_t baudrate) {
uint32_t baud_reg_value = (uint32_t) (((float) PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY /
(2 * baudrate)) + 0.5f);
if (baud_reg_value > 0xff) {
return 0;
}
return (uint8_t) baud_reg_value;
}

View File

@ -27,8 +27,13 @@
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_H
#define MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_H
#include <stdint.h>
#include "mpconfigport.h"
// Routines common across chip families.
uint8_t samd_peripherals_baudrate_to_baud_reg_value(const uint32_t baudrate);
#ifdef SAMD21
#include "samd21_peripherals.h"
#endif

View File

@ -58,7 +58,7 @@ static const uint8_t SERCOMx_GCLK_ID_SLOW[] = {
// Clock initialization as done in Atmel START.
void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
_pm_enable_bus_clock(PM_BUS_APBC, sercom);
_gclk_enable_channel(SERCOMx_GCLK_ID_CORE[sercom_index], GCLK_CLKCTRL_GEN_GCLK0_Val);
_gclk_enable_channel(SERCOMx_GCLK_ID_SLOW[sercom_index], GCLK_CLKCTRL_GEN_GCLK3_Val);
@ -70,7 +70,7 @@ void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
// <0x1=>PAD[2,3]_DO_SCK
// <0x2=>PAD[3,1]_DO_SCK
// <0x3=>PAD[0,3]_DO_SCK
uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
if (clock_pad == 1) {
if (mosi_pad == 0) {
return 0;
@ -87,6 +87,6 @@ uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
return 255;
}
bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad) {
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) {
return clock_pad == 1 || clock_pad == 3;
}

View File

@ -29,8 +29,8 @@
#include "include/sam.h"
void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad);
bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad);
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
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);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H

View File

@ -62,7 +62,7 @@ static const uint8_t SERCOMx_GCLK_ID_SLOW[] = {
// Clock initialization as done in Atmel START.
void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
hri_gclk_write_PCHCTRL_reg(GCLK,
SERCOMx_GCLK_ID_CORE[sercom_index],
GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
@ -113,7 +113,7 @@ void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) {
// <0x1=>PAD[2,3]_DO_SCK [RESERVED]
// <0x2=>PAD[3,1]_DO_SCK
// <0x3=>PAD[0,3]_DO_SCK [RESERVED]
uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
if (clock_pad != 1) {
return 255;
}
@ -126,6 +126,6 @@ uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) {
return 255;
}
bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad) {
bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) {
return clock_pad == 1;
}

View File

@ -29,9 +29,9 @@
#include "sam.h"
void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad);
bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad);
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
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);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD51_PERIPHERALS_H

View File

@ -0,0 +1,36 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 by Dan Halbert for Adafruit Industries
*
* 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.
*/
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
#define MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H
#include "include/sam.h"
void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index);
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);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H

View File

@ -43,28 +43,33 @@ void init_shared_dma(void) {
struct dma_resource_config config;
dma_get_config_defaults(&config);
// This allocates the lowest channel first so make sure the audio is first
// so it gets the highest priority.
config.peripheral_trigger = DAC_DMAC_ID_EMPTY;
config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
config.event_config.event_output_enable = true;
dma_allocate(&audio_dma, &config);
// See asf4_conf/hpl_dmac_config.h for initial settings for DMA channels
// DMA Channel 0: audio, highest priority,
// normal transfer on input, DAC 0 empty is trigger source, trigger on each beat, beat is one byte
// output enable true.
// asf3 settings:
//config.peripheral_trigger = DAC_DMAC_ID_EMPTY;
//config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
//config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
//config.event_config.event_output_enable = true;
// Turn on the transfer complete interrupt so that the job_status changes to done.
g_chan_interrupt_flag[audio_dma.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
// Prioritize the RX channel over the TX channel because TX can cause an RX
// overflow.
dma_get_config_defaults(&config);
config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
// DMA Channel 1: rx channel,
// normal transfer on input, trigger on each beat, beat is one byte
//config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
//config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
dma_allocate(&general_dma_rx, &config);
g_chan_interrupt_flag[general_dma_rx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
dma_get_config_defaults(&config);
config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
dma_allocate(&general_dma_tx, &config);
// DMA Channel 1: rx channel,
// normal transfer on input, trigger on each beat, beat is one byte
//config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
//config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
g_chan_interrupt_flag[general_dma_tx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
// Be sneaky and reuse the active descriptor memory.

View File

@ -28,20 +28,19 @@
#include <stdint.h>
#include <string.h>
<<<<<<< HEAD
#include "asf/sam0/drivers/sercom/spi/spi.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
=======
>>>>>>> WIP
#include "py/gc.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "lib/oofatfs/ff.h"
#include "peripherals.h"
#include "supervisor/shared/rgb_led_status.h"
#include "rgb_led_status.h"
#include "shared_dma.h"
//#include "shared_dma.h"
#include "hal_gpio.h"
#include "hal_spi_m_sync.h"
#define SPI_FLASH_PART1_START_BLOCK (0x1)
@ -60,7 +59,7 @@
static bool spi_flash_is_initialised = false;
struct spi_module spi_flash_instance;
struct spi_m_sync_descriptor spi_flash_desc;
// The currently cached sector in the cache, ram or flash based.
static uint32_t current_sector;
@ -74,35 +73,37 @@ static uint32_t dirty_mask;
// Enable the flash over SPI.
static void flash_enable(void) {
port_pin_set_output_level(SPI_FLASH_CS, false);
gpio_set_pin_level(SPI_FLASH_CS_PIN, false);
}
// Disable the flash over SPI.
static void flash_disable(void) {
port_pin_set_output_level(SPI_FLASH_CS, true);
gpio_set_pin_level(SPI_FLASH_CS_PIN, true);
}
// Wait until both the write enable and write in progress bits have cleared.
static bool wait_for_flash_ready(void) {
uint8_t status_request[2] = {CMD_READ_STATUS, 0x00};
uint8_t response[2] = {0x00, 0x01};
enum status_code status = STATUS_OK;
uint8_t read_status_request[2] = {CMD_READ_STATUS, 0x00};
uint8_t read_status_response[2] = {0x00, 0x00};
struct spi_xfer read_status_xfer = {read_status_request, read_status_response, 2};
int32_t status;
// Both the write enable and write in progress bits should be low.
while (status == STATUS_OK && (response[1] & 0x3) != 0) {
do {
flash_enable();
status = spi_transceive_buffer_wait(&spi_flash_instance, status_request, response, 2);
status = spi_m_sync_transfer(&spi_flash_desc, &read_status_xfer);
flash_disable();
}
return status == STATUS_OK;
} while (status >= 0 && (read_status_response[1] & 0x3) != 0);
return status >= 0; // status is number of chars read or a negative error code.
}
// Turn on the write enable bit so we can program and erase the flash.
static bool write_enable(void) {
flash_enable();
uint8_t command = CMD_ENABLE_WRITE;
enum status_code status = spi_write_buffer_wait(&spi_flash_instance, &command, 1);
uint8_t enable_write_request[1] = {CMD_ENABLE_WRITE};
struct spi_xfer enable_write_xfer = {enable_write_request, 0, 1};
int32_t status = spi_m_sync_transfer(&spi_flash_desc, &enable_write_xfer);
flash_disable();
return status == STATUS_OK;
return status >= 0; // status is number of chars read or a negative error code.
}
// Pack the low 24 bits of the address into a uint8_t array.
@ -120,17 +121,19 @@ static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) {
if (!wait_for_flash_ready()) {
return false;
}
enum status_code status;
// We can read as much as we want sequentially.
uint8_t read_request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00};
address_to_bytes(address, read_request + 1);
uint8_t read_data_request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00};
struct spi_xfer read_data_xfer = {read_data_request, 0, 4};
// Write the SPI flash read address into the bytes following the command byte.
address_to_bytes(address, read_data_request + 1);
flash_enable();
status = spi_write_buffer_wait(&spi_flash_instance, read_request, 4);
if (status == STATUS_OK) {
status = shared_dma_read(spi_flash_instance.hw, data, data_length, 0x00);
int32_t status = spi_m_sync_transfer(&spi_flash_desc, &read_data_xfer);
struct spi_xfer read_data_buffer_xfer = {0, data, data_length};
if (status >= 0) {
status = spi_m_sync_transfer(&spi_flash_desc, &read_data_buffer_xfer);
}
flash_disable();
return status == STATUS_OK;
return status >= 0;
}
// Writes data_length's worth of bytes starting at address from data. Assumes
@ -159,26 +162,29 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len
if (!wait_for_flash_ready() || !write_enable()) {
return false;
}
enum status_code status;
int32_t status;
#ifdef SPI_FLASH_SECTOR_PROTECTION
// Print out the protection status.
// uint8_t protect_check[5] = {0x3C, 0x00, 0x00, 0x00, 0x00};
// address_to_bytes(address + bytes_written, protect_check + 1);
// flash_enable();
// status = spi_write_buffer_wait(&spi_flash_instance, protect_check, 5);
// status = spi_write_buffer_wait(&spi_flash_desc, protect_check, 5);
// flash_disable();
#endif
flash_enable();
uint8_t command[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00};
address_to_bytes(address + bytes_written, command + 1);
status = spi_write_buffer_wait(&spi_flash_instance, command, 4);
if (status == STATUS_OK) {
status = shared_dma_write(spi_flash_instance.hw, data + bytes_written, SPI_FLASH_PAGE_SIZE);
uint8_t page_program_request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00};
// Write the SPI flash write address into the bytes following the command byte.
address_to_bytes(address + bytes_written, page_program_request + 1);
struct spi_xfer page_program_xfer = {page_program_request, 0, 4};
status = spi_m_sync_transfer(&spi_flash_desc, &page_program_xfer);
if (status >= 0) {
struct spi_xfer write_data_buffer_xfer = {(uint8_t*) data + bytes_written, 0, SPI_FLASH_PAGE_SIZE};
status = spi_m_sync_transfer(&spi_flash_desc, &write_data_buffer_xfer);
}
flash_disable();
if (status != STATUS_OK) {
if (status < 0) {
return false;
}
}
@ -224,11 +230,11 @@ static bool erase_sector(uint32_t sector_address) {
uint8_t erase_request[4] = {CMD_SECTOR_ERASE, 0x00, 0x00, 0x00};
address_to_bytes(sector_address, erase_request + 1);
struct spi_xfer erase_xfer = {erase_request, 0, 4};
flash_enable();
enum status_code status = spi_write_buffer_wait(&spi_flash_instance, erase_request, 4);
int32_t status = spi_m_sync_transfer(&spi_flash_desc, &erase_xfer);
flash_disable();
return status == STATUS_OK;
return status >= 0;
}
// Sector is really 24 bits.
@ -247,82 +253,103 @@ static bool copy_block(uint32_t src_address, uint32_t dest_address) {
}
void spi_flash_init(void) {
if (!spi_flash_is_initialised) {
struct spi_config config_spi_master;
spi_get_config_defaults(&config_spi_master);
config_spi_master.mux_setting = SPI_FLASH_MUX_SETTING;
config_spi_master.pinmux_pad0 = SPI_FLASH_PAD0_PINMUX;
config_spi_master.pinmux_pad1 = SPI_FLASH_PAD1_PINMUX;
config_spi_master.pinmux_pad2 = SPI_FLASH_PAD2_PINMUX;
config_spi_master.pinmux_pad3 = SPI_FLASH_PAD3_PINMUX;
config_spi_master.mode_specific.master.baudrate = SPI_FLASH_BAUDRATE;
spi_init(&spi_flash_instance, SPI_FLASH_SERCOM, &config_spi_master);
spi_enable(&spi_flash_instance);
// Manage chip select ourselves.
struct port_config pin_conf;
port_get_config_defaults(&pin_conf);
pin_conf.direction = PORT_PIN_DIR_OUTPUT;
port_pin_set_config(SPI_FLASH_CS, &pin_conf);
flash_disable();
// Activity LED for flash writes.
#ifdef MICROPY_HW_LED_MSC
port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf);
port_pin_set_output_level(MICROPY_HW_LED_MSC, false);
#endif
uint8_t jedec_id_request[4] = {CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00};
uint8_t response[4] = {0x00, 0x00, 0x00, 0x00};
flash_enable();
spi_transceive_buffer_wait(&spi_flash_instance, jedec_id_request, response, 4);
flash_disable();
uint8_t manufacturer = response[1];
if ((response[1] == SPI_FLASH_JEDEC_MANUFACTURER
#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2
|| response[1] == SPI_FLASH_JEDEC_MANUFACTURER_2
#endif
) &&
response[2] == SPI_FLASH_JEDEC_MEMORY_TYPE &&
response[3] == SPI_FLASH_JEDEC_CAPACITY) {
spi_flash_is_initialised = true;
} else {
// Unknown flash chip!
spi_flash_is_initialised = false;
return;
}
if ((manufacturer == SPI_FLASH_JEDEC_MANUFACTURER && SPI_FLASH_SECTOR_PROTECTION)
#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2
|| (manufacturer == SPI_FLASH_JEDEC_MANUFACTURER_2 && SPI_FLASH_SECTOR_PROTECTION_2)
#endif
) {
write_enable();
// Turn off sector protection
uint8_t disable_protect_request[2] = {CMD_WRITE_STATUS_BYTE1, 0x00};
uint8_t disable_protect_response[2] = {0x00, 0x00};
flash_enable();
spi_transceive_buffer_wait(&spi_flash_instance, disable_protect_request, disable_protect_response, 2);
flash_disable();
}
// Turn off writes in case this is a microcontroller only reset.
uint8_t disable_write_request[1] = {CMD_DISABLE_WRITE};
uint8_t disable_response[1] = {0x00};
flash_enable();
spi_transceive_buffer_wait(&spi_flash_instance, disable_write_request, disable_response, 1);
flash_disable();
wait_for_flash_ready();
current_sector = NO_SECTOR_LOADED;
dirty_mask = 0;
MP_STATE_VM(flash_ram_cache) = NULL;
spi_flash_is_initialised = true;
if (spi_flash_is_initialised) {
return;
}
samd_peripherals_sercom_clock_init(SPI_FLASH_SERCOM, SPI_FLASH_SERCOM_INDEX);
// Set up with defaults, then change.
spi_m_sync_init(&spi_flash_desc, SPI_FLASH_SERCOM);
hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO);
hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO);
gpio_set_pin_direction(SPI_FLASH_SCK_PIN, GPIO_DIRECTION_OUT);
gpio_set_pin_pull_mode(SPI_FLASH_SCK_PIN, GPIO_PULL_OFF);
gpio_set_pin_function(SPI_FLASH_SCK_PIN, SPI_FLASH_SCK_PIN_FUNCTION);
gpio_set_pin_direction(SPI_FLASH_MOSI_PIN, GPIO_DIRECTION_OUT);
gpio_set_pin_pull_mode(SPI_FLASH_MOSI_PIN, GPIO_PULL_OFF);
gpio_set_pin_function(SPI_FLASH_MOSI_PIN, SPI_FLASH_MOSI_PIN_FUNCTION);
gpio_set_pin_direction(SPI_FLASH_MISO_PIN, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(SPI_FLASH_MISO_PIN, GPIO_PULL_OFF);
gpio_set_pin_function(SPI_FLASH_MISO_PIN, SPI_FLASH_MISO_PIN_FUNCTION);
hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO);
hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO);
spi_m_sync_set_baudrate(&spi_flash_desc, samd_peripherals_baudrate_to_baud_reg_value(SPI_FLASH_BAUDRATE));
gpio_set_pin_direction(SPI_FLASH_CS_PIN, GPIO_DIRECTION_OUT);
// There's already a pull-up on the board.
gpio_set_pin_pull_mode(SPI_FLASH_CS_PIN, GPIO_PULL_OFF);
gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF);
// Set CS high (disabled).
flash_disable();
spi_m_sync_enable(&spi_flash_desc);
// Activity LED for flash writes.
#ifdef MICROPY_HW_LED_MSC
gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT);
// There's already a pull-up on the board.
gpio_set_pin_level(MICROPY_HW_LED_MSC, false);
#endif
uint8_t jedec_id_request[4] = {CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00};
uint8_t jedec_id_response[4] = {0x00, 0x00, 0x00, 0x00};
struct spi_xfer jedec_id_xfer = { jedec_id_request, jedec_id_response, 4 };
flash_enable();
spi_m_sync_transfer(&spi_flash_desc, &jedec_id_xfer);
flash_disable();
uint8_t manufacturer = jedec_id_response[1];
if ((jedec_id_response[1] == SPI_FLASH_JEDEC_MANUFACTURER
#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2
|| jedec_id_response[1] == SPI_FLASH_JEDEC_MANUFACTURER_2
#endif
) &&
jedec_id_response[2] == SPI_FLASH_JEDEC_MEMORY_TYPE &&
jedec_id_response[3] == SPI_FLASH_JEDEC_CAPACITY) {
spi_flash_is_initialised = true;
} else {
// Unknown flash chip!
spi_flash_is_initialised = false;
return;
}
if ((manufacturer == SPI_FLASH_JEDEC_MANUFACTURER && SPI_FLASH_SECTOR_PROTECTION)
#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2
|| (manufacturer == SPI_FLASH_JEDEC_MANUFACTURER_2 && SPI_FLASH_SECTOR_PROTECTION_2)
#endif
) {
write_enable();
// Turn off sector protection
uint8_t disable_protect_request[2] = {CMD_WRITE_STATUS_BYTE1, 0x00};
struct spi_xfer disable_protect_xfer = { disable_protect_request, 0, 4 };
flash_enable();
spi_m_sync_transfer(&spi_flash_desc, &disable_protect_xfer);
flash_disable();
}
// Turn off writes in case this is a microcontroller only reset.
uint8_t disable_write_request[1] = {CMD_DISABLE_WRITE};
struct spi_xfer disable_write_xfer = { disable_write_request, 0, 1 };
flash_enable();
spi_m_sync_transfer(&spi_flash_desc, &disable_write_xfer);
flash_disable();
wait_for_flash_ready();
current_sector = NO_SECTOR_LOADED;
dirty_mask = 0;
MP_STATE_VM(flash_ram_cache) = NULL;
spi_flash_is_initialised = true;
}
// The size of each individual block.

View File

@ -271,7 +271,11 @@ int32_t usb_msc_xfer_done(uint8_t lun) {
return ERR_NONE;
}
// The start_read callback begins a read transaction which we accept but delay our response until the "main thread" calls usb_msc_background. Once it does, we read immediately from the drive into our cache and trigger the USB DMA to output the sector. Once the sector is transmitted, xfer_done will be called.
// The start_read callback begins a read transaction which we accept
// but delay our response until the "main thread" calls
// usb_msc_background. Once it does, we read immediately from the
// drive into our cache and trigger the USB DMA to output the
// sector. Once the sector is transmitted, xfer_done will be called.
void usb_msc_background(void) {
if (active_read && !usb_busy) {
if (active_nblocks == 0) {
@ -290,7 +294,7 @@ void usb_msc_background(void) {
fs_user_mount_t * vfs = get_vfs(active_lun);
disk_write(vfs, sector_buffer, active_addr, 1);
// Since by getting here we assume the mount is read-only to
// MicroPython lets update the cached FatFs sector if its the one
// MicroPython let's update the cached FatFs sector if it's the one
// we just wrote.
#if _MAX_SS != _MIN_SS
if (vfs->ssize == FILESYSTEM_BLOCK_SIZE) {

View File

@ -38,7 +38,7 @@
//|
//| .. module:: storage
//| :synopsis: storage management
//| :platform: SAMD21
//| :platform: SAMD21, SAMD51
//|
//| The `storage` provides storage management functionality such as mounting and
//| unmounting which is typically handled by the operating system hosting Python.