From a90be859a552f4888b6ddb84b195be62cd46aa68 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 1 Feb 2025 13:46:10 +0100 Subject: [PATCH] bootloader: Add Alif port. Signed-off-by: iabdalkader --- src/boot/src/ports/alif/alif.ld.S | 164 ++++++++ src/boot/src/ports/alif/alif_flash.c | 67 +++ src/boot/src/ports/alif/alif_ospi.c | 524 ++++++++++++++++++++++++ src/boot/src/ports/alif/alif_port.c | 208 ++++++++++ src/boot/src/ports/alif/alif_port.mk | 192 +++++++++ src/boot/src/ports/alif/alif_services.c | 153 +++++++ src/boot/src/ports/alif/alif_services.h | 38 ++ src/boot/src/ports/alif/tusb_config.h | 52 +++ 8 files changed, 1398 insertions(+) create mode 100644 src/boot/src/ports/alif/alif.ld.S create mode 100644 src/boot/src/ports/alif/alif_flash.c create mode 100644 src/boot/src/ports/alif/alif_ospi.c create mode 100644 src/boot/src/ports/alif/alif_port.c create mode 100644 src/boot/src/ports/alif/alif_port.mk create mode 100644 src/boot/src/ports/alif/alif_services.c create mode 100644 src/boot/src/ports/alif/alif_services.h create mode 100644 src/boot/src/ports/alif/tusb_config.h diff --git a/src/boot/src/ports/alif/alif.ld.S b/src/boot/src/ports/alif/alif.ld.S new file mode 100644 index 000000000..bbc64c590 --- /dev/null +++ b/src/boot/src/ports/alif/alif.ld.S @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// Entry Point +ENTRY(Reset_Handler) + +#include "omv_boardconfig.h" + +MEMORY +{ + FLASH_TEXT (rx) : ORIGIN = OMV_FLASH_ORIGIN, LENGTH = OMV_FLASH_LENGTH + ITCM (rx) : ORIGIN = OMV_ITCM_ORIGIN, LENGTH = OMV_ITCM_LENGTH + DTCM (rw) : ORIGIN = OMV_DTCM_ORIGIN, LENGTH = OMV_DTCM_LENGTH + #if defined(OMV_SRAM0_ORIGIN) + SRAM0 (rw) : ORIGIN = OMV_SRAM0_ORIGIN, LENGTH = OMV_SRAM0_LENGTH + #endif + #if defined(OMV_SRAM1_ORIGIN) + SRAM1 (rw) : ORIGIN = OMV_SRAM1_ORIGIN, LENGTH = OMV_SRAM1_LENGTH + #endif + #if defined(OMV_SRAM6_A_ORIGIN) + SRAM6_A (rw) : ORIGIN = OMV_SRAM6_A_ORIGIN, LENGTH = OMV_SRAM6_A_LENGTH + #endif + #if defined(OMV_SRAM6_B_ORIGIN) + SRAM6_B (rw) : ORIGIN = OMV_SRAM6_B_ORIGIN, LENGTH = OMV_SRAM6_B_LENGTH + #endif + #if defined(OMV_SRAM7_ORIGIN) + SRAM7 (rw) : ORIGIN = OMV_SRAM7_ORIGIN, LENGTH = OMV_SRAM7_LENGTH + #endif + #if defined(OMV_SRAM8_ORIGIN) + SRAM8 (rw) : ORIGIN = OMV_SRAM8_ORIGIN, LENGTH = OMV_SRAM8_LENGTH + #endif + #if defined(OMV_SRAM9_A_ORIGIN) + SRAM9_A (rw) : ORIGIN = OMV_SRAM9_A_ORIGIN, LENGTH = OMV_SRAM9_A_LENGTH + #endif + #if defined(OMV_SRAM9_B_ORIGIN) + SRAM9_B (rw) : ORIGIN = OMV_SRAM9_B_ORIGIN, LENGTH = OMV_SRAM9_B_LENGTH + #endif +} + +SECTIONS +{ + .text : ALIGN(16) + { + KEEP(*(.vectors)) + . = ALIGN(4); + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(16); + } > FLASH_TEXT + + .copy.table : ALIGN(4) + { + __copy_table_start__ = .; + LONG ( LOADADDR(.data) ) + LONG ( ADDR(.data) ) + LONG ( SIZEOF(.data)/4 ) + __copy_table_end__ = .; + . = ALIGN(16); + } > FLASH_TEXT + + .zero.table : ALIGN(4) + { + __zero_table_start__ = .; + LONG (ADDR(.bss)) + LONG (SIZEOF(.bss)/4) + LONG (ADDR(.bss.sram)) + LONG (SIZEOF(.bss.sram)/4) + __zero_table_end__ = .; + . = ALIGN(16); + } > FLASH_TEXT + + .data : ALIGN(8) + { + *(.data) + . = ALIGN(8); + *(.data.*) + . = ALIGN(8); + . = ALIGN(16); + } > OMV_MAIN_MEMORY AT > FLASH_TEXT + + /* Peripherals in expansion master 0 (such as USB, Ethernet, SD/MMC) + are by default configured as non-secure, so they don't have access + to DTCMs. This can be fixed in the ToC by allowing access to DTCMs + to all bus masters, for now these peripherals should place buffers + in regular SRAM */ + .bss.sram (NOLOAD) : ALIGN(16) + { + *(.bss.sram*) + . = ALIGN(16); + } > SRAM1 + + .bss : ALIGN(4) + { + __bss_start__ = .; + *(.bss) + . = ALIGN(4); + *(.bss.*) + . = ALIGN(4); + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > OMV_MAIN_MEMORY + + .init_fini_arrays : ALIGN(16) + { + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.eh_frame*)) + . = ALIGN(16); + } > FLASH_TEXT + + #include "common.ld.S" +} diff --git a/src/boot/src/ports/alif/alif_flash.c b/src/boot/src/ports/alif/alif_flash.c new file mode 100644 index 000000000..f7ea8999f --- /dev/null +++ b/src/boot/src/ports/alif/alif_flash.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alif Flash driver. + */ +#include +#include ALIF_CMSIS_H + +#include "omv_boardconfig.h" +#include "omv_bootconfig.h" +#include "mram.h" + +#if OMV_BOOT_AXI_FLASH_ENABLE + +int axi_flash_read(uint32_t addr, uint8_t *buf, size_t size) { + memcpy(buf, (void *) addr, size); + return 0; +} + +int axi_flash_write(uint32_t addr, const uint8_t *buf, size_t size) { + // Note TinyUSB buffers are aligned to a multiple of the MRAM sector size. + if ((addr % MRAM_SECTOR_SIZE) || ((uint32_t) buf % MRAM_SECTOR_SIZE)) { + return -1; + } + + // Round up to multiple of the MRAM sector size. + // Note TinyUSB buffers are much bigger than the MRAM sector size, so the + // writes can be rounded up, and we can read more than the specified size. + if (size % MRAM_SECTOR_SIZE) { + size = (size + (MRAM_SECTOR_SIZE - 1)) & ~(MRAM_SECTOR_SIZE - 1); + } + + // Note writes must be 16 bytes aligned and a multiple of 16 bytes. + for (size_t i = 0; i < size / sizeof(uint64_t); i += 2) { + ((volatile uint64_t *) addr)[i + 0] = ((volatile uint64_t *) buf)[i + 0]; + ((volatile uint64_t *) addr)[i + 1] = ((volatile uint64_t *) buf)[i + 1]; + } + return 0; +} +#endif // OMV_BOOT_AXI_FLASH_ENABLE diff --git a/src/boot/src/ports/alif/alif_ospi.c b/src/boot/src/ports/alif/alif_ospi.c new file mode 100644 index 000000000..647fd85bc --- /dev/null +++ b/src/boot/src/ports/alif/alif_ospi.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ALif OSPI driver. + * Note this driver uses SPI mode, and should support any standard flash. + */ +#include +#include +#include ALIF_CMSIS_H +#include "omv_boardconfig.h" +#include "omv_bootconfig.h" + +#if OMV_BOOT_SPI_FLASH_ENABLE +#include "gpio.h" +#include "pinconf.h" +#include "ospi_private.h" + +#define OSPI_INST_0BIT (0x00U) +#define OSPI_INST_4BIT (0x01U) +#define OSPI_INST_8BIT (0x02U) +#define OSPI_INST_16BIT (0x03U) + +#define OSPI_DFS_8BIT (0x07U) +#define OSPI_DFS_16BIT (0x0FU) +#define OSPI_DFS_32BIT (0x1FU) + +#define OSPI_FRF_SINGLE (0x00U) +#define OSPI_FRF_DUAL (0x01U) +#define OSPI_FRF_QUAD (0x02U) +#define OSPI_FRF_OCTAL (0x03U) + +#define OSPI_ADDR_0BIT (0x00U) +#define OSPI_ADDR_24BIT (0x06U) +#define OSPI_ADDR_32BIT (0x08U) + +#define OSPI_FIFO_SIZE (128) // 256 words +#define OSPI_PAGE_SIZE (128) +#define OSPI_SECTOR_SIZE (4096) +#define OSPI_POLL_TIMEOUT (1000000) + +#define OSPI_WRSR_COMMAND (0x01) +#define OSPI_RDSR_COMMAND (0x05) +#define OSPI_RDSR_DCYCLES (0) + +#define OSPI_WRVL_COMMAND (0x81) +#define OSPI_RDVL_COMMAND (0x85) + +#define OSPI_WRNV_COMMAND (0xB1) +#define OSPI_RDNV_COMMAND (0xB5) + +#define OSPI_RDID_COMMAND (0x9F) +#define OSPI_RDID_DCYCLES (0) + +#define OSPI_READ_COMMAND (0x13) +#define OSPI_READ_DCYCLES (0) + +#define OSPI_WREN_COMMAND (0x06) +#define OSPI_WRITE_COMMAND (0x12) + +#define OSPI_ERASE_COMMAND (0x21) +#define OSPI_ERASE_CHIP_COMMAND (0xC7) + +typedef struct ospi_flash { + uint8_t ddr_mode; + uint8_t spi_mode; + ssi_regs_t *regs; + aes_regs_t *aes_regs; + volatile void *xip_base; + uint8_t initialized; + bool bswap16; + bool recovered; +} ospi_flash_t; + +static ospi_flash_t ospi = { 0 }; + +static void port_delay_ns(uint32_t ns) { + for (size_t i = 0; i < (ns / 2.5); i++) { + } +} + +static void ospi_setup(ospi_flash_t *ospi, uint32_t addr_len, uint32_t ndf, uint32_t wait_cycles) { + ospi_writel(ospi, ser, 0); + spi_disable(ospi); + + uint32_t val = CTRLR0_IS_MST + | (OSPI_DFS_8BIT << CTRLR0_DFS_OFFSET) + | (OSPI_FRF_SINGLE << CTRLR0_SPI_FRF_OFFSET) + | ((ndf ? SPI_TMOD_TR : TMOD_TO) << CTRLR0_TMOD_OFFSET); + + ospi_writel(ospi, ctrlr0, val); + ospi_writel(ospi, ctrlr1, (ndf ? (ndf - 1) : 0)); + + val = TRANS_TYPE_FRF_DEFINED + | (1 << CTRLR0_SPI_RXDS_EN_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (OSPI_INST_8BIT << CTRLR0_INST_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + + ospi_writel(ospi, spi_ctrlr0, val); + spi_enable(ospi); +} + +static inline void ospi_push(ospi_flash_t *ospi, uint32_t data) { + ospi_writel(ospi, data_reg, data); +} + +static int ospi_flash_wait_busy(ospi_flash_t *ospi) { + uint32_t timeout = OSPI_POLL_TIMEOUT; + while ((ospi_readl(ospi, sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + if (--timeout == 0) { + return -1; + } + } + return 0; +} + +static int ospi_send(ospi_flash_t *ospi, uint32_t cmd, uint32_t addr_len, + uint32_t addr, uint32_t data_len, const uint8_t *data) { + ospi_setup(ospi, addr_len, 0, 0); + ospi_push(ospi, cmd); + + // Push the address (0, 24, or 32 bits). + for (size_t i = 0; i < addr_len / 2; i++) { + ospi_push(ospi, addr >> ((addr_len / 2 - 1) * 8 - i * 8)); + } + + for (size_t i = 0; i < data_len; i++) { + ospi_push(ospi, data[i]); + } + + ospi_writel(ospi, ser, OMV_BOOT_OSPI_SER); + return ospi_flash_wait_busy(ospi); +} + +static int ospi_recv(ospi_flash_t *ospi, uint32_t cmd, uint32_t addr_len, + uint32_t addr, size_t data_len, uint8_t *data, uint32_t dcycles) { + + ospi_setup(ospi, addr_len, data_len, dcycles); + ospi_push(ospi, cmd); + + // Push the address (0, 24, or 32 bits). + for (size_t i = 0; i < addr_len / 2; i++) { + ospi_push(ospi, addr >> ((addr_len / 2 - 1) * 8 - i * 8)); + } + + // Push dummy bytes as many as the data size. + for (size_t i = 0; i < data_len; i++) { + ospi_push(ospi, 0x00); + } + + ospi_writel(ospi, ser, OMV_BOOT_OSPI_SER); + + // Skip the command and address high-z bytes. + uint32_t skip_len = addr_len / 2 + 1; + + for (size_t i = 0; i < skip_len + data_len; i++) { + unsigned int timeout = 100000; + + while (ospi_readl(ospi, rxflr) == 0) { + if (--timeout == 0) { + return -1; + } + } + + if (i < skip_len) { + (void) ospi_readl(ospi, data_reg); + } else { + data[i - skip_len] = ospi_readl(ospi, data_reg); + } + } + return 0; +} + +static int ospi_flash_wait_sr(ospi_flash_t *ospi, uint8_t mask, uint8_t val, uint32_t timeout) { + do { + uint8_t sr; + if (ospi_recv(ospi, OSPI_RDSR_COMMAND, OSPI_ADDR_0BIT, 0, 1, &sr, OSPI_RDSR_DCYCLES) != 0) { + return -1; + } + if ((sr & mask) == val) { + return 0; // success + } + } while (timeout--); + + return -1; +} + +static int ospi_flash_wait_wel1(ospi_flash_t *ospi) { + return ospi_flash_wait_sr(ospi, 2, 2, OSPI_POLL_TIMEOUT); +} + +static int ospi_flash_wait_wip0(ospi_flash_t *ospi) { + return ospi_flash_wait_sr(ospi, 1, 0, OSPI_POLL_TIMEOUT); +} + +static int ospi_flash_write_enable(ospi_flash_t *ospi) { + ospi_send(ospi, OSPI_WREN_COMMAND, OSPI_ADDR_0BIT, 0, 0, NULL); + return ospi_flash_wait_wel1(ospi); +} + +int ospi_flash_read_reg(ospi_flash_t *ospi, uint32_t cmd, uint32_t addr_len, uint32_t addr) { + uint8_t buf; + if (ospi_recv(ospi, cmd, addr_len, addr, 1, &buf, 0) != 0) { + return -1; + } + return buf; +} + +int ospi_flash_write_reg(ospi_flash_t *ospi, uint32_t cmd, uint32_t addr_len, uint32_t addr, uint8_t buf) { + if (ospi_flash_write_enable(ospi) != 0 || + ospi_send(ospi, cmd, addr_len, addr, 1, &buf) != 0 || + ospi_flash_wait_wip0(ospi) != 0) { + return -1; + } + return 0; +} + +static int ospi_flash_chip_erase(ospi_flash_t *ospi, uint32_t value) { + // Configure erase value + uint32_t nv_reg = 0x7F | ((value & 1) << 7); + if (ospi_flash_write_reg(ospi, OSPI_WRVL_COMMAND, OSPI_ADDR_24BIT, 0x08, nv_reg) != 0) { + return -1; + } + if (ospi_flash_write_enable(ospi) != 0 || + ospi_send(ospi, OSPI_ERASE_CHIP_COMMAND, OSPI_ADDR_0BIT, 0, 0, NULL) != 0 || + ospi_flash_wait_sr(ospi, 1, 0, 400000) != 0) { + return -1; + } + return 0; +} + +static int ospi_flash_reset(ospi_flash_t *ospi) { + port_pin_write(OMV_BOOT_OSPI_RST_PIN, 0); + port_delay_ms(1); + port_pin_write(OMV_BOOT_OSPI_RST_PIN, 1); + port_delay_ms(1); + return 0; +} + +static int ospi_flash_init(ospi_flash_t *ospi) { + if (ospi->initialized) { + return 0; + } + + // Reset flash. + ospi_flash_reset(ospi); + + // Use SPI mode for initialization. + ospi->spi_mode = true; + ospi->regs = (ssi_regs_t *) OMV_BOOT_OSPI_SSI_BASE; + ospi->aes_regs = (aes_regs_t *) OMV_BOOT_OSPI_AES_BASE; + ospi->xip_base = (volatile void *) OMV_BOOT_OSPI_XIP_BASE; + + // Initialize OSPI. + spi_disable(ospi); + ospi_writel(ospi, ser, 0); + ospi_writel(ospi, rx_sample_dly, OMV_BOOT_OSPI_RX_DELAY); + ospi_writel(ospi, txd_drive_edge, OMV_BOOT_OSPI_DDR_EDGE); + ospi->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; + ospi->aes_regs->aes_rxds_delay = OMV_BOOT_OSPI_RXDS_DELAY; + spi_set_clk(ospi, (GetSystemAXIClock() / OMV_BOOT_OSPI_CLOCK)); + spi_enable(ospi); + + // Detect onboard flash based on ID. + uint8_t id[3] = { 0 }; + ospi_recv(ospi, OSPI_RDID_COMMAND, OSPI_ADDR_0BIT, 0, 3, id, OSPI_RDID_DCYCLES); + // The byte order of 16-bit words is swapped when written in 1-1-1 and read back + // in 8D-8D-8D. The MX chips seems to be the only one that doesn't swap 16-bit words. + ospi->bswap16 = id[0] != 0xC2; + ospi->initialized = true; + return 0; +} + +static int ospi_flash_deinit(ospi_flash_t *ospi) { + if (ospi->regs) { + spi_disable(ospi); + spi_set_clk(ospi, 0); + } + ospi->initialized = false; + + // Reset flash. + return ospi_flash_reset(ospi); +} + +static int ospi_flash_erase(ospi_flash_t *ospi, uint32_t addr) { + if (ospi_flash_write_enable(ospi) != 0) { + return -1; + } + if (ospi_send(ospi, OSPI_ERASE_COMMAND, OSPI_ADDR_32BIT, addr, 0, NULL) != 0) { + return -1; + } + return ospi_flash_wait_wip0(ospi); +} + +static int ospi_flash_read_page(ospi_flash_t *ospi, uint32_t addr, uint8_t *buf, uint32_t size) { + return ospi_recv(ospi, OSPI_READ_COMMAND, OSPI_ADDR_32BIT, addr, size, buf, OSPI_READ_DCYCLES); +} + +static int ospi_flash_write_page(ospi_flash_t *ospi, uint32_t addr, const uint8_t *buf, uint32_t size) { + if (ospi_flash_write_enable(ospi) != 0) { + return -1; + } + if (ospi_send(ospi, OSPI_WRITE_COMMAND, OSPI_ADDR_32BIT, addr, size, buf) != 0) { + return -1; + } + return ospi_flash_wait_wip0(ospi); +} + +int spi_flash_read(uint32_t addr, uint8_t *buf, uint32_t size) { + if (ospi_flash_init(&ospi) != 0) { + return -1; + } + + for (size_t i = 0; i < size / OSPI_FIFO_SIZE; i++) { + if (ospi_flash_read_page(&ospi, addr, buf, OSPI_FIFO_SIZE) != 0) { + return -1; + } + buf += OSPI_FIFO_SIZE; + addr += OSPI_FIFO_SIZE; + } + + if ((size % OSPI_FIFO_SIZE)) { + return ospi_flash_read_page(&ospi, addr, buf, (size % OSPI_FIFO_SIZE)); + } + return 0; +} + +int spi_flash_write(uint32_t addr, const uint8_t *buf, uint32_t size) { + if (ospi_flash_init(&ospi) != 0) { + return -1; + } + + if ((addr % OSPI_SECTOR_SIZE) == 0) { + if (ospi_flash_erase(&ospi, addr) != 0) { + return -1; + } + } + + if (ospi.bswap16) { + uint16_t *buf16 = (uint16_t *) buf; + for (size_t i = 0; i < size / 2; i++) { + buf16[i] = __REV16(buf16[i]); + } + } + + for (size_t i = 0; i < size / OSPI_PAGE_SIZE; i++) { + if (ospi_flash_write_page(&ospi, addr, buf, OSPI_PAGE_SIZE) != 0) { + return -1; + } + buf += OSPI_PAGE_SIZE; + addr += OSPI_PAGE_SIZE; + } + + if ((size % OSPI_PAGE_SIZE)) { + return ospi_flash_write_page(&ospi, addr, buf, (size % OSPI_PAGE_SIZE)); + } + return 0; +} + +int spi_flash_recovery(uint32_t addr, const uint8_t *buf, uint32_t size) { + static const uint8_t key[] = { + 0x15, 0x9e, 0x7d, 0x42, 0x96, 0x1a, 0x71, 0xeb, + 0x73, 0xa3, 0x26, 0x29, 0x2b, 0x08, 0x09, 0x0e, + }; + + if (ospi.recovered) { + return -1; + } + + // Check recovery key. + if (size < 16 || memcmp(buf, key, sizeof(key))) { + return -1; + } + + // Init flash. + if (ospi_flash_init(&ospi) != 0) { + return -1; + } + + // 1. Perform JESD reset + const pin_t *cs = &omv_boot_pins[OMV_BOOT_OSPI_CS_PIN]; + pinconf_set(cs->port, cs->pin, OMV_BOOT_ALT_GPIO, cs->pad); + port_pin_mode(OMV_BOOT_OSPI_CS_PIN, PIN_MODE_OUTPUT); + port_pin_write(OMV_BOOT_OSPI_CS_PIN, 1); + + const pin_t *d0 = &omv_boot_pins[OMV_BOOT_OSPI_D0_PIN]; + pinconf_set(d0->port, d0->pin, OMV_BOOT_ALT_GPIO, d0->pad); + port_pin_mode(OMV_BOOT_OSPI_D0_PIN, PIN_MODE_OUTPUT); + port_pin_write(OMV_BOOT_OSPI_D0_PIN, 0); + + // Clock out 0, 1, 0, 1 + for (size_t i = 0, v = 1; i < 4; v ^= 1, i++) { + port_pin_write(OMV_BOOT_OSPI_CS_PIN, 0); + port_delay_ns(500); + + port_pin_write(OMV_BOOT_OSPI_D0_PIN, v); + + port_pin_write(OMV_BOOT_OSPI_CS_PIN, 1); + port_delay_ns(500); + } + + // Revert pin state. + pinconf_set(d0->port, d0->pin, d0->alt, d0->pad); + pinconf_set(cs->port, cs->pin, cs->alt, cs->pad); + + // 2.Write Enable. Write Opcode 0x06 to set WEL bit to 1 + if (ospi_flash_write_enable(&ospi) != 0) { + return -1; + } + + // 4. DFIM entry + if (ospi_flash_write_reg(&ospi, OSPI_WRVL_COMMAND, OSPI_ADDR_24BIT, 0x1E, 0x6B) != 0) { + return -1; + } + + if (ospi_flash_read_reg(&ospi, OSPI_RDVL_COMMAND, OSPI_ADDR_24BIT, 0x1E) != 1) { + return -1; + } + + for (size_t i = 0; i < 2; i++) { + // 5.a Initialize status register to 0xFF + if (ospi_flash_write_reg(&ospi, OSPI_WRSR_COMMAND, OSPI_ADDR_0BIT, 0x00, 0xFF) != 0) { + return -1; + } + + // 5.c Initialize status register to 0x00 + if (ospi_flash_write_reg(&ospi, OSPI_WRSR_COMMAND, OSPI_ADDR_0BIT, 0x00, 0x00) != 0) { + return -1; + } + + // 6.a Initialize NCRs (Command=0xB1; Addr=0x00 to 0x0C; Data=0x00) + for (size_t addr = 0x00; addr <= 0x0C; addr++) { + if (ospi_flash_write_reg(&ospi, 0xB1, OSPI_ADDR_24BIT, addr, 0x00) != 0) { + return -1; + } + } + + // 6.c Initialize NCRs (Command=0xB1; Addr=0x00 to 0x0C; Data=0x00) + for (size_t addr = 0x00; addr <= 0x0C; addr++) { + if (ospi_flash_write_reg(&ospi, 0xB1, OSPI_ADDR_24BIT, addr, 0xFF) != 0) { + return -1; + } + } + } + + for (size_t i = 0; i < 2; i++) { + // 7.a Disable lock: Command=0x81; Addr=0x08; Data=0xF9 + if (ospi_flash_write_reg(&ospi, 0x81, OSPI_ADDR_24BIT, 0x08, 0xF9) != 0) { + return -1; + } + + // 7.b Write 13: Command=0x42; Addr=0x00 to end (n=257); Data=0xFF + for (size_t addr = 0; addr < 256; addr++) { + if (ospi_flash_write_reg(&ospi, 0x42, OSPI_ADDR_24BIT, addr, 0xFF) != 0) { + return -1; + } + } + + // 8.a Disable lock: Command=0x81; Addr=0x08; Data=0xF9 + if (ospi_flash_write_reg(&ospi, 0x81, OSPI_ADDR_24BIT, 0x08, 0xF9) != 0) { + return -1; + } + + // 8.b Write 03: Command=0x42; Addr=0x00 to end (n=257); Data=0x00 + for (size_t addr = 0; addr < 256; addr++) { + if (ospi_flash_write_reg(&ospi, 0x42, OSPI_ADDR_24BIT, addr, 0x00) != 0) { + return -1; + } + } + + } + + for (size_t i = 0; i < 2; i++) { + // 9, 11 Write entire Device to 0 + if (ospi_flash_chip_erase(&ospi, 0) != 0) { + return -1; + } + + // 10, 12 Write entire Device to 1 + if (ospi_flash_chip_erase(&ospi, 1) != 0) { + return -1; + } + } + + // DFIM exit + if (ospi_flash_write_reg(&ospi, OSPI_WRVL_COMMAND, OSPI_ADDR_24BIT, 0x1E, 0) != 0) { + return -1; + } + + ospi.recovered = true; + return 0; +} + +int spi_flash_deinit() { + return ospi_flash_deinit(&ospi); +} +#endif // OMV_BOOT_SPI_FLASH_ENABLE diff --git a/src/boot/src/ports/alif/alif_port.c b/src/boot/src/ports/alif/alif_port.c new file mode 100644 index 000000000..7646b8b84 --- /dev/null +++ b/src/boot/src/ports/alif/alif_port.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * DFU bootloader Alif port. + */ +#include +#include + +#include ALIF_CMSIS_H +#include "gpio.h" +#include "pinconf.h" + +#include "omv_boardconfig.h" +#include "omv_bootconfig.h" +#include "flash.h" +#include "alif_services.h" + +static volatile uint32_t ticks_ms; + +void SysTick_Handler(void) { + ticks_ms += 1; +} + +int port_init(void) { + // Initialize the MPU and configure + // the default attributes and regions. + port_mpu_init(); + + // Initialize the services API. + alif_services_init(); + + // Initialize and configure SysTick. + SysTick_Config(SystemCoreClock / 1000); + + // Configure pin mux. + for (size_t i = 0; i < OMV_BOOT_PINS_COUNT; i++) { + const pin_t *pin = &omv_boot_pins[i]; + pinconf_set(pin->port, pin->pin, pin->alt, pin->pad); + } + + // Configure USB MUX GPIO. + port_pin_mode(OMV_BOOT_MUX_PIN, PIN_MODE_OUTPUT); + port_pin_write(OMV_BOOT_MUX_PIN, 1); + + // Configure LED GPIO. + port_pin_mode(OMV_BOOT_LED_PIN, PIN_MODE_OUTPUT); + port_pin_write(OMV_BOOT_LED_PIN, 1); + + // Configure OSPI reset GPIO. + port_pin_mode(OMV_BOOT_OSPI_RST_PIN, PIN_MODE_OUTPUT); + port_pin_write(OMV_BOOT_OSPI_RST_PIN, 0); + + // Configure and enable USB IRQs. + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, OMV_BOOT_USB_IRQ_PRI); + return 0; +} + +int port_deinit(void) { + // Deinit DCD and disable USB IRQs. + extern void dcd_uninit(void); + dcd_uninit(); + + // Deinit services API. + alif_services_deinit(); + + // Deinitialize SPI Flash. + #if OMV_BOOT_SPI_FLASH_ENABLE + spi_flash_deinit(); + #endif + + // Disable SysTick and its IRQ. + NVIC_DisableIRQ(SysTick_IRQn); + NVIC_ClearPendingIRQ(SysTick_IRQn); + SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk); + + // Clean/invalidate any cached lines. + SCB_CleanInvalidateDCache(); + + // Clear default regions and configure XIP regions (if any). + port_mpu_deinit(); + + // Turn off LED. + gpio_set_value_high(omv_boot_pins[OMV_BOOT_LED_PIN].gpio, omv_boot_pins[OMV_BOOT_LED_PIN].pin); + return 0; +} + +int port_get_uid(uint8_t *buf) { + alif_services_get_unique_id(buf, 12); + return 0; +} + +void port_mpu_protect(const partition_t *p, bool enable) { + if (p->region != -1) { + ARM_MPU_Disable(); + MPU->RNR = p->region; + MPU->RBAR = ARM_MPU_RBAR(p->start, ARM_MPU_SH_NON, enable & 1, 1, 0); // RO/NP/XN. + MPU->RLAR = ARM_MPU_RLAR(p->limit - 1, p->attr); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + } +} + +uint32_t port_ticks_ms() { + return ticks_ms; +} + +void port_delay_ms(uint32_t ms) { + uint32_t start_ms = port_ticks_ms(); + while ((port_ticks_ms() - start_ms) < ms) { + __WFI(); + } +} + +void port_pin_mode(uint32_t pin, uint32_t mode) { + if (mode == PIN_MODE_OUTPUT) { + gpio_set_direction_output(omv_boot_pins[pin].gpio, omv_boot_pins[pin].pin); + } else { + gpio_set_direction_input(omv_boot_pins[pin].gpio, omv_boot_pins[pin].pin); + } +} + +void port_pin_write(uint32_t pin, uint32_t state) { + if (state) { + gpio_set_value_high(omv_boot_pins[pin].gpio, omv_boot_pins[pin].pin); + } else { + gpio_set_value_low(omv_boot_pins[pin].gpio, omv_boot_pins[pin].pin); + } +} + +void port_led_blink(uint32_t interval_ms) { + static uint32_t start_ms = 0; + static uint32_t led_state = 0; + + if ((port_ticks_ms() - start_ms) < interval_ms) { + return; + } + + led_state ^= 1; + port_pin_write(OMV_BOOT_LED_PIN, led_state); + start_ms += interval_ms; +} + +// Bypass startup MPU defaults. +void MPU_Setup(void) { + +} + +// Bypass startup MPU regions. +void MPU_Load_Regions(void) { + +} + +void port_mpu_load_defaults() { + static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { + { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MEMATTR_NORMAL_RA) + }, + { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MEMATTR_NORMAL_WB_RA_WA) + }, + { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MEMATTR_DEVICE_nGnRE) + }, + { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MEMATTR_DEVICE_nGnRE) + }, + }; + + /* Load the regions from the table */ + ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); +} + +void __attribute__((noreturn)) __fatal_error(const char *msg) { + while (1) { + port_led_blink(50); + } +} diff --git a/src/boot/src/ports/alif/alif_port.mk b/src/boot/src/ports/alif/alif_port.mk new file mode 100644 index 000000000..5febf0727 --- /dev/null +++ b/src/boot/src/ports/alif/alif_port.mk @@ -0,0 +1,192 @@ +# Copyright (C) 2023-2024 OpenMV, LLC. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Any redistribution, use, or modification in source or binary form +# is done solely for personal benefit and not for any commercial +# purpose or for monetary gain. For commercial licensing options, +# please contact openmv@openmv.io +# +# THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT +# OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Include OpenMV board config first to set the port. +include $(OMV_BOARD_CONFIG_DIR)/omv_boardconfig.mk + +LDSCRIPT ?= alif +BUILD := $(BUILD)/bootloader +FIRMWARE := bootloader +CMSIS_MCU_H := '' + +# Compiler Flags +CFLAGS += -std=gnu99 \ + -Wall \ + -Werror \ + -Warray-bounds \ + -nostartfiles \ + -fdata-sections \ + -ffunction-sections \ + -fno-inline-small-functions \ + -mfloat-abi=hard \ + -mthumb \ + -mcpu=$(CPU) \ + -mtune=$(CPU) \ + -mfpu=$(FPU) \ + -march=armv8.1-m.main+fp+mve.fp \ + -fsingle-precision-constant \ + -Wdouble-promotion + +CFLAGS += -D__VFP_FP__ \ + -D$(TARGET) \ + -D$(MCU_CORE) \ + -DCORE_$(MCU_CORE)=1 \ + -D$(ARM_MATH) \ + -DARM_NN_TRUNCATE \ + -DETHOS_U \ + -DPINS_AF_H=$(PINS_AF_H) \ + -DCMSIS_MCU_H=$(CMSIS_MCU_H) \ + -DALIF_CMSIS_H=$(CMSIS_MCU_H) \ + -DBOOTLOADER=1 \ + -DOMV_NOSYS_STUBS_ENABLE=1 \ + -DTUSB_ALIF_NO_IRQ_CFG \ + -DOMV_BOOT_JUMP=$(OMV_FIRM_ADDR) \ + $(OMV_BOOT_CFLAGS) \ + $(OMV_BOARD_CFLAGS) + +CFLAGS += -I$(OMV_BOARD_CONFIG_DIR) \ + -I$(TOP_DIR)/$(BOOT_DIR)/include \ + -I$(TOP_DIR)/$(BOOT_DIR)/$(PORT_DIR) \ + -I$(TOP_DIR)/$(CMSIS_DIR)/include \ + -I$(TOP_DIR)/$(HAL_DIR)/drivers/include \ + -I$(TOP_DIR)/$(HAL_DIR)/ospi_xip/source/ospi \ + -I$(TOP_DIR)/$(HAL_DIR)/se_services/include \ + -I$(TOP_DIR)/$(HAL_DIR)/Device/common/config \ + -I$(TOP_DIR)/$(HAL_DIR)/Device/common/include \ + -I$(TOP_DIR)/$(HAL_DIR)/Device/core/$(MCU_CORE)/config \ + -I$(TOP_DIR)/$(HAL_DIR)/Device/core/$(MCU_CORE)/include \ + -I$(TOP_DIR)/$(HAL_DIR)/Device/$(MCU_SERIES)/$(MCU_VARIANT) \ + -I$(TOP_DIR)/$(TINYUSB_DIR)/src + +# Linker Flags +LDFLAGS = -mthumb \ + -mcpu=$(CPU) \ + -mfpu=$(FPU) \ + -mfloat-abi=hard \ + -mabi=aapcs-linux \ + -Wl,--print-memory-usage \ + -Wl,--gc-sections \ + -Wl,--no-warn-rwx-segment \ + -Wl,-Map=$(BUILD)/$(FIRMWARE).map \ + -Wl,-T$(BUILD)/$(LDSCRIPT).lds + +SRC_C += $(addprefix src/common/, \ + dfu.c \ + mpu.c \ + desc.c \ + main.c \ +) + +SRC_C += $(addprefix $(PORT_DIR)/, \ + alif_ospi.c \ + alif_port.c \ + alif_flash.c \ + alif_services.c \ +) + +SRC_C += $(addprefix $(OMV_COMMON_DIR)/, \ + nosys_stubs.c \ +) + +SRC_C += $(addprefix $(TINYUSB_DIR)/, \ + src/tusb.c \ + src/class/dfu/dfu_device.c \ + src/common/tusb_fifo.c \ + src/device/usbd.c \ + src/device/usbd_control.c \ +) + +SRC_C += $(addprefix $(HAL_DIR)/, \ + drivers/source/pinconf.c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ + Device/common/source/clk.c \ + Device/common/source/dcd.c \ + Device/common/source/mpu_M55.c \ + Device/common/source/tgu_M55.c \ + Device/common/source/system_M55.c \ + Device/common/source/system_utils.c \ + Device/common/source/tcm_partition.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_power.c \ + se_services/source/services_host_system.c \ + se_services/source/services_host_maintenance.c \ +) + +# Firmware objects +OBJS += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJS += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) +OBJS_DIR = $(sort $(dir $(OBJS))) + +$(BUILD)/$(HAL_DIR)/se_services/source/services_host_boot.o: override CFLAGS += -Wno-stringop-truncation +$(BUILD)/$(HAL_DIR)/se_services/source/services_host_system.o: override CFLAGS += -Wno-maybe-uninitialized + +all: $(FIRMWARE) + $(SIZE) $(FW_DIR)/$(FIRMWARE).elf + +$(OBJS_DIR): + $(MKDIR) -p $@ + +$(BUILD)/%.o : %.c + $(ECHO) "CC $<" + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/$(HAL_DIR)/%.o : $(TOP_DIR)/$(HAL_DIR)/%.c + $(ECHO) "CC $(shell realpath --relative-to=pwd $<)" + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/$(TINYUSB_DIR)/%.o : $(TOP_DIR)/$(TINYUSB_DIR)/%.c + $(ECHO) "CC $(shell realpath --relative-to=pwd $<)" + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/$(OMV_COMMON_DIR)/%.o : $(TOP_DIR)/$(OMV_COMMON_DIR)/%.c + $(ECHO) "CC $(shell realpath --relative-to=pwd $<)" + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/%.o : %.s + $(ECHO) "AS $<" + $(AS) $(AFLAGS) $< -o $@ + +FIRMWARE_OBJS: | $(OBJS_DIR) $(OBJS) + +$(FIRMWARE): FIRMWARE_OBJS + $(CPP) -P -E -DBOOTLOADER -DLINKER_SCRIPT -DCORE_$(MCU_CORE) -I$(OMV_COMMON_DIR) \ + -I$(OMV_BOARD_CONFIG_DIR) $(PORT_DIR)/$(LDSCRIPT).ld.S > $(BUILD)/$(LDSCRIPT).lds + $(CC) $(LDFLAGS) $(OBJS) -o $(FW_DIR)/$(FIRMWARE).elf + $(OBJCOPY) -Obinary $(FW_DIR)/$(FIRMWARE).elf $(FW_DIR)/$(FIRMWARE).bin + BIN_SIZE=$$(stat -c%s "$(FW_DIR)/$(FIRMWARE).bin"); \ + PADDED_SIZE=$$(( (BIN_SIZE + 15) / 16 * 16 )); \ + if [ $$BIN_SIZE -lt $$PADDED_SIZE ]; then \ + dd if=/dev/zero bs=1 count=$$((PADDED_SIZE - BIN_SIZE)) >> $(FW_DIR)/$(FIRMWARE).bin; \ + fi + +-include $(OBJS:%.o=%.d) diff --git a/src/boot/src/ports/alif/alif_services.c b/src/boot/src/ports/alif/alif_services.c new file mode 100644 index 000000000..f771c2588 --- /dev/null +++ b/src/boot/src/ports/alif/alif_services.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023-2025 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SE services helper functions. + */ +#include +#include + +#include ALIF_CMSIS_H +#include "alif_services.h" +#include "services_lib_bare_metal.h" + +// MHU indices. +#define MHU_M55_SE_MHU0 0 +#define MAX_MHU 1 + +extern void __fatal_error(const char *msg); + +static uint32_t se_handle; +static uint32_t se_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; +static mhu_driver_out_t mhu_driver_out; + +static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { + MHU_SESS_S_TX_BASE, +}; + +static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { + MHU_SESS_S_RX_BASE, +}; + +void MHU_SESS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_M55_SE_MHU0); +} + +void MHU_SESS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_M55_SE_MHU0); +} + +static int dummy_printf(const char *fmt, ...) { + (void) fmt; + return 0; +} + +void alif_services_init(void) { + // Initialize MHU. + mhu_driver_in_t mhu_driver_in = { + .sender_base_address_list = (uint32_t *) mhu_sender_base_address_list, + .receiver_base_address_list = (uint32_t *) mhu_receiver_base_address_list, + .mhu_count = MAX_MHU, + .send_msg_acked_callback = SERVICES_send_msg_acked_callback, + .rx_msg_callback = SERVICES_rx_msg_callback, + .debug_print = NULL, // not currently used by MHU_driver_initialize + }; + MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); + + // Initialize SE services. + services_lib_t services_init_params = { + .packet_buffer_address = (uint32_t) se_buffer, + .fn_send_mhu_message = mhu_driver_out.send_message, + .fn_wait_ms = NULL, // not currently used by services_host_handler.c + .wait_timeout = 1000000, + .fn_print_msg = dummy_printf, + }; + SERVICES_initialize(&services_init_params); + + // Create SE services channel for sending requests. + se_handle = SERVICES_register_channel(MHU_M55_SE_MHU0, 0); + + // Enable MHU RX IRQ. + NVIC_SetPriority(MHU_SESS_S_RX_IRQ_IRQn, 0); + NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_EnableIRQ(MHU_SESS_S_RX_IRQ_IRQn); + + // Enable MHU TX IRQ. + NVIC_SetPriority(MHU_SESS_S_TX_IRQ_IRQn, 0); + NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); + NVIC_EnableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + + // Send heartbeat services requests until one succeeds. + SERVICES_synchronize_with_se(se_handle); + + // Set default run profile + run_profile_t run_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + .aon_clk_src = CLK_SRC_LFXO, + .run_clk_src = CLK_SRC_PLL, + #if CORE_M55_HP + .cpu_clk_freq = CLOCK_FREQUENCY_400MHZ, + #else + .cpu_clk_freq = CLOCK_FREQUENCY_160MHZ, + #endif + .scaled_clk_freq = SCALED_FREQ_XO_HIGH_DIV_38_4_MHZ, + .power_domains = PD_VBAT_AON_MASK | PD_SSE700_AON_MASK | PD_SYST_MASK | + PD_DBSS_MASK | PD_SESS_MASK | PD_SRAMS_MASK | PD_SRAM_CTRL_AON_MASK, + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK, + .ip_clock_gating = OSPI_1_MASK | USB_MASK | LP_PERIPH_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + }; + + uint32_t error; + SERVICES_set_run_cfg(se_handle, &run_profile, &error); + if (error) { + __fatal_error("SERVICES_set_run_cfg"); + } +} + +void alif_services_deinit(void) { + // Disable MHU RX IRQ. + NVIC_DisableIRQ(MHU_SESS_S_RX_IRQ_IRQn); + NVIC_ClearPendingIRQ(MHU_SESS_S_RX_IRQ_IRQn); + + // Disable MHU TX IRQ. + NVIC_DisableIRQ(MHU_SESS_S_TX_IRQ_IRQn); + NVIC_ClearPendingIRQ(MHU_SESS_S_TX_IRQ_IRQn); +} + +void alif_services_get_unique_id(uint8_t *id, size_t len) { + uint32_t error; + SERVICES_version_data_t data = { 0 }; + SERVICES_system_get_device_data(se_handle, &data, &error); + if (error == 0) { + memcpy(id, data.MfgData, len); + } +} diff --git a/src/boot/src/ports/alif/alif_services.h b/src/boot/src/ports/alif/alif_services.h new file mode 100644 index 000000000..3bdffc326 --- /dev/null +++ b/src/boot/src/ports/alif/alif_services.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023-2025 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SE services helper functions. + */ +#ifndef __ALIF_PORT_SE_H__ +#define __ALIF_PORT_SE_H__ +void alif_services_init(void); +void alif_services_deinit(void); +void alif_services_get_unique_id(uint8_t *id, size_t len); +#endif // __ALIF_PORT_SE_H__ diff --git a/src/boot/src/ports/alif/tusb_config.h b/src/boot/src/ports/alif/tusb_config.h new file mode 100644 index 000000000..5b2c4f3cb --- /dev/null +++ b/src/boot/src/ports/alif/tusb_config.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023-2024 OpenMV, LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Any redistribution, use, or modification in source or binary form + * is done solely for personal benefit and not for any commercial + * purpose or for monetary gain. For commercial licensing options, + * please contact openmv@openmv.io + * + * THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT + * OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * TinyUSB config. + */ +#ifndef __ALIF_TUSB_CONFIG_H__ +#define __ALIF_TUSB_CONFIG_H__ +// Common configuration +#define CFG_TUSB_OS OPT_OS_NONE +#define CFG_TUSB_MCU OPT_MCU_NONE +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#define TUP_DCD_ENDPOINT_MAX (8) + +// Device stack configuration. +#define CFG_TUD_ENABLED (1) +#define CFG_TUD_DFU (1) +#define CFG_TUD_ENDPOINT0_SIZE (64) +#define CFG_TUD_DFU_XFER_BUFSIZE (4096) + +// Peripherals in expansion master 0 (such as USB, Ethernet, SD/MMC) +// are by default configured as non-secure, so they don't have access +// to DTCMs. These peripherals should place buffers in regular SRAM. +#define CFG_TUSB_MEM_SECTION __attribute__((section(".bss.sram0"))) +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(256))) +#endif //__ALIF_TUSB_CONFIG_H__