bootloader: Add Alif port.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
This commit is contained in:
iabdalkader 2025-02-01 13:46:10 +01:00
parent f5dd7de21a
commit a90be859a5
8 changed files with 1398 additions and 0 deletions

View File

@ -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"
}

View File

@ -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 <string.h>
#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

View File

@ -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 <stdint.h>
#include <string.h>
#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

View File

@ -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 <stdint.h>
#include <string.h>
#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);
}
}

View File

@ -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 := '<system_utils.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)

View File

@ -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 <stdio.h>
#include <string.h>
#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);
}
}

View File

@ -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__

View File

@ -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__