diff --git a/Makefile b/Makefile index bfdff2d..86799de 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,28 @@ -include lib/tinyusb/tools/top.mk -include lib/tinyusb/examples/make.mk +TINYUSB_PATH ?= lib/tinyusb + +# Force using the local board directory +TOP := $(shell realpath `pwd`) +include $(TINYUSB_PATH)/examples/make.mk INC += \ + hw \ + hw/bsp \ + hw/bsp/$(BOARD) \ src \ - $(TOP)/hw + lib/tinyusb/tools \ + _build/build-$(BOARD) -SRC_C += \ +SRC_C = \ $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) CFLAGS += -Wno-unused-parameter -include lib/tinyusb/examples/rules.mk +UF2_VERSION_BASE = $(shell git describe --always --tags) +$(BUILD)/uf2_version.h: Makefile + @echo "#define UF2_VERSION_BASE \"$(UF2_VERSION_BASE)\""> $@ + +OBJ += $(BUILD)/uf2_version.h + +include $(TOP)/hw/chip/$(CHIP_FAMILY)/$(CHIP_VARIANT).mk +include $(TINYUSB_PATH)/tools/top.mk +include $(TINYUSB_PATH)/examples/rules.mk diff --git a/hw/bsp/bsp.h b/hw/bsp/bsp.h new file mode 100644 index 0000000..9bb667a --- /dev/null +++ b/hw/bsp/bsp.h @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, Artur Pacholec + * + * 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 _BOARD_H_ +#define _BOARD_H_ + +#include +#include + +#include "board_config.h" + +void board_flash_flush(void); +uint32_t board_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks); +uint32_t board_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32_t num_blocks); +uint32_t board_millis(void); +void board_reset(void); +void board_led_write(bool state); +void board_init(void); + +#endif diff --git a/hw/bsp/mimxrt1010_evk/board.mk b/hw/bsp/mimxrt1010_evk/board.mk new file mode 100644 index 0000000..4b99828 --- /dev/null +++ b/hw/bsp/mimxrt1010_evk/board.mk @@ -0,0 +1,2 @@ +CHIP_FAMILY = mimxrt10xx +CHIP_VARIANT = MIMXRT1011DAE5A diff --git a/hw/bsp/mimxrt1010_evk/board_config.h b/hw/bsp/mimxrt1010_evk/board_config.h new file mode 100644 index 0000000..f79bf2d --- /dev/null +++ b/hw/bsp/mimxrt1010_evk/board_config.h @@ -0,0 +1,20 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#define VENDOR_NAME "NXP" +#define PRODUCT_NAME "MIMXRT1010-EVK" +#define VOLUME_LABEL "UF2BOOT" +#define INDEX_URL "" +#define BOARD_ID "MIMXRT1010-EVK" + +#define USB_VID 0x239A +#define USB_PID 0x0077 + +// The EVK uses 16MB Flash but that would force the MSC +// drive to be larger than 65535 sectors and requires +// additional modifications to the code, we'll take care +// of that in the future. For now 8MB works. +//#define BOARD_FLASH_SIZE 0x1000000 +#define BOARD_FLASH_SIZE 0x800000 + +#endif diff --git a/hw/chip/mimxrt10xx/MIMXRT1011DAE5A.mk b/hw/chip/mimxrt10xx/MIMXRT1011DAE5A.mk new file mode 100644 index 0000000..347f413 --- /dev/null +++ b/hw/chip/mimxrt10xx/MIMXRT1011DAE5A.mk @@ -0,0 +1,12 @@ +MCU_DIR = hw/mcu/nxp/sdk/devices/MIMXRT1011 + +include hw/chip/mimxrt10xx/family.mk + +CFLAGS += -DCPU_MIMXRT1011DAE5A + +SRC_C += $(MCU_DIR)/system_MIMXRT1011.c + +SRC_S += $(MCU_DIR)/gcc/startup_MIMXRT1011.S + +LD_FILE = ../../hw/chip/mimxrt10xx/MIMXRT1011xxxxx_flexspi_nor.ld + diff --git a/hw/chip/mimxrt10xx/MIMXRT1011xxxxx_flexspi_nor.ld b/hw/chip/mimxrt10xx/MIMXRT1011xxxxx_flexspi_nor.ld new file mode 100644 index 0000000..1183141 --- /dev/null +++ b/hw/chip/mimxrt10xx/MIMXRT1011xxxxx_flexspi_nor.ld @@ -0,0 +1,115 @@ +ENTRY(Reset_Handler) + +_minimum_stack_size = 64K; +_minimum_heap_size = 0; + +MEMORY +{ + FLASH_CONFIG (rx) : ORIGIN = 0x60000400, LENGTH = 3K + FLASH_IVT (rx) : ORIGIN = 0x60001000, LENGTH = 4K + FLASH_ISR (rx) : ORIGIN = 0x60002000, LENGTH = 1K + FLASH_TEXT (rx) : ORIGIN = 0x60002400, LENGTH = 27K + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 32K + OCRAM (rwx) : ORIGIN = 0x20200000, LENGTH = 64K +} + +__StackTop = ORIGIN(OCRAM) + LENGTH(OCRAM); +_bootloader_dbl_tap = ORIGIN(RAM) + LENGTH(RAM) - 4; +__RAM_VECTOR_TABLE_SIZE_BYTES = 0; + +SECTIONS +{ + .flash_config : + { + . = ALIGN(4); + KEEP(* (.boot_hdr.conf)) + . = ALIGN(4); + } > FLASH_CONFIG + + .ivt : + { + . = ALIGN(4); + KEEP(* (.boot_hdr.ivt)) + KEEP(* (.boot_hdr.boot_data)) + KEEP(* (.boot_hdr.dcd_data)) + . = ALIGN(4); + } > FLASH_IVT + + .interrupts : + { + __VECTOR_TABLE = .; + __VECTOR_RAM = .; + + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } > FLASH_ISR + + .text : + { + . = ALIGN(4); + *(EXCLUDE_FILE( + *flexspi_nor_flash_ops.o + *fsl_flexspi.o + ) .text*) /* .text* sections (code) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } > FLASH_TEXT + + .ARM.exidx : + { + *(.ARM.exidx*) + *(.gnu.linkonce.armexidx.*) + _etext = .; /* define a global symbol at end of code */ + __etext = .; /* define a global symbol at end of code */ + } > FLASH_TEXT + + /* used by the startup to initialize data */ + _sidata = .; + + .data : AT (_sidata) + { + . = ALIGN(4); + __data_start__ = .; /* create a global symbol at data start */ + + *(.data*) /* .data* sections */ + *flexspi_nor_flash_ops.o(.text*) + *fsl_flexspi.o(.text*) + . = ALIGN(4); + + __data_end__ = .; /* define a global symbol at data end */ + } > RAM + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + + *(.bss*) + *(COMMON) + + . = ALIGN(4); + __bss_end__ = .; + PROVIDE(end = .); + } > RAM + + .heap : + { + . = ALIGN(8); + _heap_start = .; /* define a global symbol at heap start */ + + . += _minimum_heap_size; + + __HeapLimit = .; + } > OCRAM + + .stack : + { + . = ALIGN(8); + . += _minimum_stack_size; + } > OCRAM + + .ARM.attributes 0 : { *(.ARM.attributes) } +} + diff --git a/hw/chip/mimxrt10xx/family.c b/hw/chip/mimxrt10xx/family.c new file mode 100644 index 0000000..0056af2 --- /dev/null +++ b/hw/chip/mimxrt10xx/family.c @@ -0,0 +1,388 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, Artur Pacholec + * + * 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 + +#include "tusb.h" +#include "board_config.h" +#include "clock_config.h" + +#include "fsl_device_registers.h" +#include "fsl_flexspi.h" +#include "fsl_cache.h" +#include "fsl_gpio.h" +#include "fsl_iomuxc.h" + +volatile uint32_t system_ticks = 0; + +// FLASH +#define NO_CACHE 0xffffffff + +#define SECTOR_SIZE 0x1000 /* 4K */ +#define FLASH_PAGE_SIZE 256 +#define FILESYSTEM_BLOCK_SIZE 256 + +#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 7 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST 13 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 0 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS 1 +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 2 +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 3 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 4 +#define NOR_CMD_LUT_SEQ_IDX_READID 8 +#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 12 +#define NOR_CMD_LUT_SEQ_IDX_ERASECHIP 5 + +#define CUSTOM_LUT_LENGTH 60 +#define FLASH_QUAD_ENABLE 0x02 +#define FLASH_BUSY_STATUS_POL 1 +#define FLASH_BUSY_STATUS_OFFSET 0 +#define FLASH_ERROR_STATUS_MASK 0x0e + +uint32_t _flash_page_addr = NO_CACHE; +uint8_t _flash_cache[SECTOR_SIZE] __attribute__((aligned(4))); + +flexspi_device_config_t deviceconfig = +{ + .flexspiRootClk = 133000000, + .flashSize = (BOARD_FLASH_SIZE / 1024), + .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle, + .CSInterval = 2, + .CSHoldTime = 3, + .CSSetupTime = 3, + .dataValidTime = 0, + .columnspace = 0, + .enableWordAddress = 0, + .AWRSeqIndex = 0, + .AWRSeqNumber = 0, + .ARDSeqIndex = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD, + .ARDSeqNumber = 1, + .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle, + .AHBWriteWaitInterval = 0, +}; + +const uint32_t customLUT[CUSTOM_LUT_LENGTH] = +{ + /* Normal read mode -SDR */ + /* Normal read mode -SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Fast read mode - SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Fast read quad mode - SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04), + + /* Read extend parameters */ + [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Write Enable */ + [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Erase Sector */ + [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + + /* Page Program - single mode */ + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Page Program - quad mode */ + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Read ID */ + [4 * NOR_CMD_LUT_SEQ_IDX_READID] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Enable Quad mode */ + [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04), + + /* Read status register */ + [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Erase whole chip */ + [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), +}; + +const uint8_t dcd_data[] = { 0x00 }; + +status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src); +status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base); +status_t flexspi_nor_erase_chip(FLEXSPI_Type *base); +status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); +void flexspi_nor_flash_init(FLEXSPI_Type *base); + +static inline uint32_t lba2addr(uint32_t block) +{ + return FlexSPI_AMBA_BASE + block * FILESYSTEM_BLOCK_SIZE; +} + +void board_flash_flush(void) +{ + status_t status; + + if (_flash_page_addr == NO_CACHE) + return; + + //printf("%s: page 0x%08lX\r\n", __func__, _flash_page_addr); + + // Skip if data is the same + if (memcmp(_flash_cache, (void *)_flash_page_addr, SECTOR_SIZE) != 0) { + //nrf_nvm_safe_flash_page_write(_flash_page_addr, _flash_cache); + + volatile uint32_t sector_addr = (_flash_page_addr - FlexSPI_AMBA_BASE); + + __disable_irq(); + status = flexspi_nor_flash_erase_sector(FLEXSPI, sector_addr); + if (status != kStatus_Success) { + printf("Page erase failure %ld!\r\n", status); + return; + } + __enable_irq(); + + for (int i = 0; i < SECTOR_SIZE / FLASH_PAGE_SIZE; ++i) { + __disable_irq(); + status = flexspi_nor_flash_page_program(FLEXSPI, sector_addr + i * FLASH_PAGE_SIZE, (void *)_flash_cache + i * FLASH_PAGE_SIZE); + if (status != kStatus_Success) { + printf("Page program failure %ld!\r\n", status); + return; + } + __enable_irq(); + } + + DCACHE_CleanInvalidateByRange(_flash_page_addr, SECTOR_SIZE); + } +} + +uint32_t board_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) +{ + // Must write out anything in cache before trying to read. + board_flash_flush(); + + uint32_t src = lba2addr(block); + memcpy(dest, (uint8_t*) src, FILESYSTEM_BLOCK_SIZE * num_blocks); + return 0; +} + +uint32_t board_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32_t num_blocks) +{ + //printf("%s: src %p, lba %ld, num %ld\r\n", __func__, src, lba, num_blocks); + + while (num_blocks) { + uint32_t const addr = lba2addr(lba); + uint32_t const page_addr = addr & ~(SECTOR_SIZE - 1); + + uint32_t count = 8 - (lba % 8); // up to page boundary + count = MIN(num_blocks, count); + + if (page_addr != _flash_page_addr) { + // Write out anything in cache before overwriting it. + board_flash_flush(); + + _flash_page_addr = page_addr; + + // Copy the current contents of the entire page into the cache. + memcpy(_flash_cache, (void *)page_addr, SECTOR_SIZE); + } + + // Overwrite part or all of the page cache with the src data. + memcpy(_flash_cache + (addr & (SECTOR_SIZE - 1)), src, count * FILESYSTEM_BLOCK_SIZE); + + // adjust for next run + lba += count; + src += count * FILESYSTEM_BLOCK_SIZE; + num_blocks -= count; + } + + return 0; // success +} + +void SysTick_Handler(void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} + +void board_reset(void) +{ + NVIC_SystemReset(); +} + +#define LED_PINMUX IOMUXC_GPIO_11_GPIOMUX_IO11 +#define LED_PORT GPIO1 +#define LED_PIN 11 +#define LED_STATE_ON 0 + +// UART +#define UART_PORT LPUART1 +#define UART_RX_PINMUX IOMUXC_GPIO_09_LPUART1_RXD +#define UART_TX_PINMUX IOMUXC_GPIO_10_LPUART1_TXD + +void board_led_write(bool state) +{ + GPIO_PinWrite(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON)); +} + +#include "fsl_lpuart.h" + +void board_init(void) +{ + // Init clock + BOARD_BootClockRUN(); + SystemCoreClockUpdate(); + + // Enable IOCON clock + CLOCK_EnableClock(kCLOCK_Iomuxc); + + // LED + IOMUXC_SetPinMux(LED_PINMUX, 0U); + IOMUXC_SetPinConfig(LED_PINMUX, 0x10B0U); + + gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0, kGPIO_NoIntmode }; + GPIO_PinInit(LED_PORT, LED_PIN, &led_config); + board_led_write(false); + + // Button + //IOMUXC_SetPinMux( BUTTON_PINMUX, 0U); + //IOMUXC_SetPinConfig(BUTTON_PINMUX, 0x01B0A0U); + //gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0, kGPIO_IntRisingEdge, }; + //GPIO_PinInit(BUTTON_PORT, BUTTON_PIN, &button_config); + + // UART + IOMUXC_SetPinMux( UART_TX_PINMUX, 0U); + IOMUXC_SetPinMux( UART_RX_PINMUX, 0U); + IOMUXC_SetPinConfig( UART_TX_PINMUX, 0x10B0u); + IOMUXC_SetPinConfig( UART_RX_PINMUX, 0x10B0u); + + lpuart_config_t uart_config; + LPUART_GetDefaultConfig(&uart_config); + uart_config.baudRate_Bps = 115200; + uart_config.enableTx = true; + uart_config.enableRx = true; + LPUART_Init(UART_PORT, &uart_config, (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U)); + + //------------- USB0 -------------// + // Clock + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, 480000000U); + + USBPHY_Type* usb_phy = USBPHY; + + // Enable PHY support for Low speed device + LS via FS Hub + usb_phy->CTRL |= USBPHY_CTRL_SET_ENUTMILEVEL2_MASK | USBPHY_CTRL_SET_ENUTMILEVEL3_MASK; + + // Enable all power for normal operation + usb_phy->PWD = 0; + + // TX Timing + uint32_t phytx = usb_phy->TX; + phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK); + phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06); + usb_phy->TX = phytx; + + //----------- FLEXSPI ----------// + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_06_FLEXSPI_A_SS0_B, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_07_FLEXSPI_A_DATA1,1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_08_FLEXSPI_A_DATA2, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_09_FLEXSPI_A_DATA0, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_10_FLEXSPI_A_SCLK, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_11_FLEXSPI_A_DATA3, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_SD_12_FLEXSPI_A_DQS, 1U); + + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_06_FLEXSPI_A_SS0_B,0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_07_FLEXSPI_A_DATA1, 0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_08_FLEXSPI_A_DATA2, 0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_09_FLEXSPI_A_DATA0, 0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_10_FLEXSPI_A_SCLK, 0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_11_FLEXSPI_A_DATA3, 0x10E1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_12_FLEXSPI_A_DQS, 0x10E1U); + + SCB_DisableDCache(); + + flexspi_nor_flash_init(FLEXSPI); + + status_t status; + uint8_t vendorID = 0; + + status = flexspi_nor_get_vendor_id(FLEXSPI, &vendorID); + if (status != kStatus_Success) { + // printf("flexspi_nor_get_vendor_id fail %ld\r\n", status); + return; + } + + status = flexspi_nor_enable_quad_mode(FLEXSPI); + if (status != kStatus_Success) { + // printf("flexspi_nor_enable_quad_mode fail %ld\r\n", status); + return; + } + + // 1ms tick timer + SysTick_Config(CLOCK_GetCoreSysClkFreq() / 1000); + + for (uint16_t i = 0; i < ADC_ETC_ERROR_IRQ_IRQn; i++) { + NVIC_SetPriority(i, (1UL << __NVIC_PRIO_BITS) - 1UL); + } + + NVIC_SetPriority(USB_OTG1_IRQn, 0); + NVIC_SetPriority(FLEXSPI_IRQn, 1); + NVIC_SetPriority(SysTick_IRQn, 2); +} + +void USB_OTG1_IRQHandler(void) +{ +#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); +#endif + +#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); +#endif +} diff --git a/hw/chip/mimxrt10xx/family.mk b/hw/chip/mimxrt10xx/family.mk new file mode 100644 index 0000000..7863ff0 --- /dev/null +++ b/hw/chip/mimxrt10xx/family.mk @@ -0,0 +1,42 @@ +CFLAGS += \ + -mthumb \ + -mabi=aapcs \ + -mcpu=cortex-m7 \ + -mfloat-abi=hard \ + -mfpu=fpv5-d16 \ + -D__ARMVFP__=0 -D__ARMFPV5__=0 \ + -D__START=main \ + -DXIP_EXTERNAL_FLASH=1 \ + -DXIP_BOOT_HEADER_ENABLE=1 \ + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ + -D__STARTUP_CLEAR_BSS + +CFLAGS += -Wno-error=unused-parameter + +CFLAGS += \ + -DAPP_START_ADDRESS=0x6000C000 \ + -DBOARD_FLASH_BASE=0x60000000 \ + -DUF2_FAMILY=0x4FB2D5BD + +SRC_C += \ + hw/chip/mimxrt10xx/family.c \ + hw/chip/mimxrt10xx/flexspi_nor_flash_ops.c \ + hw/chip/mimxrt10xx/flexspi_nor_config.c \ + $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \ + $(MCU_DIR)/project_template/clock_config.c \ + $(MCU_DIR)/drivers/fsl_clock.c \ + $(MCU_DIR)/drivers/fsl_cache.c \ + $(MCU_DIR)/drivers/fsl_gpio.c \ + $(MCU_DIR)/drivers/fsl_flexspi.c \ + $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_lpuart.c + +INC += \ + $(TOP)/$(MCU_DIR) \ + $(TOP)/$(MCU_DIR)/drivers \ + $(TOP)/$(MCU_DIR)/project_template \ + $(TOP)/$(MCU_DIR)/../../CMSIS/Include \ + +# For TinyUSB port source +VENDOR = nxp +CHIP_FAMILY = transdimension diff --git a/hw/chip/mimxrt10xx/flexspi_nor_config.c b/hw/chip/mimxrt10xx/flexspi_nor_config.c new file mode 100644 index 0000000..49cbb50 --- /dev/null +++ b/hw/chip/mimxrt10xx/flexspi_nor_config.c @@ -0,0 +1,50 @@ +/* + * Copyright 2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "flexspi_nor_config.h" + +#include "board.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_100MHz, + .sflashA1Size = BOARD_FLASH_SIZE, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/hw/chip/mimxrt10xx/flexspi_nor_config.h b/hw/chip/mimxrt10xx/flexspi_nor_config.h new file mode 100644 index 0000000..4be2760 --- /dev/null +++ b/hw/chip/mimxrt10xx/flexspi_nor_config.h @@ -0,0 +1,267 @@ +/* + * Copyright 2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1011_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1011_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.0. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_120MHz = 7, + kFlexSpiSerialClk_133MHz = 8, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1011_FLEXSPI_NOR_CONFIG__ */ diff --git a/hw/chip/mimxrt10xx/flexspi_nor_flash_ops.c b/hw/chip/mimxrt10xx/flexspi_nor_flash_ops.c new file mode 100644 index 0000000..c440936 --- /dev/null +++ b/hw/chip/mimxrt10xx/flexspi_nor_flash_ops.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_flexspi.h" + +#define FLASH_PAGE_SIZE 256 +#define EXAMPLE_SECTOR 20 +#define SECTOR_SIZE 0x1000 /* 4K */ +#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_FlexSpi + +#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 7 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST 13 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 0 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS 1 +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 2 +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 3 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 4 +#define NOR_CMD_LUT_SEQ_IDX_READID 8 +#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 12 +#define NOR_CMD_LUT_SEQ_IDX_ERASECHIP 5 + +#define CUSTOM_LUT_LENGTH 60 +#define FLASH_QUAD_ENABLE 0x02 +#define FLASH_BUSY_STATUS_POL 1 +#define FLASH_BUSY_STATUS_OFFSET 0 +#define FLASH_ERROR_STATUS_MASK 0x0e + +/*${macro:end}*/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*${prototype:start}*/ +void BOARD_InitHardware(void); + +static inline void flexspi_clock_init(void) +{ +#if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1) + /* Switch to PLL2 for XIP to avoid hardfault during re-initialize clock. */ + CLOCK_InitSysPfd(kCLOCK_Pfd2, 24); /* Set PLL2 PFD2 clock 396MHZ. */ + CLOCK_SetMux(kCLOCK_FlexspiMux, 0x2); /* Choose PLL2 PFD2 clock as flexspi source clock. */ + CLOCK_SetDiv(kCLOCK_FlexspiDiv, 2); /* flexspi clock 133M. */ +#else + const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U}; + + CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll); + CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, 24); /* Set PLL3 PFD0 clock 360MHZ. */ + CLOCK_SetMux(kCLOCK_FlexspiMux, 0x3); /* Choose PLL3 PFD0 clock as flexspi source clock. */ + CLOCK_SetDiv(kCLOCK_FlexspiDiv, 2); /* flexspi clock 120M. */ +#endif +} +/*${prototype:end}*/ + + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + *****************************************************************************/ +extern flexspi_device_config_t deviceconfig; +extern const uint32_t customLUT[CUSTOM_LUT_LENGTH]; +/******************************************************************************* + * Code + ******************************************************************************/ +status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr) +{ + flexspi_transfer_t flashXfer; + status_t status; + + /* Write enable */ + flashXfer.deviceAddress = baseAddr; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; + + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + return status; +} + +status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base) +{ + /* Wait status ready. */ + bool isBusy; + uint32_t readValue; + status_t status; + flexspi_transfer_t flashXfer; + + flashXfer.deviceAddress = 0; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Read; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG; + flashXfer.data = &readValue; + flashXfer.dataSize = 1; + + do + { + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + if (FLASH_BUSY_STATUS_POL) + { + if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET)) + { + isBusy = true; + } + else + { + isBusy = false; + } + } + else + { + if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET)) + { + isBusy = false; + } + else + { + isBusy = true; + } + } + + } while (isBusy); + + return status; +} + +status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base) +{ + flexspi_transfer_t flashXfer; + status_t status; + uint32_t writeValue = FLASH_QUAD_ENABLE; + + /* Write enable */ + status = flexspi_nor_write_enable(base, 0); + + if (status != kStatus_Success) + { + return status; + } + + /* Enable quad mode. */ + flashXfer.deviceAddress = 0; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Write; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG; + flashXfer.data = &writeValue; + flashXfer.dataSize = 1; + + status = FLEXSPI_TransferBlocking(base, &flashXfer); + if (status != kStatus_Success) + { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + /* Do software reset. */ + FLEXSPI_SoftwareReset(base); + + return status; +} + +status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) +{ + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; + + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + /* Do software reset. */ + FLEXSPI_SoftwareReset(base); + + return status; +} + +status_t flexspi_nor_flash_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t length) +{ + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, dstAddr); + + if (status != kStatus_Success) + { + return status; + } + + /* Prepare page program command */ + flashXfer.deviceAddress = dstAddr; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Write; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD; + flashXfer.data = (uint32_t *)src; + flashXfer.dataSize = length; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + /* Do software reset. */ +#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) + base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK; + base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK); +#else + FLEXSPI_SoftwareReset(base); +#endif + + return status; +} + +status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src) +{ + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, dstAddr); + + if (status != kStatus_Success) + { + return status; + } + + /* Prepare page program command */ + flashXfer.deviceAddress = dstAddr; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Write; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD; + flashXfer.data = (uint32_t *)src; + flashXfer.dataSize = FLASH_PAGE_SIZE; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + /* Do software reset. */ +#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) + base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK; + base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK); +#else + FLEXSPI_SoftwareReset(base); +#endif + + return status; +} + +status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId) +{ + uint32_t temp; + flexspi_transfer_t flashXfer; + flashXfer.deviceAddress = 0; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Read; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID; + flashXfer.data = &temp; + flashXfer.dataSize = 1; + + status_t status = FLEXSPI_TransferBlocking(base, &flashXfer); + + *vendorId = temp; + + /* Do software reset. */ +#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) + base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK; + base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK); +#else + FLEXSPI_SoftwareReset(base); +#endif + + return status; +} + +status_t flexspi_nor_erase_chip(FLEXSPI_Type *base) +{ + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, 0); + + if (status != kStatus_Success) + { + return status; + } + + flashXfer.deviceAddress = 0; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASECHIP; + + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) + { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + return status; +} + +void flexspi_nor_flash_init(FLEXSPI_Type *base) +{ + flexspi_config_t config; + + flexspi_clock_init(); + + /*Get FLEXSPI default settings and configure the flexspi. */ + FLEXSPI_GetDefaultConfig(&config); + + /*Set AHB buffer size for reading data through AHB bus. */ + config.ahbConfig.enableAHBPrefetch = true; + config.ahbConfig.enableAHBBufferable = true; + config.ahbConfig.enableReadAddressOpt = true; + config.ahbConfig.enableAHBCachable = true; + config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad; + FLEXSPI_Init(base, &config); + + /* Configure flash settings according to serial flash feature. */ + FLEXSPI_SetFlashConfig(base, &deviceconfig, kFLEXSPI_PortA1); + + /* Update LUT table. */ + FLEXSPI_UpdateLUT(base, 0, customLUT, CUSTOM_LUT_LENGTH); + + /* Do software reset. */ + FLEXSPI_SoftwareReset(base); +} diff --git a/src/cdc.c b/src/cdc.c new file mode 100644 index 0000000..32c17ee --- /dev/null +++ b/src/cdc.c @@ -0,0 +1,51 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Artur Pacholec + * + * 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 "tusb.h" + +#if CFG_TUD_CDC + +int _write(int fhdl, const char *buf, size_t count) +{ + if (!tud_cdc_connected()) + return 0; + + for (size_t i = 0; i < count; ++i) + tud_cdc_write_char(buf[i]); + + tud_cdc_write_flush(); + + return count; +} + +int _read(int fhdl, char *buf, size_t count) +{ + if (!tud_cdc_connected() || !tud_cdc_available()) + return 0; + + return tud_cdc_read(buf, count); +} + +#endif diff --git a/src/hid.c b/src/hid.c index 66cbb60..2de91e7 100644 --- a/src/hid.c +++ b/src/hid.c @@ -24,7 +24,6 @@ * */ -#include "bsp/board.h" #include "tusb.h" #if CFG_TUD_HID diff --git a/src/main.c b/src/main.c index 6aa2eb9..604212b 100644 --- a/src/main.c +++ b/src/main.c @@ -24,12 +24,15 @@ * */ +#include #include +#include -#include "bsp/board.h" +#include "bsp.h" #include "tusb.h" static uint32_t blink_interval_ms = 500; +uint32_t reset_millis = 0; void led_blinking_task(void) { @@ -46,14 +49,26 @@ void led_blinking_task(void) led_state = !led_state; } +void reset_task(void) +{ + if (!reset_millis) + return; + + if (board_millis() > reset_millis) + board_reset(); +} + int main(void) { board_init(); tusb_init(); + printf("Hello TinyUF2!\r\n"); + while (1) { tud_task(); led_blinking_task(); + reset_task(); } return 0; diff --git a/src/msc.c b/src/msc.c index 1134511..a8e5b2a 100644 --- a/src/msc.c +++ b/src/msc.c @@ -24,14 +24,156 @@ * */ -#include "bsp/board.h" +#include + +#include "bsp.h" +#include "uf2.h" #include "tusb.h" #if CFG_TUD_MSC +#define SERIAL0 (*(uint32_t *)0x0080A00C) +#define SERIAL1 (*(uint32_t *)0x0080A040) +#define SERIAL2 (*(uint32_t *)0x0080A044) +#define SERIAL3 (*(uint32_t *)0x0080A048) + +typedef struct +{ + uint8_t JumpInstruction[3]; + uint8_t OEMInfo[8]; + uint16_t SectorSize; + uint8_t SectorsPerCluster; + uint16_t ReservedSectors; + uint8_t FATCopies; + uint16_t RootDirectoryEntries; + uint16_t TotalSectors16; + uint8_t MediaDescriptor; + uint16_t SectorsPerFAT; + uint16_t SectorsPerTrack; + uint16_t Heads; + uint32_t HiddenSectors; + uint32_t TotalSectors32; + uint8_t PhysicalDriveNum; + uint8_t Reserved; + uint8_t ExtendedBootSig; + uint32_t VolumeSerialNumber; + char VolumeLabel[11]; + uint8_t FilesystemIdentifier[8]; +} __attribute__((packed)) FAT_BootBlock; + +typedef struct +{ + char name[8]; + char ext[3]; + uint8_t attrs; + uint8_t reserved; + uint8_t createTimeFine; + uint16_t createTime; + uint16_t createDate; + uint16_t lastAccessDate; + uint16_t highStartCluster; + uint16_t updateTime; + uint16_t updateDate; + uint16_t startCluster; + uint32_t size; +} __attribute__((packed)) DirEntry; + +struct TextFile +{ + const char name[11]; + const char *content; +}; + +const char infoUf2File[] = // + "UF2 Bootloader " UF2_VERSION "\r\n" + "Model: " PRODUCT_NAME "\r\n" + "Board-ID: " BOARD_ID "\r\n"; + +const char indexFile[] = // + "\n" + "" + "" + "" + "" + "\n"; + +// WARNING -- code presumes only one NULL .content for .UF2 file +// and requires it be the last element of the array +static const struct TextFile info[] = +{ + {.name = "INFO_UF2TXT", .content = infoUf2File }, + {.name = "INDEX HTM", .content = indexFile }, + {.name = "CURRENT UF2" }, +}; + +#define NUM_FILES (sizeof(info) / sizeof(info[0])) +#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry + +#define UF2_SIZE (BOARD_FLASH_SIZE / UF2_PAYLOAD_SIZE * UF2_BLOCK_SIZE) +#define UF2_SECTORS (UF2_SIZE / UF2_BLOCK_SIZE) +#define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector +#define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1) + +#define RESERVED_SECTORS 1 +#define ROOT_DIR_SECTORS 4 +#define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + (UDI_MSC_BLOCK_SIZE - 1)) / UDI_MSC_BLOCK_SIZE) + +#define START_FAT0 RESERVED_SECTORS +#define START_FAT1 (START_FAT0 + SECTORS_PER_FAT) +#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT) +#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS) + +// all directory entries must fit in a single sector +// because otherwise current code overflows buffer +#define DIRENTRIES_PER_SECTOR (512 / sizeof(DirEntry)) +STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS); + +static WriteState write_state = +{ + .numBlocks = 0xffffffff +}; + +static const FAT_BootBlock BootBlock = +{ + .JumpInstruction = {0xeb, 0x3c, 0x90}, + .OEMInfo = "UF2 UF2 ", + .SectorSize = UDI_MSC_BLOCK_SIZE, + .SectorsPerCluster = 1, + .ReservedSectors = RESERVED_SECTORS, + .FATCopies = 2, + .RootDirectoryEntries = (ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR), + .TotalSectors16 = NUM_FAT_BLOCKS, + .MediaDescriptor = 0xF8, + .SectorsPerFAT = SECTORS_PER_FAT, + .SectorsPerTrack = 1, + .Heads = 1, + .PhysicalDriveNum = 0x80, // to match MediaDescriptor of 0xF8 + .ExtendedBootSig = 0x29, + .VolumeSerialNumber = 0x00420042, + .VolumeLabel = VOLUME_LABEL, + .FilesystemIdentifier = "FAT16 ", +}; + +static void padded_memcpy(char *dst, const char *src, int len) +{ + for (int i = 0; i < len; ++i) { + if (*src) + *dst = *src++; + else + *dst = ' '; + dst++; + } +} + void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + const char rev[] = "1.00"; + memcpy(vendor_id , VENDOR_NAME, strlen(VENDOR_NAME)); + memcpy(product_id , PRODUCT_NAME, strlen(PRODUCT_NAME)); + memcpy(product_rev, rev, strlen(rev)); } bool tud_msc_test_unit_ready_cb(uint8_t lun) @@ -41,22 +183,166 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { - + *block_count = NUM_FAT_BLOCKS - 1; + *block_size = UDI_MSC_BLOCK_SIZE; } int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - return 0; + memset(buffer, 0, bufsize); + + uint32_t sector_idx = lba; + + if (lba == 0) { // Requested boot block + memcpy(buffer, &BootBlock, sizeof(BootBlock)); + ((char*)buffer)[510] = 0x55; + ((char*)buffer)[511] = 0xaa; + } else if (lba < START_ROOTDIR) { // Requested FAT table sector + sector_idx -= START_FAT0; + if (sector_idx >= SECTORS_PER_FAT) + sector_idx -= SECTORS_PER_FAT; // second FAT is same as the first... + + if (sector_idx == 0) { + ((char*)buffer)[0] = 0xf0; + // WARNING -- code presumes only one NULL .content for .UF2 file + // and all non-NULL .content fit in one sector + // and requires it be the last element of the array + for (size_t i = 1; i < NUM_FILES * 2 + 4; ++i) { + ((char*)buffer)[i] = 0xff; + } + } + + for (int i = 0; i < 256; ++i) { // Generate the FAT chain for the firmware "file" + uint32_t v = sector_idx * 256 + i; + if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR) + ((uint16_t *)(void *)buffer)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; + } + } else if (lba < START_CLUSTERS) { // Requested sector of the root directory + sector_idx -= START_ROOTDIR; + if (sector_idx == 0) { + DirEntry *d = (void *)buffer; + padded_memcpy(d->name, BootBlock.VolumeLabel, 11); + d->attrs = 0x28; + for (size_t i = 0; i < NUM_FILES; ++i) { + d++; + const struct TextFile *inf = &info[i]; + d->size = inf->content ? strlen(inf->content) : UF2_SIZE; + d->startCluster = i + 2; + padded_memcpy(d->name, inf->name, 11); + d->createDate = 0x4d99; + d->updateDate = 0x4d99; + } + } + } else { // Requested sector from file space + sector_idx -= START_CLUSTERS; + // WARNING -- code presumes all but last file take exactly one sector + if (sector_idx < NUM_FILES - 1) { + memcpy(buffer, info[sector_idx].content, strlen(info[sector_idx].content)); + } else { + sector_idx -= NUM_FILES - 1; + + uint32_t addr = sector_idx * UF2_PAYLOAD_SIZE; + if (addr < BOARD_FLASH_SIZE) { + addr += BOARD_FLASH_BASE; + + UF2_Block *bl = (void *)buffer; + bl->magicStart0 = UF2_MAGIC_START0; + bl->magicStart1 = UF2_MAGIC_START1; + bl->magicEnd = UF2_MAGIC_END; + bl->blockNo = sector_idx; + bl->numBlocks = BOARD_FLASH_SIZE / UF2_PAYLOAD_SIZE; + bl->targetAddr = addr; + bl->payloadSize = UF2_PAYLOAD_SIZE; + bl->flags |= UF2_FLAG_FAMILYID_PRESENT; + bl->familyID = UF2_FAMILY; + + board_flash_read_blocks(bl->data, sector_idx, 1); + } + } + } + + return bufsize; } int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { - return 0; + UF2_Block *bl = (void *)buffer; + if (!is_uf2_block(bl) || !UF2_IS_MY_FAMILY(bl)) { + return bufsize; + } + + if ((bl->flags & UF2_FLAG_NOFLASH) || bl->payloadSize != UF2_PAYLOAD_SIZE || + bl->targetAddr < APP_START_ADDRESS || bl->targetAddr >= (BOARD_FLASH_BASE + BOARD_FLASH_SIZE)) { + // this happens when we're trying to re-flash CURRENT.UF2 file previously + // copied from a device; we still want to count these blocks to reset properly + } else { + //printf("uf2 write, target: 0x%08lX, size: %d, block %d, num blocks %d\r\n", bl->targetAddr, bl->payloadSize, bl->blockNo, bl->numBlocks); + board_flash_write_blocks(bl->data, (bl->targetAddr - BOARD_FLASH_BASE) / UF2_PAYLOAD_SIZE, 1); + + if (bl->numBlocks) { + if (write_state.numBlocks != bl->numBlocks && bl->numBlocks < MAX_BLOCKS) { + write_state.numBlocks = bl->numBlocks; + } + + if (bl->blockNo < MAX_BLOCKS) { + const uint8_t mask = 1 << (bl->blockNo % 8); + const uint32_t pos = bl->blockNo / 8; + if (!(write_state.writtenMask[pos] & mask)) { + write_state.writtenMask[pos] |= mask; + write_state.numWritten++; + } + + if (write_state.numWritten >= write_state.numBlocks) { + board_flash_flush(); + + reset_millis = board_millis() + 30; + } + } + } else { + // reset 300ms after last block received + reset_millis = board_millis() + 300; + } + } + + return bufsize; } int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { - return 0; + void const *response = NULL; + uint16_t resplen = 0; + + // most scsi handled is input + bool in_xfer = true; + + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Host is about to read/write etc ... better not to disconnect disk + resplen = 0; + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return resplen must not larger than bufsize + if (resplen > bufsize) + resplen = bufsize; + + if (response && (resplen > 0)) { + if (in_xfer) { + memcpy(buffer, response, resplen); + } else { + // SCSI output + } + } + + return resplen; } #endif diff --git a/src/tusb_config.h b/src/tusb_config.h index 7a70357..e414f92 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -48,7 +48,7 @@ #define CFG_TUSB_OS OPT_OS_NONE // CFG_TUSB_DEBUG is defined by compiler in DEBUG build -// #define CFG_TUSB_DEBUG 0 +#define CFG_TUSB_DEBUG 2 /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put diff --git a/src/uf2.h b/src/uf2.h new file mode 100644 index 0000000..8397234 --- /dev/null +++ b/src/uf2.h @@ -0,0 +1,75 @@ +#ifndef _UF2_H_ +#define _UF2_H_ + +#include + +#include "uf2_version.h" + +#define UF2_VERSION \ + UF2_VERSION_BASE " SF" + +#define UF2_PAYLOAD_SIZE 256 +#define UF2_BLOCK_SIZE 512 + +// All entries are little endian. +#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" +#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected +#define UF2_MAGIC_END 0x0AB16F30UL // Ditto + +// If set, the block is "comment" and should not be flashed to the device +#define UF2_FLAG_NOFLASH 0x00000001 +#define UF2_FLAG_FAMILYID_PRESENT 0x00002000 + +#define UF2_IS_MY_FAMILY(bl) \ + (((bl)->flags & UF2_FLAG_FAMILYID_PRESENT) == 0 || (bl)->familyID == UF2_FAMILY) + +typedef struct +{ + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t familyID; + + // raw data; + uint8_t data[476]; + + // store magic also at the end to limit damage from partial block reads + uint32_t magicEnd; +} UF2_Block; + +static inline bool is_uf2_block(void *data) +{ + UF2_Block *bl = (UF2_Block *)data; + return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 && + bl->magicEnd == UF2_MAGIC_END; +} + +#define DBL_TAP_MAGIC 0xf01669ef // Randomly selected, adjusted to have first and last bit set +#define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef + +// Static block size for all memories +#define UDI_MSC_BLOCK_SIZE 512L + +// Needs to be more than ~4200 (to force FAT16) +#define NUM_FAT_BLOCKS 65535 + +#define CONCAT_1(a, b) a##b +#define CONCAT_0(a, b) CONCAT_1(a, b) +#define STATIC_ASSERT(e) enum { CONCAT_0(_static_assert_, __LINE__) = 1 / ((e) ? 1 : 0) } + +#define MAX_BLOCKS (BOARD_FLASH_SIZE / UF2_PAYLOAD_SIZE + 100) + +typedef struct { + uint32_t numBlocks; + uint32_t numWritten; + uint8_t writtenMask[MAX_BLOCKS / 8 + 1]; +} WriteState; + +extern uint32_t reset_millis; + +#endif // UF2_H_ diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index 17fc486..9adde51 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -25,7 +25,7 @@ */ #include "tusb.h" -#include "usb_descriptors.h" +//#include "usb_descriptors.h" /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. @@ -139,12 +139,12 @@ uint8_t const desc_configuration[] = #if CFG_TUD_CDC // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), #endif #if CFG_TUD_MSC // Interface number, string index, EP Out & EP In address, EP size - TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512 + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), #endif #if CFG_TUD_HID