summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/alif/Makefile261
-rw-r--r--ports/alif/README.md21
-rw-r--r--ports/alif/alif_flash.c160
-rw-r--r--ports/alif/boards/manifest.py5
-rw-r--r--ports/alif/fatfs_port.c38
-rw-r--r--ports/alif/irq.h84
-rw-r--r--ports/alif/machine_pin.c298
-rw-r--r--ports/alif/main.c145
-rw-r--r--ports/alif/mcu/M55_HP_cfg.json14
-rw-r--r--ports/alif/mcu/ensemble.ld.S165
-rwxr-xr-xports/alif/mcu/make-pins.py64
-rw-r--r--ports/alif/mcu/pins_prefix.c2
-rw-r--r--ports/alif/modalif.c46
-rw-r--r--ports/alif/modalif.h33
-rw-r--r--ports/alif/modmachine.c79
-rw-r--r--ports/alif/modules/_boot.py23
-rw-r--r--ports/alif/mpconfigport.h155
-rw-r--r--ports/alif/mpconfigport.mk12
-rw-r--r--ports/alif/mphalport.c152
-rw-r--r--ports/alif/mphalport.h152
-rw-r--r--ports/alif/mpuart.c113
-rw-r--r--ports/alif/mpuart.h32
-rw-r--r--ports/alif/msc_disk.c151
-rw-r--r--ports/alif/ospi_flash.c265
-rw-r--r--ports/alif/ospi_flash.h52
-rw-r--r--ports/alif/ospi_xip_user.h5
-rw-r--r--ports/alif/pendsv.c49
-rw-r--r--ports/alif/pendsv.h48
-rw-r--r--ports/alif/qstrdefsport.h2
-rw-r--r--ports/alif/tinyusb_port/tusb_config.h73
-rw-r--r--ports/alif/usbd.c41
31 files changed, 2740 insertions, 0 deletions
diff --git a/ports/alif/Makefile b/ports/alif/Makefile
new file mode 100644
index 000000000..2863ebe2c
--- /dev/null
+++ b/ports/alif/Makefile
@@ -0,0 +1,261 @@
+################################################################################
+# Initial setup of Makefile environment
+
+BOARD ?= ALIF_ENSEMBLE
+BOARD_DIR ?= boards/$(BOARD)
+BUILD ?= build-$(BOARD)
+
+ifeq ($(wildcard $(BOARD_DIR)/.),)
+$(error Invalid BOARD specified: $(BOARD_DIR))
+endif
+
+include ../../py/mkenv.mk
+include mpconfigport.mk
+include $(BOARD_DIR)/mpconfigboard.mk
+
+# qstr definitions (must come before including py.mk)
+QSTR_DEFS += qstrdefsport.h
+
+# include py core make definitions
+include $(TOP)/py/py.mk
+include $(TOP)/extmod/extmod.mk
+
+################################################################################
+# Project specific settings and compiler/linker flags
+
+CROSS_COMPILE ?= arm-none-eabi-
+GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit
+PORT ?= /dev/ttyACM0
+
+ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit
+ALIF_DFP_REL_TOP ?= lib/alif_ensemble-cmsis-dfp
+ALIF_DFP_REL_HERE ?= $(TOP)/lib/alif_ensemble-cmsis-dfp
+CMSIS_DIR ?= $(TOP)/lib/cmsis/inc
+
+MCU_CORE ?= M55_HP
+ALIF_CONFIG ?= mcu/$(MCU_CORE)_cfg.json
+LD_FILE ?= mcu/ensemble.ld.S
+
+INC += -I.
+INC += -I$(TOP)
+INC += -I$(BUILD)
+INC += -I$(BOARD_DIR)
+INC += -I$(CMSIS_DIR)
+INC += -I$(ALIF_DFP_REL_HERE)/drivers/include/
+INC += -I$(ALIF_DFP_REL_HERE)/ospi_xip/source/ospi
+INC += -I$(ALIF_DFP_REL_HERE)/Device/common/config/
+INC += -I$(ALIF_DFP_REL_HERE)/Device/common/include/
+INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/config/
+INC += -I$(ALIF_DFP_REL_HERE)/Device/core/$(MCU_CORE)/include/
+INC += -I$(ALIF_DFP_REL_HERE)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/
+INC += -I$(TOP)/lib/tinyusb/src
+INC += -Itinyusb_port
+
+GEN_PIN_MKPINS = mcu/make-pins.py
+GEN_PIN_PREFIX = mcu/pins_prefix.c
+GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv
+GEN_PINS_SRC = $(BUILD)/pins_board.c
+GEN_PINS_HDR = $(HEADER_BUILD)/pins_board.h
+
+CFLAGS_FPU += -mfloat-abi=hard -mfpu=fpv5-d16
+CFLAGS_CORTEX_M55 += -mthumb -mcpu=cortex-m55 -mtune=cortex-m55 $(CFLAGS_FPU)
+
+CFLAGS += $(INC) -Wall -Werror -std=c99 $(CFLAGS_CORTEX_M55) -nostdlib
+CFLAGS += -Wdouble-promotion -Wfloat-conversion
+CFLAGS += -fdata-sections -ffunction-sections
+CFLAGS += -D$(MCU_CORE) -DCORE_$(MCU_CORE) -DALIF_CMSIS_H="\"$(MCU_CORE).h\""
+
+ifeq ($(MICROPY_FLOAT_IMPL),float)
+CFLAGS += -fsingle-precision-constant
+CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT
+else
+CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE
+endif
+
+AFLAGS = -mthumb -march=armv8.1-m.main $(CFLAGS_FPU)
+
+LDFLAGS += -nostdlib
+LDFLAGS += -T$(BUILD)/ensemble.ld -Map=$@.map --cref --gc-sections
+LDFLAGS += --wrap=dcd_event_handler
+
+# Tune for Debugging or Optimization
+ifeq ($(DEBUG), 1)
+CFLAGS += -Og -ggdb3
+# Disable text compression in debug builds
+MICROPY_ROM_TEXT_COMPRESSION = 0
+else
+CFLAGS += -O2 -DNDEBUG
+endif
+
+LIBS += "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)"
+
+JLINK_CMD = '\
+ExitOnError 1\n\
+Device $(JLINK_DEV)\n\
+SelectInterface SWD\n\
+Speed auto\n\
+Connect\n\
+Reset\n\
+ShowHWStatus\n\
+LoadFile "$(BUILD)/firmware_toc.bin",0x8057f1c0\n\
+LoadFile "$(BUILD)/firmware.bin",0x80000000\n\
+Reset\n\
+Exit'
+
+################################################################################
+# Source files and libraries
+
+SRC_O += \
+ shared/runtime/gchelper_thumb2.o
+
+SRC_C = \
+ alif_flash.c \
+ fatfs_port.c \
+ machine_pin.c \
+ main.c \
+ modalif.c \
+ mphalport.c \
+ mpuart.c \
+ msc_disk.c \
+ ospi_flash.c \
+ pendsv.c \
+ usbd.c \
+ $(wildcard $(BOARD_DIR)/*.c)
+
+ifeq ($(MICROPY_FLOAT_IMPL),float)
+LIBM_SRC_C += $(SRC_LIB_LIBM_C)
+LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C)
+$(BUILD)/lib/libm/%.o: CFLAGS += -Wno-maybe-uninitialized
+else
+LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C)
+LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C)
+$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-maybe-uninitialized
+endif
+
+SHARED_SRC_C += $(addprefix shared/,\
+ libc/string0.c \
+ netutils/dhcpserver.c \
+ netutils/netutils.c \
+ netutils/trace.c \
+ readline/readline.c \
+ runtime/gchelper_native.c \
+ runtime/interrupt_char.c \
+ runtime/mpirq.c \
+ runtime/pyexec.c \
+ runtime/softtimer.c \
+ runtime/stdout_helpers.c \
+ runtime/sys_stdio_mphal.c \
+ timeutils/timeutils.c \
+ tinyusb/mp_usbd.c \
+ tinyusb/mp_usbd_cdc.c \
+ tinyusb/mp_usbd_descriptor.c \
+ )
+
+DRIVERS_SRC_C += $(addprefix drivers/,\
+ bus/softspi.c \
+ bus/softqspi.c \
+ memory/spiflash.c \
+ dht/dht.c \
+ )
+
+TINYUSB_SRC_C += \
+ lib/tinyusb/src/tusb.c \
+ lib/tinyusb/src/class/cdc/cdc_device.c \
+ lib/tinyusb/src/class/msc/msc_device.c \
+ lib/tinyusb/src/common/tusb_fifo.c \
+ lib/tinyusb/src/device/usbd.c \
+ lib/tinyusb/src/device/usbd_control.c \
+ tinyusb_port/tusb_alif_dcd.c \
+
+ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\
+ Device/common/source/clk.c \
+ Device/common/source/mpu_M55.c \
+ Device/common/source/system_M55.c \
+ Device/common/source/system_utils.c \
+ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \
+ drivers/source/pinconf.c \
+ drivers/source/uart.c \
+ ospi_xip/source/ospi/ospi_drv.c \
+ )
+
+$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1
+
+# List of sources for qstr extraction
+SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) $(GEN_PINS_SRC)
+
+OBJ += $(PY_O)
+OBJ += $(addprefix $(BUILD)/, $(SRC_O))
+OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(ALIF_SRC_C:.c=.o))
+OBJ += $(GEN_PINS_SRC:.c=.o)
+
+################################################################################
+# Main targets
+
+.DELETE_ON_ERROR:
+
+.PHONY: all
+all: $(BUILD)/firmware_toc.bin
+
+$(BUILD)/ensemble.ld: $(LD_FILE)
+ $(ECHO) "Preprocess linker script $@"
+ $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@
+
+$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld
+ $(ECHO) "Link $@"
+ $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
+ $(Q)$(SIZE) $@
+
+$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
+ $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin
+
+$(BUILD)/firmware_toc.bin: $(BUILD)/firmware.bin
+ $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \
+ --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \
+ --output-dir $(BUILD) \
+ --firmware-dir $(BUILD) \
+ --output $@
+
+.PHONY: deploy
+deploy: $(BUILD)/firmware_toc.bin
+ $(ECHO) "Writing $< to the board"
+ $(Q)python $(ALIF_TOOLS)/app-write-mram.py \
+ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \
+ --port $(PORT) \
+ --pad \
+ --images file:$(BUILD)/application_package.ds
+
+.PHONY: deploy-jlink
+deploy-jlink: $(BUILD)/firmware_toc.bin
+ $(Q)echo -e $(JLINK_CMD) | $(JLINK_EXE)
+
+.PHONY: maintenance
+maintenance:
+ $(Q)python $(ALIF_TOOLS)/maintenance.py \
+ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \
+ --port $(PORT)
+
+.PHONY: update-system-package
+update-system-package:
+ $(Q)python $(ALIF_TOOLS)/updateSystemPackage.py \
+ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \
+ --port $(PORT)
+
+################################################################################
+# Remaining make rules
+
+# Use a pattern rule here so that make will only call make-pins.py once to make
+# both pins_board.c and pins_board.h
+$(BUILD)/%_board.c $(HEADER_BUILD)/%_board.h: $(BOARD_DIR)/%.csv $(GEN_PIN_MKPINS) $(GEN_PIN_PREFIX) | $(HEADER_BUILD)
+ $(ECHO) "GEN $@"
+ $(Q)$(PYTHON) $(GEN_PIN_MKPINS) \
+ --board-csv $(GEN_PINS_BOARD_CSV) \
+ --prefix $(GEN_PIN_PREFIX) \
+ --output-source $(GEN_PINS_SRC) \
+ --output-header $(GEN_PINS_HDR)
+
+include $(TOP)/py/mkrules.mk
diff --git a/ports/alif/README.md b/ports/alif/README.md
new file mode 100644
index 000000000..824a63da1
--- /dev/null
+++ b/ports/alif/README.md
@@ -0,0 +1,21 @@
+MicroPython port to Alif Ensemble MCUs
+======================================
+
+This is a port of MicroPython to the Alif Ensemble series of microcontrollers.
+
+Initial development of this Alif port was sponsored by OpenMV LLC.
+
+Features currently supported:
+- UART REPL.
+- TinyUSB with CDC and MSC device support.
+- Octal SPI flash with XIP mode.
+- machine.Pin support with named pins.
+- machine.UART, machine.SPI, machine.I2C, machine.RTC peripherals.
+- WiFi and Bluetooth using cyw43.
+- Dual core support of the HE and HP cores using Open-AMP.
+- Low power modes.
+
+The following more advanced features will follow later:
+- Ethernet support.
+- SDRAM support.
+- Other machine modules.
diff --git a/ports/alif/alif_flash.c b/ports/alif/alif_flash.c
new file mode 100644
index 000000000..e3b212580
--- /dev/null
+++ b/ports/alif/alif_flash.c
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mphal.h"
+#include "py/runtime.h"
+#include "extmod/vfs.h"
+#include "modalif.h"
+#include "ospi_flash.h"
+
+typedef struct _alif_flash_obj_t {
+ mp_obj_base_t base;
+ uint32_t flash_base_addr;
+ uint32_t flash_size;
+} alif_flash_obj_t;
+
+static alif_flash_obj_t alif_flash_obj = {
+ .base = { &alif_flash_type },
+ .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR,
+ .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES,
+};
+
+static mp_obj_t alif_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ // Parse arguments
+ enum { ARG_start, ARG_len };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) {
+ // Default singleton object that accesses entire flash
+ return MP_OBJ_FROM_PTR(&alif_flash_obj);
+ }
+
+ alif_flash_obj_t *self = mp_obj_malloc(alif_flash_obj_t, &alif_flash_type);
+
+ mp_int_t start = args[ARG_start].u_int;
+ if (start == -1) {
+ start = 0;
+ } else if (!(0 <= start && start < MICROPY_HW_FLASH_STORAGE_BYTES && start % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) {
+ mp_raise_ValueError(NULL);
+ }
+
+ mp_int_t len = args[ARG_len].u_int;
+ if (len == -1) {
+ len = MICROPY_HW_FLASH_STORAGE_BYTES - start;
+ } else if (!(0 < len && start + len <= MICROPY_HW_FLASH_STORAGE_BYTES && len % MICROPY_HW_FLASH_BLOCK_SIZE_BYTES == 0)) {
+ mp_raise_ValueError(NULL);
+ }
+
+ self->flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR + start;
+ self->flash_size = len;
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+static mp_obj_t alif_flash_readblocks(size_t n_args, const mp_obj_t *args) {
+ alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES;
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
+ if (n_args == 4) {
+ offset += mp_obj_get_int(args[3]);
+ }
+ int ret = ospi_flash_read(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf);
+ mp_event_handle_nowait();
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_readblocks_obj, 3, 4, alif_flash_readblocks);
+
+static mp_obj_t alif_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
+ alif_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ uint32_t offset = mp_obj_get_int(args[1]) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES;
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
+ if (n_args == 3) {
+ uint32_t addr = self->flash_base_addr + offset;
+ int32_t len = bufinfo.len;
+ while (len > 0) {
+ int ret = ospi_flash_erase_sector(addr);
+ mp_event_handle_nowait();
+ if (ret < 0) {
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ addr += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES;
+ len -= MICROPY_HW_FLASH_BLOCK_SIZE_BYTES;
+ }
+ } else {
+ offset += mp_obj_get_int(args[3]);
+ }
+ int ret = ospi_flash_write(self->flash_base_addr + offset, bufinfo.len, bufinfo.buf);
+ mp_event_handle_nowait();
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alif_flash_writeblocks_obj, 3, 4, alif_flash_writeblocks);
+
+static mp_obj_t alif_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) {
+ alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_int_t cmd = mp_obj_get_int(cmd_in);
+ switch (cmd) {
+ case MP_BLOCKDEV_IOCTL_INIT:
+ return MP_OBJ_NEW_SMALL_INT(0);
+ case MP_BLOCKDEV_IOCTL_DEINIT:
+ return MP_OBJ_NEW_SMALL_INT(0);
+ case MP_BLOCKDEV_IOCTL_SYNC:
+ return MP_OBJ_NEW_SMALL_INT(0);
+ case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
+ return MP_OBJ_NEW_SMALL_INT(self->flash_size / MICROPY_HW_FLASH_BLOCK_SIZE_BYTES);
+ case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
+ return MP_OBJ_NEW_SMALL_INT(MICROPY_HW_FLASH_BLOCK_SIZE_BYTES);
+ case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: {
+ uint32_t offset = mp_obj_get_int(arg_in) * MICROPY_HW_FLASH_BLOCK_SIZE_BYTES;
+ int ret = ospi_flash_erase_sector(self->flash_base_addr + offset);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ default:
+ return mp_const_none;
+ }
+}
+static MP_DEFINE_CONST_FUN_OBJ_3(alif_flash_ioctl_obj, alif_flash_ioctl);
+
+static const mp_rom_map_elem_t alif_flash_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&alif_flash_readblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&alif_flash_writeblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&alif_flash_ioctl_obj) },
+};
+static MP_DEFINE_CONST_DICT(alif_flash_locals_dict, alif_flash_locals_dict_table);
+
+MP_DEFINE_CONST_OBJ_TYPE(
+ alif_flash_type,
+ MP_QSTR_Flash,
+ MP_TYPE_FLAG_NONE,
+ make_new, alif_flash_make_new,
+ locals_dict, &alif_flash_locals_dict
+ );
diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py
new file mode 100644
index 000000000..148f1d6a6
--- /dev/null
+++ b/ports/alif/boards/manifest.py
@@ -0,0 +1,5 @@
+freeze("$(PORT_DIR)/modules")
+include("$(MPY_DIR)/extmod/asyncio")
+require("dht")
+require("neopixel")
+require("onewire")
diff --git a/ports/alif/fatfs_port.c b/ports/alif/fatfs_port.c
new file mode 100644
index 000000000..5883c9f3b
--- /dev/null
+++ b/ports/alif/fatfs_port.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "lib/oofatfs/ff.h"
+
+DWORD get_fattime(void) {
+ // TODO
+ int year = 2024;
+ int month = 1;
+ int day = 1;
+ int hour = 0;
+ int min = 0;
+ int sec = 0;
+ return ((year - 1980) << 25) | (month << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec / 2);
+}
diff --git a/ports/alif/irq.h b/ports/alif/irq.h
new file mode 100644
index 000000000..723f89f1d
--- /dev/null
+++ b/ports/alif/irq.h
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2023 Damien P. George
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_ALIF_IRQ_H
+#define MICROPY_INCLUDED_ALIF_IRQ_H
+
+#include <stdint.h>
+#include ALIF_CMSIS_H
+
+// IRQ priority definitions.
+//
+// The M55-HP CPU has __NVIC_PRIO_BITS==8 bits for setting the IRQ priority.
+// It uses NVIC_SetPriorityGrouping(0) which is 7 bits for preempt priority
+// and 1 bit for the sub-priority.
+//
+// Lower number implies higher interrupt priority.
+
+#define NVIC_PRIORITYGROUP_7 ((uint32_t)0x00000000U)
+#define IRQ_PRI_SYSTEM_TICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0)
+#define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0)
+#define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0)
+#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0)
+#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0)
+
+// these states correspond to values from query_irq, enable_irq and disable_irq
+#define IRQ_STATE_DISABLED (0x00000001)
+#define IRQ_STATE_ENABLED (0x00000000)
+
+static inline uint32_t query_irq(void) {
+ return __get_PRIMASK();
+}
+
+static inline void enable_irq(uint32_t state) {
+ __set_PRIMASK(state);
+}
+
+static inline uint32_t disable_irq(void) {
+ uint32_t state = __get_PRIMASK();
+ __disable_irq();
+ return state;
+}
+
+// irqs with a priority value greater or equal to "pri" will be disabled
+static inline uint32_t raise_irq_pri(uint32_t pri) {
+ uint32_t basepri = __get_BASEPRI();
+ // If non-zero, the processor does not process any exception with a
+ // priority value greater than or equal to BASEPRI.
+ // When writing to BASEPRI_MAX the write goes to BASEPRI only if either:
+ // - Rn is non-zero and the current BASEPRI value is 0
+ // - Rn is non-zero and less than the current BASEPRI value
+ pri <<= (8 - __NVIC_PRIO_BITS);
+ __ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory");
+ return basepri;
+}
+
+// "basepri" should be the value returned from raise_irq_pri
+static inline void restore_irq_pri(uint32_t basepri) {
+ __set_BASEPRI(basepri);
+}
+
+#endif // MICROPY_INCLUDED_ALIF_IRQ_H
diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c
new file mode 100644
index 000000000..02c5e482a
--- /dev/null
+++ b/ports/alif/machine_pin.c
@@ -0,0 +1,298 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/runtime.h"
+#include "py/mphal.h"
+#include "extmod/modmachine.h"
+#include "extmod/virtpin.h"
+#include "shared/runtime/mpirq.h"
+
+extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict;
+extern const mp_obj_dict_t machine_pin_board_pins_locals_dict;
+
+static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) {
+ const mp_map_t *named_map = &named_pins->map;
+ mp_map_elem_t *named_elem = mp_map_lookup((mp_map_t *)named_map, name, MP_MAP_LOOKUP);
+ if (named_elem != NULL && named_elem->value != NULL) {
+ return MP_OBJ_TO_PTR(named_elem->value);
+ }
+ return NULL;
+}
+
+const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) {
+ // Is already a object of the proper type
+ if (mp_obj_is_type(pin, &machine_pin_type)) {
+ return MP_OBJ_TO_PTR(pin);
+ }
+ if (mp_obj_is_str(pin)) {
+ // Try to find the pin in the board pins first.
+ const machine_pin_obj_t *self = machine_pin_find_named(&machine_pin_board_pins_locals_dict, pin);
+ if (self != NULL) {
+ return self;
+ }
+
+ // If not found, try to find the pin in the cpu pins.
+ self = machine_pin_find_named(&machine_pin_cpu_pins_locals_dict, pin);
+ if (self != NULL) {
+ return self;
+ }
+
+ // Pin name not found.
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unknown named pin \"%s\""), mp_obj_str_get_str(pin));
+ }
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
+}
+
+static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+ machine_pin_obj_t *self = self_in;
+
+ uint8_t alt_func, pad_ctrl;
+ pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl);
+
+ qstr mode_qst;
+ if (gpio_get_direction(self->gpio, self->pin) == GPIO_PIN_DIR_INPUT) {
+ mode_qst = MP_QSTR_IN;
+ } else {
+ if (pad_ctrl & PADCTRL_DRIVER_OPEN_DRAIN) {
+ mode_qst = MP_QSTR_OPEN_DRAIN;
+ } else {
+ mode_qst = MP_QSTR_OUT;
+ }
+ }
+ mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst);
+ uint8_t pad_ctrl_pull = pad_ctrl & (PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP);
+ if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_UP) {
+ mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
+ } else if (pad_ctrl_pull == PADCTRL_DRIVER_DISABLED_PULL_DOWN) {
+ mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN);
+ }
+ if (alt_func != PINMUX_ALTERNATE_FUNCTION_0) {
+ mp_printf(print, ", alt=%u", alt_func);
+ }
+ mp_printf(print, ")");
+}
+
+enum {
+ ARG_mode, ARG_pull, ARG_value, ARG_alt
+};
+static const mp_arg_t allowed_args[] = {
+ {MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
+ {MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
+ {MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
+ {MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}},
+};
+
+static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+
+ // parse args
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // get initial value of pin (only valid for OUT and OPEN_DRAIN modes)
+ int value = -1;
+ if (args[ARG_value].u_obj != mp_const_none) {
+ value = mp_obj_is_true(args[ARG_value].u_obj);
+ }
+
+ // configure mode
+ if (args[ARG_mode].u_obj != mp_const_none) {
+ mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj);
+ if (mode == MP_HAL_PIN_MODE_INPUT) {
+ mp_hal_pin_input(self);
+ } else if (mode == MP_HAL_PIN_MODE_OUTPUT) {
+ if (value != -1) {
+ // set initial output value before configuring mode
+ mp_hal_pin_write(self, value);
+ }
+ mp_hal_pin_output(self);
+ } else if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) {
+ if (value != -1) {
+ // set initial output value before configuring mode
+ mp_hal_pin_write(self, value);
+ }
+ mp_hal_pin_open_drain(self);
+ }
+ }
+
+ // Configure pull (unconditionally because None means no-pull).
+ uint32_t pull = 0;
+ if (args[ARG_pull].u_obj != mp_const_none) {
+ pull = mp_obj_get_int(args[ARG_pull].u_obj);
+ }
+ uint8_t alt_func;
+ uint8_t pad_ctrl;
+ pinconf_get(self->port, self->pin, &alt_func, &pad_ctrl);
+ alt_func = PINMUX_ALTERNATE_FUNCTION_0;
+ pad_ctrl |= PADCTRL_READ_ENABLE;
+ pad_ctrl &= ~(PADCTRL_DRIVER_DISABLED_PULL_DOWN | PADCTRL_DRIVER_DISABLED_PULL_UP);
+ if (pull & MP_HAL_PIN_PULL_UP) {
+ pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_UP;
+ }
+ if (pull & MP_HAL_PIN_PULL_DOWN) {
+ pad_ctrl |= PADCTRL_DRIVER_DISABLED_PULL_DOWN;
+ }
+ pinconf_set(self->port, self->pin, alt_func, pad_ctrl);
+
+ return mp_const_none;
+}
+
+// constructor(id, ...)
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
+
+ const machine_pin_obj_t *self = machine_pin_find(args[0]);
+
+ if (n_args > 1 || n_kw > 0) {
+ // pin mode given, so configure this GPIO
+ mp_map_t kw_args;
+ mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
+ machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
+ }
+ return MP_OBJ_FROM_PTR(self);
+}
+
+// fast method for getting/setting pin value
+static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 0, 1, false);
+ machine_pin_obj_t *self = self_in;
+ if (n_args == 0) {
+ // get pin
+ return MP_OBJ_NEW_SMALL_INT(mp_hal_pin_read(self));
+ } else {
+ // set pin
+ bool value = mp_obj_is_true(args[0]);
+ mp_hal_pin_write(self, value);
+ return mp_const_none;
+ }
+}
+
+// pin.init(mode, pull)
+static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+ return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init);
+
+// pin.value([value])
+static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
+ return machine_pin_call(args[0], n_args - 1, 0, args + 1);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
+
+// pin.low()
+static mp_obj_t machine_pin_low(mp_obj_t self_in) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_hal_pin_low(self);
+ return mp_const_none;
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
+
+// pin.high()
+static mp_obj_t machine_pin_high(mp_obj_t self_in) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_hal_pin_high(self);
+ return mp_const_none;
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
+
+// pin.toggle()
+static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ gpio_toggle_value(self->gpio, self->pin);
+ return mp_const_none;
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
+
+static MP_DEFINE_CONST_OBJ_TYPE(
+ pin_cpu_pins_obj_type,
+ MP_QSTR_cpu,
+ MP_TYPE_FLAG_NONE,
+ locals_dict, &machine_pin_cpu_pins_locals_dict
+ );
+
+static MP_DEFINE_CONST_OBJ_TYPE(
+ pin_board_pins_obj_type,
+ MP_QSTR_board,
+ MP_TYPE_FLAG_NONE,
+ locals_dict, &machine_pin_board_pins_locals_dict
+ );
+
+static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
+ // instance methods
+ { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) },
+ { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) },
+ { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) },
+ { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) },
+ { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) },
+ { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) },
+ { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
+
+ // class attributes
+ { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) },
+ { MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&pin_cpu_pins_obj_type) },
+
+ // class constants
+ { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(MP_HAL_PIN_MODE_INPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(MP_HAL_PIN_MODE_OUTPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) },
+ { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) },
+ { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) },
+};
+static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
+
+static mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+ (void)errcode;
+ machine_pin_obj_t *self = self_in;
+
+ switch (request) {
+ case MP_PIN_READ: {
+ return mp_hal_pin_read(self);
+ }
+ case MP_PIN_WRITE: {
+ mp_hal_pin_write(self, arg);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static const mp_pin_p_t pin_pin_p = {
+ .ioctl = pin_ioctl,
+};
+
+MP_DEFINE_CONST_OBJ_TYPE(
+ machine_pin_type,
+ MP_QSTR_Pin,
+ MP_TYPE_FLAG_NONE,
+ make_new, mp_pin_make_new,
+ print, machine_pin_print,
+ call, machine_pin_call,
+ protocol, &pin_pin_p,
+ locals_dict, &machine_pin_locals_dict
+ );
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
+ return machine_pin_find(obj);
+}
diff --git a/ports/alif/main.c b/ports/alif/main.c
new file mode 100644
index 000000000..0eb43a382
--- /dev/null
+++ b/ports/alif/main.c
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/compile.h"
+#include "py/runtime.h"
+#include "py/gc.h"
+#include "py/mperrno.h"
+#include "py/mphal.h"
+#include "py/stackctrl.h"
+#include "shared/readline/readline.h"
+#include "shared/runtime/gchelper.h"
+#include "shared/runtime/pyexec.h"
+#include "shared/tinyusb/mp_usbd.h"
+#include "tusb.h"
+#include "mpuart.h"
+#include "ospi_flash.h"
+#include "pendsv.h"
+
+extern uint8_t __StackTop, __StackLimit;
+extern uint8_t __GcHeapStart, __GcHeapEnd;
+
+NORETURN void panic(const char *msg) {
+ mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
+ mp_hal_stdout_tx_strn(msg, strlen(msg));
+ for (;;) {
+ *(volatile uint32_t *)0x4900C000 ^= 9;
+ for (volatile uint delay = 0; delay < 10000000; delay++) {
+ }
+ }
+}
+
+void _start(void) {
+ SysTick_Config(SystemCoreClock / 1000);
+
+ MICROPY_BOARD_STARTUP();
+
+ pendsv_init();
+
+ MICROPY_BOARD_EARLY_INIT();
+
+ #if MICROPY_HW_ENABLE_UART_REPL
+ mp_uart_init();
+ #endif
+
+ if (ospi_flash_init() != 0) {
+ MICROPY_BOARD_FATAL_ERROR("ospi_init failed");
+ }
+
+ #if MICROPY_HW_ENABLE_USBDEV
+ NVIC_ClearPendingIRQ(USB_IRQ_IRQn);
+ NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB);
+ #endif
+
+ // Initialise stack extents and GC heap.
+ mp_stack_set_top(&__StackTop);
+ mp_stack_set_limit(&__StackTop - &__StackLimit - 1024);
+ gc_init(&__GcHeapStart, &__GcHeapEnd);
+
+ for (;;) {
+ // Initialise MicroPython runtime.
+ mp_init();
+
+ // Initialise sub-systems.
+ readline_init0();
+
+ // Execute _boot.py to set up the filesystem.
+ pyexec_frozen_module("_boot.py", false);
+
+ // Execute user scripts.
+ int ret = pyexec_file_if_exists("boot.py");
+ #if MICROPY_HW_ENABLE_USBDEV
+ mp_usbd_init();
+ #endif
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) {
+ ret = pyexec_file_if_exists("main.py");
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ }
+
+ for (;;) {
+ if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+ if (pyexec_raw_repl() != 0) {
+ break;
+ }
+ } else {
+ if (pyexec_friendly_repl() != 0) {
+ break;
+ }
+ }
+ }
+
+ soft_reset_exit:
+ mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
+ gc_sweep_all();
+ mp_deinit();
+ }
+}
+
+void gc_collect(void) {
+ gc_collect_start();
+ gc_helper_collect_regs_and_stack();
+ gc_collect_end();
+}
+
+void nlr_jump_fail(void *val) {
+ mp_printf(&mp_plat_print, "FATAL: uncaught exception %p\n", val);
+ mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val));
+ for (;;) {
+ __WFE();
+ }
+}
+
+#ifndef NDEBUG
+void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
+ printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
+ panic("Assertion failed");
+}
+#endif
diff --git a/ports/alif/mcu/M55_HP_cfg.json b/ports/alif/mcu/M55_HP_cfg.json
new file mode 100644
index 000000000..228ddbf6c
--- /dev/null
+++ b/ports/alif/mcu/M55_HP_cfg.json
@@ -0,0 +1,14 @@
+{
+ "USER_APP": {
+ "binary": "firmware.bin",
+ "mramAddress": "0x80000000",
+ "version": "1.0.0",
+ "cpu_id": "M55_HP",
+ "flags": ["boot"],
+ "signed": false
+ },
+ "DEVICE": {
+ "binary": "app-device-config.json",
+ "version" : "0.5.00"
+ }
+}
diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S
new file mode 100644
index 000000000..198641ba3
--- /dev/null
+++ b/ports/alif/mcu/ensemble.ld.S
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 OpenMV LLC.
+ *
+ * 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.
+ */
+
+// Entry Point
+ENTRY(Reset_Handler)
+
+MEMORY
+{
+ ROM (rx) : ORIGIN = 0x80000000, LENGTH = 0x0057F000
+ ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
+ #ifdef CORE_M55_HP
+ DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00100000
+ #else
+ DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000
+ #endif
+ SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 0x00400000
+ SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 0x00280000
+}
+
+__STACK_SIZE = 0x00004000;
+__HEAP_SIZE = 0x00004000;
+__MP_HEAP_SIZE = 0x00040000;
+
+SECTIONS
+{
+ .text : ALIGN(16)
+ {
+ KEEP(*(.vectors))
+ . = ALIGN(4);
+ *(.text*)
+ . = ALIGN(4);
+ *(.rodata*)
+ . = ALIGN(16);
+ } > ROM
+
+ .copy.table : ALIGN(4)
+ {
+ __copy_table_start__ = .;
+ LONG ( LOADADDR(.data) )
+ LONG ( ADDR(.data) )
+ LONG ( SIZEOF(.data)/4 )
+ __copy_table_end__ = .;
+ . = ALIGN(16);
+ } > ROM
+
+ .zero.table : ALIGN(4)
+ {
+ __zero_table_start__ = .;
+ LONG (ADDR(.bss))
+ LONG (SIZEOF(.bss)/4)
+ LONG (ADDR(.bss.sram0))
+ LONG (SIZEOF(.bss.sram0)/4)
+ __zero_table_end__ = .;
+ . = ALIGN(16);
+ } > ROM
+
+ .data : ALIGN(8)
+ {
+ *(.data)
+ . = ALIGN(8);
+ *(.data.*)
+ . = ALIGN(16);
+ } > DTCM AT > ROM
+
+ /* Peripherals in expansion master 0 (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.sram0 (NOLOAD) : ALIGN(4)
+ {
+ * (.bss.sram0*)
+ } > SRAM0
+
+ .bss : ALIGN(4)
+ {
+ __bss_start__ = .;
+ *(.bss)
+ . = ALIGN(4);
+ *(.bss.*)
+ . = ALIGN(4);
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } > DTCM
+
+ .heap (NOLOAD) : ALIGN(4)
+ {
+ __end__ = .;
+ PROVIDE(end = .);
+ . = . + __HEAP_SIZE;
+ . = ALIGN(4);
+ __HeapLimit = .;
+
+ /* MicroPython GC heap */
+ . = ALIGN(16);
+ __GcHeapStart = .;
+ . = . + __MP_HEAP_SIZE;
+ __GcHeapEnd = .;
+ } > DTCM
+
+ .stack (NOLOAD) : ALIGN(4)
+ {
+ __StackLimit = .;
+ . = . + __STACK_SIZE;
+ . = ALIGN(4);
+ __StackTop = .;
+ } > DTCM
+ PROVIDE(__stack = __StackTop);
+
+ .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);
+ } > ROM
+
+ /* Check if data + heap + stack exceeds RAM limit */
+ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
+}
diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py
new file mode 100755
index 000000000..b49db4cfe
--- /dev/null
+++ b/ports/alif/mcu/make-pins.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../../tools"))
+import boardgen
+
+NUM_PORTS = 16
+NUM_PINS_PER_PORT = 8
+
+
+class AlifPin(boardgen.Pin):
+ # Emit the struct which contains the pin instance.
+ def definition(self):
+ port, pin = self.name()[1:].split("_")
+ base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port)
+ return (
+ "{{ "
+ ".base = {{ .type = &machine_pin_type }}, "
+ ".gpio = (GPIO_Type *){base}, "
+ ".port = PORT_{port}, "
+ ".pin = PIN_{pin}, "
+ ".name = MP_QSTR_P{port}_{pin} "
+ "}}".format(port=port, pin=pin, base=base)
+ )
+
+ # Alif cpu names must be "Pn_m".
+ @staticmethod
+ def validate_cpu_pin_name(cpu_pin_name):
+ boardgen.Pin.validate_cpu_pin_name(cpu_pin_name)
+
+ if not (m := re.match("P([0-9]){1,2}_([0-9])", cpu_pin_name)):
+ raise boardgen.PinGeneratorError(
+ "Invalid cpu pin name '{}', must be 'Pn_m'".format(cpu_pin_name)
+ )
+
+ port = int(m.group(1))
+ pin = int(m.group(2))
+ if not (0 <= port < NUM_PORTS and 0 <= pin < NUM_PINS_PER_PORT):
+ raise boardgen.PinGeneratorError("Unknown cpu pin '{}'".format(cpu_pin_name))
+
+
+class AlifPinGenerator(boardgen.PinGenerator):
+ def __init__(self):
+ # Use custom pin type above.
+ super().__init__(pin_type=AlifPin)
+
+ # Pre-define the pins (i.e. don't require them to be listed in pins.csv).
+ for i in range(NUM_PORTS):
+ for j in range(NUM_PINS_PER_PORT):
+ self.add_cpu_pin("P{}_{}".format(i, j))
+
+ # Only use pre-defined cpu pins (do not let board.csv create them).
+ def find_pin_by_cpu_pin_name(self, cpu_pin_name, create=True):
+ return super().find_pin_by_cpu_pin_name(cpu_pin_name, create=False)
+
+ def cpu_table_size(self):
+ return "{} * {}".format(NUM_PORTS, NUM_PINS_PER_PORT)
+
+
+if __name__ == "__main__":
+ AlifPinGenerator().main()
diff --git a/ports/alif/mcu/pins_prefix.c b/ports/alif/mcu/pins_prefix.c
new file mode 100644
index 000000000..99fef5939
--- /dev/null
+++ b/ports/alif/mcu/pins_prefix.c
@@ -0,0 +1,2 @@
+#include "py/mphal.h"
+#include "extmod/modmachine.h"
diff --git a/ports/alif/modalif.c b/ports/alif/modalif.c
new file mode 100644
index 000000000..69b7e6c5c
--- /dev/null
+++ b/ports/alif/modalif.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mphal.h"
+#include "py/runtime.h"
+#include "modalif.h"
+
+static const mp_rom_map_elem_t alif_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) },
+ { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) },
+ #if MICROPY_HW_USB_MSC
+ // Attribute to indicate USB MSC is enabled.
+ { MP_ROM_QSTR(MP_QSTR_usb_msc), MP_ROM_TRUE },
+ #endif
+};
+static MP_DEFINE_CONST_DICT(alif_module_globals, alif_module_globals_table);
+
+const mp_obj_module_t mp_module_alif = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t *)&alif_module_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_alif, mp_module_alif);
diff --git a/ports/alif/modalif.h b/ports/alif/modalif.h
new file mode 100644
index 000000000..5812d6ba5
--- /dev/null
+++ b/ports/alif/modalif.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_ALIF_MODALIF_H
+#define MICROPY_INCLUDED_ALIF_MODALIF_H
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t alif_flash_type;
+
+#endif // MICROPY_INCLUDED_ALIF_MODALIF_H
diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c
new file mode 100644
index 000000000..c4102225d
--- /dev/null
+++ b/ports/alif/modmachine.c
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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.
+ */
+
+// This file is never compiled standalone, it's included directly from
+// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.
+
+#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \
+ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \
+
+static void mp_machine_idle(void) {
+ mp_event_wait_indefinite();
+}
+
+static mp_obj_t mp_machine_unique_id(void) {
+ return mp_obj_new_bytes((const uint8_t *)"ABCD", 4);
+}
+
+NORETURN static void mp_machine_reset(void) {
+ NVIC_SystemReset();
+}
+
+NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+ __disable_irq();
+
+ MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
+
+ while (1) {
+ ;
+ }
+}
+
+static mp_int_t mp_machine_reset_cause(void) {
+ // TODO
+ return 0;
+}
+
+static mp_obj_t mp_machine_get_freq(void) {
+ return MP_OBJ_NEW_SMALL_INT(SystemCoreClock);
+}
+
+static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
+ mp_raise_NotImplementedError(NULL);
+}
+
+static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
+ mp_int_t delay = -1;
+ if (n_args == 1) {
+ delay = mp_obj_get_int(args[0]);
+ }
+ mp_hal_delay_ms(delay);
+}
+
+NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+ mp_machine_lightsleep(n_args, args);
+ mp_machine_reset();
+}
diff --git a/ports/alif/modules/_boot.py b/ports/alif/modules/_boot.py
new file mode 100644
index 000000000..36044b461
--- /dev/null
+++ b/ports/alif/modules/_boot.py
@@ -0,0 +1,23 @@
+import sys, os, alif
+
+
+bdev = alif.Flash()
+if hasattr(alif, "usb_msc"):
+ try:
+ # This may fail on VfsFat construction, or mount.
+ os.mount(os.VfsFat(bdev), "/flash")
+ except:
+ os.VfsFat.mkfs(bdev)
+ os.mount(os.VfsFat(bdev), "/flash")
+else:
+ try:
+ os.mount(os.VfsLfs2(bdev, progsize=256), "/flash")
+ except:
+ os.VfsLfs2.mkfs(bdev, progsize=256)
+ os.mount(os.VfsLfs2(bdev, progsize=256), "/flash")
+
+sys.path.append("/flash")
+sys.path.append("/flash/lib")
+os.chdir("/flash")
+
+del sys, os, alif, bdev
diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h
new file mode 100644
index 000000000..2d8e3b2a0
--- /dev/null
+++ b/ports/alif/mpconfigport.h
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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.
+ */
+
+// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h
+
+#include <stdint.h>
+#include <alloca.h> // for alloca()
+
+// board specific definitions
+#include "mpconfigboard.h"
+
+#ifndef MICROPY_CONFIG_ROM_LEVEL
+#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
+#endif
+
+#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB
+#define MICROPY_HW_ENABLE_USBDEV (1)
+
+#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING
+#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode"
+#endif
+#define MICROPY_HW_USB_CDC (1)
+#define MICROPY_HW_USB_CDC_TX_TIMEOUT (500)
+#ifndef MICROPY_HW_USB_MSC
+#define MICROPY_HW_USB_MSC (0)
+#endif
+#ifndef MICROPY_HW_USB_VID
+#define MICROPY_HW_USB_VID (0xf055)
+#endif
+#ifndef MICROPY_HW_USB_PID
+#define MICROPY_HW_USB_PID (0x9802) // interface has CDC only
+#endif
+
+#define MICROPY_HW_FLASH_BLOCK_SIZE_BYTES (4096)
+
+// Memory allocation policies
+#ifndef MICROPY_ALLOC_GC_STACK_SIZE
+#define MICROPY_ALLOC_GC_STACK_SIZE (128)
+#endif
+#define MICROPY_ALLOC_PATH_MAX (128)
+#define MICROPY_QSTR_BYTES_IN_HASH (1)
+
+// MicroPython emitters
+#define MICROPY_PERSISTENT_CODE_LOAD (1)
+#define MICROPY_EMIT_THUMB (1)
+#define MICROPY_EMIT_THUMB_ARMV7M (1)
+#define MICROPY_EMIT_INLINE_THUMB (1)
+#define MICROPY_EMIT_INLINE_THUMB_FLOAT (1)
+
+// Optimisations
+#define MICROPY_OPT_COMPUTED_GOTO (1)
+
+// Python internal features
+#define MICROPY_READER_VFS (1)
+#define MICROPY_ENABLE_GC (1)
+#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
+#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
+#define MICROPY_SCHEDULER_DEPTH (8)
+#define MICROPY_SCHEDULER_STATIC_NODES (1)
+#define MICROPY_USE_INTERNAL_ERRNO (1)
+
+// Fine control over Python builtins, classes, modules, etc
+#define MICROPY_PY_SYS_PLATFORM "alif"
+
+// Extended modules
+#define MICROPY_EPOCH_IS_1970 (1)
+#define MICROPY_PY_OS_DUPTERM (1)
+#define MICROPY_PY_OS_SEP (1)
+#define MICROPY_PY_OS_SYNC (1)
+#define MICROPY_PY_OS_UNAME (1)
+#define MICROPY_PY_TIME (1)
+#define MICROPY_PY_MACHINE (1)
+#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/alif/modmachine.c"
+#define MICROPY_PY_MACHINE_RESET (1)
+#define MICROPY_PY_MACHINE_BOOTLOADER (1)
+#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
+#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
+#define MICROPY_PY_MACHINE_DHT_READINTO (1)
+#define MICROPY_PY_MACHINE_PULSE (1)
+#define MICROPY_PY_MACHINE_SOFTI2C (1)
+#define MICROPY_PY_MACHINE_SOFTSPI (1)
+#define MICROPY_PY_MACHINE_TIMER (1)
+#define MICROPY_VFS (1)
+
+// fatfs configuration
+#define MICROPY_FATFS_ENABLE_LFN (1)
+#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
+#define MICROPY_FATFS_RPATH (2)
+#if MICROPY_HW_USB_MSC
+#define MICROPY_FATFS_USE_LABEL (1)
+#define MICROPY_FATFS_MULTI_PARTITION (1)
+// Set FatFS block size to flash sector size to avoid caching
+// the flash sector in memory to support smaller block sizes.
+#define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES)
+#endif
+
+#define MP_STATE_PORT MP_STATE_VM
+
+// Miscellaneous settings
+
+// We need an implementation of the log2 function which is not a macro
+#define MP_NEED_LOG2 (1)
+
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
+
+#define MP_SSIZE_MAX (0x7fffffff)
+
+// Assume that if we already defined the obj repr then we also defined these items
+#ifndef MICROPY_OBJ_REPR
+typedef intptr_t mp_int_t; // must be pointer size
+typedef uintptr_t mp_uint_t; // must be pointer size
+typedef intptr_t mp_off_t;
+#endif
+
+// Board configuration settings.
+
+#ifndef MICROPY_BOARD_STARTUP
+#define MICROPY_BOARD_STARTUP()
+#endif
+
+#ifndef MICROPY_BOARD_EARLY_INIT
+#define MICROPY_BOARD_EARLY_INIT()
+#endif
+
+#ifndef MICROPY_BOARD_FATAL_ERROR
+extern void panic(const char *);
+#define MICROPY_BOARD_FATAL_ERROR panic
+#endif
+
+#ifndef MICROPY_BOARD_ENTER_BOOTLOADER
+#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args)
+#endif
diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk
new file mode 100644
index 000000000..3d04450a6
--- /dev/null
+++ b/ports/alif/mpconfigport.mk
@@ -0,0 +1,12 @@
+# Enable/disable extra modules and features
+
+# MicroPython feature configurations
+MICROPY_ROM_TEXT_COMPRESSION ?= 1
+MICROPY_FLOAT_IMPL ?= double
+
+# VFS support.
+MICROPY_VFS_FAT ?= 1
+MICROPY_VFS_LFS2 ?= 1
+
+# File containing description of content to be frozen into firmware.
+FROZEN_MANIFEST ?= boards/manifest.py
diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c
new file mode 100644
index 000000000..b433aa5e1
--- /dev/null
+++ b/ports/alif/mphalport.c
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Damien P. George
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mphal.h"
+#include "py/ringbuf.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "extmod/misc.h"
+#include "shared/runtime/interrupt_char.h"
+#include "shared/timeutils/timeutils.h"
+#include "shared/tinyusb/mp_usbd.h"
+#include "shared/tinyusb/mp_usbd_cdc.h"
+#include "tusb.h"
+#include "mpuart.h"
+
+#ifndef MICROPY_HW_STDIN_BUFFER_LEN
+#define MICROPY_HW_STDIN_BUFFER_LEN 512
+#endif
+
+static uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN];
+ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) };
+
+uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
+ uintptr_t ret = 0;
+ #if MICROPY_HW_USB_CDC
+ ret |= mp_usbd_cdc_poll_interfaces(poll_flags);
+ #endif
+ #if MICROPY_HW_ENABLE_UART_REPL
+ if (poll_flags & MP_STREAM_POLL_WR) {
+ ret |= MP_STREAM_POLL_WR;
+ }
+ #endif
+ #if MICROPY_PY_OS_DUPTERM
+ ret |= mp_os_dupterm_poll(poll_flags);
+ #endif
+ return ret;
+}
+
+// Receive single character
+int mp_hal_stdin_rx_chr(void) {
+ for (;;) {
+ #if MICROPY_HW_USB_CDC
+ mp_usbd_cdc_poll_interfaces(0);
+ #endif
+ int c = ringbuf_get(&stdin_ringbuf);
+ if (c != -1) {
+ return c;
+ }
+ #if MICROPY_PY_OS_DUPTERM
+ int dupterm_c = mp_os_dupterm_rx_chr();
+ if (dupterm_c >= 0) {
+ return dupterm_c;
+ }
+ #endif
+ mp_event_wait_indefinite();
+ }
+}
+
+// Send string of given length
+mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
+ mp_uint_t ret = len;
+ bool did_write = false;
+
+ #if MICROPY_HW_ENABLE_UART_REPL
+ mp_uart_write_strn(str, len);
+ did_write = true;
+ #endif
+
+ #if MICROPY_HW_USB_CDC
+ mp_uint_t cdc_res = mp_usbd_cdc_tx_strn(str, len);
+ if (cdc_res > 0) {
+ did_write = true;
+ ret = MIN(cdc_res, ret);
+ }
+ #endif
+
+ #if MICROPY_PY_OS_DUPTERM
+ int dupterm_res = mp_os_dupterm_tx_strn(str, len);
+ if (dupterm_res >= 0) {
+ did_write = true;
+ ret = MIN((mp_uint_t)dupterm_res, ret);
+ }
+ #endif
+
+ return did_write ? ret : 0;
+}
+
+static uint32_t volatile ticks_ms;
+
+void SysTick_Handler(void) {
+ ++ticks_ms;
+}
+
+mp_uint_t mp_hal_ticks_cpu(void) {
+ if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) {
+ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
+ #if defined(__CORTEX_M) && __CORTEX_M == 7
+ // on Cortex-M7 we must unlock the DWT before writing to its registers
+ DWT->LAR = 0xc5acce55;
+ #endif
+ DWT->CYCCNT = 0;
+ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
+ }
+ return DWT->CYCCNT;
+}
+
+mp_uint_t mp_hal_ticks_us(void) {
+ return ticks_ms / 1000;
+}
+
+mp_uint_t mp_hal_ticks_ms(void) {
+ return ticks_ms;
+}
+
+void mp_hal_delay_us(mp_uint_t us) {
+ mp_hal_delay_ms(us / 1000);
+}
+
+void mp_hal_delay_ms(mp_uint_t ms) {
+ uint32_t t0 = mp_hal_ticks_ms();
+ while ((mp_hal_ticks_ms() - t0) < ms) {
+ __WFI();
+ }
+}
+
+uint64_t mp_hal_time_ns(void) {
+ return 0;
+}
diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h
new file mode 100644
index 000000000..1b8c854fd
--- /dev/null
+++ b/ports/alif/mphalport.h
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/ringbuf.h"
+#include "shared/runtime/interrupt_char.h"
+#include "irq.h"
+#include ALIF_CMSIS_H
+
+#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
+#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
+
+// For regular code that wants to prevent "background tasks" from running.
+// These background tasks (LWIP, Bluetooth) run in PENDSV context.
+#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV);
+#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV);
+#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state);
+
+// Port level Wait-for-Event macro
+//
+// Do not use this macro directly, include py/runtime.h and
+// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout)
+#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \
+ do { \
+ if ((TIMEOUT_MS) < 0) { \
+ __WFE(); \
+ } else { \
+ /* TODO */ \
+ __WFE(); \
+ } \
+ } while (0)
+
+// TODO requires mods to py/emitglue.c for this to be picked up
+#define MP_HAL_CLEAN_DCACHE(addr, size) \
+ (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \
+ ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f)))
+
+extern ringbuf_t stdin_ringbuf;
+
+// TODO
+#define mp_hal_quiet_timing_enter() 0
+#define mp_hal_quiet_timing_exit(x) (void)x
+#define mp_hal_delay_us_fast mp_hal_delay_us
+
+/******************************************************************************/
+// C-level pin HAL
+
+#include "py/obj.h"
+#include "gpio.h"
+#include "pinconf.h"
+
+#define MP_HAL_PIN_FMT "%q"
+#define MP_HAL_PIN_MODE_INPUT (0)
+#define MP_HAL_PIN_MODE_OUTPUT (1)
+#define MP_HAL_PIN_MODE_OPEN_DRAIN (2)
+#define MP_HAL_PIN_PULL_NONE (0)
+#define MP_HAL_PIN_PULL_UP (1)
+#define MP_HAL_PIN_PULL_DOWN (2)
+
+#define mp_hal_pin_obj_t const machine_pin_obj_t *
+
+typedef struct _machine_pin_obj_t {
+ mp_obj_base_t base;
+ GPIO_Type *gpio;
+ uint8_t port;
+ uint8_t pin;
+ qstr name;
+} machine_pin_obj_t;
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in);
+
+static inline qstr mp_hal_pin_name(mp_hal_pin_obj_t pin) {
+ return pin->name;
+}
+
+static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) {
+ uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0;
+ uint8_t pad_ctrl = PADCTRL_READ_ENABLE;
+ pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl);
+ gpio_set_direction_input(pin->gpio, pin->pin);
+}
+
+static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) {
+ uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0;
+ uint8_t pad_ctrl = PADCTRL_READ_ENABLE;
+ pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl);
+ gpio_set_direction_output(pin->gpio, pin->pin);
+}
+
+static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) {
+ uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0;
+ uint8_t pad_ctrl = PADCTRL_DRIVER_OPEN_DRAIN | PADCTRL_READ_ENABLE;
+ pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl);
+ gpio_set_direction_output(pin->gpio, pin->pin);
+}
+
+static inline void mp_hal_pin_low(mp_hal_pin_obj_t pin) {
+ gpio_set_value_low(pin->gpio, pin->pin);
+}
+
+static inline void mp_hal_pin_high(mp_hal_pin_obj_t pin) {
+ gpio_set_value_high(pin->gpio, pin->pin);
+}
+
+static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) {
+ return gpio_get_value(pin->gpio, pin->pin);
+}
+
+static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) {
+ if (v) {
+ mp_hal_pin_high(pin);
+ } else {
+ mp_hal_pin_low(pin);
+ }
+}
+
+static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) {
+ mp_hal_pin_low(pin);
+}
+
+static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) {
+ mp_hal_pin_high(pin);
+}
+
+static inline void mp_hal_wake_main_task_from_isr(void) {
+ // Defined for tinyusb support, nothing needs to be done here.
+}
+
+// Include all the pin definitions.
+#include "genhdr/pins_board.h"
diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c
new file mode 100644
index 000000000..e9cfcf34f
--- /dev/null
+++ b/ports/alif/mpuart.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 <string.h>
+#include "py/runtime.h"
+#include "py/mphal.h"
+#include "mpuart.h"
+
+#if MICROPY_HW_ENABLE_UART_REPL
+
+#include "pinconf.h"
+#include "sys_ctrl_uart.h"
+#include "uart.h"
+
+#define TX_PORT PORT_12
+#define TX_PIN PIN_2
+#define RX_PORT PORT_12
+#define RX_PIN PIN_1
+#define UART_ID 4
+#define UART_IRQN UART4_IRQ_IRQn
+#define UART_PTR ((UART_Type *)UART4_BASE)
+#define BAUDRATE 115200
+#define SYST_PCLK 100000000
+
+static UART_TRANSFER transfer;
+
+void mp_uart_init(void) {
+ pinconf_set(TX_PORT, TX_PIN, PINMUX_ALTERNATE_FUNCTION_2, 0);
+ pinconf_set(RX_PORT, RX_PIN, PINMUX_ALTERNATE_FUNCTION_2, PADCTRL_READ_ENABLE);
+ select_uart_clock_syst_pclk(UART_ID);
+ enable_uart_clock(UART_ID);
+ uart_software_reset(UART_PTR);
+ uart_enable_fifo(UART_PTR);
+ uart_disable_tx_irq(UART_PTR);
+ uart_disable_rx_irq(UART_PTR);
+ uart_set_baudrate(UART_PTR, SYST_PCLK, BAUDRATE);
+ uart_set_data_parity_stop_bits(UART_PTR, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1);
+ uart_set_flow_control(UART_PTR, UART_FLOW_CONTROL_NONE);
+ NVIC_ClearPendingIRQ(UART_IRQN);
+ NVIC_SetPriority(UART_IRQN, IRQ_PRI_UART_REPL);
+ NVIC_EnableIRQ(UART_IRQN);
+ uart_set_tx_trigger(UART_PTR, UART_TX_FIFO_EMPTY);
+ uart_set_rx_trigger(UART_PTR, UART_RX_ONE_CHAR_IN_FIFO);
+ uart_enable_rx_irq(UART_PTR);
+}
+
+void mp_uart_write_strn(const char *str, size_t len) {
+ memset(&transfer, 0, sizeof(transfer));
+ transfer.tx_buf = (uint8_t *)str;
+ transfer.tx_total_num = len;
+ transfer.tx_curr_cnt = 0U;
+ transfer.status = UART_TRANSFER_STATUS_NONE;
+
+ uart_enable_tx_irq(UART_PTR);
+
+ uint32_t start = mp_hal_ticks_ms();
+ while (transfer.status == UART_TRANSFER_STATUS_NONE) {
+ if (mp_hal_ticks_ms() - start > 10 * len) {
+ break;
+ }
+ __WFE();
+ }
+ uart_disable_tx_irq(UART_PTR);
+}
+
+void UART4_IRQHandler(void) {
+ if (UART_PTR->UART_RFL) {
+ for (;;) {
+ uint32_t rx_fifo_available_cnt = UART_PTR->UART_RFL;
+ if (rx_fifo_available_cnt == 0) {
+ break;
+ }
+ for (uint32_t i = 0; i < rx_fifo_available_cnt; ++i) {
+ int c = UART_PTR->UART_RBR;
+ #if MICROPY_KBD_EXCEPTION
+ if (c == mp_interrupt_char) {
+ mp_sched_keyboard_interrupt();
+ continue;
+ }
+ #endif
+ ringbuf_put(&stdin_ringbuf, c);
+ }
+ }
+ } else {
+ uart_irq_handler(UART_PTR, &transfer);
+ }
+ __SEV();
+}
+
+#endif
diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h
new file mode 100644
index 000000000..0c2e510a9
--- /dev/null
+++ b/ports/alif/mpuart.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_ALIF2_UART_H
+#define MICROPY_INCLUDED_ALIF2_UART_H
+
+void mp_uart_init(void);
+void mp_uart_write_strn(const char *str, size_t len);
+
+#endif // MICROPY_INCLUDED_ALIF2_UART_H
diff --git a/ports/alif/msc_disk.c b/ports/alif/msc_disk.c
new file mode 100644
index 000000000..a01494d20
--- /dev/null
+++ b/ports/alif/msc_disk.c
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Damien P. George
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mpconfig.h"
+#include "py/misc.h"
+#include "ospi_flash.h"
+#include "tusb.h"
+
+#if CFG_TUD_MSC
+
+#if MICROPY_FATFS_MAX_SS != MICROPY_HW_FLASH_BLOCK_SIZE_BYTES
+#error MICROPY_FATFS_MAX_SS must be the same size as MICROPY_HW_FLASH_BLOCK_SIZE_BYTES
+#endif
+
+#define BLOCK_SIZE (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES)
+#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE)
+#define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE_ADDR)
+
+static bool ejected = false;
+
+// Invoked on SCSI_CMD_INQUIRY.
+// Fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively.
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
+ memcpy(vendor_id, MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING), 8));
+ memcpy(product_id, MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING), 16));
+ memcpy(product_rev, MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING), 4));
+}
+
+// Invoked on Test-Unit-Ready command.
+// Return true allowing host to read/write this LUN (e.g SD card inserted).
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ if (ejected) {
+ tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
+ return false;
+ }
+ return true;
+}
+
+// Invoked on SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size.
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
+ *block_size = BLOCK_SIZE;
+ *block_count = BLOCK_COUNT;
+}
+
+// Invoked on Start-Stop-Unit command:
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
+ if (load_eject) {
+ if (start) {
+ // load disk storage
+ ejected = false;
+ } else {
+ // unload disk storage
+ ejected = true;
+ }
+ }
+ return true;
+}
+
+// Callback invoked on READ10 command.
+// Copy disk's data to buffer (up to bufsize) and return number of read bytes.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
+ uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE;
+ uint32_t block_count = bufsize / BLOCK_SIZE;
+ int ret = ospi_flash_read(addr, block_count * BLOCK_SIZE, buffer);
+ if (ret < 0) {
+ return ret;
+ }
+ return block_count * BLOCK_SIZE;
+}
+
+// Callback invoked on WRITE10 command.
+// Process data in buffer to disk's storage and return number of written bytes.
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
+ if (bufsize < BLOCK_SIZE) {
+ // Workaround for issue with TinyUSB passing in a small buffer.
+ uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE + offset;
+ if (offset == 0) {
+ int ret = ospi_flash_erase_sector(addr);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ int ret = ospi_flash_write(addr, bufsize, buffer);
+ if (ret < 0) {
+ return ret;
+ }
+ return bufsize;
+ }
+
+ uint32_t addr = FLASH_BASE_ADDR + lba * BLOCK_SIZE;
+ uint32_t block_count = bufsize / BLOCK_SIZE;
+ for (uint32_t block = 0; block < block_count; ++block) {
+ int ret = ospi_flash_erase_sector(addr);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = ospi_flash_write(addr, BLOCK_SIZE, buffer);
+ if (ret < 0) {
+ return ret;
+ }
+ addr += BLOCK_SIZE;
+ buffer += BLOCK_SIZE;
+ }
+ return block_count * BLOCK_SIZE;
+}
+
+// Callback invoked on a SCSI command that's not handled by TinyUSB.
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ int32_t resplen = 0;
+ switch (scsi_cmd[0]) {
+ case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ // Sync the logical unit if needed.
+ 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;
+}
+
+#endif
diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c
new file mode 100644
index 000000000..fa96053b3
--- /dev/null
+++ b/ports/alif/ospi_flash.c
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mperrno.h"
+#include "py/mphal.h"
+#include "ospi_flash.h"
+
+#include "ospi_drv.h"
+#include "pinconf.h"
+
+#define CMD_RDSR (0x05)
+#define CMD_WREN (0x06)
+#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address
+#define CMD_WRVOL (0x81)
+#define CMD_RD_DEVID (0x9f)
+
+#define WAIT_SR_TIMEOUT (1000000)
+
+// maximum bytes we can write in one SPI transfer
+// limited by 256 byte FIFO buffer (can't go up to 256)
+// need to use DMA to make this 256
+#define PAGE_SIZE (128)
+
+#define ISSI_MODE_OCTAL_DDR_DQS (0xe7)
+
+// All OSPI1 pins use the same alternate function.
+#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1
+
+typedef struct _mp_spiflash_t {
+ ospi_flash_cfg_t cfg;
+} mp_spiflash_t;
+
+static mp_spiflash_t global_flash;
+
+// Alif version of this function can overwrite the destination buffer.
+static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) {
+ uint32_t val;
+
+ ospi_writel(ospi_cfg, data_reg, command);
+ ospi_writel(ospi_cfg, ser, ospi_cfg->ser);
+
+ ospi_cfg->rx_cnt = 0;
+
+ while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) {
+ unsigned int timeout = 100000;
+ while (ospi_readl(ospi_cfg, rxflr) == 0) {
+ if (--timeout == 0) {
+ return;
+ }
+ }
+ val = ospi_readl(ospi_cfg, data_reg);
+ *buffer++ = (uint8_t)val;
+ ospi_cfg->rx_cnt++;
+ }
+}
+
+static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) {
+ ospi_setup_read(&self->cfg, 0, len, 8);
+ ospi_recv_blocking2(&self->cfg, cmd, dest);
+ return 0;
+}
+
+static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) {
+ ospi_setup_write(&self->cfg, 0);
+ ospi_send_blocking(&self->cfg, cmd);
+ return 0;
+}
+
+static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) {
+ do {
+ uint8_t sr;
+ int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
+ if (ret != 0) {
+ return ret;
+ }
+ if ((sr & mask) == val) {
+ return 0; // success
+ }
+ } while (timeout--);
+
+ return -MP_ETIMEDOUT;
+}
+
+static int mp_spiflash_wait_wel1(mp_spiflash_t *self) {
+ return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT);
+}
+
+static int mp_spiflash_wait_wip0(mp_spiflash_t *self) {
+ return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT);
+}
+
+static uint32_t ospi_flash_read_id(mp_spiflash_t *self) {
+ uint8_t buf[8];
+ ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles);
+ ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf);
+ return buf[0] | buf[1] << 8 | buf[2] << 16;
+}
+
+static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) {
+ mp_spiflash_write_cmd(self, CMD_WREN);
+ ospi_setup_write_sdr(&self->cfg, 6);
+ ospi_push(&self->cfg, cmd);
+ ospi_push(&self->cfg, 0x00);
+ ospi_push(&self->cfg, 0x00);
+ ospi_push(&self->cfg, addr);
+ ospi_send_blocking(&self->cfg, value);
+}
+
+int ospi_flash_init(void) {
+ mp_spiflash_t *self = &global_flash;
+
+ uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE;
+
+ pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA);
+ pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST);
+ pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ #if defined(pin_OSPI1_D4)
+ pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ #endif
+ #if defined(pin_OSPI1_RXDS)
+ pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl);
+ #endif
+
+ #if defined(pin_OSPI1_RXDS)
+ if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) {
+ // Alif: P5_6 is needed to support proper alt function selection of P10_7.
+ pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl);
+ }
+ #endif
+
+ // Reset the SPI flash.
+ mp_hal_pin_output(pin_OSPI1_RESET);
+ mp_hal_pin_low(pin_OSPI1_RESET);
+ mp_hal_delay_us(30);
+ mp_hal_pin_high(pin_OSPI1_RESET);
+
+ // Configure the OSPI peripheral.
+ self->cfg.regs = (ssi_regs_t *)OSPI1_BASE;
+ self->cfg.aes_regs = (aes_regs_t *)AES1_BASE;
+ self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE;
+ self->cfg.ser = 1;
+ self->cfg.addrlen = 8; // 32-bit address length
+ self->cfg.ospi_clock = ospi_flash_settings.freq_mhz;
+ self->cfg.ddr_en = 0;
+ self->cfg.wait_cycles = 0; // used only for ospi_xip_exit
+ ospi_init(&self->cfg);
+
+ if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) {
+ // Switch SPI flash to Octal DDR mode.
+ ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS);
+ self->cfg.ddr_en = 1;
+ }
+
+ // Check the device ID.
+ if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ospi_flash_erase_sector(uint32_t addr) {
+ mp_spiflash_t *self = &global_flash;
+
+ mp_spiflash_write_cmd(self, CMD_WREN);
+ int ret = mp_spiflash_wait_wel1(self);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/);
+ ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR);
+ ospi_send_blocking(&self->cfg, addr);
+
+ return mp_spiflash_wait_wip0(self);
+}
+
+int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) {
+ // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read.
+ mp_spiflash_t *self = &global_flash;
+
+ while (len) {
+ uint32_t l = len;
+ if (l > 256) {
+ l = 256;
+ }
+ ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles);
+ ospi_push(&self->cfg, ospi_flash_settings.read_command);
+ ospi_recv_blocking2(&self->cfg, addr, dest);
+ addr += l;
+ len -= l;
+ dest += l;
+ }
+
+ return 0;
+}
+
+static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) {
+ mp_spiflash_t *self = &global_flash;
+
+ mp_spiflash_write_cmd(self, CMD_WREN);
+ int ret = mp_spiflash_wait_wel1(self);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/);
+ ospi_push(&self->cfg, ospi_flash_settings.write_command);
+ ospi_push(&self->cfg, addr);
+ while (--len) {
+ ospi_push(&self->cfg, *src++);
+ }
+ ospi_send_blocking(&self->cfg, *src);
+
+ return mp_spiflash_wait_wip0(self);
+}
+
+int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) {
+ int ret = 0;
+ uint32_t offset = addr & (PAGE_SIZE - 1);
+ while (len) {
+ size_t rest = PAGE_SIZE - offset;
+ if (rest > len) {
+ rest = len;
+ }
+ ret = ospi_flash_write_page(addr, rest, src);
+ if (ret != 0) {
+ break;
+ }
+ len -= rest;
+ addr += rest;
+ src += rest;
+ offset = 0;
+ }
+ return ret;
+}
diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h
new file mode 100644
index 000000000..945dc4bb0
--- /dev/null
+++ b/ports/alif/ospi_flash.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_ALIF_OSPI_FLASH_H
+#define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct _ospi_flash_settings_t {
+ uint32_t jedec_id;
+ uint32_t freq_mhz;
+ bool is_quad : 1;
+ bool is_oct : 1;
+ bool is_ddr : 1;
+ uint8_t read_id_dummy_cycles;
+ uint8_t read_dummy_cycles;
+ uint8_t read_command;
+ uint8_t write_command;
+} ospi_flash_settings_t;
+
+// Provided by the board when it enables OSPI1.
+extern const ospi_flash_settings_t ospi_flash_settings;
+
+int ospi_flash_init(void);
+int ospi_flash_erase_sector(uint32_t addr);
+int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest);
+int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src);
+
+#endif // MICROPY_INCLUDED_ALIF_OSPI_FLASH_H
diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h
new file mode 100644
index 000000000..a15baff2a
--- /dev/null
+++ b/ports/alif/ospi_xip_user.h
@@ -0,0 +1,5 @@
+// This file is needed by ospi_xip/source/ospi/ospi_drv.c.
+#define OSPI_XIP_ENABLE_AES_DECRYPTION (0)
+#define OSPI_XIP_RX_SAMPLE_DELAY (4)
+#define OSPI_XIP_DDR_DRIVE_EDGE (1)
+#define OSPI_XIP_RXDS_DELAY (12)
diff --git a/ports/alif/pendsv.c b/ports/alif/pendsv.c
new file mode 100644
index 000000000..965053e6c
--- /dev/null
+++ b/ports/alif/pendsv.c
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "pendsv.h"
+#include "irq.h"
+
+static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS];
+
+void pendsv_init(void) {
+ NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV);
+}
+
+void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
+ pendsv_dispatch_table[slot] = f;
+ SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
+}
+
+void PendSV_Handler(void) {
+ for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) {
+ if (pendsv_dispatch_table[i] != NULL) {
+ pendsv_dispatch_t f = pendsv_dispatch_table[i];
+ pendsv_dispatch_table[i] = NULL;
+ f();
+ }
+ }
+}
diff --git a/ports/alif/pendsv.h b/ports/alif/pendsv.h
new file mode 100644
index 000000000..43a4b7a85
--- /dev/null
+++ b/ports/alif/pendsv.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_ALIF_PENDSV_H
+#define MICROPY_INCLUDED_ALIF_PENDSV_H
+
+#include "py/mpconfig.h"
+
+#ifndef MICROPY_BOARD_PENDSV_ENTRIES
+#define MICROPY_BOARD_PENDSV_ENTRIES
+#endif
+
+enum {
+ PENDSV_DISPATCH_SOFT_TIMER,
+ MICROPY_BOARD_PENDSV_ENTRIES
+ PENDSV_DISPATCH_MAX
+};
+
+#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX
+
+typedef void (*pendsv_dispatch_t)(void);
+
+void pendsv_init(void);
+void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f);
+
+#endif // MICROPY_INCLUDED_ALIF_PENDSV_H
diff --git a/ports/alif/qstrdefsport.h b/ports/alif/qstrdefsport.h
new file mode 100644
index 000000000..00d3e2ae3
--- /dev/null
+++ b/ports/alif/qstrdefsport.h
@@ -0,0 +1,2 @@
+// qstrs specific to this port
+// *FORMAT-OFF*
diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h
new file mode 100644
index 000000000..d244b4c24
--- /dev/null
+++ b/ports/alif/tinyusb_port/tusb_config.h
@@ -0,0 +1,73 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 _ALIF_TUSB_CONFIG_H_
+#define _ALIF_TUSB_CONFIG_H_
+
+// --------------------------------------------------------------------+
+// Board Specific Configuration
+// --------------------------------------------------------------------+
+
+#define CFG_TUSB_MCU OPT_MCU_NONE
+// #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
+#define TUP_DCD_ENDPOINT_MAX 8
+#define TUD_OPT_RHPORT 0
+
+// --------------------------------------------------------------------
+// COMMON CONFIGURATION
+// --------------------------------------------------------------------
+
+#define CFG_TUSB_OS OPT_OS_NONE
+#define CFG_TUSB_DEBUG 0
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED OPT_MODE_HIGH_SPEED
+
+/* 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
+ * into those specific section.
+ */
+#define CFG_TUSB_MEM_SECTION __attribute__((section(".bss.sram0")))
+
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(32)))
+
+// --------------------------------------------------------------------
+// DEVICE CONFIGURATION
+// --------------------------------------------------------------------
+
+#define CFG_TUD_ENDPOINT0_SIZE 64
+
+// CDC Endpoint transfer buffer size, more is faster
+#define CFG_TUD_CDC_EP_BUFSIZE (4096)
+#define CFG_TUD_CDC_RX_BUFSIZE (4096)
+#define CFG_TUD_CDC_TX_BUFSIZE (4096)
+
+#include "shared/tinyusb/tusb_config.h"
+
+#endif /* _ALIF_TUSB_CONFIG_H_ */
diff --git a/ports/alif/usbd.c b/ports/alif/usbd.c
new file mode 100644
index 000000000..9df661068
--- /dev/null
+++ b/ports/alif/usbd.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 OpenMV LLC.
+ *
+ * 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 "py/mpconfig.h"
+
+#if MICROPY_HW_ENABLE_USBDEV
+
+#include "shared/tinyusb/mp_usbd.h"
+#include "tusb.h"
+
+void mp_usbd_port_get_serial_number(char *serial_buf) {
+ // TODO
+ uint8_t id[8] = "ABCDEFGH";
+ MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX);
+ mp_usbd_hex_str(serial_buf, id, sizeof(id));
+}
+
+#endif