8000 mimxrt: Add MSC support for the mimxrt port. by robert-hh · Pull Request #8820 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

mimxrt: Add MSC support for the mimxrt port. #8820

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

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/mimxrt/quickref.rst
< 10000 tr data-hunk="96ced1d84a7b9abcf626c939eaeaded853d0f9d0db97078382a6122ac7636e91" class="show-top-border">
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,71 @@ port and LAN(1) for the 1G port.

For details of the network interface refer to the class :ref:`network.LAN <network.LAN>`.


External drive mode
-------------------

On some boards a mode is enabled, that mounts the board's internal file system as
external USB drive, called MSC support. Data of that drive can be accessed and changed by the PC.
In that state, access to the internal drive by the MicroPython is limited to
read-only mode, avoiding file corruption. Changes made by the PC to the file system may not be visible
at the board until the drive is ejected by the PC or a soft reset of the board
is made.

To enable write access to by the board, eject the drive at the PC **and** perform
a soft-reset on the board, either by pushing Ctrl-D at REPL or calling machine.soft_reset().

The external drive mode (MSC mode) is enabled of disabled according to the following rules:

a) For a FAT file system, MSC will be enabled by default.
b) For a LFS file system, MSC will be disabled by default.
c) The setting can be overridden by a file with the name
set_usb_mode.py, which can define the usb_mode with the lines:

usb_mode = "vcp+msc" # enable MSC

or

usb_mode = "vcp" # do not enable MSC

If the file set_usb_mode.py does not exist or is faulty, the default is used.

The read-only state of the local file system access can be told by an IOCTL call of the file
system's block device.::

import mimxrt

bdev = mimxrt.Flash()
readonly = bdev.ioctl(7, 0)

If the drive is in read-only state, bdev.ioctl(7, 0) returns `True`.

The file system of the board has to be of FAT type for mounting and using with standard PC
tools. But FAT is not enforced at the board. If the board's file system is littlefs, MSC
mode is disable by default. If enabled in boot.py, the file system will be attached to the
PC and will be accessible as a drive (e.g. /dev/sdc using Linux), but by default
there is no file access. In that case, the files are locally still writeable.
Changing the board's file system to FAT can be done then by formatting it from the PC.
Alternatively, you can erase the root sector. Then, the FAT file system will be
created at the next power-up. For erasing the root sector, write::

from mimxrt import Flash
Flash().ioctl(6, 0)

Using littlefs-fuse for Linux you can mount the board's littlefs file system to the PC.
See: https://github.com/littlefs-project/littlefs-fuse
The block_size if 4096, the block_count depends on the size of the filesystem. e.g.::

# mounting the lfs2 file system of a Teensy 4.1 board at the PC
mkdir mount
sudo ./lfs --block_size=4096 --block_count=1791 -o allow_other -o nonempty /dev/sdc mount
cd mount
ls -l

In that case, the exclusive write access is NOT enforced. So be careful to only write
to the file system by the PC.


Transferring files
------------------

Expand Down
1 change: 1 addition & 0 deletions extmod/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#define MP_BLOCKDEV_IOCTL_BLOCK_COUNT (4)
#define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5)
#define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6)
#define MP_BLOCKDEV_IOCTL_STATUS (7)

// At the moment the VFS protocol just has import_stat, but could be extended to other methods
typedef struct _mp_vfs_proto_t {
Expand Down
26 changes: 20 additions & 6 deletions extmod/vfs_fat_diskio.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ DRESULT disk_ioctl(
[GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT,
[GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE,
[IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT,
[IOCTL_STATUS] = MP_BLOCKDEV_IOCTL_STATUS,
};
uint8_t bp_op = op_map[cmd & 7];
mp_obj_t ret = mp_const_none;
Expand Down Expand Up @@ -147,16 +148,29 @@ DRESULT disk_ioctl(
*((DWORD *)buff) = 1; // erase block size in units of sector size
return RES_OK;

case IOCTL_INIT:
case IOCTL_STATUS: {
DSTATUS stat;
case IOCTL_INIT: {
DSTATUS stat = 0;
if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
// error initialising
stat = STA_NOINIT;
} else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) {
stat = STA_PROTECT;
} else {
stat = 0;
// IOCTL_INIT only returns non 0 for all errors/flags. To return
// a more accurate disk state, IOCTL_STATUS is called again here.
ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, MP_BLOCKDEV_IOCTL_STATUS, 0);
if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL ||
(ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) {
stat = STA_PROTECT;
}
}
*((DSTATUS *)buff) = stat;
return RES_OK;
}

case IOCTL_STATUS: {
DSTATUS stat = 0;
if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL ||
(ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) {
stat = STA_PROTECT;
}
*((DSTATUS *)buff) = stat;
return RES_OK;
Expand Down
2 changes: 2 additions & 0 deletions ports/mimxrt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ SRC_C += \
dma_manager.c \
drivers/bus/softspi.c \
drivers/dht/dht.c \
flash.c \
eth.c \
fatfs_port.c \
hal/pwm_backport.c \
Expand All @@ -204,6 +205,7 @@ SRC_C += \
modutime.c \
mphalport.c \
mpnetworkport.c \
msc_disk.c \
network_lan.c \
pendsv.c \
pin.c \
Expand Down
2 changes: 1 addition & 1 deletion ports/mimxrt/boards/MIMXRT1011.ld
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ ocrm_start = 0x20200000;
ocrm_size = 0x00010000;

/* 20kiB stack. */
__stack_size__ = 0x5000;
__stack_size__ = 0x4800;
_estack = __StackTop;
_sstack = __StackLimit;

Expand Down
2 changes: 2 additions & 0 deletions ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1020evk"

#define MICROPY_HW_USB_MSC (1)

// i.MX RT1020 EVK has 1 board LED
// Todo: think about replacing the define with searching in the generated pins?
#define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_05)
Expand Down
3 changes: 3 additions & 0 deletions ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1050evk"

// Disable MSC for the device with Hyperflash
#define MICROPY_HW_USB_MSC (0)

// MIMXRT1050_EVKB has 1 user LED
#define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_09)
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
Expand Down
2 changes: 2 additions & 0 deletions ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1070evk"

#define MICROPY_HW_USB_MSC (1)

#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
Expand Down
2 changes: 2 additions & 0 deletions ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#define MICROPY_HW_USB_PID 0x0046
#define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (0)

#define MICROPY_HW_USB_MSC (1)

// Olimex RT1010-Py has 1 board LED
#define MICROPY_HW_LED1_PIN (pin_GPIO_11)
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin))
Expand Down
1 change: 1 addition & 0 deletions ports/mimxrt/boards/TEENSY41/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define MICROPY_HW_MCU_NAME "MIMXRT1062DVJ6A"

#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-teensy41"
#define MICROPY_HW_USB_MSC (1)

// Teensy 4.1 has 1 board LED
#define MICROPY_HW_LED1_PIN (pin_GPIO_B0_03)
Expand Down
134 changes: 134 additions & 0 deletions ports/mimxrt/flash.c
10449
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Philipp Ebensberger
*
* 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 "flash.h"

// --------------------------------------------------------------------+
// Global Function Definitions
// --------------------------------------------------------------------+
void flash_init(void) {
// Upload the custom flash configuration
// This should be performed by the boot ROM but for some reason it is not.
FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0,
qspiflash_config.memConfig.lookupTable,
ARRAY_SIZE(qspiflash_config.memConfig.lookupTable));

// Configure FLEXSPI IP FIFO access.
BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK);
BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK);
BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0);
BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0);

FLEXSPI_EnableIPParallelMode(BOARD_FLEX_SPI, true);
}

// flash_erase_block(erase_addr)
// erases the block starting at addr. Block size according to the flash properties.
status_t flash_erase_block(uint32_t erase_addr) {
status_t status = kStatus_Fail;

SCB_CleanInvalidateDCache();
SCB_DisableDCache();
__disable_irq();

status = flexspi_nor_flash_erase_block(BOARD_FLEX_SPI, erase_addr);

__enable_irq();
SCB_EnableDCache();

return status;
}

// flash_erase_sector(erase_addr_bytes)
// erases the sector starting at addr. Sector size according to the flash properties.
status_t flash_erase_sector(uint32_t erase_addr) {
status_t status = kStatus_Fail;

SCB_CleanInvalidateDCache();
SCB_DisableDCache();
__disable_irq();

status = flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, erase_addr);

__enable_irq();
SCB_EnableDCache();

return status;
}

// flash_read_block(flash_src_addr_bytes, data_dest, length_bytes)
// read length_byte data to the source address
// It is just a shim to provide the same structure for read_block and write_block.
void inline flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length) {
memcpy(dest, (uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + src_addr), length);
}

// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes)
// writes length_byte data to the destination address
// the vfs driver takes care for erasing the sector if required
status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) {
#if FLASH_DISABLE_OP == 1
return kStatus_Success;
#else
status_t status = kStatus_Fail;
uint32_t size;
uint32_t next_addr;

if (length == 0) {
status = kStatus_Success; // Nothing to do
} else {

SCB_CleanInvalidateDCache();
SCB_DisableDCache();

// write data in chunks not crossing a page boundary
while (length > 0) {
next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary
size = next_addr - dest_addr; // maximal chunk length
if (size > length) { // compare against remaining data size
size = length;
}

__disable_irq();

status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, size);

__enable_irq();

if (status != kStatus_Success) {
break;
}
length -= size;
src += size;
dest_addr += size;
}

SCB_EnableDCache();

}
return status;
#endif
}
63 changes: 63 additions & 0 deletions ports/mimxrt/flash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Philipp Ebensberger
*
* 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_MIMXRT_FLASH_H
#define MICROPY_INCLUDED_MIMXRT_FLASH_H

#include BOARD_FLASH_OPS_HEADER_H

// --------------------------------------------------------------------+
// Defines and Macros
// --------------------------------------------------------------------+
#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize)
#define PAGE_SIZE_BYTES (qspiflash_config.pageSize)
#define BLOCK_SIZE_BYTES (qspiflash_config.blockSize)

#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start))
#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start))

extern uint8_t __vfs_start;
extern uint8_t __vfs_end;
extern uint8_t __flash_start;

extern flexspi_nor_config_t qspiflash_config;

enum {
MOUNTED = 0,
EJECTED,
TRANSIT
};

// --------------------------------------------------------------------+
// Global Function Declarations
// --------------------------------------------------------------------+
void flash_init(void);
__attribute__((section(".ram_functions"))) status_t flash_erase_sector(uint32_t erase_addr);
__attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t erase_addr);
__attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length);
__attribute__((section(".ram_functions"))) void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length);

#endif // MICROPY_INCLUDED_MIMXRT_FLASH_H
Loading
0