diff options
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 |
