diff --git a/.github/workflows/ports_alif.yml b/.github/workflows/ports_alif.yml new file mode 100644 index 0000000000000..0e96e7d816e50 --- /dev/null +++ b/.github/workflows/ports_alif.yml @@ -0,0 +1,33 @@ +name: alif port + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + - 'py/**' + - 'extmod/**' + - 'shared/**' + - 'lib/**' + - 'drivers/**' + - 'ports/alif/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_alif: + strategy: + fail-fast: false + matrix: + ci_func: # names are functions in ci.sh + - alif_ae3_build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_alif_setup + - name: Build ci_${{matrix.ci_func }} + run: source tools/ci.sh && ci_${{ matrix.ci_func }} diff --git a/.gitmodules b/.gitmodules index 6338f0e66d63d..02849ec9bdd11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -68,3 +68,9 @@ [submodule "lib/arduino-lib"] path = lib/arduino-lib url = https://github.com/arduino/arduino-lib-mpy.git +[submodule "lib/alif_ensemble-cmsis-dfp"] + path = lib/alif_ensemble-cmsis-dfp + url = https://github.com/alifsemi/alif_ensemble-cmsis-dfp.git +[submodule "lib/alif-security-toolkit"] + path = lib/alif-security-toolkit + url = https://github.com/micropython/alif-security-toolkit.git diff --git a/extmod/extmod.mk b/extmod/extmod.mk index a510f3c54c25c..859f610c90ac1 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -454,15 +454,14 @@ CYW43_DIR = lib/cyw43-driver GIT_SUBMODULES += $(CYW43_DIR) CFLAGS_EXTMOD += -DMICROPY_PY_NETWORK_CYW43=1 SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ + cyw43_bthci_uart.c \ cyw43_ctrl.c \ cyw43_lwip.c \ cyw43_ll.c \ cyw43_sdio.c \ + cyw43_spi.c \ cyw43_stats.c \ ) -ifeq ($(MICROPY_PY_BLUETOOTH),1) -DRIVERS_SRC_C += drivers/cyw43/cywbt.c -endif $(BUILD)/$(CYW43_DIR)/src/cyw43_%.o: CFLAGS += -std=c11 endif # MICROPY_PY_NETWORK_CYW43 diff --git a/lib/alif-security-toolkit b/lib/alif-security-toolkit new file mode 160000 index 0000000000000..63698efe8567e --- /dev/null +++ b/lib/alif-security-toolkit @@ -0,0 +1 @@ +Subproject commit 63698efe8567eed115fe620d5e5de75f460310d7 diff --git a/lib/alif_ensemble-cmsis-dfp b/lib/alif_ensemble-cmsis-dfp new file mode 160000 index 0000000000000..04b3176a0031c --- /dev/null +++ b/lib/alif_ensemble-cmsis-dfp @@ -0,0 +1 @@ +Subproject commit 04b3176a0031c945d4306db00663379e9b431e08 diff --git a/ports/alif/Makefile b/ports/alif/Makefile new file mode 100644 index 0000000000000..68706674365c1 --- /dev/null +++ b/ports/alif/Makefile @@ -0,0 +1,125 @@ +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD) +MCU_CORE ?= M55_HP +GIT_SUBMODULES += lib/tinyusb lib/alif_ensemble-cmsis-dfp lib/alif-security-toolkit +PORT ?= /dev/ttyACM0 + +ALIF_TOOLS ?= ../../lib/alif-security-toolkit/toolkit + +JLINK_CMD_PREFIX = \ +ExitOnError 1\n\ +Device $(JLINK_DEV)\n\ +SelectInterface SWD\n\ +Speed auto\n\ +Connect\n\ +Reset\n\ +ShowHWStatus\n + +JLINK_CMD_SUFFIX = \ +Reset\n\ +Exit + +ALIF_TOC_CONFIG = alif_cfg.json +ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG) +ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE) + +ifeq ($(MCU_CORE),M55_HP) + +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +$(JLINK_CMD_SUFFIX)' + +else ifeq ($(MCU_CORE),M55_HE) + +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' + +else ifeq ($(MCU_CORE),M55_DUAL) + +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HP_APP=1 +ALIF_TOC_CFLAGS += -DTOC_CORE_M55_HE_APP=1 +ALIF_TOC_APPS += $(BUILD)/M55_HP/firmware.bin $(BUILD)/M55_HE/firmware.bin +JLINK_CMD = '\ +$(JLINK_CMD_PREFIX)\ +LoadFile "$(BUILD)/M55_HP/firmware.bin",0x80020000\n\ +LoadFile "$(BUILD)/M55_HE/firmware.bin",0x80320000\n\ +$(JLINK_CMD_SUFFIX)' + +else +$(error Invalid MCU core specified)) +endif + +include ../../py/mkenv.mk +include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +################################################################################ +# Main targets + +.PHONY: all +all: $(BUILD)/firmware.toc.bin + +# Force make commands to run the targets every time +# regardless of whether firmware.toc.bin already exists +# to detect changes in the source files and rebuild. +.PHONY: $(BUILD)/M55_HE/firmware.bin +.PHONY: $(BUILD)/M55_HP/firmware.bin + +$(BUILD): + $(MKDIR) -p $@ + +$(BUILD)/M55_HP/firmware.bin: + make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 + +$(BUILD)/M55_HE/firmware.bin: + make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 + +$(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) + $(ECHO) "Preprocess toc config $@" + $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ + +$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) + $(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: $(ALIF_TOC_APPS) + $(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) + +include $(TOP)/py/mkrules.mk diff --git a/ports/alif/README.md b/ports/alif/README.md new file mode 100644 index 0000000000000..824a63da186d2 --- /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.mk b/ports/alif/alif.mk new file mode 100644 index 0000000000000..eee27b3b7d698 --- /dev/null +++ b/ports/alif/alif.mk @@ -0,0 +1,292 @@ +################################################################################ +# Initial setup of Makefile environment + +BOARD ?= ALIF_ENSEMBLE +BOARD_DIR ?= boards/$(BOARD) +BUILD ?= build-$(BOARD)/$(MCU_CORE) + +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- +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)/se_services/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 +INC += -Ilwip_inc + +GEN_PIN_MKPINS = mcu/make-pins.py +GEN_PIN_PREFIX = mcu/pins_prefix.c +GEN_PINS_BOARD_CSV = $(BOARD_DIR)/pins.csv +GEN_PINS_MCU_CSV = mcu/ensemble_pin_alt.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 += $(INC) \ + -std=c99 \ + -Wall \ + -Werror \ + -Wdouble-promotion \ + -Wfloat-conversion \ + -mthumb \ + -mcpu=cortex-m55 \ + -mtune=cortex-m55 \ + $(CFLAGS_FPU) \ + -march=armv8.1-m.main+fp+mve.fp \ + -fdata-sections \ + -ffunction-sections \ + --specs=nosys.specs \ + -D$(MCU_CORE)=1 \ + -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 + +# 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 + +CFLAGS += $(CFLAGS_EXTRA) + +AFLAGS = -mthumb -march=armv8.1-m.main+fp+mve.fp $(CFLAGS_FPU) + +CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \ + -Wl,-Map=$@.map \ + -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--print-memory-usage \ + -Wl,--no-warn-rwx-segment + +ifeq ($(MCU_CORE),M55_HP) +CFLAGS += -Wl,--wrap=dcd_event_handler +endif + +################################################################################ +# Source files and libraries + +SRC_O += \ + shared/runtime/gchelper_thumb2.o + +SRC_C = \ + alif_flash.c \ + cyw43_port_spi.c \ + fatfs_port.c \ + machine_pin.c \ + machine_i2c.c \ + machine_spi.c \ + machine_rtc.c \ + main.c \ + modalif.c \ + mphalport.c \ + mpnetworkport.c \ + mpu.c \ + mpuart.c \ + msc_disk.c \ + nosys_stubs.c \ + ospi_ext.c \ + ospi_flash.c \ + pendsv.c \ + system_tick.c \ + se_services.c \ + usbd.c \ + vfs_rom_ioctl.c \ + $(wildcard $(BOARD_DIR)/*.c) + +ifeq ($(MICROPY_SSL_MBEDTLS),1) +SRC_C += mbedtls/mbedtls_port.c +endif + +ifeq ($(MICROPY_PY_BLUETOOTH),1) +SRC_C += mpbthciport.c mpnimbleport.c +endif + +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/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/common/source/tcm_partition.c \ + Device/common/source/tgu_M55.c \ + Device/common/source/pm.c \ + Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ + drivers/source/adc.c \ + drivers/source/i2c.c \ + drivers/source/mhu_driver.c \ + drivers/source/mhu_receiver.c \ + drivers/source/mhu_sender.c \ + drivers/source/mram.c \ + drivers/source/pinconf.c \ + drivers/source/spi.c \ + drivers/source/uart.c \ + drivers/source/utimer.c \ + ospi_xip/source/ospi/ospi_drv.c \ + se_services/source/services_host_application.c \ + se_services/source/services_host_boot.c \ + se_services/source/services_host_clocks.c \ + se_services/source/services_host_cryptocell.c \ + se_services/source/services_host_handler.c \ + se_services/source/services_host_system.c \ + se_services/source/services_host_power.c \ + se_services/source/services_host_maintenance.c \ + ) + +$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/mram.o: CFLAGS += -Wno-strict-aliasing +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation +$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized + +# Add Alif-specific implementation of libmetal (and optionally OpenAMP's rproc). +# Note: libmetal code is generated via a pre-processor so ensure that runs first. +ifeq ($(MICROPY_PY_OPENAMP),1) +SRC_C += mpmetalport.c +$(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +SRC_C += mpremoteprocport.c +$(BUILD)/mpremoteprocport.o: $(BUILD)/openamp/metal/config.h +endif +endif + +# 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 + +.PHONY: all erase deploy gdb objdump release + +.DELETE_ON_ERROR: + +obj: $(OBJ) +all: $(BUILD)/firmware.bin + +$(BUILD): + $(MKDIR) -p $@ + +$(BUILD)/ensemble.ld: $(LD_FILE) + $(ECHO) "Preprocess linker script $@" + $(Q)$(CPP) -P -E $(CFLAGS) $^ > $@ + +$(BUILD)/firmware.elf: $(OBJ) $(BUILD)/ensemble.ld + $(ECHO) "Link $@" + $(Q)$(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -Obinary $^ $(BUILD)/firmware.bin + +################################################################################ +# 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) \ + --af-csv $(GEN_PINS_MCU_CSV) \ + --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/alif_flash.c b/ports/alif/alif_flash.c new file mode 100644 index 0000000000000..722afadae1f7b --- /dev/null +++ b/ports/alif/alif_flash.c @@ -0,0 +1,176 @@ +/* + * 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" + +#if MICROPY_HW_ENABLE_OSPI +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 const alif_flash_obj_t alif_flash_fs_obj = { + .base = { &alif_flash_type }, + .flash_base_addr = MICROPY_HW_FLASH_STORAGE_BASE_ADDR, + .flash_size = MICROPY_HW_FLASH_STORAGE_FS_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 writable-filesystem flash + return MP_OBJ_FROM_PTR(&alif_flash_fs_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_int_t alif_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + alif_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)(ospi_flash_get_xip_base() + self->flash_base_addr); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Can't return a writable buffer. + return 1; + } +} + +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, + buffer, alif_flash_get_buffer, + locals_dict, &alif_flash_locals_dict + ); +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.c b/ports/alif/boards/ALIF_ENSEMBLE/board.c new file mode 100644 index 0000000000000..72b93e31ffbea --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.c @@ -0,0 +1,56 @@ +/* + * 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 "ospi_ext.h" +#include "ospi_flash.h" + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 1, + .pin_reset = pin_OSPI1_RESET, + .pin_cs = pin_OSPI1_CS, + .pin_clk_p = pin_OSPI1_SCLK, + .pin_clk_n = NULL, + .pin_rwds = pin_OSPI1_RXDS, + .pin_d0 = pin_OSPI1_D0, + .pin_d1 = pin_OSPI1_D1, + .pin_d2 = pin_OSPI1_D2, + .pin_d3 = pin_OSPI1_D3, + .pin_d4 = pin_OSPI1_D4, + .pin_d5 = pin_OSPI1_D5, + .pin_d6 = pin_OSPI1_D6, + .pin_d7 = pin_OSPI1_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x1a5b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, +}; +const size_t ospi_flash_settings_len = 1; diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S new file mode 100644 index 0000000000000..7adcdbbcc3761 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI1 flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xc1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h new file mode 100644 index 0000000000000..8a6003ebbfb53 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.h @@ -0,0 +1,56 @@ +#define MICROPY_HW_BOARD_NAME "Alif Ensemble DevKit" +#define MICROPY_HW_MCU_NAME "AE722F80F55D5XX" + +#define MICROPY_HW_ENABLE_UART_REPL (CORE_M55_HP) +#define MICROPY_HW_UART_REPL (4) +#define MICROPY_HW_USB_MSC (1) +#define MICROPY_HW_ENABLE_HW_I2C (1) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C0_SCL (pin_P0_3) +#define MICROPY_HW_I2C0_SDA (pin_P0_2) +#define MICROPY_HW_I2C1_SCL (pin_P3_7) +#define MICROPY_HW_I2C1_SDA (pin_P3_6) +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P1_0) +#define MICROPY_HW_SPI0_MOSI (pin_P1_1) +#define MICROPY_HW_SPI0_SCK (pin_P1_2) +#define MICROPY_HW_SPI1_MISO (pin_P2_4) +#define MICROPY_HW_SPI1_MOSI (pin_P2_5) +#define MICROPY_HW_SPI1_SCK (pin_P2_6) +#define MICROPY_HW_SPI2_MISO (pin_P4_2) +#define MICROPY_HW_SPI2_MOSI (pin_P4_3) +#define MICROPY_HW_SPI2_SCK (pin_P4_4) +#define MICROPY_HW_SPI3_MISO (pin_P12_4) +#define MICROPY_HW_SPI3_MOSI (pin_P12_5) +#define MICROPY_HW_SPI3_SCK (pin_P12_6) +#define MICROPY_HW_LPSPI0_MISO (pin_P7_4) +#define MICROPY_HW_LPSPI0_MOSI (pin_P7_5) +#define MICROPY_HW_LPSPI0_SCK (pin_P7_6) + +// UART buses +#define MICROPY_HW_UART0_TX (pin_P0_1) +#define MICROPY_HW_UART0_RX (pin_P0_0) +#define MICROPY_HW_UART0_RTS (pin_P0_3) +#define MICROPY_HW_UART0_CTS (pin_P0_2) +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART1_RTS (pin_P0_7) +#define MICROPY_HW_UART1_CTS (pin_P0_6) +#define MICROPY_HW_REPL_UART_TX (pin_P12_2) +#define MICROPY_HW_REPL_UART_RX (pin_P12_1) + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk new file mode 100644 index 0000000000000..d35a7aad84a07 --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/mpconfigboard.mk @@ -0,0 +1,11 @@ +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE722F80F55D5_HP +LD_FILE = boards/ALIF_ENSEMBLE/board.ld.S + +ALIF_TOOLKIT_CFG_PART = AE722F80F55D5LS +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae7.json\" + +MICROPY_FLOAT_IMPL = float +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h b/ports/alif/boards/ALIF_ENSEMBLE/ospi_xip_user.h new file mode 100644 index 0000000000000..49921bdd22ddc --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/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 (3) +#define OSPI_XIP_DDR_DRIVE_EDGE (1) +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/ALIF_ENSEMBLE/pins.csv b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv new file mode 100644 index 0000000000000..ec397d9cf604c --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/pins.csv @@ -0,0 +1,30 @@ +# OSP1 flash +OSPI1_RESET,P15_7 +OSPI1_CS,P5_7 +OSPI1_SCLK,P5_5 +OSPI1_D0,P9_5 +OSPI1_D1,P9_6 +OSPI1_D2,P9_7 +OSPI1_D3,P10_0 +OSPI1_D4,P10_1 +OSPI1_D5,P10_2 +OSPI1_D6,P10_3 +OSPI1_D7,P10_4 +OSPI1_RXDS,P10_7 + +LED_BLUE,P12_0 +LED_RED,P12_3 +JOY_LEFT,P15_0 +JOY_RIGHT,P15_1 + +# UART buses +UART0_TX,P0_1 +UART0_RX,P0_0 +UART0_RTS,P0_3 +UART0_CTS,P0_2 +UART1_TX,P0_5 +UART1_RX,P0_4 +UART1_RTS,P0_7 +UART1_CTS,P0_6 +REPL_UART_TX,P12_2 +REPL_UART_RX,P12_1 diff --git a/ports/alif/boards/OPENMV_AE3/board.c b/ports/alif/boards/OPENMV_AE3/board.c new file mode 100644 index 0000000000000..65da152d8536f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.c @@ -0,0 +1,192 @@ +/* + * 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 "ospi_ext.h" +#include "ospi_flash.h" +#include "se_services.h" + +#define OMV_BOOT_MAGIC_ADDR (0x200FFFFCU) +#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU) + +#if CORE_M55_HP +#define NPU_IRQ_NUMBER NPU_HP_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HP_BASE +#else +#define NPU_IRQ_NUMBER NPU_HE_IRQ_IRQn +#define NPU_BASE_ADDRESS (void *)NPU_HE_BASE +#endif + +typedef struct { + volatile uint32_t ID; // 0x0 + volatile uint32_t STATUS; // 0x4 + volatile uint32_t CMD; // 0x8 + volatile uint32_t RESET; // 0xC +} npu_regs_t; + +#define NPU ((npu_regs_t *)NPU_BASE_ADDRESS) + +const ospi_pin_settings_t ospi_pin_settings = { + .peripheral_number = 0, + .pin_reset = pin_FLASH_RESET, + .pin_cs = pin_FLASH_CS, + .pin_clk_p = pin_FLASH_SCLK_P, + .pin_clk_n = pin_FLASH_SCLK_N, + .pin_rwds = pin_FLASH_DQSM, + .pin_d0 = pin_FLASH_D0, + .pin_d1 = pin_FLASH_D1, + .pin_d2 = pin_FLASH_D2, + .pin_d3 = pin_FLASH_D3, + .pin_d4 = pin_FLASH_D4, + .pin_d5 = pin_FLASH_D5, + .pin_d6 = pin_FLASH_D6, + .pin_d7 = pin_FLASH_D7, +}; + +const ospi_flash_settings_t ospi_flash_settings[] = { + { + .jedec_id = 0x3980c2, + .freq_hz = 100000000, + .read_dummy_cycles = 10, + OSPI_FLASH_SETTINGS_MX25, + }, + { + .jedec_id = 0x195b9d, + .freq_hz = 100000000, + .read_dummy_cycles = 9, + OSPI_FLASH_SETTINGS_IS25, + }, + { + .jedec_id = 0x17bb6b, + .freq_hz = 100000000, + .read_dummy_cycles = 7, + OSPI_FLASH_SETTINGS_EM, + }, +}; +const size_t ospi_flash_settings_len = 3; + +void board_startup(void) { + // Switch the USB multiplexer to use the Alif USB port. + mp_hal_pin_output(pin_USB_D_SEL); + mp_hal_pin_high(pin_USB_D_SEL); +} + +void board_enter_bootloader(void) { + *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE; + NVIC_SystemReset(); +} + +void board_early_init(void) { + // Set default run profile + run_profile_t run_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .run_clk_src = CLK_SRC_PLL, + #if CORE_M55_HP + .cpu_clk_freq = CLOCK_FREQUENCY_400MHZ, + #else + .cpu_clk_freq = CLOCK_FREQUENCY_160MHZ, + #endif + .scaled_clk_freq = SCALED_FREQ_XO_HIGH_DIV_38_4_MHZ, + // AON, modem aon, SSE-700 AON, modem, SYSTOP, DEBUG, SE + .power_domains = PD_VBAT_AON_MASK | PD_SSE700_AON_MASK | PD_SYST_MASK | + PD_DBSS_MASK | PD_SESS_MASK | PD_SRAMS_MASK | PD_SRAM_CTRL_AON_MASK, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + }; + + if (se_services_set_run_profile(&run_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_run_profile"); + } + + // Set default off profile + off_profile_t off_profile = { + .dcdc_mode = DCDC_MODE_PWM, + .dcdc_voltage = DCDC_VOUT_0825, + // CLK_SRC_LFRC or CLK_SRC_LFXO + .aon_clk_src = CLK_SRC_LFXO, + // CLK_SRC_HFRC, CLK_SRC_HFXO or CLK_SRC_PLL + .stby_clk_src = CLK_SRC_HFRC, + .stby_clk_freq = SCALED_FREQ_RC_STDBY_76_8_MHZ, + // Disable all power domains. + .power_domains = 0, + // Add all memories + .memory_blocks = SERAM_MASK | SRAM0_MASK | SRAM1_MASK | MRAM_MASK | BACKUP4K_MASK | + SRAM6A_MASK | SRAM6B_MASK | SRAM7_1_MASK | SRAM7_2_MASK | SRAM7_3_MASK | + SRAM8_MASK | SRAM9_MASK | FWRAM_MASK, + .phy_pwr_gating = LDO_PHY_MASK | USB_PHY_MASK | MIPI_TX_DPHY_MASK | MIPI_RX_DPHY_MASK | + MIPI_PLL_DPHY_MASK, + .vdd_ioflex_3V3 = IOFLEX_LEVEL_3V3, + .vtor_address = SCB->VTOR, + .vtor_address_ns = SCB->VTOR, + .ewic_cfg = EWIC_RTC_A, + .wakeup_events = WE_LPRTC, + }; + + if (se_services_set_off_profile(&off_profile)) { + MICROPY_BOARD_FATAL_ERROR("se_services_set_off_profile"); + } + + // Select PLL for PD4 memory. + if (se_services_select_pll_source(PLL_SOURCE_PLL, PLL_TARGET_PD4_SRAM)) { + MICROPY_BOARD_FATAL_ERROR("se_services_select_pll_source"); + } +} + +MP_WEAK void board_enter_stop(void) { + // Disable NPU interrupt + NVIC_DisableIRQ(NPU_IRQ_NUMBER); + NVIC_ClearPendingIRQ(NPU_IRQ_NUMBER); + + // Soft-reset NPU + NPU->RESET = 0x00000000; + + // Wait until reset + uint32_t data = 0; + do { + // Poll channel0 status registers + data = NPU->STATUS; + } while (data); + + // Set default value, enables off for clocks and power. + NPU->CMD = 0x0000000C; +} + +MP_WEAK void board_enter_standby(void) { + +} + +MP_WEAK void board_exit_standby(void) { + +} diff --git a/ports/alif/boards/OPENMV_AE3/board.ld.S b/ports/alif/boards/OPENMV_AE3/board.ld.S new file mode 100644 index 0000000000000..0d09bb15f874f --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/board.ld.S @@ -0,0 +1,14 @@ +#include "mcu/ensemble.ld.S" + +/* Define ROMFS partition locations. */ +#if CORE_M55_HP +/* The HP core has access to the external OSPI flash and MRAM ROMFS partitions. */ +_micropy_hw_romfs_part0_start = 0xa1000000; +_micropy_hw_romfs_part0_size = 16M; +_micropy_hw_romfs_part1_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part1_size = LENGTH(MRAM_FS); +#else +/* The HP core has access to the MRAM ROMFS partition. */ +_micropy_hw_romfs_part0_start = ORIGIN(MRAM_FS); +_micropy_hw_romfs_part0_size = LENGTH(MRAM_FS); +#endif diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.h b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h new file mode 100644 index 0000000000000..cbdbd063ed3df --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.h @@ -0,0 +1,85 @@ +#define MICROPY_HW_BOARD_NAME "OpenMV-AE3" +#define MICROPY_HW_MCU_NAME "AE302F80F55D5AE" + +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) +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; + +#define MICROPY_HW_USB_MSC (CORE_M55_HP) +#define MICROPY_HW_ENABLE_HW_I2C (1) +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) + +// ROMFS partitions +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (CORE_M55_HP) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_P0_5) +#define MICROPY_HW_I2C1_SDA (pin_P0_4) + +#define MICROPY_HW_I2C2_SCL (pin_P5_1) +#define MICROPY_HW_I2C2_SDA (pin_P5_0) + +#define MICROPY_HW_I2C3_SCL (pin_P1_1) +#define MICROPY_HW_I2C3_SDA (pin_P1_0) + +// SPI buses +#define MICROPY_HW_SPI0_MISO (pin_P5_0) +#define MICROPY_HW_SPI0_MOSI (pin_P5_1) +// #define MICROPY_HW_SPI0_NSS (pin_P5_2) +#define MICROPY_HW_SPI0_SCK (pin_P5_3) + +// UART buses +#define MICROPY_HW_UART1_TX (pin_P0_5) +#define MICROPY_HW_UART1_RX (pin_P0_4) +#define MICROPY_HW_UART3_TX (pin_P1_3) +#define MICROPY_HW_UART3_RX (pin_P1_2) +#define MICROPY_HW_UART3_RTS (pin_P7_3) +#define MICROPY_HW_UART3_CTS (pin_P7_2) +#define MICROPY_HW_UART4_TX (pin_P5_1) +#define MICROPY_HW_UART4_RX (pin_P5_0) +#define MICROPY_HW_UART5_TX (pin_P5_3) +#define MICROPY_HW_UART5_RX (pin_P5_2) + +#define MICROPY_HW_USB_VID 0x37C5 +#define MICROPY_HW_USB_PID 0x16E3 + +#define MICROPY_HW_USB_MANUFACTURER_STRING "OpenMV" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "OpenMV Camera" +#define MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING "OpenMV" + +extern void board_startup(void); +#define MICROPY_BOARD_STARTUP board_startup + +extern void board_early_init(void); +#define MICROPY_BOARD_EARLY_INIT board_early_init + +extern void board_enter_bootloader(void); +#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) board_enter_bootloader() + +extern void board_enter_stop(void); +#define MICROPY_BOARD_ENTER_STOP board_enter_stop + +extern void board_enter_standby(void); +#define MICROPY_BOARD_ENTER_STANDBY board_enter_standby + +extern void board_exit_standby(void); +#define MICROPY_BOARD_EXIT_STANDBY board_exit_standby + +// This is used for alif.Flash() and USB MSC. +#define MICROPY_HW_FLASH_STORAGE_BASE_ADDR (0) +#define MICROPY_HW_FLASH_STORAGE_BYTES (32 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_FS_BYTES (16 * 1024 * 1024) +#define MICROPY_HW_FLASH_STORAGE_ROMFS_BYTES (16 * 1024 * 1024) + +// Murata 1YN configuration +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" +#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h" +#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE (2000000) +#define CYW43_BT_UART_BAUDRATE_ACTIVE_USE (2000000) + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) diff --git a/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk new file mode 100644 index 0000000000000..83cc17dd15ed9 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/mpconfigboard.mk @@ -0,0 +1,22 @@ +# TODO: alif_ensemble-cmsis-dfp only supports AE722F80F55D5XX at the moment. +MCU_SERIES = E7 +MCU_VARIANT = AE722F80F55D5XX +JLINK_DEV = AE302F80F55D5_HP +LD_FILE = boards/OPENMV_AE3/board.ld.S +PORT = /dev/ttyUSB0 + +ALIF_TOOLKIT_CFG_PART = AE302F80F55D5AE +ALIF_TOOLKIT_CFG_FILE = \"app-device-config-ae3.json\" + +CORE_M55_HP := $(if $(filter M55_HP,$(MCU_CORE)),1,0) + +# MicroPython settings +MICROPY_FLOAT_IMPL = float +MICROPY_PY_BLUETOOTH = $(CORE_M55_HP) +MICROPY_BLUETOOTH_NIMBLE = $(CORE_M55_HP) +MICROPY_PY_LWIP = $(CORE_M55_HP) +MICROPY_PY_NETWORK_CYW43 = $(CORE_M55_HP) +MICROPY_PY_SSL = $(CORE_M55_HP) +MICROPY_SSL_MBEDTLS = $(CORE_M55_HP) +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h new file mode 100644 index 0000000000000..c0bd9afeb5cea --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/ospi_xip_user.h @@ -0,0 +1,6 @@ +// 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 (0) +// floor(1/4 OSPI clock cycle + 3.6ns) * 2 +#define OSPI_XIP_RXDS_DELAY (12) diff --git a/ports/alif/boards/OPENMV_AE3/pins.csv b/ports/alif/boards/OPENMV_AE3/pins.csv new file mode 100644 index 0000000000000..ab4f1a34f7e11 --- /dev/null +++ b/ports/alif/boards/OPENMV_AE3/pins.csv @@ -0,0 +1,69 @@ +# USB multiplexer +USB_D_SEL,P15_0 +USB_D_SEL_FB,P15_1 + +# LEDs +LED_RED,P0_0 +LED_GREEN,P6_3 +LED_BLUE,P6_0 + +# User switch +USR_SW,P15_7 + +# Flash on OSPI0 +FLASH_RESET,P3_3 +FLASH_CS,P3_2 +FLASH_SCLK_P,P3_0 +FLASH_SCLK_N,P3_1 +FLASH_DQSM,P1_6 +FLASH_D0,P2_0 +FLASH_D1,P2_1 +FLASH_D2,P2_2 +FLASH_D3,P2_3 +FLASH_D4,P2_4 +FLASH_D5,P2_5 +FLASH_D6,P2_6 +FLASH_D7,P2_7 + +# Murata 1YN +BT_UART_RX,P1_4 +BT_UART_TX,P1_5 +BT_UART_CTS,P6_6 +BT_UART_RTS,P6_7 +BT_REG_ON,P5_7 +BT_DEV_WAKE,P9_2 +BT_HOST_WAKE,P13_3 +WL_REG_ON,P10_4 +WL_HOST_WAKE,P11_0 +WL_I2S_SDI,P12_0 +WL_I2S_SDO,P12_1 +WL_I2S_SCLK,P12_2 +WL_I2S_WS,P12_3 +WL_IRQ,P9_6 +WL_MISO,P12_4 +WL_MOSI,P12_5 +WL_SCLK,P12_6 +WL_CS,P12_7 + +P0,P5_1 +P1,P5_0 +P2,P5_3 +P3,P5_2 +P4,P0_5 +P5,P0_4 +P6,P7_2 +P7,P7_3 +P8,P1_2 +P9,P1_3 + +# UART buses +UART1_TX,P0_5 +UART1_RX,P0_4 +UART3_TX,P1_3 +UART3_RX,P1_2 +UART3_RTS,P7_3 +UART3_CTS,P7_2 +UART4_TX,P5_1 +UART4_RX,P5_0 +UART5_TX,P5_3 +UART5_RX,P5_2 diff --git a/ports/alif/boards/manifest.py b/ports/alif/boards/manifest.py new file mode 100644 index 0000000000000..834aa46702731 --- /dev/null +++ b/ports/alif/boards/manifest.py @@ -0,0 +1,6 @@ +freeze("$(PORT_DIR)/modules/$(MCU_CORE)") +include("$(MPY_DIR)/extmod/asyncio") +require("dht") +require("neopixel") +require("onewire") +require("bundle-networking") diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h new file mode 100644 index 0000000000000..4b5cce67132e9 --- /dev/null +++ b/ports/alif/cyw43_configport.h @@ -0,0 +1,141 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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_CYW43_CONFIGPORT_H +#define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H + +// The board-level config will be included here, so it can set some CYW43 values. +#include "py/mpconfig.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/modnetwork.h" +#include "extmod/mpbthci.h" +#include "pendsv.h" + +#ifndef static_assert +#define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1] +#endif + +#define CYW43_USE_SPI (1) +#define CYW43_ENABLE_BLUETOOTH_OVER_UART (1) +#define CYW43_LWIP (1) +#define CYW43_USE_STATS (0) +#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) + +#if 0 +#define CYW43_VERBOSE_DEBUG (1) +#define CYW43_VDEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_DEBUG(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_INFO(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) +#endif + +#define CYW43_IOCTL_TIMEOUT_US (1000000) +#define CYW43_SLEEP_MAX (50) +#define CYW43_NETUTILS (1) +#define CYW43_CLEAR_SDIO_INT (1) + +#define CYW43_EPERM MP_EPERM // Operation not permitted +#define CYW43_EIO MP_EIO // I/O error +#define CYW43_EINVAL MP_EINVAL // Invalid argument +#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out + +#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER +#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT +#define CYW43_THREAD_LOCK_CHECK + +#define CYW43_HOST_NAME mod_network_hostname_data + +#define CYW43_SDPCM_SEND_COMMON_WAIT __WFE() +#define CYW43_DO_IOCTL_WAIT // __WFE() +#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() + +#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) + +#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT +#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT +#define CYW43_HAL_PIN_PULL_NONE 0 + +#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR + +#define cyw43_hal_ticks_us mp_hal_ticks_us +#define cyw43_hal_ticks_ms mp_hal_ticks_ms + +#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t +#define cyw43_hal_pin_read mp_hal_pin_read +#define cyw43_hal_pin_low mp_hal_pin_low +#define cyw43_hal_pin_high mp_hal_pin_high + +#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate +#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write +#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar + +#define cyw43_hal_get_mac mp_hal_get_mac +#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii +#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac + +#define CYW43_PIN_WL_REG_ON pin_WL_REG_ON +#define CYW43_PIN_WL_IRQ pin_WL_IRQ + +#define CYW43_PIN_BT_REG_ON pin_BT_REG_ON +#define CYW43_PIN_BT_HOST_WAKE pin_BT_HOST_WAKE +#define CYW43_PIN_BT_DEV_WAKE pin_BT_DEV_WAKE +#define CYW43_PIN_BT_CTS pin_BT_UART_CTS + +#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) + +void cyw43_post_poll_hook(void); + +static inline void cyw43_delay_us(uint32_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +static inline void cyw43_delay_ms(uint32_t ms) { + mp_hal_delay_ms(ms); +} + +static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) { + if (mode == MP_HAL_PIN_MODE_INPUT) { + mp_hal_pin_input(pin); + } else { + mp_hal_pin_output(pin); + } +} + +static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + mp_hal_pin_config_irq_falling(pin, enable); +} + +#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init +#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit +#define cyw43_bluetooth_controller_sleep_maybe mp_bluetooth_hci_controller_sleep_maybe +#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken +#define cyw43_bluetooth_controller_wakeup mp_bluetooth_hci_controller_wakeup + +#endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/cyw43_port_spi.c b/ports/alif/cyw43_port_spi.c new file mode 100644 index 0000000000000..6ebcaa057dc97 --- /dev/null +++ b/ports/alif/cyw43_port_spi.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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. + */ + +#if MICROPY_PY_NETWORK_CYW43 + +#include "lib/cyw43-driver/src/cyw43.h" +#include "lib/cyw43-driver/src/cyw43_internal.h" +#include "lib/cyw43-driver/src/cyw43_spi.h" + +#include "spi.h" +#include "sys_ctrl_spi.h" + +// CYW43 is connected to SPI3. +#define HW_SPI_UNIT (3) +#define HW_SPI ((SPI_Type *)SPI3_BASE) +#define SPI_BAUDRATE (16000000) +#define SPI_RX_FIFO_SIZE (16) + +// WL_IRQ is on P9_6. +#define WL_IRQ_IRQN (GPIO9_IRQ6_IRQn) +#define WL_IRQ_HANDLER GPIO9_IRQ6Handler + +// Must run at IRQ priority above PendSV so it can wake cyw43-driver when PendSV is disabled. +void WL_IRQ_HANDLER(void) { + if (gpio_read_int_rawstatus(pin_WL_IRQ->gpio, pin_WL_IRQ->pin)) { + gpio_interrupt_eoi(pin_WL_IRQ->gpio, pin_WL_IRQ->pin); + pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); + __SEV(); + } +} + +static void spi_bus_init(void) { + // Configure pins. + mp_hal_pin_output(pin_WL_CS); + mp_hal_pin_high(pin_WL_CS); + // NOTE: Alif recommends enabled input read for all SPI pins. + mp_hal_pin_config(pin_WL_SCLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_SCLK, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MOSI, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MOSI, HW_SPI_UNIT), true); + mp_hal_pin_config(pin_WL_MISO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(SPI_MISO, HW_SPI_UNIT), true); + + // Starts out clock_polarity=1, clock_phase=0. + spi_mode_master(HW_SPI); + spi_set_bus_speed(HW_SPI, SPI_BAUDRATE, GetSystemAHBClock()); + spi_set_mode(HW_SPI, SPI_MODE_2); + spi_set_protocol(HW_SPI, SPI_PROTO_SPI); + spi_set_dfs(HW_SPI, 8); + spi_set_tmod(HW_SPI, SPI_TMOD_TX_AND_RX); + spi_control_ss(HW_SPI, 0, SPI_SS_STATE_ENABLE); +} + +int cyw43_spi_init(cyw43_int_t *self) { + spi_bus_init(); + + // Configure IRQ for WL_IRQ (active low input). + NVIC_SetPriority(WL_IRQ_IRQN, IRQ_PRI_CYW43); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); + NVIC_EnableIRQ(WL_IRQ_IRQN); + + return 0; +} + +void cyw43_spi_deinit(cyw43_int_t *self) { + // Disable clock, SS and SPI. + spi_mask_interrupts(HW_SPI); + spi_control_ss(HW_SPI, 0, 0); + spi_disable(HW_SPI); + + // Disable SPI IRQ. + NVIC_DisableIRQ(WL_IRQ_IRQN); + NVIC_ClearPendingIRQ(WL_IRQ_IRQN); +} + +void cyw43_spi_gpio_setup(void) { +} + +void cyw43_spi_reset(void) { +} + +void cyw43_spi_set_polarity(cyw43_int_t *self, int pol) { + (void)self; + + if (pol == 0) { + spi_set_mode(HW_SPI, SPI_MODE_0); + } else { + spi_set_mode(HW_SPI, SPI_MODE_2); + } +} + +// tx must not be NULL. +// rx_len must be 0, or the same as tx_len. +int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_len, uint8_t *rx, size_t rx_len) { + (void)self; + + if (tx_len == 0 && rx_len == 0) { + return 0; + } + + mp_hal_pin_low(pin_WL_CS); + + // Must read the same amount of data that is written out to SPI. + rx_len = tx_len; + + while (tx_len || rx_len) { + // Only add data to the TX FIFO if: + // - there's data to add + // - and TX is not ahead of RX by more than the RX FIFO size + // - and there's space in the TX FIFO + if (tx_len && tx_len + SPI_RX_FIFO_SIZE > rx_len && (HW_SPI->SPI_SR & SPI_SR_TFNF) != 0) { + HW_SPI->SPI_DR[0] = *tx++; + --tx_len; + } + + // Take data from the RX FIFO and store it into the output buffer (if given). + if (rx_len && (HW_SPI->SPI_SR & SPI_SR_RFNE) != 0) { + uint8_t data = HW_SPI->SPI_DR[0]; + if (rx != NULL) { + *rx++ = data; + } + --rx_len; + } + } + + mp_hal_pin_high(pin_WL_CS); + + return 0; +} + +#endif diff --git a/ports/alif/fatfs_port.c b/ports/alif/fatfs_port.c new file mode 100644 index 0000000000000..5883c9f3b9447 --- /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 0000000000000..08b8ef8086bc3 --- /dev/null +++ b/ports/alif/irq.h @@ -0,0 +1,91 @@ +/* + * 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 +#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_MHU 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_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0) +#define IRQ_PRI_CSI NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) +#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) +#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) +#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) +#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 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/lwip_inc/arch/cc.h b/ports/alif/lwip_inc/arch/cc.h new file mode 100644 index 0000000000000..35d45afa71c8c --- /dev/null +++ b/ports/alif/lwip_inc/arch/cc.h @@ -0,0 +1,10 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H +#define MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H + +#include +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) { assert(1); } + +#define LWIP_NO_CTYPE_H 1 + +#endif // MICROPY_INCLUDED_ALIF_LWIP_ARCH_CC_H diff --git a/ports/alif/lwip_inc/arch/sys_arch.h b/ports/alif/lwip_inc/arch/sys_arch.h new file mode 100644 index 0000000000000..8b1a393741c96 --- /dev/null +++ b/ports/alif/lwip_inc/arch/sys_arch.h @@ -0,0 +1 @@ +// empty diff --git a/ports/alif/lwip_inc/lwipopts.h b/ports/alif/lwip_inc/lwipopts.h new file mode 100644 index 0000000000000..c0622225e10de --- /dev/null +++ b/ports/alif/lwip_inc/lwipopts.h @@ -0,0 +1,60 @@ +#ifndef MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H +#define MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H + +#include + +// This protection is not needed, instead we execute all lwIP code at PendSV priority +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#define LWIP_LOOPIF_MULTICAST 1 +#define LWIP_LOOPBACK_MAX_PBUFS 8 + +#define LWIP_IPV6 0 +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 + +#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER +#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +extern uint64_t se_services_rand64(void); +#define LWIP_RAND() se_services_rand64() + +#define MEM_SIZE (16 * 1024) +#define TCP_MSS (1460) +#define TCP_OVERSIZE (TCP_MSS) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN (2 * (TCP_SND_BUF / TCP_MSS)) +#define TCP_QUEUE_OOSEQ (1) +#define MEMP_NUM_TCP_SEG (2 * TCP_SND_QUEUELEN) + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_ALIF_LWIP_LWIPOPTS_H diff --git a/ports/alif/machine_adc.c b/ports/alif/machine_adc.c new file mode 100644 index 0000000000000..91eb95b5ba002 --- /dev/null +++ b/ports/alif/machine_adc.c @@ -0,0 +1,225 @@ +/* + * 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/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. + +#include "py/mphal.h" + +#include "analog_config.h" +#include "adc.h" +#include "sys_ctrl_adc.h" + +#define ADC_CHANNEL_TEMP_SENSOR (6) +#define ADC_CHANNEL_INT_VREF (7) + +static const uint8_t adc_instance_table[4] = { + ADC_INSTANCE_ADC12_0, + ADC_INSTANCE_ADC12_1, + ADC_INSTANCE_ADC12_2, + ADC_INSTANCE_ADC24_0, +}; + +static ADC_Type *const adc_regs_table[4] = { + (ADC_Type *)ADC120_BASE, + (ADC_Type *)ADC121_BASE, + (ADC_Type *)ADC122_BASE, + (ADC_Type *)ADC24_BASE, +}; + +static bool adc_inited[4]; +static conv_info_t adc_conv_info_table[4]; + +static void adc_nvic_config(unsigned int irq) { + NVIC_ClearPendingIRQ(irq); + NVIC_SetPriority(irq, IRQ_PRI_ADC); + NVIC_EnableIRQ(irq); +} + +static void adc_init(uint8_t adc_periph) { + if (adc_inited[adc_periph]) { + return; + } + + ADC_Type *regs = adc_regs_table[adc_periph]; + uint8_t adc_instance = adc_instance_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + adc_set_clk_control(adc_instance, true); + enable_cmp_periph_clk(); + analog_config_vbat_reg2(); + analog_config_cmp_reg2(); + adc_set_differential_ctrl(adc_instance, false); + adc_set_comparator_ctrl(adc_instance, true, 2); + + if (adc_instance == ADC_INSTANCE_ADC24_0) { + enable_adc24(); + set_adc24_output_rate(0); + set_adc24_bias(0); + } else { + adc_set_sample_width(regs, 32); + } + + adc_set_clk_div(regs, 4); + adc_set_avg_sample(regs, 32); + adc_set_n_shift_bit(regs, 1, 1); + adc_set_single_ch_scan_mode(regs, conv_info); + adc_unmask_interrupt(regs); + + adc_nvic_config(ADC120_DONE0_IRQ_IRQn); + adc_nvic_config(ADC120_DONE1_IRQ_IRQn); + adc_nvic_config(ADC121_DONE0_IRQ_IRQn); + adc_nvic_config(ADC121_DONE1_IRQ_IRQn); + adc_nvic_config(ADC122_DONE0_IRQ_IRQn); + adc_nvic_config(ADC122_DONE1_IRQ_IRQn); +} + +static uint16_t adc_config_and_read_u16(unsigned int adc_periph, uint32_t channel) { + ADC_Type *adc_regs = adc_regs_table[adc_periph]; + conv_info_t *conv_info = &adc_conv_info_table[adc_periph]; + + conv_info->status = ADC_CONV_STAT_NONE; + conv_info->mode = ADC_CONV_MODE_SINGLE_SHOT; + + // Select channel and start conversion. + adc_init_channel_select(adc_regs, channel); + adc_set_single_ch_scan_mode(adc_regs, conv_info); + adc_enable_single_shot_conv(adc_regs); + + // Wait for conversion to complete. + while (!(conv_info->status & ADC_CONV_STAT_COMPLETE)) { + __WFE(); + } + + return conv_info->sampled_value; +} + +static void adc_done0_irq_handler_helper(unsigned int adc_periph) { + adc_done0_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +static void adc_done1_irq_handler_helper(unsigned int adc_periph) { + adc_done1_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]); +} + +void ADC120_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(0); + __SEV(); +} + +void ADC120_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(0); + __SEV(); +} + +void ADC121_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(1); + __SEV(); +} + +void ADC121_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(1); + __SEV(); +} + +void ADC122_DONE0_IRQHandler(void) { + adc_done0_irq_handler_helper(2); + __SEV(); +} + +void ADC122_DONE1_IRQHandler(void) { + adc_done1_irq_handler_helper(2); + __SEV(); +} + +/******************************************************************************/ +// MicroPython bindings for machine.ADC + +#define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMP_SENSOR) }, \ + { MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_INT_VREF) }, \ + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + uint8_t adc_periph; + uint8_t adc_channel; +} machine_adc_obj_t; + +static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->adc_periph, self->adc_channel); +} + +// ADC(id) +static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t source = all_args[0]; + + uint8_t adc_periph; + uint8_t adc_channel; + const machine_pin_obj_t *pin = NULL; + + if (mp_obj_is_int(source)) { + // Get and validate channel number. + adc_channel = mp_obj_get_int(source); + if (adc_channel < ADC_CHANNEL_TEMP_SENSOR) { + adc_periph = 0; + } else if (adc_channel == ADC_CHANNEL_TEMP_SENSOR || adc_channel == ADC_CHANNEL_INT_VREF) { + adc_periph = 2; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); + } + } else { + // Get GPIO and check it has ADC capabilities. + pin = mp_hal_get_pin_obj(source); + if (pin->adc12_periph <= 2 && pin->adc12_channel <= 5) { + // Select the ADC12 peripheral and channel. + adc_periph = pin->adc12_periph; + adc_channel = pin->adc12_channel; + // Configure the pad for analog input. + pinconf_set(pin->port, pin->pin, PINMUX_ALTERNATE_FUNCTION_7, PADCTRL_READ_ENABLE); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities")); + } + } + + // Initialise the ADC peripheral. + adc_init(adc_periph); + + // Create ADC object. + machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type); + o->adc_periph = adc_periph; + o->adc_channel = adc_channel; + + return MP_OBJ_FROM_PTR(o); +} + +// read_u16() +static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { + return adc_config_and_read_u16(self->adc_periph, self->adc_channel); +} diff --git a/ports/alif/machine_i2c.c b/ports/alif/machine_i2c.c new file mode 100644 index 0000000000000..a710aeeb01132 --- /dev/null +++ b/ports/alif/machine_i2c.c @@ -0,0 +1,301 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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 "py/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_HW_ENABLE_HW_I2C +#include "i2c.h" + +#define I2C_DEFAULT_FREQ (400000) +#define I2C_DEFAULT_TIMEOUT (50000) + +#define I2C_DAT_INDEX (0) +#define I2C_TX_FIFO_LEN (I2C_FIFO_DEPTH / 2) +#define I2C_RX_FIFO_LEN (I2C_FIFO_DEPTH / 2) + +#define I2C_SPEED(freq) \ + ((freq) <= 100000 ? I2C_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_SPEED_FAST : \ + I2C_SPEED_FASTPLUS)) + +#define I2C_IC_CON_SPEED(freq) \ + ((freq) <= 100000 ? I2C_IC_CON_SPEED_STANDARD : \ + ((freq) <= 400000 ? I2C_IC_CON_SPEED_FAST : \ + I2C_IC_CON_SPEED_HIGH)) +#define I2C_IC_CON_MASTER_TX_EMPTY_CTRL (1 << 8) + +#define I2C_IC_STATUS_RFNE I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY +#define I2C_IC_STATUS_TFNF I2C_IC_STATUS_TRANSMIT_FIFO_NOT_FULL +#define I2C_STAT_ERRORS (I2C_IC_INTR_STAT_TX_ABRT | I2C_IC_INTR_STAT_TX_OVER | \ + I2C_IC_INTR_STAT_RX_OVER | I2C_IC_INTR_STAT_RX_UNDER) + +#define debug_printf(...) // mp_printf(&mp_plat_print, "i2c.c: " __VA_ARGS__) +#define I2C_CHECK_ERRORS(base) \ + if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \ + uint32_t status = base->I2C_RAW_INTR_STAT; \ + debug_printf("status: 0x%lx raw_int: 0x%lx abort: 0x%lx line: %d\n", \ + base->I2C_STATUS, status, base->I2C_TX_ABRT_SOURCE, __LINE__); \ + (void)status; \ + (void)base->I2C_CLR_TX_ABRT; \ + (void)base->I2C_CLR_ACTIVITY; \ + return -MP_EIO; \ + } + +typedef struct _machine_i2c_obj_t { + mp_obj_base_t base; + uint32_t i2c_id; + I2C_Type *i2c; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; + uint32_t freq; + uint32_t timeout; +} machine_i2c_obj_t; + +static machine_i2c_obj_t machine_i2c_obj[] = { + #if defined(MICROPY_HW_I2C0_SCL) + [0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA}, + #endif + #if defined(MICROPY_HW_I2C1_SCL) + [1] = {{&machine_i2c_type}, 1, (I2C_Type *)I2C1_BASE, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA}, + #endif + #if defined(MICROPY_HW_I2C2_SCL) + [2] = {{&machine_i2c_type}, 2, (I2C_Type *)I2C2_BASE, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA}, + #endif + #if defined(MICROPY_HW_I2C3_SCL) + [3] = {{&machine_i2c_type}, 3, (I2C_Type *)I2C3_BASE, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA}, + #endif +}; + +static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2C(%u, freq=%u, scl=%q, sda=%q, timeout=%u)", + self->i2c_id, self->freq, self->scl->name, self->sda->name, self->timeout); +} + +mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = I2C_DEFAULT_FREQ} }, + { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2C_DEFAULT_TIMEOUT} }, + }; + + // Parse args. + 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); + + // Get I2C bus. + int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj) || !machine_i2c_obj[i2c_id].i2c) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + // Get static peripheral object. + machine_i2c_obj_t *self = &machine_i2c_obj[i2c_id]; + + // Set args + self->freq = args[ARG_freq].u_int; + self->timeout = args[ARG_timeout].u_int; + + // here we would check the scl/sda pins and configure them, but it's not implemented + if (args[ARG_scl].u_obj != mp_const_none || args[ARG_sda].u_obj != mp_const_none) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of scl/sda is not implemented")); + } + + // Disable I2C controller. + i2c_disable(self->i2c); + + // Configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); + + // Initialize I2C controller. + self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE | + I2C_IC_CON_MASTER_RESTART_EN | + I2C_IC_CON_MASTER_TX_EMPTY_CTRL | + I2C_IC_CON_SPEED(self->freq); + + // Configure FIFO threshold. + self->i2c->I2C_TX_TL = I2C_TX_FIFO_LEN; + self->i2c->I2C_RX_TL = I2C_RX_FIFO_LEN; + + // Configure clock. + i2c_master_set_clock(self->i2c, GetSystemAPBClock() / 1000, I2C_SPEED(self->freq)); + + // Enable I2C controller. + i2c_clear_all_interrupt(self->i2c); + i2c_enable(self->i2c); + + return MP_OBJ_FROM_PTR(self); +} + +static int machine_i2c_poll_flags(I2C_Type *base, uint32_t flags, uint32_t timeout_us) { + mp_uint_t tick_start = mp_hal_ticks_us(); + while (!(base->I2C_STATUS & flags)) { + I2C_CHECK_ERRORS(base); + if ((mp_hal_ticks_us() - tick_start) >= timeout_us) { + return -MP_ETIMEDOUT; + } + // Can't delay or handle pending events here otherwise we risk + // the FIFO getting empty, which will generate a STOP condition. + } + return 0; +} + +static int machine_i2c_write(machine_i2c_obj_t *self, uint8_t *buf, size_t tx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0; tx_idx < tx_size;) { + // Write data to FIFO + if (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF) { + self->i2c->I2C_DATA_CMD = (uint16_t)buf[tx_idx++]; + I2C_CHECK_ERRORS(self->i2c); + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +static int machine_i2c_read(machine_i2c_obj_t *self, uint8_t *buf, size_t rx_size) { + mp_uint_t tick_start = mp_hal_ticks_us(); + for (size_t tx_idx = 0, rx_idx = 0; rx_idx < rx_size;) { + // Write command to FIFO + if (tx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF)) { + self->i2c->I2C_DATA_CMD = I2C_IC_DATA_CMD_READ_REQ; + I2C_CHECK_ERRORS(self->i2c); + ++tx_idx; + } + + // Read data from FIFO + while (rx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_RFNE)) { + buf[rx_idx++] = self->i2c->I2C_DATA_CMD & 0xFF; + tick_start = mp_hal_ticks_us(); + } + + // Check for timeout + if ((mp_hal_ticks_us() - tick_start) >= self->timeout) { + return -MP_ETIMEDOUT; + } + } + return 0; +} + +int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + int ret = 0; + uint32_t bytes = 0; + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + + // The DesignWare I2C IP on AE3 is configured such that it auto-generates a STOP + // condition when the TX FIFO gets empty. In other words, the code can't have any + // control over STOP condition generation. The only fix for this would be to buffer + // complete read/write sequences and send them out when the STOP flag is set. + if (!(flags & MP_MACHINE_I2C_FLAG_STOP)) { + mp_raise_ValueError(MP_ERROR_TEXT("nostop flag is not supported")); + } + + i2c_clear_all_interrupt(self->i2c); + i2c_set_target_addr(self->i2c, addr, I2C_7BIT_ADDRESS, 0); + + // Workaround issue with hardware I2C not accepting zero-length writes. + if (!bufs->len) { + mp_machine_i2c_buf_t bufs = { 0 }; + + mp_machine_soft_i2c_obj_t soft_i2c = { + .base = { &mp_machine_soft_i2c_type }, + .scl = self->scl, + .sda = self->sda, + .us_timeout = self->timeout, + .us_delay = 500000 / self->freq + 1, + }; + + // Switch pins to GPIO/OD. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, 0, true); + + // Perform the transfer. + ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); + + // Re-configure I2C pins. + mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SCL, self->i2c_id), true); + mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(I2C_SDA, self->i2c_id), true); + + return ret; + } + + for (size_t i = 0; i < n; i++) { + mp_machine_i2c_buf_t *buf = &bufs[i]; + if (i == 0 && (flags & MP_MACHINE_I2C_FLAG_WRITE1)) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } else if (flags & MP_MACHINE_I2C_FLAG_READ) { + ret = machine_i2c_read(self, buf->buf, buf->len); + } else if (bufs->len != 0) { + ret = machine_i2c_write(self, buf->buf, buf->len); + } + if (ret < 0) { + return ret; + } + bytes += bufs->len; + } + + // Wait for TX FIFO empty + ret = machine_i2c_poll_flags(self->i2c, I2C_IC_STATUS_TFE, self->timeout); + if (ret < 0) { + return ret; + } + + return bytes; +} + +static const mp_machine_i2c_p_t machine_i2c_p = { + .transfer_supports_write1 = true, + .transfer = machine_i2c_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_i2c_type, + MP_QSTR_I2C, + MP_TYPE_FLAG_NONE, + make_new, machine_i2c_make_new, + print, machine_i2c_print, + protocol, &machine_i2c_p, + locals_dict, &mp_machine_i2c_locals_dict + ); +#endif // MICROPY_HW_ENABLE_HW_I2C diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c new file mode 100644 index 0000000000000..02c5e482adb89 --- /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/machine_rtc.c b/ports/alif/machine_rtc.c new file mode 100644 index 0000000000000..6473d1d80fbff --- /dev/null +++ b/ports/alif/machine_rtc.c @@ -0,0 +1,109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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 "py/mperrno.h" +#include "extmod/modmachine.h" +#include "rtc.h" +#include "sys_ctrl_rtc.h" + +typedef struct _machine_rtc_obj_t { + mp_obj_base_t base; + LPRTC_Type *rtc; +} machine_rtc_obj_t; + +// Singleton RTC object. +static const machine_rtc_obj_t machine_rtc = {{&machine_rtc_type}, (LPRTC_Type *)LPRTC_BASE}; + +void LPRTC_IRQHandler(void) { + lprtc_interrupt_ack(machine_rtc.rtc); + lprtc_interrupt_disable(machine_rtc.rtc); +} + +static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + const machine_rtc_obj_t *self = &machine_rtc; + + // Check arguments. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + enable_lprtc_clk(); + lprtc_prescaler_disable(self->rtc); + lprtc_counter_wrap_disable(self->rtc); + lprtc_interrupt_disable(self->rtc); + lprtc_interrupt_unmask(self->rtc); + + NVIC_SetPriority(LPRTC_IRQ_IRQn, IRQ_PRI_RTC); + NVIC_ClearPendingIRQ(LPRTC_IRQ_IRQn); + NVIC_EnableIRQ(LPRTC_IRQ_IRQn); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_id, ARG_time, ARG_repeat }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_time, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_repeat, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + machine_rtc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); + + if (mp_obj_is_int(args[ARG_time].u_obj)) { + uint32_t seconds = mp_obj_get_int(args[1].u_obj) / 1000; + + lprtc_counter_disable(self->rtc); + lprtc_load_count(self->rtc, 1); + lprtc_load_counter_match_register(self->rtc, seconds * 32768); + + lprtc_interrupt_ack(self->rtc); + lprtc_interrupt_enable(self->rtc); + lprtc_counter_enable(self->rtc); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s)")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_alarm_obj, 1, machine_rtc_alarm); + +static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_alarm), MP_ROM_PTR(&machine_rtc_alarm_obj) }, +}; +static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_rtc_type, + MP_QSTR_RTC, + MP_TYPE_FLAG_NONE, + make_new, machine_rtc_make_new, + locals_dict, &machine_rtc_locals_dict + ); diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c new file mode 100644 index 0000000000000..abb60bc4e8325 --- /dev/null +++ b/ports/alif/machine_spi.c @@ -0,0 +1,332 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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 "py/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_PY_MACHINE_SPI +#include "clk.h" +#include "spi.h" +#include "sys_ctrl_spi.h" + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + uint8_t id; + SPI_Type *inst; + bool is_lp; +} machine_spi_obj_t; + +static machine_spi_obj_t machine_spi_obj[] = { + #if defined(MICROPY_HW_SPI0_SCK) + [0] = {{&machine_spi_type}, 0, (SPI_Type *)SPI0_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI1_SCK) + [1] = {{&machine_spi_type}, 1, (SPI_Type *)SPI1_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI2_SCK) + [2] = {{&machine_spi_type}, 2, (SPI_Type *)SPI2_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI3_SCK) + [3] = {{&machine_spi_type}, 3, (SPI_Type *)SPI3_BASE, false}, + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + [4] = {{&machine_spi_type}, 4, (SPI_Type *)LPSPI0_BASE, true}, + #endif + +}; + +static const uint8_t spi_pin_alt[] = { + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SS0, +}; + +static const uint8_t lpspi_pin_alt[] = { + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SS, +}; + +static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { + return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); +} + +static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, + uint32_t polarity, uint32_t phase, uint32_t bits, uint32_t firstbit) { + const machine_pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL }; + switch (spi->id) { + #if defined(MICROPY_HW_SPI0_SCK) + case 0: + pins[0] = MICROPY_HW_SPI0_SCK; + pins[1] = MICROPY_HW_SPI0_MISO; + pins[2] = MICROPY_HW_SPI0_MOSI; + #if defined(MICROPY_HW_SPI0_NSS) + pins[3] = MICROPY_HW_SPI0_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI1_SCK) + case 1: + pins[0] = MICROPY_HW_SPI1_SCK; + pins[1] = MICROPY_HW_SPI1_MISO; + pins[2] = MICROPY_HW_SPI1_MOSI; + #if defined(MICROPY_HW_SPI1_NSS) + pins[3] = MICROPY_HW_SPI1_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI2_SCK) + case 2: + pins[0] = MICROPY_HW_SPI2_SCK; + pins[1] = MICROPY_HW_SPI2_MISO; + pins[2] = MICROPY_HW_SPI2_MOSI; + #if defined(MICROPY_HW_SPI2_NSS) + pins[3] = MICROPY_HW_SPI2_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI3_SCK) + case 3: + pins[0] = MICROPY_HW_SPI3_SCK; + pins[1] = MICROPY_HW_SPI3_MISO; + pins[2] = MICROPY_HW_SPI3_MOSI; + #if defined(MICROPY_HW_SPI3_NSS) + pins[3] = MICROPY_HW_SPI3_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + case 4: // LPSPI0 + pins[0] = MICROPY_HW_LPSPI0_SCK; + pins[1] = MICROPY_HW_LPSPI0_MISO; + pins[2] = MICROPY_HW_LPSPI0_MOSI; + #if defined(MICROPY_HW_LPSPI0_NSS) + pins[3] = MICROPY_HW_LPSPI0_NSS; + #endif + break; + #endif + default: + return; + } + + // Disable SPI. + spi_disable(spi->inst); + + // Enable clocks. + if (spi->is_lp) { + enable_lpspi_clk(); + } + + // Configure SPI pins. + const uint8_t *alt; + if (spi->id <= 3) { + alt = spi_pin_alt; + } else { + alt = lpspi_pin_alt; + } + for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { + mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_MAKE(alt[i], spi->id), true); + } + + // Disable all interrupts. + spi_mask_interrupts(spi->inst); + + // Configure baudrate clock + spi_set_bus_speed(spi->inst, baudrate, spi_get_clk(spi)); + + // Configure FIFOs + spi_set_tx_threshold(spi->inst, 0); + spi_set_rx_threshold(spi->inst, 0); + if (!spi->is_lp) { + spi_set_rx_sample_delay(spi->inst, 0); + spi_set_tx_fifo_start_level(spi->inst, 0); + } + + // Configure SPI bus mode. + uint32_t spi_mode = (polarity << 1) | phase; + if (!spi->is_lp) { + spi_set_mode(spi->inst, spi_mode); + } else { + lpspi_set_mode(spi->inst, spi_mode); + } + + // Configure SPI bus protocol. + uint32_t spi_proto = SPI_PROTO_SPI; + if (!spi->is_lp) { + spi_set_protocol(spi->inst, spi_proto); + } else { + lpspi_set_protocol(spi->inst, spi_proto); + } + + // Configure SPI transfer mode. + if (!spi->is_lp) { + spi_mode_master(spi->inst); + } + + // Configure frame size. + if (!spi->is_lp) { + spi_set_dfs(spi->inst, bits); + } else { + lpspi_set_dfs(spi->inst, bits); + } + + // Configure slave select pin + spi_control_ss(spi->inst, 0, true); + if (!spi->is_lp) { + spi_set_sste(spi->inst, false); + } else { + lpspi_set_sste(spi->inst, false); + } + + // Clear IRQs. + (void)spi->inst->SPI_ICR; + + // Enable SPI. + spi_enable(spi->inst); +} + +static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate = spi_get_bus_speed(self->inst, spi_get_clk(self)); + mp_printf(print, "SPI(%u, baudrate=%u, lp=%u)", self->id, baudrate, self->is_lp); +} + +mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + // Parse args. + 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); + + // Get spi bus. + int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj) || !machine_spi_obj[spi_id].inst) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get static peripheral object. + machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented + if (args[ARG_sck].u_obj != MP_OBJ_NULL || + args[ARG_mosi].u_obj != MP_OBJ_NULL || + args[ARG_miso].u_obj != MP_OBJ_NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of sck/mosi/miso is not implemented")); + } + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + 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); + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); +} + +static void machine_spi_deinit(mp_obj_base_t *self_in) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + // Disable all interrupts. + spi_mask_interrupts(self->inst); + // Disable SS pin. + spi_control_ss(self->inst, 0, 0); + // Disable SPI. + spi_disable(self->inst); + // Deinitialize GPIOs and clocks. + if (self->is_lp) { + disable_lpspi_clk(); + } +} + +static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + spi_transfer_t spi_xfer = { + .tx_buff = src, + .tx_total_cnt = len, + .rx_buff = dest, + .rx_total_cnt = len, + .tx_default_val = 0xFF, + .tx_default_enable = true, + .mode = SPI_TMOD_TX_AND_RX, + }; + // TODO redo transfer_blocking to timeout and poll events. + if (!self->is_lp) { + spi_transfer_blocking(self->inst, &spi_xfer); + } else { + lpspi_transfer_blocking(self->inst, &spi_xfer); + } +} + +static const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .deinit = machine_spi_deinit, + .transfer = machine_spi_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_spi_type, + MP_QSTR_SPI, + MP_TYPE_FLAG_NONE, + make_new, machine_spi_make_new, + print, machine_spi_print, + protocol, &machine_spi_p, + locals_dict, &mp_machine_spi_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/ports/alif/machine_uart.c b/ports/alif/machine_uart.c new file mode 100644 index 0000000000000..746c86677f956 --- /dev/null +++ b/ports/alif/machine_uart.c @@ -0,0 +1,560 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 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/machine_uart.c via MICROPY_PY_MACHINE_UART_INCLUDEFILE. + +#include "py/mphal.h" +#include "py/mperrno.h" +#include "mpuart.h" + +#define DEFAULT_UART_BAUDRATE (115200) +#define DEFAULT_UART_BITS (8) +#define DEFAULT_UART_PARITY (MP_ROM_NONE) +#define DEFAULT_UART_STOP (1) +#define DEFAULT_UART_RX_BUFFER_SIZE (256) + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + unsigned int uart_id; + uint32_t baudrate; + UART_DATA_BITS bits; + UART_PARITY parity; + UART_STOP_BITS stop; + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; + uint32_t flow; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) + uint8_t *rx_ringbuf_array; + ringbuf_t rx_ringbuf; + uint32_t irq_flags; + uint32_t irq_trigger; + mp_irq_obj_t irq_obj; +} machine_uart_obj_t; + +typedef struct _machine_uart_default_pins_t { + mp_hal_pin_obj_t tx; + mp_hal_pin_obj_t rx; + mp_hal_pin_obj_t rts; + mp_hal_pin_obj_t cts; +} machine_uart_default_pins_t; + +static const machine_uart_default_pins_t machine_uart_default_pins[UART_MAX] = { + [0] = { + #if defined(MICROPY_HW_UART0_TX) && defined(MICROPY_HW_UART0_RX) + MICROPY_HW_UART0_TX, MICROPY_HW_UART0_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART0_RTS) && defined(MICROPY_HW_UART0_CTS) + MICROPY_HW_UART0_RTS, MICROPY_HW_UART0_CTS, + #else + NULL, NULL, + #endif + }, + [1] = { + #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX) + MICROPY_HW_UART1_TX, MICROPY_HW_UART1_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART1_RTS) && defined(MICROPY_HW_UART1_CTS) + MICROPY_HW_UART1_RTS, MICROPY_HW_UART1_CTS, + #else + NULL, NULL, + #endif + }, + [2] = { + #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX) + MICROPY_HW_UART2_TX, MICROPY_HW_UART2_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART2_RTS) && defined(MICROPY_HW_UART2_CTS) + MICROPY_HW_UART2_RTS, MICROPY_HW_UART2_CTS, + #else + NULL, NULL, + #endif + }, + [3] = { + #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX) + MICROPY_HW_UART3_TX, MICROPY_HW_UART3_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART3_RTS) && defined(MICROPY_HW_UART3_CTS) + MICROPY_HW_UART3_RTS, MICROPY_HW_UART3_CTS, + #else + NULL, NULL, + #endif + }, + [4] = { + #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX) + MICROPY_HW_UART4_TX, MICROPY_HW_UART4_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART4_RTS) && defined(MICROPY_HW_UART4_CTS) + MICROPY_HW_UART4_RTS, MICROPY_HW_UART4_CTS, + #else + NULL, NULL, + #endif + }, + [5] = { + #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX) + MICROPY_HW_UART5_TX, MICROPY_HW_UART5_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART5_RTS) && defined(MICROPY_HW_UART5_CTS) + MICROPY_HW_UART5_RTS, MICROPY_HW_UART5_CTS, + #else + NULL, NULL, + #endif + }, + [6] = { + #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX) + MICROPY_HW_UART6_TX, MICROPY_HW_UART6_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART6_RTS) && defined(MICROPY_HW_UART6_CTS) + MICROPY_HW_UART6_RTS, MICROPY_HW_UART6_CTS, + #else + NULL, NULL, + #endif + }, + [7] = { + #if defined(MICROPY_HW_UART7_TX) && defined(MICROPY_HW_UART7_RX) + MICROPY_HW_UART7_TX, MICROPY_HW_UART7_RX, + #else + NULL, NULL, + #endif + #if defined(MICROPY_HW_UART7_RTS) && defined(MICROPY_HW_UART7_CTS) + MICROPY_HW_UART7_RTS, MICROPY_HW_UART7_CTS, + #else + NULL, NULL, + #endif + }, +}; + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger); + +MP_REGISTER_ROOT_POINTER(struct _machine_uart_obj_t *machine_uart_obj_all[UART_MAX]); + +/******************************************************************************/ +// MicroPython bindings for UART + +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart___del___obj) }, \ + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_FLOW_CONTROL_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_FLOW_CONTROL_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(MP_UART_IRQ_RX) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(MP_UART_IRQ_RXIDLE) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(MP_UART_IRQ_TXIDLE) }, \ + +#define GET_PIN_WITH_DEFAULT(uart_id_, pin_name, pin_selection) \ + (pin_selection == mp_const_none ? machine_uart_default_pins[uart_id_].pin_name : mp_hal_get_pin_obj(pin_selection)) + +static void machine_uart_set_bits(machine_uart_obj_t *self, mp_int_t bits) { + if (!(5 <= bits && bits <= 8)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + self->bits = UART_DATA_BITS_5 + (bits - 5); +} + +static void machine_uart_set_parity(machine_uart_obj_t *self, mp_obj_t parity) { + if (parity == mp_const_none) { + self->parity = UART_PARITY_NONE; + } else if (mp_obj_get_int(parity) & 1) { + self->parity = UART_PARITY_ODD; + } else { + self->parity = UART_PARITY_EVEN; + } +} + +static void machine_uart_set_stop(machine_uart_obj_t *self, mp_int_t stop) { + if (!(1 <= stop && stop <= 2)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid stop")); + } + self->stop = UART_STOP_BITS_1 + (stop - 1); +} + +static void machine_uart_set_timeout_char(machine_uart_obj_t *self, mp_int_t timeout_char) { + // Make sure timeout_char is at least as long as a whole character (13 bits to be safe). + uint32_t min_timeout_char = 13000 / self->baudrate + 1; + self->timeout_char = MAX(min_timeout_char, timeout_char); +} + +static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + static const char *parity_name[] = {[UART_PARITY_NONE] = "None", [UART_PARITY_EVEN] = "0", [UART_PARITY_ODD] = "1"}; + + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + unsigned int bits = 5 + (self->bits - UART_DATA_BITS_5); + const char *parity = parity_name[self->parity]; + unsigned int stop = 1 + (self->stop - UART_STOP_BITS_1); + + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " + "tx=%q, rx=%q, ", + self->uart_id, self->baudrate, bits, parity, stop, + mp_hal_pin_name(self->tx), mp_hal_pin_name(self->rx)); + if (self->rts != NULL) { + mp_printf(print, "rts=%q, ", mp_hal_pin_name(self->rts)); + } + if (self->cts != NULL) { + mp_printf(print, "cts=%q, ", mp_hal_pin_name(self->cts)); + } + mp_printf(print, "flow=", self->rts); + if (self->flow == 0) { + mp_printf(print, "0"); + } else { + if (self->flow & UART_FLOW_CONTROL_RTS) { + mp_printf(print, "RTS"); + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "|"); + } + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + mp_printf(print, "CTS"); + } + } + mp_printf(print, ", timeout=%u, timeout_char=%u, rxbuf=%u)", + self->timeout, self->timeout_char, self->rx_ringbuf.size - 1); +} + +static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { + ARG_id, + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_tx, ARG_rx, ARG_rts, ARG_cts, + ARG_flow, ARG_timeout, ARG_timeout_char, ARG_rxbuf, + }; + static const mp_arg_t allowed_args[] = { + #if !defined(MICROPY_HW_DEFAULT_UART_ID) + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + #else + { MP_QSTR_id, MP_ARG_INT, {.u_int = MICROPY_HW_DEFAULT_UART_ID} }, + #endif + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_UART_BAUDRATE} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = DEFAULT_UART_BITS} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = DEFAULT_UART_PARITY} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = DEFAULT_UART_STOP} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_UART_RX_BUFFER_SIZE} }, + }; + + // Parse args. + 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); + + // Get UART bus. + int uart_id = args[ARG_id].u_int; + if (uart_id < 0 || uart_id >= UART_MAX) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); + } + + machine_uart_obj_t *self; + bool need_init; + if (MP_STATE_PORT(machine_uart_obj_all)[uart_id] == NULL) { + // Create new UART object. + self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type); + self->uart_id = uart_id; + need_init = true; + } else { + // Reference existing UART object. + self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + need_init = n_args > 1 || n_kw > 0; + } + + if (need_init) { + // Set the UART parameters. + self->baudrate = args[ARG_baudrate].u_int; + machine_uart_set_bits(self, args[ARG_bits].u_int); + machine_uart_set_parity(self, args[ARG_parity].u_obj); + machine_uart_set_stop(self, args[ARG_stop].u_int); + self->tx = GET_PIN_WITH_DEFAULT(self->uart_id, tx, args[ARG_tx].u_obj); + self->rx = GET_PIN_WITH_DEFAULT(self->uart_id, rx, args[ARG_rx].u_obj); + self->rts = GET_PIN_WITH_DEFAULT(self->uart_id, rts, args[ARG_rts].u_obj); + self->cts = GET_PIN_WITH_DEFAULT(self->uart_id, cts, args[ARG_cts].u_obj); + self->flow = args[ARG_flow].u_int; + self->timeout = args[ARG_timeout].u_int; + uint32_t min_timeout_char = 13000 / self->baudrate + 1; // 13 bits to be safe + self->timeout = MAX(min_timeout_char, args[ARG_timeout_char].u_int); + + // Check TX/RX pins are given. + if (self->tx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("tx not given")); + } + if (self->rx == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rx not given")); + } + + // Configure flow control. + mp_hal_pin_obj_t rts = NULL; + mp_hal_pin_obj_t cts = NULL; + if (self->flow & UART_FLOW_CONTROL_RTS) { + if (self->rts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("rts not given")); + } + rts = self->rts; + } + if (self->flow & UART_FLOW_CONTROL_CTS) { + if (self->cts == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("cts not given")); + } + cts = self->cts; + } + + // Allocate the RX buffer. + size_t rxbuf_len = args[ARG_rxbuf].u_int; + self->rx_ringbuf_array = m_new(uint8_t, rxbuf_len + 1); + self->rx_ringbuf.buf = self->rx_ringbuf_array; + self->rx_ringbuf.size = rxbuf_len + 1; + self->rx_ringbuf.iput = 0; + self->rx_ringbuf.iget = 0; + + // Reset the IRQ state + self->irq_flags = 0; + self->irq_trigger = 0; + self->irq_obj.base.type = NULL; + + // Initialize the UART hardware. + mp_uart_init(self->uart_id, self->baudrate, self->bits, self->parity, self->stop, self->tx, self->rx, &self->rx_ringbuf); + + mp_uart_set_flow(self->uart_id, rts, cts); + } + + MP_STATE_PORT(machine_uart_obj_all)[uart_id] = self; + + return MP_OBJ_FROM_PTR(self); +} + +static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, + ARG_timeout, ARG_timeout_char, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // 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); + + bool change_baudrate = false; + bool change_bits_parity_stop = false; + + // Change baudrate if requested. + if (args[ARG_baudrate].u_int > 0) { + self->baudrate = args[ARG_baudrate].u_int; + change_baudrate = true; + } + + // Change data bits if requested. + if (args[ARG_bits].u_int > 0) { + machine_uart_set_bits(self, args[ARG_bits].u_int); + change_bits_parity_stop = true; + } + + // Change parity if requested. + if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) { + machine_uart_set_parity(self, args[ARG_parity].u_obj); + change_bits_parity_stop = true; + } + + // Change stop bits if requested. + if (args[ARG_stop].u_int > 0) { + machine_uart_set_stop(self, args[ARG_stop].u_int); + change_bits_parity_stop = true; + } + + // Change timeout if requested. + if (args[ARG_timeout].u_int >= 0) { + self->timeout = args[ARG_timeout].u_int; + } + + // Change timeout_char if requested. + if (args[ARG_timeout_char].u_int >= 0) { + machine_uart_set_timeout_char(self, args[ARG_timeout_char].u_int); + } + + // Reconfigure the hardware parameters that have changed. + if (change_baudrate) { + mp_uart_set_baudrate(self->uart_id, self->baudrate); + } + if (change_bits_parity_stop) { + mp_uart_set_bits_parity_stop(self->uart_id, self->bits, self->parity, self->stop); + } +} + +static mp_obj_t machine_uart___del__(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uart_deinit(self->uart_id); + MP_STATE_PORT(machine_uart_obj_all)[self->uart_id] = NULL; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_uart___del___obj, machine_uart___del__); + +// Turn off the UART bus. +static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + mp_uart_deinit(self->uart_id); +} + +// Return number of characters waiting. +static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { + return mp_uart_rx_any(self->uart_id); +} + +// Since uart.write() waits up to the last byte, uart.txdone() always returns True. +static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { + return !!mp_uart_tx_any(self->uart_id); +} + +// Change the trigger for the UART IRQ. +static mp_uint_t machine_uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->irq_trigger = new_trigger; + mp_uart_set_irq_callback(self->uart_id, self->irq_trigger, machine_uart_irq_callback); + return 0; +} + +// Get UART IRQ state. +static mp_uint_t machine_uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t machine_uart_irq_methods = { + .trigger = machine_uart_irq_trigger, + .info = machine_uart_irq_info, +}; + +// Retrieve and configure the UART IRQ object. +static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + if (self->irq_obj.base.type == NULL) { + mp_irq_init(&self->irq_obj, &machine_uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + self->irq_obj.handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + self->irq_obj.ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + machine_uart_irq_trigger(MP_OBJ_FROM_PTR(self), args[MP_IRQ_ARG_INIT_trigger].u_int); + } + + return &self->irq_obj; +} + +static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t start = mp_hal_ticks_ms(); + mp_uint_t timeout = self->timeout; + uint8_t *dest = buf_in; + + for (size_t i = 0; i < size; i++) { + // Wait for the first/next character + while (!mp_uart_rx_any(self->uart_id)) { + mp_uint_t elapsed = mp_hal_ticks_ms() - start; + if (elapsed > timeout) { // timed out + if (i <= 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + mp_event_handle_nowait(); + } + *dest++ = mp_uart_rx_char(self->uart_id); + start = mp_hal_ticks_ms(); // Inter-character timeout + timeout = self->timeout_char; + } + return size; +} + +static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uart_tx_data(self->uart_id, buf_in, size); + return size; +} + +static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && mp_uart_rx_any(self->uart_id)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR)) { + ret |= MP_STREAM_POLL_WR; + } + } else if (request == MP_STREAM_FLUSH) { + uint32_t t0 = mp_hal_ticks_ms(); + const uint32_t timeout_ms = 100 + 10 * mp_uart_tx_any(self->uart_id); + while (mp_uart_tx_any(self->uart_id)) { + if (mp_hal_ticks_ms() - t0 > timeout_ms) { + *errcode = MP_ETIMEDOUT; + ret = MP_STREAM_ERROR; + } + mp_event_handle_nowait(); + } + return 0; + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +static void machine_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + machine_uart_obj_t *self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; + self->irq_flags = trigger; + if (self->irq_obj.base.type != NULL && (self->irq_flags & self->irq_trigger)) { + mp_irq_handler(&self->irq_obj); + } +} diff --git a/ports/alif/main.c b/ports/alif/main.c new file mode 100644 index 0000000000000..fd35604cbc234 --- /dev/null +++ b/ports/alif/main.c @@ -0,0 +1,191 @@ +/* + * 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 "extmod/modbluetooth.h" +#include "extmod/modnetwork.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" +#include "shared/runtime/softtimer.h" +#include "shared/tinyusb/mp_usbd.h" +#include "tusb.h" +#include "mpbthciport.h" +#include "mpuart.h" +#include "ospi_flash.h" +#include "pendsv.h" +#include "se_services.h" +#include "system_tick.h" + +#if MICROPY_PY_LWIP +#include "lwip/init.h" +#include "lwip/apps/mdns.h" +#endif +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif + +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++) { + } + } +} + +int main(void) { + system_tick_init(); + + MICROPY_BOARD_STARTUP(); + + pendsv_init(); + se_services_init(); + + MICROPY_BOARD_EARLY_INIT(); + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_init_repl(); + #endif + #if MICROPY_HW_ENABLE_OSPI + if (ospi_flash_init() != 0) { + MICROPY_BOARD_FATAL_ERROR("ospi_init failed"); + } + #endif + #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); + + #if MICROPY_PY_LWIP + // lwIP doesn't allow to reinitialise itself by subsequent calls to this function + // because the system timeout list (next_timeout) is only ever reset by BSS clearing. + // So for now we only init the lwIP stack once on power-up. + lwip_init(); + #if LWIP_MDNS_RESPONDER + mdns_resp_init(); + #endif + mod_network_lwip_init(); + #endif + + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_hci_init(); + #endif + + #if MICROPY_PY_NETWORK_CYW43 + { + cyw43_init(&cyw43_state); + uint8_t buf[8]; + memcpy(&buf[0], "ALIF", 4); + mp_hal_get_mac_ascii(MP_HAL_MAC_WLAN0, 8, 4, (char *)&buf[4]); + cyw43_wifi_ap_set_ssid(&cyw43_state, 8, buf); + cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_WPA2_AES_PSK); + cyw43_wifi_ap_set_password(&cyw43_state, 8, (const uint8_t *)"alif0123"); + } + #endif + + 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"); + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_deinit(); + #endif + soft_timer_deinit(); + 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/mbedtls/mbedtls_config_port.h b/ports/alif/mbedtls/mbedtls_config_port.h new file mode 100644 index 0000000000000..d9566304d28b9 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_config_port.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H + +// Time hook. +#include +extern time_t alif_mbedtls_time(time_t *timer); +#define MBEDTLS_PLATFORM_TIME_MACRO alif_mbedtls_time +#define MBEDTLS_PLATFORM_MS_TIME_ALT undefined_and_unused + +// Set MicroPython-specific options. +#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (1) + +// Include common mbedtls configuration. +#include "extmod/mbedtls/mbedtls_config_common.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/alif/mbedtls/mbedtls_port.c b/ports/alif/mbedtls/mbedtls_port.c new file mode 100644 index 0000000000000..a8c155e31ae65 --- /dev/null +++ b/ports/alif/mbedtls/mbedtls_port.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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/obj.h" +#include "se_services.h" +#include "mbedtls_config_port.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "shared/timeutils/timeutils.h" +#include "mbedtls/platform_time.h" +#endif + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { + uint32_t val = 0; + int n = 0; + *olen = len; + while (len--) { + if (!n) { + val = se_services_rand64(); + n = 4; + } + *output++ = val; + val >>= 8; + --n; + } + return 0; +} + +#if defined(MBEDTLS_HAVE_TIME) + +time_t alif_mbedtls_time(time_t *timer) { + // TODO implement proper RTC time + unsigned int year = 2025; + unsigned int month = 1; + unsigned int date = 1; + unsigned int hours = 12; + unsigned int minutes = 0; + unsigned int seconds = 0; + return timeutils_seconds_since_epoch(year, month, date, hours, minutes, seconds); +} + +#endif diff --git a/ports/alif/mcu/alif_cfg.json.in b/ports/alif/mcu/alif_cfg.json.in new file mode 100644 index 0000000000000..7760bd91d2e43 --- /dev/null +++ b/ports/alif/mcu/alif_cfg.json.in @@ -0,0 +1,47 @@ +{ + "DEVICE": { + "disabled" : false, + "binary": TOC_CFG_FILE, + "version" : "0.5.00", + "signed": false + }, + "HP_BOOT": { + #if TOC_CORE_M55_HP_BOOT + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "bootloader.bin", + "mramAddress": "0x80000000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HP_APP": { + #if TOC_CORE_M55_HP_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HP/firmware.bin", + "mramAddress": "0x80020000", + "version": "1.0.0", + "cpu_id": "M55_HP", + "flags": ["boot"], + "signed": false + }, + "HE_APP": { + #if TOC_CORE_M55_HE_APP + "disabled" : false, + #else + "disabled" : true, + #endif + "binary": "M55_HE/firmware.bin", + "mramAddress": "0x80320000", + "version": "1.0.0", + "cpu_id": "M55_HE", + "flags": ["deferred"], + "signed": false + } +} diff --git a/ports/alif/mcu/ensemble.ld.S b/ports/alif/mcu/ensemble.ld.S new file mode 100644 index 0000000000000..aeefdb7eacb78 --- /dev/null +++ b/ports/alif/mcu/ensemble.ld.S @@ -0,0 +1,192 @@ +/* + * 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. + */ + +#ifdef CORE_M55_HP +__DTCM_SIZE = 1024K; +#else +__DTCM_SIZE = 256K; +#endif + +// Entry Point +ENTRY(Reset_Handler) + +MEMORY +{ + MRAM_BL (rx) : ORIGIN = 0x80000000, LENGTH = 128K + MRAM_HP (rx) : ORIGIN = 0x80020000, LENGTH = 3072K + MRAM_HE (rx) : ORIGIN = 0x80320000, LENGTH = 1400K + MRAM_FS (rx) : ORIGIN = 0x8047e000, LENGTH = 1024K + MRAM_TOC (rx): ORIGIN = 0x8057e000, LENGTH = 8K + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 256K + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = __DTCM_SIZE + SRAM0 (rwx) : ORIGIN = 0x02000000, LENGTH = 4096K + SRAM1 (rwx) : ORIGIN = 0x08000000, LENGTH = 2560K + SRAM6_A (rw) : ORIGIN = 0x62000000, LENGTH = 1024K + SRAM6_B (rw) : ORIGIN = 0x62400000, LENGTH = 1024K + SRAM7 (rw) : ORIGIN = 0x63000000, LENGTH = 512K + SRAM8 (rw) : ORIGIN = 0x63200000, LENGTH = 2048K + SRAM9_A (rw) : ORIGIN = 0x60000000, LENGTH = 256K + SRAM9_B (rw) : ORIGIN = 0x60040000, LENGTH = 512K +} + +#ifdef CORE_M55_HP +REGION_ALIAS("ROM", MRAM_HP); +__MP_HEAP_SIZE = 256K; +#else +REGION_ALIAS("ROM", MRAM_HE); +__MP_HEAP_SIZE = 128K; +#endif + +__STACK_SIZE = 16K; +__HEAP_SIZE = 16K; + +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 + + /* Open-AMP Shared Memory Region */ + .openamp_memory (NOLOAD) : ALIGN(32) + { + _openamp_shm_region_start = .; + . = . + 64K; + _openamp_shm_region_end = .; + } >SRAM6_A + + .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/ensemble_pin_alt.csv b/ports/alif/mcu/ensemble_pin_alt.csv new file mode 100644 index 0000000000000..34f13e86f6d3c --- /dev/null +++ b/ports/alif/mcu/ensemble_pin_alt.csv @@ -0,0 +1,130 @@ +Pin,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7 +P0_0,GPIO,OSPI0_D0,UART0_RX,I3C_SDA,UT0_T0,LPCAM_HSYNC,CAM_HSYNC,ANA_S0 +P0_1,GPIO,OSPI0_D1,UART0_TX,I3C_SCL,UT0_T1,LPCAM_VSYNC,CAM_VSYNC,ANA_S1 +P0_2,GPIO,OSPI0_D2,UART0_CTS,I2C0_SDA,UT1_T0,LPCAM_PCLK,CAM_PCLK,ANA_S2 +P0_3,GPIO,OSPI0_D3,UART0_RTS,I2C0_SCL,UT1_T1,LPCAM_XVCLK,CAM_XVCLK,ANA_S3 +P0_4,GPIO,OSPI0_D4,UART1_RX,PDM_D0,I2C1_SDA,UT2_T0,,ANA_S4 +P0_5,GPIO,OSPI0_D5,UART1_TX,PDM_C0,I2C1_SCL,UT2_T1,,ANA_S5 +P0_6,GPIO,OSPI0_D6,UART1_CTS,PDM_D1,I2C2_SCL,UT3_T0,,ANA_S6 +P0_7,GPIO,OSPI0_D7,UART1_RTS,PDM_C1,I2C2_SDA,UT3_T1,CDC_DE,ANA_S7 +P1_0,GPIO,UART2_RX,SPI0_MISO,I2C3_SDA,UT4_T0,LPCAM_HSYNC,ETH_RXD0,ANA_S8 +P1_1,GPIO,UART2_TX,SPI0_MOSI,I2C3_SCL,UT4_T1,LPCAM_VSYNC,ETH_RXD1,ANA_S9 +P1_2,GPIO,UART3_RX,SPI0_SCLK,I3C_SDA,UT5_T0,LPCAM_PCLK,ETH_RST,ANA_S10 +P1_3,GPIO,UART3_TX,SPI0_SS0,I3C_SCL,UT5_T1,LPCAM_XVCLK,ETH_TXD0,ANA_S11 +P1_4,GPIO,OSPI0_SS0,UART0_RX,SPI0_SS1,UT6_T0,LPCAM_D0,ETH_TXD1,ANA_S12 +P1_5,GPIO,OSPI0_SS1,UART0_TX,SPI0_SS2,UT6_T1,LPCAM_D1,ETH_TXEN,ANA_S13 +P1_6,GPIO,OSPI0_RXDS,UART1_RX,I2S0_SDI,UT7_T0,LPCAM_D2,ETH_IRQ,ANA_S14 +P1_7,GPIO,OSPI0_SCLK,UART1_TX,I2S0_SDO,UT7_T1,LPCAM_D3,ETH_REFCLK,ANA_S15 +P2_0,GPIO,OSPI0_D0,UART2_RX,LPPDM_D0,UT8_T0,LPCAM_D4,ETH_MDIO,ANA_S16 +P2_1,GPIO,OSPI0_D1,UART2_TX,LPPDM_C0,UT8_T1,LPCAM_D5,ETH_MDC,ANA_S17 +P2_2,GPIO,OSPI0_D2,UART3_RX,LPPDM_D1,UT9_T0,LPCAM_D6,ETH_CRS_DV_C,ANA_S18 +P2_3,GPIO,OSPI0_D3,UART3_TX,LPPDM_C1,UT9_T1,LPCAM_D7,CDC_PCLK,ANA_S19 +P2_4,GPIO,OSPI0_D4,LPI2S_SDI,SPI1_MISO,UT10_T0,LPCAM_D0,CAM_D0,ANA_S20 +P2_5,GPIO,OSPI0_D5,LPI2S_SDO,SPI1_MOSI,UT10_T1,LPCAM_D1,CAM_D1,ANA_S21 +P2_6,GPIO,OSPI0_D6,LPI2S_SCLK,SPI1_SCLK,UT11_T0,LPCAM_D2,CAM_D2,ANA_S22 +P2_7,GPIO,OSPI0_D7,LPI2S_WS,SPI1_SS0,UT11_T1,LPCAM_D3,CAM_D3,ANA_S23 +P3_0,GPIO,OSPI0_SCLK,UART4_RX,PDM_D0,I2S0_SCLK,QEC0_X,LPCAM_D4,CAM_D4 +P3_1,GPIO,OSPI0_SCLKN,UART4_TX,PDM_C0,I2S0_WS,QEC0_Y,LPCAM_D5,CAM_D5 +P3_2,GPIO,OSPI0_SS0,PDM_D1,I2S1_SDI,I3C_SDA,QEC0_Z,LPCAM_D6,CAM_D6 +P3_3,GPIO,OSPI0_SS1,PDM_C1,I2S1_SDO,I3C_SCL,QEC1_X,LPCAM_D7,CAM_D7 +P3_4,GPIO,OSPI0_RXDS,UART5_RX,LPPDM_C0,I2S1_SCLK,I2C0_SCL,QEC1_Y,CAM_D8 +P3_5,GPIO,OSPI0_SCLKN,UART5_TX,LPPDM_D0,SPI0_SS1,I2C0_SDA,QEC1_Z,CAM_D9 +P3_6,GPIO,HFXO_OUT,LPUART_CTS,LPPDM_C1,SPI0_SS2,I2C1_SDA,QEC2_X,CAM_D10 +P3_7,GPIO,JTAG_TRACECLK,LPUART_RTS,LPPDM_D1,SPI1_SS1,I2C1_SCL,QEC2_Y,CAM_D11 +P4_0,GPIO,JTAG_TDATA0,,I2S1_WS,SPI1_SS2,QEC2_Z,CDC_VSYNC,CAM_D12 +P4_1,GPIO,JTAG_TDATA1,I2S0_SDI,SPI1_SS3,QEC3_X,SD_CLK,CDC_HSYNC,CAM_D13 +P4_2,GPIO,JTAG_TDATA2,,I2S0_SDO,SPI2_MISO,QEC3_Y,SD_CMD,CAM_D14 +P4_3,GPIO,JTAG_TDATA3,,I2S0_SCLK,SPI2_MOSI,QEC3_Z,SD_RST,CAM_D15 +P4_4,GPIO,JTAG_TCK,I2S0_WS,SPI2_SCLK,FAULT0_A,,, +P4_5,GPIO,JTAG_TMS,SPI2_SS0,FAULT1_A,,,, +P4_6,GPIO,JTAG_TDI,SPI2_SS1,FAULT2_A,,,, +P4_7,GPIO,JTAG_TDO,SPI2_SS2,FAULT3_A,,,, +P5_0,GPIO,OSPI1_RXDS,UART4_RX,PDM_D2,SPI0_MISO,I2C2_SDA,UT0_T0,SD_D0 +P5_1,GPIO,OSPI1_SS0,UART4_TX,PDM_D3,SPI0_MOSI,I2C2_SCL,UT0_T1,SD_D1 +P5_2,GPIO,OSPI1_SCLKN,UART5_RX,PDM_C3,SPI0_SS0,LPI2C_SCL,UT1_T0,SD_D2 +P5_3,GPIO,OSPI1_SCLK,UART5_TX,SPI0_SCLK,LPI2C_SDA,UT1_T1,SD_D3,CDC_PCLK +P5_4,GPIO,OSPI1_SS1,UART3_CTS,PDM_D2,SPI0_SS3,UT2_T0,SD_D4,CDC_DE +P5_5,GPIO,OSPI1_SCLK,UART3_RTS,PDM_D3,UT2_T1,SD_D5,ETH_RXD0,CDC_HSYNC +# P5_6 doesn't really have OSPI on AF1 but it's needed for P10_7 to be in OSPI1_RXDS mode +P5_6,GPIO,OSPI1_RXDS,UART1_CTS,I2C2_SCL,UT3_T0,SD_D6,ETH_RXD1,CDC_VSYNC +P5_7,GPIO,OSPI1_SS0,UART1_RTS,I2C2_SDA,UT3_T1,SD_D7,ETH_RST, +P6_0,GPIO,OSPI0_D0,UART4_DE,PDM_D0,UT4_T0,SD_D0,ETH_TXD0, +P6_1,GPIO,OSPI0_D1,UART5_DE,PDM_C0,UT4_T1,SD_D1,ETH_TXD1, +P6_2,GPIO,OSPI0_D2,UART2_CTS,,PDM_D1,UT5_T0,SD_D2,ETH_TXEN +P6_3,GPIO,OSPI0_D3,UART2_RTS,,PDM_C1,UT5_T1,SD_D3,ETH_IRQ +P6_4,GPIO,OSPI0_D4,UART2_CTS,,SPI1_SS0,UT6_T0,SD_D4,ETH_REFCLK +P6_5,GPIO,OSPI0_D5,UART2_RTS,,SPI1_SS1,UT6_T1,SD_D5,ETH_MDIO +P6_6,GPIO,OSPI0_D6,UART0_CTS,,SPI1_SS2,UT7_T0,SD_D6,ETH_MDC +P6_7,GPIO,OSPI0_D7,UART0_RTS,PDM_C2,SPI1_SS3,UT7_T1,SD_D7,ETH_CRS_DV_A +P7_0,GPIO,,CMP3_OUT,SPI0_MISO,I2C0_SDA,UT8_T0,SD_CMD, +P7_1,GPIO,,CMP2_OUT,SPI0_MOSI,I2C0_SCL,UT8_T1,SD_CLK, +P7_2,GPIO,,UART3_CTS,CMP1_OUT,SPI0_SCLK,I2C1_SDA,UT9_T0,SD_RST +P7_3,GPIO,,UART3_RTS,CMP0_OUT,SPI0_SS0,I2C1_SCL,UT9_T1, +P7_4,GPIO,,LPUART_CTS,LPPDM_C2,LPSPI_MISO,LPI2C_SCL,UT10_T0, +P7_5,GPIO,,LPUART_RTS,,LPPDM_D2,LPSPI_MOSI,LPI2C_SDA,UT10_T1 +P7_6,GPIO,,LPUART_RX,,LPPDM_C3,LPSPI_SCLK,I3C_SDA,UT11_T0 +P7_7,GPIO,,LPUART_TX,,LPPDM_D3,LPSPI_SS,I3C_SCL,UT11_T1 +P8_0,GPIO,OSPI1_SCLKN,AUDIO_CLK,FAULT0_B,LPCAM_D0,SD_D0,CDC_D0,CAM_D0 +P8_1,GPIO,I2S2_SDI,FAULT1_B,LPCAM_D1,SD_D1,CDC_D1,CAM_D1, +P8_2,GPIO,I2S2_SDO,SPI0_SS3,FAULT2_B,LPCAM_D2,SD_D2,CDC_D2,CAM_D2 +P8_3,GPIO,I2S2_SCLK,SPI1_MISO,FAULT3_B,LPCAM_D3,SD_D3,CDC_D3,CAM_D3 +P8_4,GPIO,I2S2_WS,SPI1_MOSI,QEC0_X,LPCAM_D4,SD_D4,CDC_D4,CAM_D4 +P8_5,GPIO,,SPI1_SCLK,QEC0_Y,LPCAM_D5,SD_D5,CDC_D5,CAM_D5 +P8_6,GPIO,,I2S3_SCLK,QEC0_Z,LPCAM_D6,SD_D6,CDC_D6,CAM_D6 +P8_7,GPIO,,I2S3_WS,QEC1_X,LPCAM_D7,SD_D7,CDC_D7,CAM_D7 +P9_0,GPIO,,I2S3_SDI,QEC1_Y,SD_CMD,CDC_D8,CAM_D8, +P9_1,GPIO,LPUART_RX,I2S3_SDO,QEC1_Z,SD_CLK,CDC_D9,CAM_D9, +P9_2,GPIO,LPUART_TX,I2S3_SDI,SPI2_MISO,QEC2_X,SD_RST,CDC_D10,CAM_D10 +P9_3,GPIO,HFXO_OUT,UART7_RX,I2S3_SDO,SPI2_MOSI,QEC2_Y,CDC_D11,CAM_D11 +P9_4,GPIO,UART7_TX,I2S3_SCLK,SPI2_SCLK,I2C3_SDA,QEC2_Z,CDC_D12,CAM_D12 +P9_5,GPIO,OSPI1_D0,I2S3_WS,SPI2_SS0,I2C3_SCL,QEC3_X,CDC_D13,CAM_D13 +P9_6,GPIO,OSPI1_D1,AUDIO_CLK,SPI2_SS1,I2C3_SDA,QEC3_Y,CDC_D14,CAM_D14 +P9_7,GPIO,OSPI1_D2,UART7_DE,SPI2_SS2,I2C3_SCL,QEC3_Z,CDC_D15,CAM_D15 +P10_0,GPIO,OSPI1_D3,UART6_DE,SPI2_SS3,UT0_T0,LPCAM_HSYNC,CDC_D16,CAM_HSYNC +P10_1,GPIO,OSPI1_D4,,LPI2S_SDI,UT0_T1,LPCAM_VSYNC,CDC_D17,CAM_VSYNC +P10_2,GPIO,OSPI1_D5,,LPI2S_SDO,UT1_T0,LPCAM_PCLK,CDC_D18,CAM_PCLK +P10_3,GPIO,OSPI1_D6,,LPI2S_SCLK,UT1_T1,LPCAM_XVCLK,CDC_D19,CAM_XVCLK +P10_4,GPIO,OSPI1_D7,,LPI2S_WS,I2C0_SDA,UT2_T0,ETH_TXD0,CDC_D20 +P10_5,GPIO,UART6_RX,I2S2_SDI,SPI3_MISO,I2C0_SCL,UT2_T1,ETH_TXD1,CDC_D21 +P10_6,GPIO,UART6_TX,I2S2_SDO,SPI3_MOSI,I2C1_SDA,UT3_T0,ETH_TXEN,CDC_D22 +P10_7,GPIO,UART7_RX,I2S2_SCLK,SPI3_SCLK,I2C1_SCL,UT3_T1,CDC_D23,OSPI1_RXDS +P11_0,GPIO,OSPI1_D0,UART7_TX,I2S2_WS,SPI3_SS0,UT4_T0,ETH_REFCLK,CDC_D0 +P11_1,GPIO,OSPI1_D1,UART7_DE,SPI3_SS1,UT4_T1,ETH_MDIO,CDC_D1, +P11_2,GPIO,OSPI1_D2,UART6_DE,LPPDM_C2,SPI3_SS2,UT5_T0,ETH_MDC,CDC_D2 +P11_3,GPIO,OSPI1_D3,UART5_RX,LPPDM_C3,SPI3_SS3,UT5_T1,ETH_RXD0,CDC_D3 +P11_4,GPIO,OSPI1_D4,UART5_TX,PDM_C2,LPSPI_MISO,UT6_T0,ETH_RXD1,CDC_D4 +P11_5,GPIO,OSPI1_D5,UART6_RX,PDM_C3,LPSPI_MOSI,UT6_T1,ETH_CRS_DV_B,CDC_D5 +P11_6,GPIO,OSPI1_D6,UART6_TX,LPPDM_D2,LPSPI_SCLK,UT7_T0,ETH_RST,CDC_D6 +P11_7,GPIO,OSPI1_D7,UART5_DE,LPPDM_D3,LPSPI_SS,UT7_T1,ETH_IRQ,CDC_D7 +P12_0,GPIO,OSPI0_SCLK,AUDIO_CLK,I2S1_SDI,UT8_T0,CDC_D8,, +P12_1,GPIO,OSPI0_SCLKN,UART4_RX,I2S1_SDO,UT8_T1,CDC_D9,, +P12_2,GPIO,OSPI0_RXDS,UART4_TX,I2S1_SCLK,UT9_T0,CDC_D10,, +P12_3,GPIO,OSPI0_SS0,UART4_DE,I2S1_WS,UT9_T1,CDC_D11,, +P12_4,GPIO,OSPI0_SS1,SPI3_MISO,UT10_T0,,CDC_D12,, +P12_5,GPIO,,SPI3_MOSI,UT10_T1,,CDC_D13,, +P12_6,GPIO,,SPI3_SCLK,UT11_T0,,CDC_D14,, +P12_7,GPIO,OSPI1_RXDS,,SPI3_SS0,UT11_T1,CDC_D15,, +P13_0,GPIO,OSPI1_D0,,SPI3_SS1,QEC0_X,SD_D0,CDC_D16, +P13_1,GPIO,OSPI1_D1,SPI3_SS2,QEC0_Y,SD_D1,CDC_D17,, +P13_2,GPIO,OSPI1_D2,SPI3_SS3,QEC0_Z,SD_D2,CDC_D18,, +P13_3,GPIO,OSPI1_D3,SPI2_SS3,QEC1_X,SD_D3,CDC_D19,, +P13_4,GPIO,OSPI1_D4,LPI2S_SDI,QEC1_Y,SD_D4,CDC_D20,, +P13_5,GPIO,OSPI1_D5,LPI2S_SDO,QEC1_Z,SD_D5,CDC_D21,, +P13_6,GPIO,OSPI1_D6,LPI2S_SCLK,QEC2_X,SD_D6,CDC_D22,, +P13_7,GPIO,OSPI1_D7,LPI2S_WS,QEC2_Y,SD_D7,CDC_D23,, +P14_0,GPIO,OSPI1_SCLK,UART6_RX,QEC2_Z,SD_CMD,,, +P14_1,GPIO,OSPI1_SCLKN,UART6_TX,,QEC3_X,SD_CLK,, +P14_2,GPIO,OSPI1_SS0,UART7_RX,,QEC3_Y,SD_RST,, +P14_3,GPIO,OSPI1_SS1,UART7_TX,,QEC3_Z,,, +P14_4,GPIO,CMP3_OUT,SPI1_MISO,FAULT0_C,,,, +P14_5,GPIO,CMP2_OUT,SPI1_MOSI,FAULT1_C,,,, +P14_6,GPIO,CMP1_OUT,SPI1_SCLK,FAULT2_C,,,, +P14_7,GPIO,CMP0_OUT,SPI1_SS0,FAULT3_C,,,, +P15_0,GPIO,LPTMR0_CLK,,,,,, +P15_1,GPIO,LPTMR1_CLK,,,,,, +P15_2,GPIO,LPTMR2_CLK,,,,,, +P15_3,GPIO,LPTMR3_CLK,,,,,, +P15_4,GPIO,,,,,,, +P15_5,GPIO,,,,,,, +P15_6,GPIO,,,,,,, +P15_7,GPIO,,,,,,, diff --git a/ports/alif/mcu/make-pins.py b/ports/alif/mcu/make-pins.py new file mode 100755 index 0000000000000..3666953e613f5 --- /dev/null +++ b/ports/alif/mcu/make-pins.py @@ -0,0 +1,113 @@ +#!/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 + +# Maps pad name to (adc12_periph, adc12_channel). +ADC12_ANA_MAP = { + "P0_0": (0, 0), + "P0_1": (0, 1), + "P0_2": (0, 2), + "P0_3": (0, 3), + "P0_4": (0, 4), + "P0_5": (0, 5), + "P0_6": (1, 0), + "P0_7": (1, 1), + "P1_0": (1, 2), + "P1_1": (1, 3), + "P1_2": (1, 4), + "P1_3": (1, 5), + "P1_4": (2, 0), + "P1_5": (2, 1), + "P1_6": (2, 2), + "P1_7": (2, 3), + "P2_0": (2, 4), + "P2_1": (2, 5), +} + + +class AlifPin(boardgen.Pin): + def __init__(self, cpu_pin_name): + super().__init__(cpu_pin_name) + self._afs = [("NONE", -1)] * 8 + + # Called for each AF defined in the csv file for this pin. + def add_af(self, af_idx, af_name, af): + if af == "GPIO": + self._afs[af_idx] = "GPIO", -1 + elif af.startswith("ANA_S"): + self._afs[af_idx] = "ANA", int(af[5:]) + else: + m = re.match(r"([A-Z0-9]+[A-Z])(\d*)_([A-Z0-9_]+)$", af) + periph, unit, line = m.groups() + unit = -1 if unit == "" else int(unit) + self._afs[af_idx] = f"{periph}_{line}", unit + + # Emit the struct which contains the pin instance. + def definition(self): + port, pin = self.name()[1:].split("_") + adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7)) + base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port) + return ( + "{{\n" + " .name = MP_QSTR_P{port}_{pin},\n" + " .base = {{ .type = &machine_pin_type }},\n" + " .gpio = (GPIO_Type *){base},\n" + " .port = PORT_{port},\n" + " .pin = PIN_{pin},\n" + " .adc12_periph = {adc12_periph},\n" + " .adc12_channel = {adc12_channel},\n" + " .alt = {{{alt}}},\n" + "}}".format( + port=port, + pin=pin, + base=base, + adc12_periph=adc12_periph, + adc12_channel=adc12_channel, + alt=", ".join([f"MP_HAL_PIN_ALT({func}, {unit})" for func, unit in self._afs]), + ) + ) + + # 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, enable_af=True) + + # 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 0000000000000..99fef5939a0ce --- /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 0000000000000..1c3c34394cd11 --- /dev/null +++ b/ports/alif/modalif.c @@ -0,0 +1,56 @@ +/* + * 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" +#include "se_services.h" + +static mp_obj_t alif_info(void) { + se_services_dump_device_data(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(alif_info_obj, alif_info); + +static const mp_rom_map_elem_t alif_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alif) }, + #if MICROPY_HW_ENABLE_OSPI + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&alif_flash_type) }, + #endif + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&alif_info_obj) }, + #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 0000000000000..5812d6ba54597 --- /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 0000000000000..9868abbeeaf35 --- /dev/null +++ b/ports/alif/modmachine.c @@ -0,0 +1,129 @@ +/* + * 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. + +#include "se_services.h" +#include "tusb.h" + +extern void dcd_uninit(void); + +#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, \ + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ + { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, \ + +static void mp_machine_idle(void) { + mp_event_wait_indefinite(); +} + +static mp_obj_t mp_machine_unique_id(void) { + uint8_t id[8] = {0}; + se_services_get_unique_id(id); + return mp_obj_new_bytes(id, sizeof(id)); +} + +NORETURN static void mp_machine_reset(void) { + se_services_reset_soc(); +} + +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); +} + +#if MICROPY_HW_ENABLE_USBDEV +static void mp_machine_enable_usb(bool enable) { + if (enable) { + // Initialize TinyUSB and DCD. + tusb_init(); + } else { + // Disconnect USB device. + tud_disconnect(); + // Deinitialize TinyUSB. + tud_deinit(TUD_OPT_RHPORT); + // Deinitialize DCD (disables IRQs). + dcd_uninit(); + } +} +#endif + +static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STANDBY + MICROPY_BOARD_ENTER_STANDBY(); + #endif + + // This enters the deepest possible CPU sleep state, without + // losing CPU state. CPU and subsystem power will remain on. + pm_core_enter_deep_sleep(); + + #ifdef MICROPY_BOARD_EXIT_STANDBY + MICROPY_BOARD_EXIT_STANDBY(); + #endif + + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(true); + #endif +} + +NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { + #if MICROPY_HW_ENABLE_USBDEV + mp_machine_enable_usb(false); + #endif + + #ifdef MICROPY_BOARD_ENTER_STOP + MICROPY_BOARD_ENTER_STOP(); + #endif + + // If power is removed from the subsystem, the function does + // not return, and the CPU will reboot when/if the subsystem + // is next powered up. + pm_core_enter_deep_sleep_request_subsys_off(); + mp_machine_reset(); +} diff --git a/ports/alif/modos.c b/ports/alif/modos.c new file mode 100644 index 0000000000000..9c8c84268ca16 --- /dev/null +++ b/ports/alif/modos.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 "py/runtime.h" +#include "se_services.h" + +#if MICROPY_PY_OS_URANDOM +static mp_obj_t mp_os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + uint64_t rnd = 0; + size_t rnd_bits = 0; + for (int i = 0; i < n; i++) { + if (rnd_bits == 0) { + rnd = se_services_rand64(); + rnd_bits = 64; + } + vstr.buf[i] = rnd; + rnd >>= 8; + rnd_bits -= 8; + } + return mp_obj_new_bytes_from_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom); +#endif diff --git a/ports/alif/modules/he/_boot.py b/ports/alif/modules/he/_boot.py new file mode 100644 index 0000000000000..3df1804709073 --- /dev/null +++ b/ports/alif/modules/he/_boot.py @@ -0,0 +1,7 @@ +# Change working directory to ROMFS, so boot.py and main.py can run from there. +try: + import os + + os.chdir("/rom") +except: + pass diff --git a/ports/alif/modules/hp/_boot.py b/ports/alif/modules/hp/_boot.py new file mode 100644 index 0000000000000..36044b461e662 --- /dev/null +++ b/ports/alif/modules/hp/_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/mpbthciport.c b/ports/alif/mpbthciport.c new file mode 100644 index 0000000000000..10a0505fe02d8 --- /dev/null +++ b/ports/alif/mpbthciport.c @@ -0,0 +1,146 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 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/mpbthci.h" +#include "shared/runtime/softtimer.h" +#include "mpbthciport.h" + +#if MICROPY_PY_BLUETOOTH + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Soft timer and scheduling node for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; +static mp_sched_node_t mp_bluetooth_hci_sched_node; + +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_init(void) { + soft_timer_static_init( + &mp_bluetooth_hci_soft_timer, + SOFT_TIMER_MODE_ONE_SHOT, + 0, + mp_bluetooth_hci_soft_timer_callback + ); +} + +static void mp_bluetooth_hci_start_polling(void) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); +} + +// For synchronous mode, we run all BLE stack code inside a scheduled task. +// This task is scheduled periodically via a timer, or immediately after UART RX IRQ. +static void run_events_scheduled_task(mp_sched_node_t *node) { + (void)node; + // This will process all buffered HCI UART data, and run any callouts or events. + mp_bluetooth_hci_poll(); +} + +// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to +// request that processing happens ASAP in the scheduler. +void mp_bluetooth_hci_poll_now(void) { + mp_sched_schedule_node(&mp_bluetooth_hci_sched_node, run_events_scheduled_task); +} + +/******************************************************************************/ +// HCI over UART + +#include "mpuart.h" + +static uint32_t hci_uart_id; +static bool hci_uart_first_char; +static uint8_t hci_rx_ringbuf_array[768]; +static ringbuf_t hci_rx_ringbuf = { + .buf = hci_rx_ringbuf_array, + .size = sizeof(hci_rx_ringbuf_array), + .iget = 0, + .iput = 0, +}; + +static void mp_bluetooth_hci_uart_irq_callback(unsigned int uart_id, unsigned int trigger) { + if (trigger == MP_UART_IRQ_RXIDLE) { + mp_bluetooth_hci_poll_now(); + } +} + +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + hci_uart_id = port; + hci_uart_first_char = true; + + // Initialise the UART. + mp_uart_init(hci_uart_id, baudrate, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); + mp_uart_set_irq_callback(hci_uart_id, MP_UART_IRQ_RXIDLE, mp_bluetooth_hci_uart_irq_callback); + + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + mp_uart_deinit(hci_uart_id); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + mp_uart_set_baudrate(hci_uart_id, baudrate); + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + mp_bluetooth_hci_controller_wakeup(); + mp_uart_tx_data(hci_uart_id, (void *)buf, len); + return 0; +} + +// This function expects the controller to be in the wake state via a previous call +// to mp_bluetooth_hci_controller_woken. +int mp_bluetooth_hci_uart_readchar(void) { + if (mp_uart_rx_any(hci_uart_id)) { + int c = mp_uart_rx_char(hci_uart_id); + if (hci_uart_first_char) { + if (c == 0) { + return -1; + } + hci_uart_first_char = false; + } + return c; + } else { + return -1; + } +} + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/alif/mpbthciport.h b/ports/alif/mpbthciport.h new file mode 100644 index 0000000000000..65e9d1752a798 --- /dev/null +++ b/ports/alif/mpbthciport.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Damien P. George + * + * 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_MPBTHCIPORT_H +#define MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H + +#include "py/mpconfig.h" + +// Initialise the HCI subsystem (should be called once, early on). +void mp_bluetooth_hci_init(void); + +// Call this to poll the HCI now. +void mp_bluetooth_hci_poll_now(void); + +// Call this to poll the HCI after a certain timeout. +void mp_bluetooth_hci_poll_in_ms(uint32_t ms); + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +// Request new data from the uart and pass to the stack, and run pending events/callouts. +// This is a low-level function and should not be called directly, use +// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead. +void mp_bluetooth_hci_poll(void); + +#endif // MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h new file mode 100644 index 0000000000000..6c9e6af635f57 --- /dev/null +++ b/ports/alif/mpconfigport.h @@ -0,0 +1,220 @@ +/* + * 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 +#include // 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 + +// Select the low-level system tick implementation. +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_SYSTICK) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#if CORE_M55_HP +#define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#else +#define MICROPY_HW_SYSTEM_TICK_USE_SYSTICK (1) +#endif +#endif +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK +#define MICROPY_SOFT_TIMER_TICKS_MS system_tick_ms_counter +#endif + +#ifndef MICROPY_HW_ENABLE_OSPI +#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) +#endif +#ifndef MICROPY_HW_ENABLE_USBDEV +#define MICROPY_HW_ENABLE_USBDEV (CORE_M55_HP) +#endif +#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in HS mode" +#endif +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (CORE_M55_HP) +#endif +#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 +#ifndef MICROPY_HW_ENABLE_UART_REPL +#define MICROPY_HW_ENABLE_UART_REPL (0) +#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) +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#endif +#define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_SCHEDULER_STATIC_NODES (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_TRACKED_ALLOC (MICROPY_PY_OPENAMP) + +// 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_INCLUDEFILE "ports/alif/modos.c" +#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_OS_URANDOM (1) +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (se_services_rand64()) +#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_ADC (1) +#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c" +#define MICROPY_PY_MACHINE_DHT_READINTO (1) +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) +#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) +#define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_SOFTSPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/alif/machine_uart.c" +#define MICROPY_PY_MACHINE_UART_IRQ (1) +#define MICROPY_PY_NETWORK (CORE_M55_HP) +#ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif" +#endif +#define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (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 + +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + +#define MICROPY_PORT_NETWORK_INTERFACES \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_BOARD_NETWORK_INTERFACES \ + +// Bluetooth code only runs in the scheduler, no locking/mutex required. +#define MICROPY_PY_BLUETOOTH_ENTER uint32_t atomic_state = 0; +#define MICROPY_PY_BLUETOOTH_EXIT (void)atomic_state; +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (1) + +#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 + +// Needed for MICROPY_PY_RANDOM_SEED_INIT_FUNC. +uint64_t se_services_rand64(void); diff --git a/ports/alif/mpconfigport.mk b/ports/alif/mpconfigport.mk new file mode 100644 index 0000000000000..92d9a5db8faf0 --- /dev/null +++ b/ports/alif/mpconfigport.mk @@ -0,0 +1,13 @@ +# 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 +MICROPY_MANIFEST_MCU_CORE := $(shell echo $(MCU_CORE) | awk -F'_' '{print tolower($$2)}') diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c new file mode 100644 index 0000000000000..39528a4b9f272 --- /dev/null +++ b/ports/alif/mphalport.c @@ -0,0 +1,269 @@ +/* + * 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/runtime/softtimer.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" +#include "pendsv.h" +#include "se_services.h" +#include "system_tick.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) { + #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC || MICROPY_PY_OS_DUPTERM + mp_uint_t ret = len; + bool did_write = false; + #endif + + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_write_strn_repl(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; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return system_tick_get_u32(); +} + +mp_uint_t mp_hal_ticks_us(void) { + // Convert system tick to microsecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64(); + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000000 / system_tick_source_hz; + #else + return system_tick_get_u64() / system_core_clock_mhz; + #endif +} + +mp_uint_t mp_hal_ticks_ms(void) { + // Convert system tick to millisecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + return system_tick_get_u64() / 1000ULL; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000ULL / system_tick_source_hz; + #else + return system_tick_get_u64() / (SystemCoreClock / 1000); + #endif +} + +void mp_hal_delay_us(mp_uint_t us) { + #if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + uint64_t ticks_delay = (uint64_t)us; + #elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; + #else + uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + #endif + uint64_t start = system_tick_get_u64(); + while (system_tick_get_u64() - start < ticks_delay) { + } +} + +void mp_hal_delay_ms(mp_uint_t ms) { + uint32_t t0 = mp_hal_ticks_ms(); + mp_event_handle_nowait(); + for (;;) { + uint32_t t1 = mp_hal_ticks_ms(); + if (t1 - t0 >= ms) { + break; + } + mp_event_wait_ms(ms - (t1 - t0)); + } +} + +uint64_t mp_hal_time_ns(void) { + return 0; +} + +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren) { + uint8_t alt_func = PINMUX_ALTERNATE_FUNCTION_0; + uint8_t pad_ctrl = drive | speed | (ren ? PADCTRL_READ_ENABLE : 0); + + // Configure pull-up or pull-down. + 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; + } + + // Configure open-drain mode. + if (mode == MP_HAL_PIN_MODE_OPEN_DRAIN) { + pad_ctrl |= PADCTRL_DRIVER_OPEN_DRAIN; + } + + // For ALT mode, find alternate function. + if (mode == MP_HAL_PIN_MODE_ALT) { + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(pin->alt); i++) { + if (alt == pin->alt[i]) { + alt_func = i; + break; + } + } + if (alt_func == PINMUX_ALTERNATE_FUNCTION_0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), alt); + } + } + + // Set pad config. + pinconf_set(pin->port, pin->pin, alt_func, pad_ctrl); + + // For INPUT/OUTPUT/OD modes, set the GPIO direction. + switch (mode) { + case MP_HAL_PIN_MODE_INPUT: + gpio_set_direction_input(pin->gpio, pin->pin); + break; + case MP_HAL_PIN_MODE_OUTPUT: + case MP_HAL_PIN_MODE_OPEN_DRAIN: + gpio_set_direction_output(pin->gpio, pin->pin); + break; + default: + break; + } +} + + +void system_tick_schedule_callback(void) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); +} + +#if !defined(MICROPY_SOFT_TIMER_TICKS_MS) + +uint32_t soft_timer_get_ms(void) { + return mp_hal_ticks_ms(); +} + +void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + int32_t ms = soft_timer_ticks_diff(ticks_ms, mp_hal_ticks_ms()); + ms = MAX(0, ms); + ms = MIN(ms, 4000000); // ensure ms * 1000 doesn't overflow + system_tick_schedule_after_us(ms * 1000); +} + +#endif + +/*******************************************************************************/ +// MAC address + +// Generate a random locally administered MAC address (LAA) +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) { + uint8_t id[8]; + se_services_get_unique_id(id); + buf[0] = 0x02; // LAA range + buf[1] = id[4]; + buf[2] = id[3]; + buf[3] = id[2]; + buf[4] = id[1]; + buf[5] = (id[0] << 2) | idx; +} + +// A board can override this if needed +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + mp_hal_generate_laa_mac(idx, buf); +} + +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { + static const char hexchr[16] = "0123456789ABCDEF"; + uint8_t mac[6]; + mp_hal_get_mac(idx, mac); + for (; chr_len; ++chr_off, --chr_len) { + *dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf]; + } +} diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h new file mode 100644 index 0000000000000..4f81439b5492e --- /dev/null +++ b/ports/alif/mphalport.h @@ -0,0 +1,372 @@ +/* + * 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 "system_tick.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); + +// Prevent the "lwIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + +// 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 { \ + system_tick_wfe_with_timeout_us(TIMEOUT_MS * 1000); \ + } \ + } 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))) + +#define mp_hal_quiet_timing_enter() raise_irq_pri(IRQ_PRI_QUIET_TIMING) +#define mp_hal_quiet_timing_exit(irq_state) restore_irq_pri(irq_state) +#define mp_hal_delay_us_fast mp_hal_delay_us + +extern ringbuf_t stdin_ringbuf; + +/******************************************************************************/ +// 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_MODE_ALT (3) +#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_DRIVE_2MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_2MA) +#define MP_HAL_PIN_DRIVE_4MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_4MA) +#define MP_HAL_PIN_DRIVE_8MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_8MA) +#define MP_HAL_PIN_DRIVE_12MA (PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA) +#define MP_HAL_PIN_SPEED_LOW (0) +#define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST) + +#define mp_hal_pin_obj_t const machine_pin_obj_t * + +#define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit))) +#define MP_HAL_PIN_ALT_MAKE(function, unit) ((function) | ((unit) << 8)) +#define MP_HAL_PIN_ALT_GET_FUNC(alt) ((alt) & 0xff) +#define MP_HAL_PIN_ALT_GET_UNIT(alt) ((alt) >> 8) + +enum { + MP_HAL_PIN_ALT_NONE = 0, + MP_HAL_PIN_ALT_GPIO, + MP_HAL_PIN_ALT_ANA, + MP_HAL_PIN_ALT_AUDIO_CLK, + MP_HAL_PIN_ALT_CAM_D0, + MP_HAL_PIN_ALT_CAM_D1, + MP_HAL_PIN_ALT_CAM_D2, + MP_HAL_PIN_ALT_CAM_D3, + MP_HAL_PIN_ALT_CAM_D4, + MP_HAL_PIN_ALT_CAM_D5, + MP_HAL_PIN_ALT_CAM_D6, + MP_HAL_PIN_ALT_CAM_D7, + MP_HAL_PIN_ALT_CAM_D8, + MP_HAL_PIN_ALT_CAM_D9, + MP_HAL_PIN_ALT_CAM_D10, + MP_HAL_PIN_ALT_CAM_D11, + MP_HAL_PIN_ALT_CAM_D12, + MP_HAL_PIN_ALT_CAM_D13, + MP_HAL_PIN_ALT_CAM_D14, + MP_HAL_PIN_ALT_CAM_D15, + MP_HAL_PIN_ALT_CAM_HSYNC, + MP_HAL_PIN_ALT_CAM_PCLK, + MP_HAL_PIN_ALT_CAM_VSYNC, + MP_HAL_PIN_ALT_CAM_XVCLK, + MP_HAL_PIN_ALT_CDC_D0, + MP_HAL_PIN_ALT_CDC_D1, + MP_HAL_PIN_ALT_CDC_D2, + MP_HAL_PIN_ALT_CDC_D3, + MP_HAL_PIN_ALT_CDC_D4, + MP_HAL_PIN_ALT_CDC_D5, + MP_HAL_PIN_ALT_CDC_D6, + MP_HAL_PIN_ALT_CDC_D7, + MP_HAL_PIN_ALT_CDC_D8, + MP_HAL_PIN_ALT_CDC_D9, + MP_HAL_PIN_ALT_CDC_D10, + MP_HAL_PIN_ALT_CDC_D11, + MP_HAL_PIN_ALT_CDC_D12, + MP_HAL_PIN_ALT_CDC_D13, + MP_HAL_PIN_ALT_CDC_D14, + MP_HAL_PIN_ALT_CDC_D15, + MP_HAL_PIN_ALT_CDC_D16, + MP_HAL_PIN_ALT_CDC_D17, + MP_HAL_PIN_ALT_CDC_D18, + MP_HAL_PIN_ALT_CDC_D19, + MP_HAL_PIN_ALT_CDC_D20, + MP_HAL_PIN_ALT_CDC_D21, + MP_HAL_PIN_ALT_CDC_D22, + MP_HAL_PIN_ALT_CDC_D23, + MP_HAL_PIN_ALT_CDC_DE, + MP_HAL_PIN_ALT_CDC_HSYNC, + MP_HAL_PIN_ALT_CDC_PCLK, + MP_HAL_PIN_ALT_CDC_VSYNC, + MP_HAL_PIN_ALT_CMP_OUT, + MP_HAL_PIN_ALT_ETH_CRS_DV_A, + MP_HAL_PIN_ALT_ETH_CRS_DV_B, + MP_HAL_PIN_ALT_ETH_CRS_DV_C, + MP_HAL_PIN_ALT_ETH_IRQ, + MP_HAL_PIN_ALT_ETH_MDC, + MP_HAL_PIN_ALT_ETH_MDIO, + MP_HAL_PIN_ALT_ETH_REFCLK, + MP_HAL_PIN_ALT_ETH_RST, + MP_HAL_PIN_ALT_ETH_RXD0, + MP_HAL_PIN_ALT_ETH_RXD1, + MP_HAL_PIN_ALT_ETH_TXD0, + MP_HAL_PIN_ALT_ETH_TXD1, + MP_HAL_PIN_ALT_ETH_TXEN, + MP_HAL_PIN_ALT_FAULT_A, + MP_HAL_PIN_ALT_FAULT_B, + MP_HAL_PIN_ALT_FAULT_C, + MP_HAL_PIN_ALT_HFXO_OUT, + MP_HAL_PIN_ALT_I2C_SCL, + MP_HAL_PIN_ALT_I2C_SDA, + MP_HAL_PIN_ALT_I2S_SCLK, + MP_HAL_PIN_ALT_I2S_SDI, + MP_HAL_PIN_ALT_I2S_SDO, + MP_HAL_PIN_ALT_I2S_WS, + MP_HAL_PIN_ALT_I3C_SCL, + MP_HAL_PIN_ALT_I3C_SDA, + MP_HAL_PIN_ALT_JTAG_TCK, + MP_HAL_PIN_ALT_JTAG_TDATA0, + MP_HAL_PIN_ALT_JTAG_TDATA1, + MP_HAL_PIN_ALT_JTAG_TDATA2, + MP_HAL_PIN_ALT_JTAG_TDATA3, + MP_HAL_PIN_ALT_JTAG_TDI, + MP_HAL_PIN_ALT_JTAG_TDO, + MP_HAL_PIN_ALT_JTAG_TMS, + MP_HAL_PIN_ALT_JTAG_TRACECLK, + MP_HAL_PIN_ALT_LPCAM_D0, + MP_HAL_PIN_ALT_LPCAM_D1, + MP_HAL_PIN_ALT_LPCAM_D2, + MP_HAL_PIN_ALT_LPCAM_D3, + MP_HAL_PIN_ALT_LPCAM_D4, + MP_HAL_PIN_ALT_LPCAM_D5, + MP_HAL_PIN_ALT_LPCAM_D6, + MP_HAL_PIN_ALT_LPCAM_D7, + MP_HAL_PIN_ALT_LPCAM_HSYNC, + MP_HAL_PIN_ALT_LPCAM_PCLK, + MP_HAL_PIN_ALT_LPCAM_VSYNC, + MP_HAL_PIN_ALT_LPCAM_XVCLK, + MP_HAL_PIN_ALT_LPI2C_SCL, + MP_HAL_PIN_ALT_LPI2C_SDA, + MP_HAL_PIN_ALT_LPI2S_SCLK, + MP_HAL_PIN_ALT_LPI2S_SDI, + MP_HAL_PIN_ALT_LPI2S_SDO, + MP_HAL_PIN_ALT_LPI2S_WS, + MP_HAL_PIN_ALT_LPPDM_C0, + MP_HAL_PIN_ALT_LPPDM_C1, + MP_HAL_PIN_ALT_LPPDM_C2, + MP_HAL_PIN_ALT_LPPDM_C3, + MP_HAL_PIN_ALT_LPPDM_D0, + MP_HAL_PIN_ALT_LPPDM_D1, + MP_HAL_PIN_ALT_LPPDM_D2, + MP_HAL_PIN_ALT_LPPDM_D3, + MP_HAL_PIN_ALT_LPSPI_MISO, + MP_HAL_PIN_ALT_LPSPI_MOSI, + MP_HAL_PIN_ALT_LPSPI_SCLK, + MP_HAL_PIN_ALT_LPSPI_SS, + MP_HAL_PIN_ALT_LPTMR_CLK, + MP_HAL_PIN_ALT_LPUART_CTS, + MP_HAL_PIN_ALT_LPUART_RTS, + MP_HAL_PIN_ALT_LPUART_RX, + MP_HAL_PIN_ALT_LPUART_TX, + MP_HAL_PIN_ALT_OSPI_D0, + MP_HAL_PIN_ALT_OSPI_D1, + MP_HAL_PIN_ALT_OSPI_D2, + MP_HAL_PIN_ALT_OSPI_D3, + MP_HAL_PIN_ALT_OSPI_D4, + MP_HAL_PIN_ALT_OSPI_D5, + MP_HAL_PIN_ALT_OSPI_D6, + MP_HAL_PIN_ALT_OSPI_D7, + MP_HAL_PIN_ALT_OSPI_RXDS, + MP_HAL_PIN_ALT_OSPI_SCLK, + MP_HAL_PIN_ALT_OSPI_SCLKN, + MP_HAL_PIN_ALT_OSPI_SS0, + MP_HAL_PIN_ALT_OSPI_SS1, + MP_HAL_PIN_ALT_PDM_C0, + MP_HAL_PIN_ALT_PDM_C1, + MP_HAL_PIN_ALT_PDM_C2, + MP_HAL_PIN_ALT_PDM_C3, + MP_HAL_PIN_ALT_PDM_D0, + MP_HAL_PIN_ALT_PDM_D1, + MP_HAL_PIN_ALT_PDM_D2, + MP_HAL_PIN_ALT_PDM_D3, + MP_HAL_PIN_ALT_QEC_X, + MP_HAL_PIN_ALT_QEC_Y, + MP_HAL_PIN_ALT_QEC_Z, + MP_HAL_PIN_ALT_SD_CLK, + MP_HAL_PIN_ALT_SD_CMD, + MP_HAL_PIN_ALT_SD_D0, + MP_HAL_PIN_ALT_SD_D1, + MP_HAL_PIN_ALT_SD_D2, + MP_HAL_PIN_ALT_SD_D3, + MP_HAL_PIN_ALT_SD_D4, + MP_HAL_PIN_ALT_SD_D5, + MP_HAL_PIN_ALT_SD_D6, + MP_HAL_PIN_ALT_SD_D7, + MP_HAL_PIN_ALT_SD_RST, + MP_HAL_PIN_ALT_SPI_MISO, + MP_HAL_PIN_ALT_SPI_MOSI, + MP_HAL_PIN_ALT_SPI_SCLK, + MP_HAL_PIN_ALT_SPI_SS0, + MP_HAL_PIN_ALT_SPI_SS1, + MP_HAL_PIN_ALT_SPI_SS2, + MP_HAL_PIN_ALT_SPI_SS3, + MP_HAL_PIN_ALT_UART_CTS, + MP_HAL_PIN_ALT_UART_DE, + MP_HAL_PIN_ALT_UART_RTS, + MP_HAL_PIN_ALT_UART_RX, + MP_HAL_PIN_ALT_UART_TX, + MP_HAL_PIN_ALT_UT_T0, + MP_HAL_PIN_ALT_UT_T1, +}; + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + GPIO_Type *gpio; + uint8_t port; + uint8_t pin; + uint8_t adc12_periph : 2; + uint8_t adc12_channel : 3; + qstr name; + const uint16_t alt[8]; // holds result of MP_HAL_PIN_ALT_MAKE(function, unit) +} 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_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { + if (enable) { + gpio_enable_interrupt(pin->gpio, pin->pin); + gpio_interrupt_set_edge_trigger(pin->gpio, pin->pin); + gpio_interrupt_set_polarity_low(pin->gpio, pin->pin); + } else { + gpio_disable_interrupt(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. +} + +void mp_hal_pin_config(const machine_pin_obj_t *pin, uint32_t mode, + uint32_t pull, uint32_t speed, uint32_t drive, uint32_t alt, bool ren); + +// Include all the pin definitions. +#include "genhdr/pins_board.h" + +/******************************************************************************/ +// Other HAL functions. + +enum { + MP_HAL_MAC_WLAN0 = 0, + MP_HAL_MAC_WLAN1, + MP_HAL_MAC_BDADDR, + MP_HAL_MAC_ETH0, +}; + +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); diff --git a/ports/alif/mpmetalport.c b/ports/alif/mpmetalport.c new file mode 100644 index 0000000000000..9d774cb906766 --- /dev/null +++ b/ports/alif/mpmetalport.c @@ -0,0 +1,104 @@ +/* + * 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. + * + * libmetal Alif port. + */ + +#include ALIF_CMSIS_H +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "metal/sys.h" +#include "metal/utilities.h" +#include "metal/device.h" + +#include "se_services.h" + +struct metal_state _metal; + +static bool metal_active; +static mp_sched_node_t rproc_notify_node; + +int metal_sys_init(const struct metal_init_params *params) { + metal_unused(params); + + // If cache management is not enabled, configure the MPU to disable + // caching for the entire Open-AMP shared memory region. + #ifndef VIRTIO_USE_DCACHE + ARM_MPU_Disable(); + MPU->RNR = METAL_MPU_REGION_ID; + MPU->RBAR = ARM_MPU_RBAR(METAL_MPU_REGION_BASE, ARM_MPU_SH_NON, 0, 1, 1); // RO-0, NP-1, XN-1 + MPU->RLAR = ARM_MPU_RLAR(METAL_MPU_REGION_BASE + METAL_MPU_REGION_SIZE - 1, MP_MPU_ATTR_NORMAL_NON_CACHEABLE); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + #endif + + metal_bus_register(&metal_generic_bus); + metal_active = true; + + return 0; +} + +void metal_sys_finish(void) { + metal_active = false; + metal_bus_unregister(&metal_generic_bus); +} + +unsigned int sys_irq_save_disable(void) { + return disable_irq(); +} + +void sys_irq_restore_enable(unsigned int state) { + enable_irq(state); +} + +void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa, + size_t size, unsigned int flags) { + metal_unused(pa); + metal_unused(size); + metal_unused(flags); + return va; +} + +void metal_machine_cache_flush(void *addr, unsigned int len) { + SCB_CleanDCache_by_Addr(addr, len); +} + +void metal_machine_cache_invalidate(void *addr, unsigned int len) { + SCB_InvalidateDCache_by_Addr(addr, len); +} + +int metal_rproc_notify(void *priv, uint32_t id) { + // Notify the remote core. + se_services_notify(); + return 0; +} + +void metal_rproc_notified(void) { + if (!metal_active) { + return; + } + // The remote core notified this core. + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); +} diff --git a/ports/alif/mpmetalport.h b/ports/alif/mpmetalport.h new file mode 100644 index 0000000000000..c11725a1f1e45 --- /dev/null +++ b/ports/alif/mpmetalport.h @@ -0,0 +1,76 @@ +/* + * 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. + * + * libmetal alif port. + */ +#ifndef MICROPY_INCLUDED_ALIF_MPMETALPORT_H +#define MICROPY_INCLUDED_ALIF_MPMETALPORT_H + +#include +#include "py/mphal.h" +#include "py/runtime.h" +#include "mpu.h" + +#define METAL_HAVE_STDATOMIC_H 0 +#define METAL_HAVE_FUTEX_H 0 + +#define METAL_MAX_DEVICE_REGIONS 2 + +// Set to 1 to enable log output. +#define METAL_LOG_HANDLER_ENABLE 0 + +#define metal_cpu_yield() + +// Shared memory config +#define METAL_SHM_NAME "OPENAMP_SHM" +// Note 1K must be reserved at the start of the openamp +// shared memory region, for the shared resource table. +#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start) +#define METAL_RSC_SIZE (1024) + +#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) +#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) + +#define METAL_MPU_REGION_ID (MP_MPU_REGION_OPENAMP) +#define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) +#define METAL_MPU_REGION_SIZE (0x00010000U) + +extern const char _openamp_shm_region_start[]; +extern const char _openamp_shm_region_end[]; + +int metal_rproc_notify(void *priv, uint32_t id); +extern void openamp_remoteproc_notified(mp_sched_node_t *node); + +static inline int __metal_sleep_usec(unsigned int usec) { + mp_hal_delay_us(usec); + return 0; +} + +static inline void metal_generic_default_poll(void) { + mp_event_handle_nowait(); + __WFI(); +} + +#endif // MICROPY_INCLUDED_ALIF_METAL_PORT_H diff --git a/ports/alif/mpnetworkport.c b/ports/alif/mpnetworkport.c new file mode 100644 index 0000000000000..40def7f7fd7e9 --- /dev/null +++ b/ports/alif/mpnetworkport.c @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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/obj.h" + +#if MICROPY_PY_LWIP + +#include "shared/runtime/softtimer.h" +#include "lwip/timeouts.h" + +#if MICROPY_PY_NETWORK_CYW43 +#include "lib/cyw43-driver/src/cyw43.h" +#endif + +// Poll lwIP every 64ms by default +#define LWIP_TICK_RATE_MS 64 + +// Soft timer for running lwIP in the background. +static soft_timer_entry_t mp_network_soft_timer; + +u32_t sys_now(void) { + return mp_hal_ticks_ms(); +} + +// This is called by soft_timer and executes at PendSV level. +static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { + // Run the lwIP internal updates. + sys_check_timeouts(); + + #if LWIP_NETIF_LOOPBACK + netif_poll_all(); + #endif + + #if MICROPY_PY_NETWORK_CYW43 + if (cyw43_poll) { + if (cyw43_sleep != 0) { + if (--cyw43_sleep == 0) { + cyw43_poll(); + } + } + } + #endif +} + +void mod_network_lwip_init(void) { + soft_timer_static_init( + &mp_network_soft_timer, + SOFT_TIMER_MODE_PERIODIC, + LWIP_TICK_RATE_MS, + mp_network_soft_timer_callback + ); + + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); +} + +#endif // MICROPY_PY_LWIP diff --git a/ports/alif/mpnimbleport.c b/ports/alif/mpnimbleport.c new file mode 100644 index 0000000000000..8da5ce293c9bd --- /dev/null +++ b/ports/alif/mpnimbleport.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2020 Damien P. George + * + * 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/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#define DEBUG_printf(...) // printf("mpnimbleport.c: " __VA_ARGS__) + +#include "host/ble_hs.h" +#include "nimble/nimble_npl.h" + +#include "extmod/mpbthci.h" +#include "extmod/modbluetooth.h" +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/hal/hal_uart.h" +#include "mpbthciport.h" + +// Get any pending data from the UART and send it to NimBLE's HCI buffers. +// Any further processing by NimBLE will be run via its event queue. +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // DEBUG_printf("mp_bluetooth_hci_poll_uart %d\n", mp_bluetooth_nimble_ble_state); + + // Run any timers. + mp_bluetooth_nimble_os_callout_process(); + + // Process incoming UART data, and run events as they are generated. + mp_bluetooth_nimble_hci_uart_process(true); + + // Run any remaining events (e.g. if there was no UART data). + mp_bluetooth_nimble_os_eventq_run_all(); + } + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + // Call this function again in 128ms to check for new events. + // TODO: improve this by only calling back when needed. + mp_bluetooth_hci_poll_in_ms(128); + } +} + +// --- Port-specific helpers for the generic NimBLE bindings. ----------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFE(); + + // This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK. + // Do not need to run events here (it must not invoke Python code), only processing incoming HCI data. + mp_bluetooth_nimble_hci_uart_process(false); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/alif/mpnimbleport.h b/ports/alif/mpnimbleport.h new file mode 100644 index 0000000000000..150ec42da76d7 --- /dev/null +++ b/ports/alif/mpnimbleport.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_NIMBLE_PORT_H +#define MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#endif // MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H diff --git a/ports/alif/mpremoteprocport.c b/ports/alif/mpremoteprocport.c new file mode 100644 index 0000000000000..27210a824057e --- /dev/null +++ b/ports/alif/mpremoteprocport.c @@ -0,0 +1,172 @@ +/* + * 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. + * + * modremoteproc alif port. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" +#include "metal/sys.h" +#include "metal/device.h" +#include "metal/utilities.h" +#include "extmod/modopenamp_remoteproc.h" +#include "se_services.h" + +typedef struct mmap { + uintptr_t base; + uintptr_t limit; +} mmap_t; + +static const mmap_t mmap_nocache[] = { + { .base = ITCM_BASE, .limit = ITCM_BASE + ITCM_SIZE }, + { .base = DTCM_BASE, .limit = DTCM_BASE + ITCM_SIZE }, + { .base = MRAM_BASE, .limit = MRAM_BASE + MRAM_SIZE } +}; + +static bool is_cacheable(const void *p, size_t bytes) { + uintptr_t base = (uintptr_t)p; + if (bytes == 0) { + return false; + } + uintptr_t limit = base + bytes; + for (unsigned int i = 0; i < sizeof(mmap_nocache) / sizeof(mmap_nocache[0]); i++) { + if (base >= mmap_nocache[i].base && limit < mmap_nocache[i].limit) { + return false; + } + } + return true; +} + +struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc, + const struct remoteproc_ops *ops, void *arg) { + metal_log(METAL_LOG_DEBUG, "rproc_init()\n"); + + rproc->ops = ops; + rproc->state = RPROC_OFFLINE; + // Allocate the image store and save it in private data. + rproc->priv = mp_openamp_remoteproc_store_alloc(); + + // Reset the remote core. + se_services_boot_reset_cpu(EXTSYS_1); + return rproc; +} + +void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, unsigned int attribute, + struct metal_io_region **io) { + metal_log(METAL_LOG_DEBUG, "rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size); + + struct remoteproc_mem *mem; + metal_phys_addr_t lpa = *pa; + metal_phys_addr_t lda = *da; + + if (lda == METAL_BAD_PHYS) { + return NULL; + } + + if (lpa == METAL_BAD_PHYS) { + lpa = lda; + } + + // Currently this port doesn't support loading firmware to flash, + // only SD/SRAM images are supported. Check of load address is in + // the flash region, and if so return NULL. + if (lda >= MRAM_BASE && lda < (MRAM_BASE + MRAM_SIZE)) { + return NULL; + } + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) { + return NULL; + } + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + remoteproc_init_mem(mem, NULL, lpa, lda, size, *io); + + metal_io_init(*io, (void *)mem->da, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + return metal_io_phys_to_virt(*io, mem->pa); +} + +int mp_openamp_remoteproc_start(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_start()\n"); + + // Flush cached areas for the remote core. + struct metal_list *node; + metal_list_for_each(&rproc->mems, node) { + struct remoteproc_mem *mem; + mem = metal_container_of(node, struct remoteproc_mem, node); + if (is_cacheable((uint32_t *)mem->pa, mem->size)) { + SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size); + } + } + + se_services_boot_reset_cpu(EXTSYS_1); + se_services_boot_cpu(EXTSYS_1, (uint32_t)rproc->bootaddr); // GlobalToLocal + return 0; +} + +int mp_openamp_remoteproc_stop(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_stop()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} + +int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data) { + metal_log(METAL_LOG_DEBUG, "rproc_config()\n"); + (void)rproc; + return 0; +} + +void mp_openamp_remoteproc_remove(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_remove()\n"); + (void)rproc; +} + +int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc) { + metal_log(METAL_LOG_DEBUG, "rproc_shutdown()\n"); + if (rproc->state == RPROC_RUNNING) { + se_services_boot_reset_cpu(EXTSYS_1); + } + return 0; +} diff --git a/ports/alif/mpu.c b/ports/alif/mpu.c new file mode 100644 index 0000000000000..9a051794de845 --- /dev/null +++ b/ports/alif/mpu.c @@ -0,0 +1,93 @@ +/* + * 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" +#include "irq.h" +#include "mpu.h" +#include ALIF_CMSIS_H + +static const ARM_MPU_Region_t mpu_table[] __STARTUP_RO_DATA_ATTRIBUTE = { + [MP_MPU_REGION_SRAM0] = { /* SRAM0 - 4MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x02000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x023FFFFF, MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT) + }, + [MP_MPU_REGION_SRAM1] = { /* SRAM1 - 2.5MB : RO-0, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x08000000, ARM_MPU_SH_NON, 0, 1, 0), + .RLAR = ARM_MPU_RLAR(0x0827FFFF, MP_MPU_ATTR_NORMAL_WB_RA_WA) + }, + [MP_MPU_REGION_HOST_PERIPHERALS] = { /* Host Peripherals - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x1A000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x1AFFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) + }, + [MP_MPU_REGION_MRAM] = { /* MRAM - 5.5MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0x80000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0x8057FFFF, MP_MPU_ATTR_NORMAL_WT_RA) + }, + [MP_MPU_REGION_OSPI_REGISTERS] = { /* OSPI Regs - 16MB : RO-0, NP-1, XN-1 */ + .RBAR = ARM_MPU_RBAR(0x83000000, ARM_MPU_SH_NON, 0, 1, 1), + .RLAR = ARM_MPU_RLAR(0x83FFFFFF, MP_MPU_ATTR_DEVICE_nGnRE) + }, + [MP_MPU_REGION_OSPI0_XIP] = { /* OSPI0 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xA0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xBFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) + }, + [MP_MPU_REGION_OSPI1_XIP] = { /* OSPI1 XIP flash - 512MB : RO-1, NP-1, XN-0 */ + .RBAR = ARM_MPU_RBAR(0xC0000000, ARM_MPU_SH_NON, 1, 1, 0), + .RLAR = ARM_MPU_RLAR(0xDFFFFFFF, MP_MPU_ATTR_NORMAL_NON_CACHEABLE) + }, +}; + +void MPU_Load_Regions(void) { + // Configure memory attributes. + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_DEVICE_nGnRE, + ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE, ARM_MPU_ATTR_DEVICE_nGnRE)); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WB_RA_WA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_WT_RA, + ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0))); + + ARM_MPU_SetMemAttr(MP_MPU_ATTR_NORMAL_NON_CACHEABLE, + ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, ARM_MPU_ATTR_NON_CACHEABLE)); + + // Load the MPU regions from the table. + ARM_MPU_Load(0, mpu_table, sizeof(mpu_table) / sizeof(ARM_MPU_Region_t)); +} + +void mpu_config_mram(bool read_only) { + uintptr_t atomic = disable_irq(); + ARM_MPU_Disable(); + MPU->RNR = MP_MPU_REGION_MRAM; + MPU->RBAR = ARM_MPU_RBAR(MRAM_BASE, ARM_MPU_SH_NON, read_only, 1, 0); + MPU->RLAR = ARM_MPU_RLAR(MRAM_BASE + MRAM_SIZE - 1, MP_MPU_ATTR_NORMAL_WT_RA); + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + enable_irq(atomic); +} diff --git a/ports/alif/mpu.h b/ports/alif/mpu.h new file mode 100644 index 0000000000000..f4df496683e55 --- /dev/null +++ b/ports/alif/mpu.h @@ -0,0 +1,44 @@ +/* + * 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 + +#define MP_MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0) +#define MP_MPU_ATTR_DEVICE_nGnRE (1) +#define MP_MPU_ATTR_NORMAL_WB_RA_WA (2) +#define MP_MPU_ATTR_NORMAL_WT_RA (3) +#define MP_MPU_ATTR_NORMAL_NON_CACHEABLE (4) + +#define MP_MPU_REGION_SRAM0 (0) +#define MP_MPU_REGION_SRAM1 (1) +#define MP_MPU_REGION_HOST_PERIPHERALS (2) +#define MP_MPU_REGION_MRAM (3) +#define MP_MPU_REGION_OSPI_REGISTERS (4) +#define MP_MPU_REGION_OSPI0_XIP (5) +#define MP_MPU_REGION_OSPI1_XIP (6) +#define MP_MPU_REGION_OPENAMP (7) + +void mpu_config_mram(bool read_only); diff --git a/ports/alif/mpuart.c b/ports/alif/mpuart.c new file mode 100644 index 0000000000000..b646e6f2be471 --- /dev/null +++ b/ports/alif/mpuart.c @@ -0,0 +1,346 @@ +/* + * 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 "shared/runtime/softtimer.h" +#include "mpuart.h" +#include "sys_ctrl_uart.h" + +#define mp_container_of(ptr, structure, member) (void *)((uintptr_t)(ptr) - offsetof(structure, member)) + +#define UART_LSR_TEMT_Pos (6) +#define SYST_PCLK (100000000) + +#define CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits) \ + (1 + ((data_bits) + 5) + ((parity) != UART_PARITY_NONE) + ((stop_bits) + 1)) + +typedef struct _uart_state_t { + UART_TRANSFER_STATUS status; + uint32_t baudrate; + uint32_t bits_per_char; + ringbuf_t *rx_ringbuf; + const uint8_t *tx_src; + const uint8_t *tx_src_max; + soft_timer_entry_t rx_idle_timer; + unsigned int irq_trigger; + void (*irq_callback)(unsigned int uart_id, unsigned int trigger); +} uart_state_t; + +static const uint8_t uart_irqn[UART_MAX] = { + UART0_IRQ_IRQn, + UART1_IRQ_IRQn, + UART2_IRQ_IRQn, + UART3_IRQ_IRQn, + UART4_IRQ_IRQn, + UART5_IRQ_IRQn, + UART6_IRQ_IRQn, + UART7_IRQ_IRQn, +}; + +static UART_Type *const uart_periph[UART_MAX] = { + (UART_Type *)UART0_BASE, + (UART_Type *)UART1_BASE, + (UART_Type *)UART2_BASE, + (UART_Type *)UART3_BASE, + (UART_Type *)UART4_BASE, + (UART_Type *)UART5_BASE, + (UART_Type *)UART6_BASE, + (UART_Type *)UART7_BASE, +}; + +static uart_state_t uart_state[UART_MAX]; + +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +static void rx_idle_timer_callback(soft_timer_entry_t *self) { + uart_state_t *state = mp_container_of(self, uart_state_t, rx_idle_timer); + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RXIDLE) { + unsigned int uart_id = state - &uart_state[0]; + state->irq_callback(uart_id, MP_UART_IRQ_RXIDLE); + } +} + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) { + + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Configure TX/RX pins. + mp_hal_pin_config(tx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_TX, uart_id), false); + mp_hal_pin_config(rx, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RX, uart_id), true); + + // Configure the UART peripheral. + select_uart_clock_syst_pclk(uart_id); + enable_uart_clock(uart_id); + uart_software_reset(uart); + uart_enable_fifo(uart); + uart_disable_tx_irq(uart); + uart_disable_rx_irq(uart); + uart_set_baudrate(uart, SYST_PCLK, baudrate); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); + uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE); + uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET; + uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY); + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); + + // Initialise the state. + state->status = UART_TRANSFER_STATUS_NONE; + state->baudrate = baudrate; + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); + state->rx_ringbuf = rx_ringbuf; + state->tx_src = NULL; + state->tx_src_max = NULL; + state->irq_trigger = 0; + state->irq_callback = NULL; + + // Enable interrupts. + NVIC_ClearPendingIRQ(uart_irqn[uart_id]); + NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL); + NVIC_EnableIRQ(uart_irqn[uart_id]); + uart_enable_rx_irq(uart); + + soft_timer_static_init(&state->rx_idle_timer, SOFT_TIMER_MODE_ONE_SHOT, 0, rx_idle_timer_callback); +} + +void mp_uart_deinit(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + + uart_disable_rx_irq(uart); + NVIC_DisableIRQ(uart_irqn[uart_id]); +} + +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + state->irq_trigger = trigger; + state->irq_callback = callback; + if (trigger & MP_UART_IRQ_RX) { + uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO); + } else { + uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL); + } +} + +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) { + UART_Type *uart = uart_periph[uart_id]; + + unsigned int flow = UART_FLOW_CONTROL_NONE; + if (rts != NULL) { + flow |= UART_FLOW_CONTROL_RTS; + mp_hal_pin_config(rts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_RTS, uart_id), false); + } + if (cts != NULL) { + flow |= UART_FLOW_CONTROL_CTS; + mp_hal_pin_config(cts, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT(UART_CTS, uart_id), true); + } + uart_set_flow_control(uart, flow); +} + +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + state->baudrate = baudrate; + uart_set_baudrate(uart, SYST_PCLK, baudrate); +} + +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits); + uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits); +} + +size_t mp_uart_rx_any(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL) { + return ringbuf_avail(state->rx_ringbuf); + } + return 0; +} + +size_t mp_uart_tx_any(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + return uart->UART_TFL + !((uart->UART_LSR >> UART_LSR_TEMT_Pos) & 1); +} + +int mp_uart_rx_char(unsigned int uart_id) { + uart_state_t *state = &uart_state[uart_id]; + if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) { + return ringbuf_get(state->rx_ringbuf); + } + return -1; +} + +void mp_uart_tx_data_blocking(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + for (size_t i = 0; i < len; ++i) { + for (;;) { + size_t tx_avail = UART_FIFO_DEPTH - uart->UART_TFL; + if (tx_avail > 0) { + break; + } + mp_event_handle_nowait(); + } + uart->UART_THR = src[i]; + } +} + +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len) { + UART_Type *uart = uart_periph[uart_id]; + + // Configure transmission, to be handled by IRQ. + uart_state_t *state = &uart_state[uart_id]; + state->status = UART_TRANSFER_STATUS_NONE; + state->tx_src = src; + state->tx_src_max = src + len; + + // Enable TX IRQ to start transmission. + uart_enable_tx_irq(uart); + + // Wait for transfer to complete. + const uint32_t total_timeout_ms = 100 * len; + uint32_t start = mp_hal_ticks_ms(); + while (state->status == UART_TRANSFER_STATUS_NONE) { + if (mp_hal_ticks_ms() - start > total_timeout_ms) { + break; + } + mp_event_wait_indefinite(); + } + + // Disable TX IRQ. + uart_disable_tx_irq(uart); +} + +static void mp_uart_irq_handler(unsigned int uart_id) { + UART_Type *uart = uart_periph[uart_id]; + uart_state_t *state = &uart_state[uart_id]; + + // Process pending interrupt (below is order of highest to lowest priority). + uint32_t iir = uart->UART_IIR & UART_IIR_INTERRUPT_ID_MASK; + switch (iir) { + case UART_IIR_RECEIVER_LINE_STATUS: { + uint32_t lsr = uart->UART_LSR; + if (lsr & (UART_LSR_RECEIVER_FIFO_ERR | UART_LSR_OVERRUN_ERR)) { + state->status = UART_TRANSFER_STATUS_ERROR; + } + break; + } + + case UART_IIR_RECEIVED_DATA_AVAILABLE: + case UART_IIR_CHARACTER_TIMEOUT: { + bool had_char = false; + while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) { + for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) { + had_char = true; + int c = uart->UART_RBR; + #if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION + if (uart_id == MICROPY_HW_UART_REPL) { + if (c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + } + #endif + if (state->rx_ringbuf != NULL) { + ringbuf_put(state->rx_ringbuf, c); + } + } + } + + if (had_char) { + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RX) { + state->irq_callback(uart_id, MP_UART_IRQ_RX); + } + } + + if (state->irq_trigger & MP_UART_IRQ_RXIDLE) { + // Wait for 2 characters worth of time before triggering the RXIDLE event. + soft_timer_reinsert(&state->rx_idle_timer, 2000 * state->bits_per_char / state->baudrate + 1); + } + break; + } + + case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY: + while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) { + if (state->tx_src < state->tx_src_max) { + uart->UART_THR = *state->tx_src++; + } else { + uart_disable_tx_irq(uart); + state->status = UART_TRANSFER_STATUS_SEND_COMPLETE; + if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_TXIDLE) { + state->irq_callback(uart_id, MP_UART_IRQ_TXIDLE); + } + break; + } + } + break; + + case UART_IIR_MODEM_STATUS: + (void)uart->UART_MSR; + break; + } + + __SEV(); +} + +#define DEFINE_IRQ_HANDLER(id) \ + void UART##id##_IRQHandler(void) { \ + mp_uart_irq_handler(id); \ + } + +DEFINE_IRQ_HANDLER(0) +DEFINE_IRQ_HANDLER(1) +DEFINE_IRQ_HANDLER(2) +DEFINE_IRQ_HANDLER(3) +DEFINE_IRQ_HANDLER(4) +DEFINE_IRQ_HANDLER(5) +DEFINE_IRQ_HANDLER(6) +DEFINE_IRQ_HANDLER(7) + +#if MICROPY_HW_ENABLE_UART_REPL + +#define REPL_BAUDRATE (115200) + +void mp_uart_init_repl(void) { + mp_uart_init(MICROPY_HW_UART_REPL, + REPL_BAUDRATE, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, + pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf); +} + +void mp_uart_write_strn_repl(const char *str, size_t len) { + mp_uart_tx_data(MICROPY_HW_UART_REPL, (const uint8_t *)str, len); +} + +#endif diff --git a/ports/alif/mpuart.h b/ports/alif/mpuart.h new file mode 100644 index 0000000000000..76dadaab38015 --- /dev/null +++ b/ports/alif/mpuart.h @@ -0,0 +1,55 @@ +/* + * 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 + +#include "py/ringbuf.h" +#include "uart.h" + +#define UART_MAX (8) +#define MP_UART_IRQ_RX (1) +#define MP_UART_IRQ_RXIDLE (2) +#define MP_UART_IRQ_TXIDLE (4) + +void mp_uart_init(unsigned int uart_id, uint32_t baudrate, + UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits, + mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf); +void mp_uart_deinit(unsigned int uart_id); + +void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)); +void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts); +void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate); +void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits); + +size_t mp_uart_rx_any(unsigned int uart_id); +size_t mp_uart_tx_any(unsigned int uart_id); +int mp_uart_rx_char(unsigned int uart_id); +void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len); + +void mp_uart_init_repl(void); +void mp_uart_write_strn_repl(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 0000000000000..0b8ddddeda248 --- /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_FS_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/nosys_stubs.c b/ports/alif/nosys_stubs.c new file mode 100644 index 0000000000000..a394ec8f1cf66 --- /dev/null +++ b/ports/alif/nosys_stubs.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 + +int _write(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _read(int handle, char *buffer, int size) { + errno = ENOSYS; + return -1; +} + +int _close(int f) { + errno = ENOSYS; + return -1; +} + +int _lseek(int f, int ptr, int dir) { + errno = ENOSYS; + return -1; +} diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c new file mode 100644 index 0000000000000..6ddaaf4e5a325 --- /dev/null +++ b/ports/alif/ospi_ext.c @@ -0,0 +1,302 @@ +/* + * 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 ALIF_CMSIS_H +#include "ospi_ext.h" +#include "ospi_xip_user.h" + +static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; +} + +static void ospi_xip_enable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control |= AES_CONTROL_XIP_EN; + #if OSPI_XIP_ENABLE_AES_DECRYPTION + ospi_cfg->aes_regs->aes_control |= (AES_CONTROL_LD_KEY | AES_CONTROL_DECRYPT_EN); + #endif +} + +// Standard SPI transfer. +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t ctrlr0 = CTRLR0_IS_MST + | (SINGLE << CTRLR0_SPI_FRF_OFFSET) + | (SPI_TMOD_TR << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_8bit << CTRLR0_DFS_OFFSET) + ; + + uint32_t spi_ctrlr0 = TRANS_TYPE_STANDARD; + + ospi_writel(ospi_cfg, ctrlr0, ctrlr0); + ospi_writel(ospi_cfg, ctrlr1, len - 1); + ospi_writel(ospi_cfg, spi_ctrlr0, spi_ctrlr0); + spi_enable(ospi_cfg); + + // Buffer output data in SPI FIFO. + for (int i = 0; i < len; ++i) { + ospi_writel(ospi_cfg, data_reg, buf_out[i]); + } + + // Enable the SPI peripheral. + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + // Read in data. + for (int i = 0; i < len; ++i) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + buf_in[i] = ospi_readl(ospi_cfg, data_reg); + } +} + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, read_len - 1); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + ospi_cfg->rx_req = read_len; + spi_enable(ospi_cfg); +} + +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + 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 -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer) { + 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 -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer) { + 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 -OSPI_ETIMEDOUT; + } + } + *buffer++ = __ROR(ospi_readl(ospi_cfg, data_reg), 16); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_TO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, 0); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + spi_enable(ospi_cfg); +} + +static inline uint32_t ospi_xip_ctrlr0(uint32_t data_len) { + return CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); +} + +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { + spi_disable(ospi_cfg); + + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); + + uint32_t val = (OCTAL << XIP_CTRL_FRF_OFFSET) + | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) + | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) + | (inst_len << XIP_CTRL_INST_L_OFFSET) + | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) + | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) + | (0x1 << XIP_CTRL_DFC_HC_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) + | (0x1 << XIP_CTRL_INST_EN_OFFSET) + | (0x1 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_SIG_EN) + | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) + | (0x0 << XIP_PREFETCH_EN_OFFSET) + | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << XIP_CTRL_INST_DDR_EN_OFFSET; + } + + ospi_writel(ospi_cfg, xip_ctrl, val); + + ospi_writel(ospi_cfg, rx_sample_dly, OSPI_XIP_RX_SAMPLE_DELAY); + ospi_writel(ospi_cfg, txd_drive_edge, OSPI_XIP_DDR_DRIVE_EDGE); + ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; + + ospi_writel(ospi_cfg, xip_mode_bits, 0x0); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); + + spi_enable(ospi_cfg); + ospi_xip_enable(ospi_cfg); +} + +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_32bit << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = TRANS_TYPE_FRF_DEFINED + | ((ospi_cfg->ddr_en) << CTRLR0_SPI_DDR_EN_OFFSET) + | (2 << CTRLR0_XIP_MBL_OFFSET) + | (1 << CTRLR0_XIP_DFS_HC_OFFSET) + | (1 << CTRLR0_XIP_INST_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) + | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + + ospi_writel(ospi_cfg, xip_mode_bits, 0x1); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); + + spi_enable(ospi_cfg); + + ospi_xip_enable(ospi_cfg); + ospi_xip_disable(ospi_cfg); +} + +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len) { + spi_disable(ospi_cfg); + ospi_writel(ospi_cfg, ctrlr0, ospi_xip_ctrlr0(data_len)); + spi_enable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h new file mode 100644 index 0000000000000..65e2ced1ff7bc --- /dev/null +++ b/ports/alif/ospi_ext.h @@ -0,0 +1,58 @@ +/* + * 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_EXT_H +#define MICROPY_INCLUDED_ALIF_OSPI_EXT_H + +#include +#include "ospi_drv.h" + +#define OSPI_ETIMEDOUT (110) + +#define OSPI_INST_L_8bit (CTRLR0_INST_L_8bit) +#define OSPI_INST_L_16bit (CTRLR0_INST_L_16bit) + +#define OSPI_ADDR_L_0bit UINT32_C(0x0) +#define OSPI_ADDR_L_24bit UINT32_C(0x6) +#define OSPI_ADDR_L_32bit UINT32_C(0x8) + +#define OSPI_DATA_L_8bit (CTRLR0_DFS_8bit) +#define OSPI_DATA_L_16bit (CTRLR0_DFS_16bit) +#define OSPI_DATA_L_32bit (CTRLR0_DFS_32bit) + +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in); + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles); +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer); +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer); +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer); + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); + +void ospi_xip_enter_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t inst_len, uint16_t incr_command, uint16_t wrap_command); +void ospi_xip_restore_ext(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c new file mode 100644 index 0000000000000..f78002eb0ef97 --- /dev/null +++ b/ports/alif/ospi_flash.c @@ -0,0 +1,480 @@ +/* + * 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" + +#if MICROPY_HW_ENABLE_OSPI + +#include "ospi_ext.h" +#include "ospi_flash.h" +#include "ospi_drv.h" +#include "ospi_xip_user.h" +#include "pinconf.h" + +#define WAIT_SR_TIMEOUT (1000000) + +// Generic SPI flash commands. +#define CMD_SPI_WREN (0x06) + +// This is the maximum number of bytes that can be written to SPI flash at once. +#define PAGE_SIZE (256) + +// All OSP0/OSPI1 pins use the same alternate function. +#define OSPI_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 + +typedef struct _ospi_flash_t { + const ospi_pin_settings_t *pin; + const ospi_flash_settings_t *set; + ospi_flash_cfg_t cfg; + bool xip_active; +} ospi_flash_t; + +static ospi_flash_t global_flash; + +/******************************************************************************/ +// Generic SPI-flash helper functions. + +static void ospi_flash_wren_spi(ospi_flash_t *self) { + uint8_t buf[1] = {CMD_SPI_WREN}; + ospi_spi_transfer(&self->cfg, 1, buf, buf); +} + +static int ospi_flash_read_cmd(ospi_flash_t *self, uint32_t cmd, uint8_t cmd_dummy_cycles, size_t len, uint8_t *dest) { + int ret = 0; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, len, cmd_dummy_cycles); + ret = ospi_recv_blocking_8bit_data(&self->cfg, cmd, dest); + } else { + uint16_t dest16 = 0; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, len, cmd_dummy_cycles); + ospi_push(&self->cfg, cmd); + ret = ospi_recv_blocking_16bit_data(&self->cfg, 0 /* addr */, &dest16); + *dest = dest16; + } + return ret; +} + +static int ospi_flash_write_cmd_addr(ospi_flash_t *self, uint32_t cmd, uint32_t addr_len, uint32_t addr) { + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_write_ext(&self->cfg, false, self->set->inst_len, addr_len, OSPI_DATA_L_8bit); + } else { + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, addr_len, OSPI_DATA_L_16bit); + } + if (addr_len == OSPI_ADDR_L_0bit) { + ospi_send_blocking(&self->cfg, cmd); + } else { + ospi_push(&self->cfg, cmd); + ospi_send_blocking(&self->cfg, addr); + } + return 0; +} + +static int ospi_flash_write_cmd(ospi_flash_t *self, uint32_t cmd) { + return ospi_flash_write_cmd_addr(self, cmd, OSPI_ADDR_L_0bit, 0); +} + +static uint32_t ospi_flash_read_id_spi(ospi_flash_t *self) { + uint8_t buf[4] = {0x9f, 0, 0, 0}; + ospi_spi_transfer(&self->cfg, 4, buf, buf); + return buf[1] | buf[2] << 8 | buf[3] << 16; +} + +static int ospi_flash_wait_sr(ospi_flash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { + do { + uint8_t sr = 0; + int ret = ospi_flash_read_cmd(self, self->set->read_sr, self->set->read_sr_dummy_cycles, 1, &sr); + if (ret != 0) { + return ret; + } + if ((sr & mask) == val) { + return 0; // success + } + } while (timeout--); + + return -MP_ETIMEDOUT; +} + +static int ospi_flash_wait_wel1(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +} + +static int ospi_flash_wait_wip0(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +} + +static uint32_t ospi_flash_read_id(ospi_flash_t *self) { + uint8_t buf[4] = {0}; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, 3, self->set->read_id_dummy_cycles); + ospi_recv_blocking_8bit_data(&self->cfg, self->set->read_id, buf); + } else { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 4, self->set->read_id_dummy_cycles); + ospi_push(&self->cfg, self->set->read_id); + // Read 8-bit values because data is in SDR mode for read id. + ospi_recv_blocking_8bit_data(&self->cfg, 0, buf); + } + return buf[0] | buf[1] << 8 | buf[2] << 16; +} + +/******************************************************************************/ +// Functions specific to ISSI flash chips. + +int ospi_flash_issi_init(ospi_flash_t *self) { + const uint8_t cmd_wrvol = 0x81; + + // Configure dummy cycles. + ospi_flash_wren_spi(self); + uint8_t buf0[5] = {cmd_wrvol, 0, 0, 1, self->set->read_dummy_cycles}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal DDR mode. + const uint8_t issi_mode_octal_ddr_dqs = 0xe7; + ospi_flash_wren_spi(self); + uint8_t buf1[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); + self->cfg.ddr_en = 1; + return 0; +} + +/******************************************************************************/ +// Functions specific to MX flash chips. + +int ospi_flash_mx_init(ospi_flash_t *self) { + const uint8_t cmd_wrcr2 = 0x72; + const uint8_t mx_mode_enable_sopi = 0x01; + const uint8_t mx_mode_enable_dopi = 0x02; + uint8_t mx_mode; + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + mx_mode = mx_mode_enable_dopi; + } else { + mx_mode = mx_mode_enable_sopi; + } + + // Configure dummy cycles. + uint8_t ddc_value = 0; + const uint8_t ospi_flash_mx_ddc[][2] = { + {20, 0}, {18, 1}, {16, 2}, {14, 3}, {12, 4}, {10, 5}, {8, 6}, {6, 7} + }; + for (size_t i = 0; i < MP_ARRAY_SIZE(ospi_flash_mx_ddc); i++) { + if (self->set->read_dummy_cycles == ospi_flash_mx_ddc[i][0]) { + ddc_value = ospi_flash_mx_ddc[i][1]; + break; + } + } + + ospi_flash_wren_spi(self); + uint8_t buf0[6] = {cmd_wrcr2, 0, 0, 3, 0, ddc_value}; + ospi_spi_transfer(&self->cfg, sizeof(buf0), buf0, buf0); + + // Switch SPI flash to Octal SDR or DDR mode. + ospi_flash_wren_spi(self); + uint8_t buf1[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf1), buf1, buf1); + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + self->cfg.ddr_en = 1; + } else { + self->cfg.ddr_en = 0; + } + return 0; +} + +static uint8_t ospi_flash_mx_read_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr) { + // TODO: currently only works in DDR mode + + uint16_t buf[1] = {0}; + ospi_setup_read_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 1, 4); + ospi_push(&self->cfg, command); + ospi_recv_blocking_16bit_data(&self->cfg, addr, buf); + return buf[0] & 0xff; +} + +uint8_t ospi_flash_mx_read_cr(ospi_flash_t *self) { + return ospi_flash_mx_read_cr_helper(self, 0x15ea, 0); +} + +uint8_t ospi_flash_mx_read_cr2(ospi_flash_t *self, uint32_t addr) { + return ospi_flash_mx_read_cr_helper(self, 0x718e, addr); +} + +static int ospi_flash_mx_write_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr, uint8_t value) { + // TODO: currently only works in DDR mode + + // Enable writes so that the register can be modified. + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + // Do the write. + ospi_setup_write_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit); + ospi_push(&self->cfg, command); + ospi_push(&self->cfg, addr); + ospi_push(&self->cfg, value << 8); // in DDR mode, MSByte contains the register value to write + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + // Wait for the write to finish. + return ospi_flash_wait_wip0(self); +} + +int ospi_flash_mx_write_cr(ospi_flash_t *self, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x01fe, 1, value); +} + +int ospi_flash_mx_write_cr2(ospi_flash_t *self, uint32_t addr, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x728d, addr, value); +} + +/******************************************************************************/ +// SPI flash initialisation. + +int ospi_flash_init(void) { + ospi_flash_t *self = &global_flash; + + const ospi_pin_settings_t *pin = &ospi_pin_settings; + const ospi_flash_settings_t *set = NULL; + + self->pin = pin; + + unsigned int unit = pin->peripheral_number; + mp_hal_pin_config(pin->pin_cs, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SS0, unit), false); + mp_hal_pin_config(pin->pin_clk_p, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLK, unit), false); + if (pin->pin_clk_n != NULL) { + mp_hal_pin_config(pin->pin_clk_n, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_SCLKN, unit), false); + } + if (pin->pin_rwds != NULL) { + mp_hal_pin_config(pin->pin_rwds, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); + if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + mp_hal_pin_config(pin_P5_6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_RXDS, unit), true); + } + } + mp_hal_pin_config(pin->pin_d0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D0, unit), true); + mp_hal_pin_config(pin->pin_d1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D1, unit), true); + mp_hal_pin_config(pin->pin_d2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D2, unit), true); + mp_hal_pin_config(pin->pin_d3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D3, unit), true); + if (pin->pin_d4 != NULL) { + mp_hal_pin_config(pin->pin_d4, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D4, unit), true); + mp_hal_pin_config(pin->pin_d5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D5, unit), true); + mp_hal_pin_config(pin->pin_d6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D6, unit), true); + mp_hal_pin_config(pin->pin_d7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_12MA, MP_HAL_PIN_ALT(OSPI_D7, unit), true); + } + + // Reset the SPI flash. + mp_hal_pin_output(pin->pin_reset); + mp_hal_pin_low(pin->pin_reset); + mp_hal_delay_us(100); + mp_hal_pin_high(pin->pin_reset); + mp_hal_delay_us(1000); + + // Configure the OSPI peripheral. + if (pin->peripheral_number == 0) { + self->cfg.regs = (ssi_regs_t *)OSPI0_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES0_BASE; + self->cfg.xip_base = (volatile void *)OSPI0_XIP_BASE; + } else { + 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; // enable slave select + self->cfg.addrlen = 8; // 32-bit address length + self->cfg.ddr_en = 0; + self->cfg.wait_cycles = 0; // used only for ospi_xip_exit + self->cfg.ospi_clock = 100000; // use 100KHz for detection. + ospi_init(&self->cfg); + + // Read the device ID. + uint32_t jedec_id = ospi_flash_read_id_spi(self); + + // Auto-detect the flash. + for (size_t i = 0; i < ospi_flash_settings_len; i++) { + set = &ospi_flash_settings[i]; + if (jedec_id == set->jedec_id) { + self->set = set; + break; + } + } + + if (self->set == NULL) { + // Flash part is not supported. + return -1; + } + + // Switch to the higher frequency. + self->cfg.ospi_clock = set->freq_hz; + ospi_init(&self->cfg); + + // Switch to octal mode if needed. + if (set->flash_init != NULL) { + set->flash_init(self); + + // Check the device ID after switching mode. + if (ospi_flash_read_id(self) != set->jedec_id) { + return -1; + } + } + + // Enter XIP mode. + ospi_flash_xip_enter(self); + + return 0; +} + +uintptr_t ospi_flash_get_xip_base(void) { + ospi_flash_t *self = &global_flash; + return (uintptr_t)self->cfg.xip_base; +} + +int ospi_flash_xip_enter(ospi_flash_t *self) { + if (!self->xip_active) { + uint32_t irq_state = disable_irq(); + self->xip_active = true; + ospi_xip_enter_ext(&self->cfg, self->set->inst_len, self->set->xip_data_len, + self->set->read_command, self->set->read_command, self->set->read_dummy_cycles); + enable_irq(irq_state); + } + return 0; +} + +int ospi_flash_xip_exit(ospi_flash_t *self) { + if (self->xip_active) { + uint32_t irq_state = disable_irq(); + ospi_xip_exit_ext(&self->cfg, self->set->inst_len, self->set->read_command, self->set->read_command); + self->xip_active = false; + enable_irq(irq_state); + } + return 0; +} + +int ospi_flash_xip_restore(ospi_flash_t *self) { + if (self->xip_active) { + ospi_xip_restore_ext(&self->cfg, self->set->xip_data_len); + } + return 0; +} + +/******************************************************************************/ +// Top-level read/erase/write functions. + +int ospi_flash_erase_sector(uint32_t addr) { + ospi_flash_t *self = &global_flash; + + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret == 0) { + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); + ret = ospi_flash_wait_wip0(self); + } + + ospi_flash_xip_restore(self); + + return ret; +} + +int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { + ospi_flash_t *self = &global_flash; + // Perform an XIP read (memcpy) + memcpy(dest, (void *)self->cfg.xip_base + addr, len); + return 0; +} + +static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_t *self = &global_flash; + + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit); + ospi_push(&self->cfg, self->set->write_command); + ospi_push(&self->cfg, addr); + + const uint32_t *src32 = (const uint32_t *)src; + if (self->set->bswap16) { + // MX flashes swap 16-bit words when read in 8D-8D-8D. + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); + } + } else { + // For the rest of the flashes, we just correct the endianness. + for (; len; len -= 4) { + ospi_push(&self->cfg, __REV(*src32++)); + } + } + + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + return ospi_flash_wait_wip0(self); +} + +int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { + ospi_flash_t *self = &global_flash; + + 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; + } + + ospi_flash_xip_restore(self); + + return ret; +} + +#endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h new file mode 100644 index 0000000000000..f909f4a43c889 --- /dev/null +++ b/ports/alif/ospi_flash.h @@ -0,0 +1,104 @@ +/* + * 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 "py/mphal.h" +#include "ospi_flash_settings.h" + +// Format of command, address and data phases. +enum { + OSPI_FLASH_OCTAL_MODE_SSS, + OSPI_FLASH_OCTAL_MODE_SDD, + OSPI_FLASH_OCTAL_MODE_DDD, +}; + +struct _ospi_flash_t; + +typedef struct _ospi_pin_settings_t { + uint32_t peripheral_number; + const mp_hal_pin_obj_t pin_reset; + const mp_hal_pin_obj_t pin_cs; + const mp_hal_pin_obj_t pin_clk_p; + const mp_hal_pin_obj_t pin_clk_n; + const mp_hal_pin_obj_t pin_rwds; + const mp_hal_pin_obj_t pin_d0; + const mp_hal_pin_obj_t pin_d1; + const mp_hal_pin_obj_t pin_d2; + const mp_hal_pin_obj_t pin_d3; + const mp_hal_pin_obj_t pin_d4; + const mp_hal_pin_obj_t pin_d5; + const mp_hal_pin_obj_t pin_d6; + const mp_hal_pin_obj_t pin_d7; +} ospi_pin_settings_t; + +typedef struct _ospi_flash_settings_t { + uint32_t jedec_id; + uint32_t freq_hz; + int (*flash_init)(struct _ospi_flash_t *); + uint8_t octal_mode; + bool rxds; + bool bswap16; + uint8_t inst_len; + uint8_t xip_data_len; + uint16_t read_sr; + uint8_t read_sr_dummy_cycles; + uint16_t read_id; + uint8_t read_id_dummy_cycles; + uint16_t write_en; + uint16_t read_command; + uint8_t read_dummy_cycles; + uint16_t write_command; + uint16_t erase_command; +} ospi_flash_settings_t; + +// Provided by the board when it enables OSPI. +extern const ospi_pin_settings_t ospi_pin_settings; +extern const ospi_flash_settings_t ospi_flash_settings[]; +extern const size_t ospi_flash_settings_len; + +// Functions specific to ISSI flash chips. +int ospi_flash_issi_init(struct _ospi_flash_t *self); + +// Functions specific to MX flash chips. +int ospi_flash_mx_init(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); +int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); +int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); + +// XIP control +int ospi_flash_xip_enter(struct _ospi_flash_t *self); +int ospi_flash_xip_exit(struct _ospi_flash_t *self); + +// SPI flash interface. +int ospi_flash_init(void); +uintptr_t ospi_flash_get_xip_base(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_flash_settings.h b/ports/alif/ospi_flash_settings.h new file mode 100644 index 0000000000000..89686c4946c4b --- /dev/null +++ b/ports/alif/ospi_flash_settings.h @@ -0,0 +1,82 @@ +/* + * 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 __OSPI_FLASH_SETTINGS_H__ +#define __OSPI_FLASH_SETTINGS_H__ +#include +#include "ospi_flash.h" + +// Macronix MX25 +#define OSPI_FLASH_SETTINGS_MX25 \ + .flash_init = ospi_flash_mx_init, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = true, \ + .bswap16 = true, \ + .inst_len = OSPI_INST_L_16bit, \ + .xip_data_len = OSPI_DATA_L_16bit, \ + .read_sr = 0x05fa, \ + .read_sr_dummy_cycles = 4, \ + .write_en = 0x06f9, \ + .read_id = 0x9f60, \ + .read_id_dummy_cycles = 4, \ + .read_command = 0xee11, \ + .write_command = 0x12ed, \ + .erase_command = 0x21de + +// Everspin EM. +#define OSPI_FLASH_SETTINGS_EM \ + .flash_init = ospi_flash_issi_init, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = false, \ + .bswap16 = false, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 + +// ISSI IS25. +#define OSPI_FLASH_SETTINGS_IS25 \ + .flash_init = ospi_flash_issi_init, \ + .octal_mode = OSPI_FLASH_OCTAL_MODE_DDD, \ + .rxds = false, \ + .bswap16 = false, \ + .inst_len = OSPI_INST_L_8bit, \ + .xip_data_len = OSPI_DATA_L_8bit, \ + .read_sr = 0x05, \ + .read_sr_dummy_cycles = 8, \ + .write_en = 0x06, \ + .read_id = 0x9f, \ + .read_id_dummy_cycles = 8, \ + .read_command = 0xfd, \ + .write_command = 0xc2, \ + .erase_command = 0x21 +#endif // __OSPI_FLASH_SETTINGS_H__ diff --git a/ports/alif/pendsv.c b/ports/alif/pendsv.c new file mode 100644 index 0000000000000..965053e6c595a --- /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 0000000000000..17d7e82dfc7e7 --- /dev/null +++ b/ports/alif/pendsv.h @@ -0,0 +1,51 @@ +/* + * 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, + #if MICROPY_PY_NETWORK_CYW43 + PENDSV_DISPATCH_CYW43, + #endif + 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 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/alif/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/alif/se_services.c b/ports/alif/se_services.c new file mode 100644 index 0000000000000..1ed6e3fbfdad0 --- /dev/null +++ b/ports/alif/se_services.c @@ -0,0 +1,302 @@ +/* + * 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 +#include + +#include "irq.h" +#include "se_services.h" + +#include "mhu.h" +#include "services_lib_bare_metal.h" +#include "services_lib_protocol.h" + +#include "py/mphal.h" + +// MHU indices. +#define MHU_SESS_MHU0 0 +#define MHU_RTSS_MHU0 1 +#define MAX_MHU 2 + +// The following timeout is implemented in se_services_handle.c as a +// simple loop busy polling on a variable set from an IRQ. +#define TIMEOUT 10000000 + +typedef struct { + volatile unsigned int RST_CTRL; // 0x1A010318 + volatile unsigned int RST_STAT; // 0x1A01031C +} CPU_Type; + +// HE CPU register flags +#define RST_CTRL_CPUWAIT_MASK (1 << 0) +#define RST_CTRL_RST_REQ_MASK (1 << 1) +#define RST_STAT_RST_ACK_MASK (3 << 1) +#define HE_CPU ((CPU_Type *)0x1A010318) + +static const uint32_t mhu_sender_base_address_list[MAX_MHU] = { + MHU_SESS_S_TX_BASE, + MHU_RTSS_S_TX_BASE +}; + +static const uint32_t mhu_receiver_base_address_list[MAX_MHU] = { + MHU_SESS_S_RX_BASE, + MHU_RTSS_S_RX_BASE +}; + +// Must be aligned as a uint32_t. +static uint32_t packet_buffer[SERVICES_MAX_PACKET_BUFFER_SIZE / sizeof(uint32_t)]; + +static uint32_t se_sess_handle; +static uint32_t se_rtss_handle; + +static mhu_driver_out_t mhu_driver_out; + +void MHU_SESS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_SESS_MHU0); +} + +void MHU_SESS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_SESS_MHU0); +} + +void MHU_RTSS_S_TX_IRQHandler(void) { + mhu_driver_out.sender_irq_handler(MHU_RTSS_MHU0); +} + +void MHU_RTSS_S_RX_IRQHandler(void) { + mhu_driver_out.receiver_irq_handler(MHU_RTSS_MHU0); +} + +int dummy_printf(const char *fmt, ...) { + (void)fmt; + return 0; +} + +static void se_services_irq_config(IRQn_Type irqn, bool enable) { + if (enable) { + NVIC_ClearPendingIRQ(irqn); + NVIC_SetPriority(irqn, IRQ_PRI_MHU); + NVIC_EnableIRQ(irqn); + } else { + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + } +} + +void se_services_rx_callback(uint32_t id, uint32_t channel, uint32_t data) { + switch (id) { + case MHU_SESS_MHU0: + SERVICES_rx_msg_callback(id, channel, data); + break; + case MHU_RTSS_MHU0: + #if MICROPY_PY_OPENAMP + extern void metal_rproc_notified(void); + metal_rproc_notified(); + #endif + break; + default: + break; + } +} + +void se_services_init(void) { + // Initialize MHU. + mhu_driver_in_t mhu_driver_in; + mhu_driver_in.sender_base_address_list = (uint32_t *)mhu_sender_base_address_list; + mhu_driver_in.receiver_base_address_list = (uint32_t *)mhu_receiver_base_address_list; + mhu_driver_in.mhu_count = MAX_MHU; + mhu_driver_in.send_msg_acked_callback = SERVICES_send_msg_acked_callback; + mhu_driver_in.rx_msg_callback = se_services_rx_callback; + mhu_driver_in.debug_print = NULL; // not currently used by MHU_driver_initialize + MHU_driver_initialize(&mhu_driver_in, &mhu_driver_out); + + // Initialize SE services. + services_lib_t services_init_params = { + .packet_buffer_address = (uint32_t)packet_buffer, + .fn_send_mhu_message = mhu_driver_out.send_message, + .fn_wait_ms = NULL, // not currently used by services_host_handler.c + .wait_timeout = TIMEOUT, + .fn_print_msg = dummy_printf, + }; + SERVICES_initialize(&services_init_params); + + // Register SESS MHU channel. + se_sess_handle = SERVICES_register_channel(MHU_SESS_MHU0, 0); + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, true); + + // Register RTSS MHU channel. + se_rtss_handle = SERVICES_register_channel(MHU_RTSS_MHU0, 0); + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, true); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, true); + + // Send heartbeat services requests until one succeeds. + SERVICES_synchronize_with_se(se_sess_handle); +} + +void se_services_deinit(void) { + // Disable SESS MHU channel IRQs. + se_services_irq_config(MHU_SESS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_SESS_S_TX_IRQ_IRQn, false); + + // Disable RTSS MHU channel IRQs. + se_services_irq_config(MHU_RTSS_S_RX_IRQ_IRQn, false); + se_services_irq_config(MHU_RTSS_S_TX_IRQ_IRQn, false); +} + +void se_services_dump_device_data(void) { + uint32_t error_code; + + uint8_t revision[80]; + SERVICES_get_se_revision(se_sess_handle, revision, &error_code); + + SERVICES_version_data_t data; + SERVICES_system_get_device_data(se_sess_handle, &data, &error_code); + + printf("SE revision: %s\n", revision); + printf("ALIF_PN: %s\n", data.ALIF_PN); + printf("Raw device data:\n"); + for (int i = 0; i < sizeof(data); ++i) { + printf(" %02x", ((uint8_t *)&data)[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + printf("\n"); +} + +void se_services_get_unique_id(uint8_t id[8]) { + uint32_t error_code; + SERVICES_system_get_eui_extension(se_sess_handle, false, id, &error_code); +} + +__attribute__((noreturn)) void se_services_reset_soc(void) { + SERVICES_boot_reset_soc(se_sess_handle); + NVIC_SystemReset(); +} + +uint64_t se_services_rand64(void) { + // If the SE core is not ready then the return value can be + // SERVICES_REQ_NOT_ACKNOWLEDGE. So retry a few times. + for (int retry = 0; retry < 100; ++retry) { + uint64_t value; + int32_t error_code; + uint32_t ret = SERVICES_cryptocell_get_rnd(se_sess_handle, sizeof(uint64_t), &value, &error_code); + if (ret == SERVICES_REQ_SUCCESS) { + return value; + } + } + + // No random number available. + return 0; +} + +uint32_t se_services_notify(void) { + uint32_t ret = SERVICES_send_msg(se_rtss_handle, LocalToGlobal(0)); + if (ret != SERVICES_REQ_SUCCESS) { + return -1; + } + return 0; +} + +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable) { + uint32_t error_code; + SERVICES_clocks_enable_clock(se_sess_handle, clock, enable, &error_code); + return error_code; +} + +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target) { + uint32_t error_code; + SERVICES_clocks_select_pll_source(se_sess_handle, source, target, &error_code); + return error_code; +} + +uint32_t se_services_get_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_get_run_cfg(se_sess_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_run_profile(run_profile_t *profile) { + uint32_t error_code; + SERVICES_set_run_cfg(se_sess_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_get_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_get_off_cfg(se_sess_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_set_off_profile(off_profile_t *profile) { + uint32_t error_code; + SERVICES_set_off_cfg(se_sess_handle, profile, &error_code); + return error_code; +} + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id) { + uint32_t error_code; + SERVICES_boot_process_toc_entry(se_sess_handle, image_id, &error_code); + return error_code; +} + +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address) { + uint32_t error_code; + SERVICES_boot_cpu(se_sess_handle, cpu_id, address, &error_code); + return error_code; +} + +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id) { + uint32_t error_code; + if (HE_CPU->RST_CTRL & RST_CTRL_CPUWAIT_MASK) { + // CPU held in reset + return SERVICES_REQ_SUCCESS; + } + + for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { + uint32_t ret = SERVICES_boot_reset_cpu(se_sess_handle, cpu_id, &error_code); + if (ret != SERVICES_REQ_SUCCESS) { + return error_code; + } + + if ((HE_CPU->RST_STAT & RST_STAT_RST_ACK_MASK) == 0x4) { + return SERVICES_REQ_SUCCESS; + } + + if ((mp_hal_ticks_ms() - start) >= 100) { + return SERVICES_REQ_TIMEOUT; + } + } + + return error_code; +} + +uint32_t se_services_boot_release_cpu(uint32_t cpu_id) { + uint32_t error_code; + SERVICES_boot_release_cpu(se_sess_handle, cpu_id, &error_code); + return error_code; +} diff --git a/ports/alif/se_services.h b/ports/alif/se_services.h new file mode 100644 index 0000000000000..44b6584a20000 --- /dev/null +++ b/ports/alif/se_services.h @@ -0,0 +1,51 @@ +/* + * 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_SE_SERVICES_H +#define MICROPY_INCLUDED_ALIF_SE_SERVICES_H + +#include "services_lib_api.h" + +void se_services_init(void); +void se_services_deinit(void); +void se_services_dump_device_data(void); +void se_services_get_unique_id(uint8_t id[8]); +__attribute__((noreturn)) void se_services_reset_soc(void); +uint64_t se_services_rand64(void); +uint32_t se_services_notify(void); + +uint32_t se_services_enable_clock(clock_enable_t clock, bool enable); +uint32_t se_services_select_pll_source(pll_source_t source, pll_target_t target); + +uint32_t se_services_get_run_profile(run_profile_t *profile); +uint32_t se_services_set_run_profile(run_profile_t *profile); +uint32_t se_services_get_off_profile(off_profile_t *profile); +uint32_t se_services_set_off_profile(off_profile_t *profile); + +uint32_t se_services_boot_process_toc_entry(const uint8_t *image_id); +uint32_t se_services_boot_cpu(uint32_t cpu_id, uint32_t address); +uint32_t se_services_boot_reset_cpu(uint32_t cpu_id); +uint32_t se_services_boot_release_cpu(uint32_t cpu_id); +#endif // MICROPY_INCLUDED_ALIF_SE_SERVICES_H diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c new file mode 100644 index 0000000000000..4f161e6709e4b --- /dev/null +++ b/ports/alif/system_tick.c @@ -0,0 +1,363 @@ +/* + * 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 "irq.h" +#include "system_tick.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#if MICROPY_HW_SYSTEM_TICK_USE_SYSTICK + +#include "shared/runtime/softtimer.h" +#include "pendsv.h" + +volatile uint32_t system_tick_ms_counter; + +void system_tick_init(void) { + // Configure SysTick to run at 1kHz (1ms interval) + SysTick_Config(SystemCoreClock / 1000); + NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(SysTick_IRQn); +} + +void SysTick_Handler(void) { + uint32_t uw_tick = system_tick_ms_counter + 1; + system_tick_ms_counter = uw_tick; + + // Read the systick control register to clear the COUNTFLAG bit. + SysTick->CTRL; + + if (soft_timer_next == uw_tick) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); + } +} + +uint32_t system_tick_get_u32(void) { + return system_tick_get_u64(); +} + +uint64_t system_tick_get_u64(void) { + mp_uint_t irq_state = disable_irq(); + uint32_t counter = SysTick->VAL; + uint32_t milliseconds = system_tick_ms_counter; + uint32_t status = SysTick->CTRL; + enable_irq(irq_state); + + // It's still possible for the COUNTFLAG bit to get set if the counter was + // reloaded between reading VAL and reading CTRL. With interrupts disabled + // it definitely takes less than 50 cycles between reading VAL and + // reading CTRL, so the test (counter > 50) is to cover the case where VAL + // is +ve and very close to zero, and the COUNTFLAG bit is also set. + if ((status & SysTick_CTRL_COUNTFLAG_Msk) && counter > 50) { + // This means that the HW reloaded VAL between the time we read VAL and the + // time we read CTRL, which implies that there is an interrupt pending + // to increment the tick counter. + milliseconds++; + } + uint32_t load = SysTick->LOAD; + counter = load - counter; // Convert from decrementing to incrementing + + // Calculate 64-bit microsecond counter. + return (uint64_t)milliseconds * 1000ULL + (uint64_t)((counter * 1000) / (load + 1)); +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + if (timeout_us > 1000) { + // SysTick will wake us in at most 1ms. + __WFI(); + } +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + +#include "lptimer.h" +#include "sys_ctrl_lptimer.h" + +// Channel 0 and 1 are cascaded to make a 64-bit counter. +// Channel 2 is used for system_tick_wfe_with_timeout_us. +// Channel 3 is used for system_tick_schedule_after_us. +#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE) +#define LPTIMER_CH_A (0) +#define LPTIMER_CH_B (1) +#define LPTIMER_CH_C (2) +#define LPTIMER_CH_D (3) + +uint64_t system_tick_source_hz; + +void system_tick_init(void) { + lptimer_disable_counter(LPTIMER, LPTIMER_CH_A); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_B); + + ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO + + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A); + select_lptimer_clk(LPTIMER_CLK_SOURCE_CASCADE, LPTIMER_CH_B); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_C); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_D); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_A); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_A); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_B); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_B); + + lptimer_enable_counter(LPTIMER, LPTIMER_CH_B); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_A); + + system_tick_source_hz = 32768; + + NVIC_ClearPendingIRQ(LPTIMER2_IRQ_IRQn); + NVIC_SetPriority(LPTIMER2_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER2_IRQ_IRQn); + + NVIC_ClearPendingIRQ(LPTIMER3_IRQ_IRQn); + NVIC_SetPriority(LPTIMER3_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER3_IRQ_IRQn); +} + +void LPTIMER2_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + __SEV(); +} + +void LPTIMER3_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + system_tick_schedule_callback(); + __SEV(); +} + +uint32_t system_tick_get_u32(void) { + return 0xffffffff - lptimer_get_count(LPTIMER, LPTIMER_CH_A); +} + +uint64_t system_tick_get_u64(void) { + // Get 64-bit counter value from the hardware timer. + // Sample it twice in case the low counter wraps around while sampling. + uint32_t irq_state = disable_irq(); + uint32_t lo0 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi0 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + uint32_t lo1 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi1 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + enable_irq(irq_state); + + if (hi0 == hi1) { + // Low counter may have wrapped around between sampling of lo0 and hi0, so prefer second sampling. + lo0 = lo1; + hi0 = hi1; + } else { + // Low counter wrapped around either between sampling of hi0 and lo1, or sampling of lo1 and hi1. + // In either case use the first sampling. + } + + // Convert from descending count to ascending. + lo0 = 0xffffffff - lo0; + hi0 = 0xffffffff - hi0; + + // Return a 64-bit value. + return ((uint64_t)hi0 << 32) | (uint64_t)lo0; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_C); + lptimer_load_count(LPTIMER, LPTIMER_CH_C, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_C); + + // Wait for an event. + __WFE(); + + // Disable the LPTIMER interrupt (in case a different interrupt woke the WFE). + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); +} + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(ticks_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_D); + lptimer_load_count(LPTIMER, LPTIMER_CH_D, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_D); +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER + +#include "utimer.h" + +#define UTIMER ((UTIMER_Type *)UTIMER_BASE) +#define UTIMER_CHANNEL (11) + +uint64_t system_core_clock_mhz; +static volatile uint32_t system_tick_hi; + +static void system_tick_nvic_config(unsigned int index) { + NVIC_ClearPendingIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); + NVIC_SetPriority(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(UTIMER_IRQ0_IRQn + UTIMER_CHANNEL * 8 + index); +} + +void system_tick_init(void) { + system_tick_hi = 0; + system_core_clock_mhz = SystemCoreClock / 1000000; + + // Configure NVIC OVER_FLOW interrupt. + system_tick_nvic_config(7); + + utimer_clock_enable(UTIMER, UTIMER_CHANNEL); + utimer_channel_config cfg = { 0 }; + cfg.fixed_buffer = false; + utimer_config_direction(UTIMER, UTIMER_CHANNEL, UTIMER_COUNTER_UP, &cfg); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR_PTR, 0xffffffff); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + utimer_control_enable(UTIMER, UTIMER_CHANNEL); + utimer_counter_start(UTIMER, UTIMER_CHANNEL); + + // Set up the UTIMER compare A interrupt, to be used by system_tick_wfe_with_timeout_us. + system_tick_nvic_config(2); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_A |= COMPARE_CTRL_DRV_COMPARE_EN; + + // Set up the UTIMER compare B interrupt, to be used by soft-timer. + system_tick_nvic_config(4); + UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_COMPARE_CTRL_B |= COMPARE_CTRL_DRV_COMPARE_EN; +} + +// COMPARE_A_BUF1 +void UTIMER_IRQ90Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_A_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + __SEV(); + } +} + +// COMPARE_B_BUF1 +void UTIMER_IRQ92Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + __SEV(); + } +} + +// OVER_FLOW +void UTIMER_IRQ95Handler(void) { + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_OVER_FLOW_MASK); + ++system_tick_hi; + } +} + +uint32_t system_tick_get_u32(void) { + return utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); +} + +uint64_t system_tick_get_u64(void) { + uint32_t irq_state = disable_irq(); + uint32_t ticks_lo = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + uint32_t ticks_hi = system_tick_hi; + uint32_t chan_interrupt = UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT; + enable_irq(irq_state); + + if (chan_interrupt & CHAN_INTERRUPT_OVER_FLOW_MASK) { + // The timer had an overflow while interrupts were disabled. + if (ticks_lo < 0x80000000) { + // The timer had an overflow just before we sampled it. + ++ticks_hi; + } + } + + // This ticks runs at SystemCoreClock. + return (uint64_t)ticks_hi << 32 | ticks_lo; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 10 second timeout, to not overflow 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(timeout_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_A_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); + + // Wait for an event (or compare timeout event) if the timeout hasn't expired yet + // (this check handles the case of short timeouts). + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) < timeout_ticks) { + __WFE(); + } + + // Disable the UTIMER compare interrupt. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_A_BUF1_MASK); +} + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Maximum 10 second timeout, to not overflow 32-bit ticks when + // system_core_clock_mhz==400. + uint32_t timeout_ticks = MIN(ticks_us, 10000000) * system_core_clock_mhz; + + // Set up the UTIMER compare interrupt to fire after the given timeout. + uint32_t cntr = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + utimer_set_count(UTIMER, UTIMER_CHANNEL, UTIMER_COMPARE_B_BUF1, cntr + timeout_ticks); + utimer_clear_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + utimer_unmask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + + // Handle the case of short timeouts. + uint32_t cntr2 = utimer_get_count(UTIMER, UTIMER_CHANNEL, UTIMER_CNTR); + if ((uint32_t)(cntr2 - cntr) >= timeout_ticks) { + if (!(UTIMER->UTIMER_CHANNEL_CFG[UTIMER_CHANNEL].UTIMER_CHAN_INTERRUPT_MASK & CHAN_INTERRUPT_COMPARE_B_BUF1_MASK)) { + // Interrupt is still enabled, so disable it and manually call the callback. + utimer_mask_interrupt(UTIMER, UTIMER_CHANNEL, CHAN_INTERRUPT_COMPARE_B_BUF1_MASK); + system_tick_schedule_callback(); + } + } +} + +#endif diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h new file mode 100644 index 0000000000000..a380808b2e043 --- /dev/null +++ b/ports/alif/system_tick.h @@ -0,0 +1,44 @@ +/* + * 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_SYSTEM_TICK_H +#define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H + +#include "py/mpconfig.h" + +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +extern uint64_t system_tick_source_hz; +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER +extern uint64_t system_core_clock_mhz; +#endif + +void system_tick_init(void); +uint32_t system_tick_get_u32(void); +uint64_t system_tick_get_u64(void); +void system_tick_wfe_with_timeout_us(uint32_t timeout_us); +void system_tick_schedule_after_us(uint32_t ticks_us); +void system_tick_schedule_callback(void); + +#endif // MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H diff --git a/ports/alif/tinyusb_port/alif_dcd_reg.h b/ports/alif/tinyusb_port/alif_dcd_reg.h new file mode 100644 index 0000000000000..84220f6be4ce4 --- /dev/null +++ b/ports/alif/tinyusb_port/alif_dcd_reg.h @@ -0,0 +1,691 @@ +// *FORMAT-OFF* +///------------------------------------------------------------------------------------------------- +/// @file alif_dcd_reg.h +/// @author karol.saja@alifsemi.com +/// @version 0.0.1 +/// @date 2023-09-08 +/// @brief Low Level SPI driver +///------------------------------------------------------------------------------------------------- + +#ifndef __ALIF_DCD_REG_H__ +#define __ALIF_DCD_REG_H__ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- + +#define _rw volatile uint32_t +#define __w volatile uint32_t +#define __r volatile const uint32_t + +volatile struct { + union { + _rw gsbuscfg0; + struct { + _rw incrbrstena : 1; + _rw incr4brstena : 1; + _rw incr8brstena : 1; + _rw incr16brstena : 1; + _rw incr32brstena : 1; + _rw incr64brstena : 1; + _rw incr128brstena : 1; + _rw incr256brstena : 1; + __r : 2; + _rw desbigend : 1; + _rw datbigend : 1; + __r : 4; + _rw deswrreqinfo : 4; + _rw datwrreqinfo : 4; + _rw desrdreqinfo : 4; + _rw datrdreqinfo : 4; + } gsbuscfg0_b; + }; + + union { + _rw gsbuscfg1; + struct { + __r : 8; + _rw pipetranslimit : 4; + _rw en1kpage : 1; + __r : 19; + } gsbuscfg1_b; + }; + + __r : 32; __r : 32; + + union { + _rw gctl; + struct { + _rw dsblclkgtng : 1; + _rw gblhibernationen : 1; + __r : 1; + _rw disscramble : 1; + _rw scaledown : 2; + _rw ramclksel : 2; + __r : 2; + _rw sofitpsync : 1; + _rw coresoftreset : 1; + _rw prtcapdir : 2; + _rw frmscldwn : 2; + __r : 1; + _rw bypssetaddr : 1; + __r : 14; + } gctl_b; + }; + + __r : 32; + + union { + _rw gsts; + struct { + __r curmod : 2; + __r : 2; + _rw buserraddrvld : 1; + _rw csrtimeout : 1; + __r device_ip : 1; + __r host_ip : 1; + __r adp_ip : 1; + __r bc_ip : 1; + __r otg_ip : 1; + __r ssic_ip : 1; + __r : 8; + __r cbelt : 12; + } gsts_b; + }; + + union { + _rw guctl1; + struct { + _rw loa_filter_en : 1; + _rw ovrld_l1_susp_com : 1; + _rw hc_parchk_disable : 1; + _rw hc_errata_enable : 1; + _rw l1_susp_thrld_for_host : 4; + _rw l1_susp_thrld_en_for_host : 1; + _rw dev_hs_nyet_bulk_spr : 1; + _rw resume_opmode_hs_host : 1; + __r : 1; + _rw disusb2refclkgtng : 1; + __r : 2; + _rw parkmode_disable_fsls : 1; + _rw parkmode_disable_hs : 1; + __r : 1; + _rw nak_per_enh_hs : 1; + _rw nak_per_enh_fs : 1; + _rw dev_lsp_tail_lock_dis : 1; + _rw ip_gap_add_on : 2; + _rw dev_l1_exit_by_hw : 1; + __r : 2; + _rw dev_trb_out_spr_ind : 1; + _rw tx_ipgap_linecheck_dis : 1; + _rw filter_se0_fsls_eop : 1; + __r : 1; + _rw dev_decouple_l1l2_evt : 1; + } guctl1_b; + }; + + union { + _rw gsnpsid; + }; + + __r : 32; + + union { + _rw guid; + struct { + _rw userid : 32; + } guid_b; + }; + + union { // <- host only, leaving unimplemented for now [FIXME] + _rw guctl; + struct { + } guctl_b; + }; + + union { + _rw gbuserraddrlo; + struct { + _rw buserraddr : 32; + } gbuserraddrlo_b; + }; + + union { + _rw gbuserraddrhi; + struct { + _rw buserraddr : 32; + } gbuserraddrhi_b; + }; + + __r : 32; __r : 32; + + __r ghwparams[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; + + // base + 0x9C here + + union { + _rw guctl2; + struct { + __r : 11; + _rw disablecfc : 1; + _rw enableepcacheevict : 1; + __r : 1; + _rw rst_actbitlater : 1; + __r : 4; + _rw en_hp_pm_timer : 7; + __r : 6; + } guctl2_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + union { + _rw gusb2phycfg0; + struct { + _rw toutcal : 3; + _rw phyif : 1; + __r ulpi_utmi_sel : 1; + __r fsintf : 1; + _rw suspendusb20 : 1; + _rw physel : 1; + _rw enblslpm : 1; + _rw xcvrdly : 1; + _rw usbtrdtim : 4; + __r : 5; + _rw lsipd : 3; + _rw lstrd : 3; + _rw ovrd_fsls_disc_time : 1; + __r inv_sel_hsic : 1; + __r hsic_con_width_adj : 2; + _rw ulpi_lpm_with_opmode_chk : 1; + _rw u2_freeclk_exists : 1; + _rw physoftrst : 1; + } gusb2phycfg0_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x200 here + + union { + _rw gtxfifosiz[4]; + struct { + _rw txfdep : 16; + _rw txfstaddr : 16; + } gtxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x280 here + + union { + _rw grxfifosiz[4]; + struct { + _rw rxfdep : 16; + _rw rxfstaddr : 16; + } grxfifosiz_b[4]; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw gevntadrlo0; + struct { + _rw evntadrlo : 32; + } gevntadrlo0_b; + }; + + union { + _rw gevntadrhi0; + struct { + _rw evntadrhi : 32; + } gevntadrhi0_b; + }; + + union { + _rw gevntsiz0; + struct { + _rw eventsiz : 16; + __r : 15; + _rw evntintrptmask : 1; + } gevntsiz0_b; + }; + + union { + _rw gevntcount0; + struct { + _rw evntcount : 16; + __r : 15; + _rw evnt_handler_busy : 1; + } gevntcount0_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x500 here + + __r ghwparams8; + + __r : 32; __r : 32; __r : 32; + + // base + 0x510 here + + union { + _rw gtxfifopridev; + struct { + _rw gtxfifopridev : 4; + __r : 28; + } gtxfifopridev_b; + }; + + __r : 32; + + union { + _rw gtxfifoprihst; + struct { + _rw gtxfifoprihst : 2; + __r : 30; + } gtxfifoprihst_b; + }; + + union { + _rw grxfifoprihst; + struct { + _rw grxfifoprihst : 2; + __r : 30; + } grxfifoprihst_b; + }; + + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x530 here + + union { + _rw gfladj; + struct { + _rw gfladj_30mhz : 6; + __r : 1; + _rw gfladj_30mhz_sdbnd_sel : 1; + _rw gfladj_refclk_fladj : 14; + __r : 1; + _rw gfladj_refclk_lpm_sel : 1; + _rw gfladj_refclk_240mhz_decr : 7; + _rw gfladj_refclk_240mhzdecr_pls1 : 1; + } gfladj_b; + }; + + __r : 32; __r : 32; __r : 32; + + // base + 0x540 here + + union { + _rw gusb2rhbctl0; + struct { + _rw ovrd_l1timeout : 4; + __r : 28; + } gusb2rhbctl0_b; + }; + +} *ugbl = (void *) (USB_BASE + 0xC100); + +volatile struct { + union { + _rw dcfg; + struct { + _rw devspd : 3; + _rw devaddr : 7; + __r : 2; + _rw intrnum : 5; + _rw nump : 5; + _rw lpmcap : 1; + _rw ignstrmpp : 1; + __r : 8; + } dcfg_b; + }; + + union { + _rw dctl; + struct { + __r : 1; + _rw tstctl : 4; + __w ulstchngreq : 4; + __r : 7; + _rw css : 1; + _rw crs : 1; + _rw l1hibernationen : 1; + _rw keepconnect : 1; + _rw lpm_nyet_thres : 4; + _rw hirdthres : 5; + __r : 1; + _rw csftrst : 1; + _rw run_stop : 1; + } dctl_b; + }; + + union { + _rw devten; + struct { + _rw dissconnevten : 1; + _rw usbrstevten : 1; + _rw connectdoneevten : 1; + _rw ulstcngen : 1; + _rw wkupevten : 1; + _rw hibernationreqevten : 1; + _rw u3l2l1suspen : 1; + _rw softevten : 1; + _rw l1suspen : 1; + _rw errticerrevten : 1; + __r : 2; + _rw vendevtstrcvden : 1; + __r : 1; + _rw l1wkupevten : 1; + __r : 1; + _rw eccerren : 1; + __r : 15; + } devten_b; + }; + + union { + _rw dsts; + struct { + __r connectspd : 3; + __r soffn : 14; + __r rxfifoempty : 1; + __r usblnkst : 4; + __r devctrlhlt : 1; + __r coreidle : 1; + __r sss : 1; + __r rss : 1; + __r : 2; + _rw sre : 1; + __r dcnrd : 1; + __r : 2; + } dsts_b; + }; + + union { + _rw dgcmdpar; + struct { + _rw parameter : 32; + } dgcmdpar_b; + }; + + union { + _rw dgcmd; + struct { + _rw cmdtyp : 8; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + __r : 1; + __r cmdstatus : 4; + __r : 16; + } dgcmd_b; + }; + + __r : 32; __r : 32; + + union { + _rw dalepena; + struct { + _rw usbactep : 32; + } dalepena_b; + }; + + __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x100 here + + struct { + union { + _rw par2; + struct { + _rw parameter : 32; + } par2_b; + }; + + union { + _rw par1; + struct { + _rw parameter : 32; + } par1_b; + }; + + union { + _rw par0; + struct { + _rw parameter : 32; + } par0_b; + }; + + union { + _rw depcmd; + struct { + _rw cmdtyp : 4; + __r : 4; + _rw cmdioc : 1; + __r : 1; + _rw cmdact : 1; + _rw hipri_forcerm : 1; + _rw cmdstatus : 4; + _rw commandparam : 16; + } depcmd_b; + }; + } depcmd[8]; + + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + __r : 32; __r : 32; __r : 32; __r : 32; + + // base + 0x300 here + + union { + _rw dev_imod0; + struct { + _rw device_imodi : 16; + _rw device_imocd : 16; + } dev_imod0_b; + }; +} *udev = (void *) (USB_BASE + 0xC700); + +//-------------------------------------------------------------------------------------------------- + +typedef union { + _rw val : 32; + struct { + _rw sig : 8; + _rw evt : 5; + __r : 3; + _rw info : 9; + __r : 7; + } devt; + struct { + _rw sig : 1; + _rw ep : 5; + _rw evt : 4; + __r : 2; + _rw sts : 4; + _rw par : 16; + } depevt; +} evt_t; + +//-------------------------------------------------------------------------------------------------- + +enum { + CMDTYP_RESERVED = 0, + CMDTYP_DEPCFG, + CMDTYP_DEPXFERCFG, + CMDTYP_DEPGETSTATE, + CMDTYP_DEPSSTALL, + CMDTYP_DEPCSTALL, + CMDTYP_DEPSTRTXFER, + CMDTYP_DEPUPDXFER, + CMDTYP_DEPENDXFER, + CMDTYP_DEPSTARTCFG +} DEPCMD_CMDTYP; + +enum { + DEVT_DISCONNEVT = 0, + DEVT_USBRST, + DEVT_CONNECTDONE, + DEVT_ULSTCHNG, + DEVT_WKUPEVT, + // DEVT_HIBRQ not implemented + DEVT_USBSUSP = 6, + DEVT_SOF, + DEVT_L1SUSP, + DEVT_ERRTICERR, + DEVT_CMDCMPLT, + DEVT_EVNTOVERFLOW, + DEVT_VNDDEVTSTRCVED, + // reserved + DEVT_L1RESM = 14, + // reserved + DEVT_ECCERR = 16 +} DEVT; + +enum { + // reserved + DEPEVT_XFERCOMPLETE = 1, + DEPEVT_XFERINPROGRESS, + DEPEVT_XFERNOTREADY, + // not implemented + DEPEVT_STREAMEVT = 6, + DEPEVT_EPCMDCMPLT +} DEPEVT; + +enum { + // reserved + TRBCTL_NORMAL = 1, + TRBCTL_CTL_SETUP, + TRBCTL_CTL_STAT2, + TRBCTL_CTL_STAT3, + TRBCTL_CTL_DATA, + TRBCTL_ISO_FIRST, + TRBCTL_ISO, + TRBCTL_LINK, + TRBCTL_NORMAL_ZLP +} TRBCTL; + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif // __ALIF_DCD_REG_H__ diff --git a/ports/alif/tinyusb_port/tusb_alif_dcd.c b/ports/alif/tinyusb_port/tusb_alif_dcd.c new file mode 100644 index 0000000000000..9a990cedbe387 --- /dev/null +++ b/ports/alif/tinyusb_port/tusb_alif_dcd.c @@ -0,0 +1,695 @@ +// *FORMAT-OFF* +/* Copyright (C) 2024 Alif Semiconductor - All Rights Reserved. + * Use, distribution and modification of this code is permitted under the + * terms stated in the Alif Semiconductor Software License Agreement + * + * You should have received a copy of the Alif Semiconductor Software + * License Agreement with this file. If not, please write to: + * contact@alifsemi.com, or visit: https://alifsemi.com/license + * + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#if defined(CORE_M55_HE) +#include "M55_HE.h" +#elif defined(CORE_M55_HP) +#include "M55_HP.h" +#else +#error "Unsupported core!" +#endif + +#include "device/dcd.h" + +#include "alif_dcd_reg.h" + +#include "clk.h" +#include "power.h" + +// #define TUSB_ALIF_DEBUG +// #define TUSB_ALIF_DEBUG_DEPTH (2048) + +#if defined(TUSB_ALIF_DEBUG) +#if (1 < TUSB_ALIF_DEBUG_DEPTH) +#define LOG(...) memset(logbuf[bi % TUSB_ALIF_DEBUG_DEPTH], ' ', 48);\ + snprintf(logbuf[(bi++) % TUSB_ALIF_DEBUG_DEPTH], 48, __VA_ARGS__); +char logbuf[TUSB_ALIF_DEBUG_DEPTH][48]; +int bi = 0; +#else +#define LOG(...) memset(logbuf, ' ', 48);\ + snprintf(logbuf, 48, __VA_ARGS__) +char logbuf[48]; +#endif +#else +#define LOG(...) +#endif + +/// Structs and Buffers -------------------------------------------------------- + +static uint32_t _evnt_buf[1024] CFG_TUSB_MEM_SECTION __attribute__((aligned(4096))); // [TODO] runtime alloc +static volatile uint32_t* _evnt_tail; + +static uint8_t _ctrl_buf[64] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint32_t _xfer_trb[8][4] CFG_TUSB_MEM_SECTION __attribute__((aligned(32))); // [TODO] runtime alloc +static uint16_t _xfer_bytes[8]; +static bool _ctrl_long_data = false; +static bool _xfer_cfgd = false; +// static bool _addr_req = false; +static uint32_t _sts_stage = 0; + +/// Private Functions ---------------------------------------------------------- + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param); +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type); + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par); +static void _dcd_handle_devt(uint8_t evt, uint16_t info); + +/// API Extension -------------------------------------------------------------- + +void dcd_uninit(void); + +/// Device Setup --------------------------------------------------------------- + +// Initializes the USB peripheral for device mode and enables it. +// This function should enable internal D+/D- pull-up for enumeration. +void dcd_init(uint8_t rhport) +{ + // enable 20mhz clock + enable_cgu_clk20m(); + // enable usb peripheral clock + enable_usb_periph_clk(); + // power up usb phy + enable_usb_phy_power(); + // disable usb phy isolation + disable_usb_phy_isolation(); + // clear usb phy power-on-reset signal + CLKCTL_PER_MST->USB_CTRL2 &= ~(1 << 8); + + // force stop/disconnect + udev->dctl_b.run_stop = 0; + + // dctl set csftrst to 1 and wait for 0 + udev->dctl_b.csftrst = 1; + while(0 != udev->dctl_b.csftrst); + + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 1; + ugbl->gusb2phycfg0_b.physoftrst = 1; + sys_busy_loop_us(50000); + ugbl->gusb2phycfg0_b.physoftrst = 0; + sys_busy_loop_us(50000); + ugbl->gctl_b.coresoftreset = 0; + sys_busy_loop_us(50000); + + ugbl->gsbuscfg0 = 0x00000009; + // ugbl->gusb2phycfg0 = 0x4000154F; // [TODO] document as bits + uint32_t reg = ugbl->gusb2phycfg0; + reg &= ~((1 << 3) | (1 << 4) | (0xF << 10)); // clear phyif, ulpi_utmi_sel and usbtrdtim + reg |= ((1 << 3) | (5 << 10)); + ugbl->gusb2phycfg0 = reg; + + // set device speed (USBHS only) + udev->dcfg_b.devspd = 0x0; // HS, this will need #if condition [TODO] + + // allocate ring buffer for events + memset(_evnt_buf, 0, sizeof(_evnt_buf)); + RTSS_CleanDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + _evnt_tail = _evnt_buf; + ugbl->gevntadrlo0 = (uint32_t) LocalToGlobal(_evnt_buf); + ugbl->gevntsiz0_b.eventsiz = (uint32_t) sizeof(_evnt_buf); + ugbl->gevntcount0_b.evntcount = 0; + + // write devten to enable usb reset, conn done, link state change... + udev->devten_b.dissconnevten = 1; + udev->devten_b.usbrstevten = 1; + udev->devten_b.connectdoneevten = 1; + udev->devten_b.ulstcngen = 1; + + // begin endpoint setup + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 0); + + // configure CONTROL IN and OUT eps + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (0 << 22) | (0 << 17) | (512 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + + // set initial xfer configuration for CONTROL eps + udev->depcmd[0].par0 = 1; + _dcd_cmd_wait(0, CMDTYP_DEPXFERCFG, 0); + + udev->depcmd[1].par0 = 1; + _dcd_cmd_wait(1, CMDTYP_DEPXFERCFG, 0); + + // enable pull-ups + dcd_connect(rhport); + + // prepare trb for the first setup packet + memset(_ctrl_buf, 0, sizeof(_ctrl_buf)); + _xfer_trb[0][0] = (uint32_t) LocalToGlobal(_ctrl_buf); + _xfer_trb[0][1] = 0; + _xfer_trb[0][2] = 8; + _xfer_trb[0][3] = (1 << 11) | (1 << 10) | (TRBCTL_CTL_SETUP << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[0], sizeof(_xfer_trb[0])); + + // send trb to the usb dma + udev->depcmd[0].par1 = (uint32_t) LocalToGlobal(_xfer_trb[0]); + udev->depcmd[0].par0 = 0; + _dcd_cmd_wait(0, CMDTYP_DEPSTRTXFER, 0); + + // enable ep event interrupts for CONTROL OUT and IN + udev->dalepena_b.usbactep = (1 << 1) | (1 << 0); + + // enable interrupts in the NVIC +#if !defined(TUSB_ALIF_NO_IRQ_CFG) + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); + NVIC_SetPriority(USB_IRQ_IRQn, 5); +#endif + dcd_int_enable(rhport); +} + + +// Processes all the hardware generated events e.g bus reset, new data packet +// from host... It will be called by application in the MCU USB interrupt handler. +void dcd_int_handler(uint8_t rhport) +{ + LOG("%010u IRQ enter, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); + + // process failures first + if (ugbl->gsts_b.device_ip) { + if (ugbl->gsts_b.csrtimeout || ugbl->gsts_b.buserraddrvld) { + // buserraddrvld is usually set when USB tries to write to protected + // memory region. Check linker script to ensure USB event buffer and + // TRBs reside in bulk memory or other NS-allowed region + __BKPT(0); + } + } + + // cycle through event queue + // evaluate on every iteration to prevent unnecessary isr exit/reentry + while (0 < ugbl->gevntcount0_b.evntcount) { + RTSS_InvalidateDCache_by_Addr(_evnt_buf, sizeof(_evnt_buf)); + volatile evt_t e = {.val = *_evnt_tail++}; + + LOG("%010u IRQ loop, evntcount %u evnt %08x", DWT->CYCCNT, + ugbl->gevntcount0_b.evntcount, e.val); + + // wrap around + if (_evnt_tail >= (_evnt_buf + 1024)) _evnt_tail = _evnt_buf; + + // dispatch the right handler for the event type + if (0 == e.depevt.sig) { // DEPEVT + _dcd_handle_depevt(e.depevt.ep, e.depevt.evt, e.depevt.sts, e.depevt.par); + } else if (1 == e.devt.sig) { // DEVT + _dcd_handle_devt(e.devt.evt, e.devt.info); + } else { + // bad event?? + LOG("Unknown event %u", e.val); + __BKPT(0); + } + + // consume one event + ugbl->gevntcount0 = 4; + } + + LOG("%010u IRQ exit, evntcount %u", DWT->CYCCNT, ugbl->gevntcount0_b.evntcount); +} + + +// Enables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_enable (uint8_t rhport) +{ + NVIC_EnableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Disables the USB device interrupt. +// May be used to prevent concurrency issues when mutating data structures +// shared between main code and the interrupt handler. +void dcd_int_disable(uint8_t rhport) +{ + NVIC_DisableIRQ(USB_IRQ_IRQn); + + (void) rhport; +} + +// Receive Set Address request, mcu port must also include status IN response. +// If your peripheral automatically changes address during enumeration you may +// leave this empty and also no queue an event for the corresponding SETUP packet. +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->dcfg_b.devaddr = dev_addr; + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); +} + +// Called to remote wake up host when suspended (e.g hid keyboard) +void dcd_remote_wakeup(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) +{ + udev->dctl_b.run_stop = 1; + + (void) rhport; +} + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) +{ + // [TODO] clear all xfers and eps first + + udev->dctl_b.run_stop = 0; + + (void) rhport; +} + +// Enable/Disable Start-of-frame interrupt. Default is disabled +void dcd_sof_enable(uint8_t rhport, bool en) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + udev->devten_b.softevten = en; +} + + +/// Endpoint Management -------------------------------------------------------- + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); + + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); +} + +// Opening an endpoint is done for all non-control endpoints once the host picks +// a configuration that the device should use. +// At this point, the endpoint should be enabled in the peripheral and +// configured to match the endpoint descriptor. +// Pay special attention to the direction of the endpoint you can get from the +// helper methods above. It will likely change what registers you are setting. +// Also make sure to enable endpoint specific interrupts. +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) +{ + LOG("%010u >%s %u %s %u %u", DWT->CYCCNT, __func__, desc_ep->bEndpointAddress, + desc_ep->bmAttributes.xfer == TUSB_XFER_BULK ? "bulk" : "int", + desc_ep->wMaxPacketSize, desc_ep->bInterval); + + if (TUSB_XFER_ISOCHRONOUS == desc_ep->bmAttributes.xfer) + return false; + + uint8_t ep = (tu_edpt_number(desc_ep->bEndpointAddress) << 1) | + tu_edpt_dir(desc_ep->bEndpointAddress); + + // [TODO] verify that the num doesn't exceed hw max + + if (false == _xfer_cfgd) { + _dcd_cmd_wait(0, CMDTYP_DEPSTARTCFG, 2); + _xfer_cfgd = true; + } + + uint8_t fifo_num = TUSB_DIR_IN == tu_edpt_dir(desc_ep->bEndpointAddress) ? + tu_edpt_number(desc_ep->bEndpointAddress) : 0; + uint8_t interval = 0 < desc_ep->bInterval ? (desc_ep->bInterval - 1) : 0; + + udev->depcmd[ep].par1 = (ep << 25) | (interval << 16) | + (1 << 10) | (1 << 8); + udev->depcmd[ep].par0 = (0 << 30) | (0 << 22) | (fifo_num << 17) | + ((desc_ep->wMaxPacketSize & 0x7FF) << 3) | + (desc_ep->bmAttributes.xfer << 1); + + _dcd_cmd_wait(ep, CMDTYP_DEPCFG, 0); + udev->depcmd[ep].par0 = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPXFERCFG, 0); + + udev->dalepena_b.usbactep |= (1 << ep); + + return true; +} + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore +// required for multiple configuration support. +void dcd_edpt_close_all(uint8_t rhport) +{ + LOG("%010u >%s", DWT->CYCCNT, __func__); +} + +// Close an endpoint. his function is used for implementing alternate settings. +// After calling this, the device should not respond to any packets directed +// towards this endpoint. When called, this function must abort any transfers in +// progress through this endpoint, before returning. +// Implementation is optional. Must be called from the USB task. +// Interrupts could be disabled or enabled during the call. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK; + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to +// notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + // DEPSTRTXFER command + LOG("%010u >%s %u %x %u", DWT->CYCCNT, __func__, ep_addr, (uint32_t) buffer, total_bytes); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + switch (ep) { + case 0: { // CONTROL OUT + if (0 < total_bytes) { + // DATA OUT request + _xfer_bytes[0] = total_bytes; + _dcd_start_xfer(0, buffer, total_bytes, TRBCTL_CTL_STAT3); + } else { + // TinyUSB explicitly requests STATUS OUT fetch after DATA IN + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + } + } + } break; + case 1: { // CONTROL IN + _xfer_bytes[1] = total_bytes; + + if (0 < total_bytes) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + uint8_t type = _ctrl_long_data ? TRBCTL_NORMAL : TRBCTL_CTL_DATA; + if (64 == total_bytes) { + _ctrl_long_data = true; + } + _dcd_start_xfer(1, buffer, total_bytes, type); + } else { + // status events are handled directly from the ISR when USB + // controller triggers XferNotReady event for status stage + + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + } + // dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), + // 0, XFER_RESULT_SUCCESS, false); + } + } break; + default: { // DATA EPs (BULK & INTERRUPT only) + _xfer_bytes[ep] = total_bytes; + if (TUSB_DIR_IN == tu_edpt_dir(ep_addr)) { + RTSS_CleanDCache_by_Addr(buffer, total_bytes); + } else { + total_bytes = 512; // temporary hack, controller requires max + // size requests on OUT endpoints [FIXME] + } + uint8_t ret = _dcd_start_xfer(ep, buffer, total_bytes, + total_bytes ? TRBCTL_NORMAL : TRBCTL_NORMAL_ZLP); + LOG("start xfer sts %u", ret); + } + } + + return true; +} + +// Submit a transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack +// This API is optional, may be useful for register-based for transferring data. +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPSSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPSSTALL, 0); + + if (0 == tu_edpt_number(ep_addr)) { + _ctrl_long_data = false; + _dcd_start_xfer(TUSB_DIR_OUT, _ctrl_buf, 8, TRBCTL_CTL_SETUP); + } +} + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when +// receiving setup packet +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + // DEPCSTALL command + LOG(">%s", __func__); + + uint8_t ep = (tu_edpt_number(ep_addr) << 1) | tu_edpt_dir(ep_addr); + + _dcd_cmd_wait(ep, CMDTYP_DEPCSTALL, 0); +} + +void dcd_uninit(void) +{ + CLKCTL_PER_MST->USB_CTRL2 |= 1 << 8; // set usb phy power-on-reset signal + enable_usb_phy_isolation(); // enable usb phy isolation + disable_usb_phy_power(); // power down usb phy + disable_usb_periph_clk(); // disable usb peripheral clock + dcd_int_disable(TUD_OPT_RHPORT); + NVIC_ClearPendingIRQ(USB_IRQ_IRQn); +} + +void USB_IRQHandler(void) +{ + dcd_int_handler(TUD_OPT_RHPORT); +} + + +static uint8_t _dcd_cmd_wait(uint8_t ep, uint8_t typ, uint16_t param) +{ + // capture phy state and disable lpm and suspend + uint32_t phycfg = ugbl->gusb2phycfg0; + ugbl->gusb2phycfg0_b.enblslpm = 0; + ugbl->gusb2phycfg0_b.suspendusb20 = 0; + + // set up command in depcmd register + udev->depcmd[ep].depcmd_b.cmdtyp = typ; + udev->depcmd[ep].depcmd_b.cmdioc = 0; + udev->depcmd[ep].depcmd_b.commandparam = param; + + // dispatch command and wait for completion + udev->depcmd[ep].depcmd_b.cmdact = 1; + while(0 != udev->depcmd[ep].depcmd_b.cmdact); + + // restore phy state + ugbl->gusb2phycfg0 = phycfg; + + return udev->depcmd[ep].depcmd_b.cmdstatus; +} + + +static void _dcd_handle_depevt(uint8_t ep, uint8_t evt, uint8_t sts, uint16_t par) +{ + LOG("%010u DEPEVT ep%u evt%u sts%u par%u", DWT->CYCCNT, ep, evt, sts, par); + + switch (evt) { + case DEPEVT_XFERCOMPLETE: { + LOG("Transfer complete"); + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + if (0 == ep) { + uint8_t trbctl = (_xfer_trb[0][3] >> 4) & 0x3F; + LOG("ep0 xfer trb3 = %08x", _xfer_trb[0][3]); + if (TRBCTL_CTL_SETUP == trbctl) { + RTSS_InvalidateDCache_by_Addr(_ctrl_buf, sizeof(_ctrl_buf)); + LOG("%02x %02x %02x %02x %02x %02x %02x %02x", + _ctrl_buf[0], _ctrl_buf[1], _ctrl_buf[2], _ctrl_buf[3], + _ctrl_buf[4], _ctrl_buf[5], _ctrl_buf[6], _ctrl_buf[7]); + dcd_event_setup_received(TUD_OPT_RHPORT, _ctrl_buf, true); + } else if (TRBCTL_CTL_STAT3 == trbctl) { + if (0 < _xfer_bytes[0]) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[0][0], _xfer_bytes[0]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + _xfer_bytes[0] - (_xfer_trb[0][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_OUT), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // invalid TRBCTL value + __BKPT(0); + } + } else if (1 == ep) { + uint8_t trbctl = (_xfer_trb[1][3] >> 4) & 0x3F; + LOG("ep1 xfer trb3 = %08x trb2 = %08x", _xfer_trb[1][3], _xfer_trb[1][2]); + if (TRBCTL_CTL_STAT2 != trbctl) { // STATUS IN notification is done at xfer request + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + _xfer_bytes[1] - (_xfer_trb[1][2] & 0xFFFFFF), + XFER_RESULT_SUCCESS, true); + } else { + if (2 == ++_sts_stage) { + _sts_stage = 0; + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(0, TUSB_DIR_IN), + 0, XFER_RESULT_SUCCESS, true); + + // *(volatile uint32_t*) 0x4900C000 ^= 8; // [TEMP] + } + } + } else { + // [TODO] check if ep is open + LOG("ep%u xfer trb3 = %08x trb2 = %08x", ep, _xfer_trb[ep][3], _xfer_trb[ep][2]); + if (TUSB_DIR_OUT == tu_edpt_dir(tu_edpt_addr(ep >> 1, ep & 1))) { + RTSS_InvalidateDCache_by_Addr((void*) _xfer_trb[ep][0], + 512 - _xfer_trb[ep][2]); + // _xfer_bytes[ep] - _xfer_trb[ep][2]); + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + 512 - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } else + dcd_event_xfer_complete(TUD_OPT_RHPORT, tu_edpt_addr(ep >> 1, ep & 1), + _xfer_bytes[ep] - _xfer_trb[ep][2], + XFER_RESULT_SUCCESS, true); + } + } break; + case DEPEVT_XFERINPROGRESS: { + LOG("Transfer in progress"); + } break; + case DEPEVT_XFERNOTREADY: { + LOG("Transfer not ready: %s", sts & 8 ? "no TRB" : "no XFER"); + + // XferNotReady NotActive for status stage + if ((1 == ep) && (0b0010 == (sts & 0b1011))) { + _dcd_start_xfer(1, NULL, 0, TRBCTL_CTL_STAT2); + break; + } + + if ((0 == ep) && (0b0010 == (sts & 0b1011))) { + _xfer_bytes[0] = 0; + _dcd_start_xfer(0, _ctrl_buf, 64, TRBCTL_CTL_STAT3); + break; + } + + if ((1 > ep) && (sts & (1 << 3))) { + if (_xfer_trb[ep][3] & (1 << 0)) { // transfer was configured + // dependxfer can only block when actbitlater is set + ugbl->guctl2_b.rst_actbitlater = 1; + _dcd_cmd_wait(ep, CMDTYP_DEPENDXFER, 0); + ugbl->guctl2_b.rst_actbitlater = 0; + + // reset the trb byte count and clean the cache + RTSS_InvalidateDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[0])); + _xfer_trb[ep][2] = _xfer_bytes[ep]; + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + // issue the block command and pass the status + _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); + + *(volatile uint32_t*) 0x49007000 ^= 16; // [TEMP] + } + } + } break; + case DEPEVT_EPCMDCMPLT: { + // redundant, currently no commands are issued with IOC bit set + } break; + } +} + + +static void _dcd_handle_devt(uint8_t evt, uint16_t info) +{ + LOG("%010u DEVT evt%u info%u", DWT->CYCCNT, evt, info); + switch (evt) { + case DEVT_USBRST: { + _xfer_cfgd = false; + + // [TODO] issue depcstall for any ep in stall mode + udev->dcfg_b.devaddr = 0; + LOG("USB reset"); + dcd_event_bus_reset(TUD_OPT_RHPORT, TUSB_SPEED_HIGH, true); // [TODO] actual speed + } break; + case DEVT_CONNECTDONE: { + // read conn speed from dsts + // program ramclksel in gctl if needed + LOG("Connect done"); + + udev->depcmd[0].par1 = (0 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[0].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(0, CMDTYP_DEPCFG, 0); + + udev->depcmd[1].par1 = (1 << 25) | (1 << 10) | (1 << 8); + udev->depcmd[1].par0 = (2 << 30) | (0 << 22) | (0 << 17) | (64 << 3) | (0 << 1); + _dcd_cmd_wait(1, CMDTYP_DEPCFG, 0); + } break; + case DEVT_ULSTCHNG: { + LOG("Link status change"); + switch (info) { + case 0x3: { // suspend (L2) + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SUSPEND, true); + } break; + case 0x4: { // disconnected + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_UNPLUGGED, true); + } break; + case 0xF: { // resume + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_RESUME, true); + } break; + default: {} + } + // 0x0: ON state + // 0x2: L1 state (sleep) + // 0x3: L2 state (suspend) + // 0x4: disconnected state + // 0x5: early suspend + // 0xE: reset + // 0xF: resume + } break; + case DEVT_SOF: { + dcd_event_bus_signal(TUD_OPT_RHPORT, DCD_EVENT_SOF, true); + } break; + case DEVT_ERRTICERR: { + __BKPT(0); + } break; + default: { + LOG("Unknown DEVT event"); + } + } +} + + +static uint8_t _dcd_start_xfer(uint8_t ep, void* buf, uint32_t size, uint8_t type) +{ + dcd_int_disable(TUD_OPT_RHPORT); // prevent race conditions + + // program the trb and clean the cache + _xfer_trb[ep][0] = buf ? (uint32_t) LocalToGlobal(buf) : 0; + _xfer_trb[ep][1] = 0; + _xfer_trb[ep][2] = size; + _xfer_trb[ep][3] = (1 << 11) | (1 << 10) | (type << 4) | (1 << 1) | (1 << 0); + RTSS_CleanDCache_by_Addr(_xfer_trb[ep], sizeof(_xfer_trb[ep])); + + // prepare ep command + udev->depcmd[ep].par1 = (uint32_t) LocalToGlobal(_xfer_trb[ep]); + udev->depcmd[ep].par0 = 0; + + dcd_int_enable(TUD_OPT_RHPORT); + + // issue the block command and pass the status + return _dcd_cmd_wait(ep, CMDTYP_DEPSTRTXFER, 0); +} + +#endif // CFG_TUD_ENABLED diff --git a/ports/alif/tinyusb_port/tusb_config.h b/ports/alif/tinyusb_port/tusb_config.h new file mode 100644 index 0000000000000..6c4ead1128846 --- /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 (CORE_M55_HP) + +// 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 0000000000000..8d841d188c333 --- /dev/null +++ b/ports/alif/usbd.c @@ -0,0 +1,42 @@ +/* + * 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" +#include "se_services.h" + +void mp_usbd_port_get_serial_number(char *serial_buf) { + uint8_t id[8] = {0}; + se_services_get_unique_id(id); + MP_STATIC_ASSERT(sizeof(id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id, sizeof(id)); +} + +#endif diff --git a/ports/alif/vfs_rom_ioctl.c b/ports/alif/vfs_rom_ioctl.c new file mode 100644 index 0000000000000..d82ac6f8417e2 --- /dev/null +++ b/ports/alif/vfs_rom_ioctl.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 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/objarray.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "modalif.h" +#include "mpu.h" +#include "mram.h" +#include "ospi_flash.h" + +#if MICROPY_VFS_ROM_IOCTL + +#if MICROPY_HW_ROMFS_ENABLE_PART0 && !defined(MICROPY_HW_ROMFS_PART0_START) +#define MICROPY_HW_ROMFS_PART0_START (uintptr_t)(&_micropy_hw_romfs_part0_start) +#define MICROPY_HW_ROMFS_PART0_SIZE (uintptr_t)(&_micropy_hw_romfs_part0_size) +extern uint8_t _micropy_hw_romfs_part0_start; +extern uint8_t _micropy_hw_romfs_part0_size; +#endif + +#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) +#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +extern uint8_t _micropy_hw_romfs_part1_start; +extern uint8_t _micropy_hw_romfs_part1_size; +#endif + +#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} + +static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART0 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART0_START, MICROPY_HW_ROMFS_PART0_SIZE), + #endif + #if MICROPY_HW_ROMFS_ENABLE_PART1 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + #endif +}; + +static inline bool mram_is_valid_addr(uintptr_t addr) { + return MRAM_BASE <= addr && addr < MRAM_BASE + MRAM_SIZE; +} + +static inline bool ospi_is_valid_addr(uintptr_t xip_base, uintptr_t addr) { + MP_STATIC_ASSERT(OSPI0_XIP_SIZE == OSPI1_XIP_SIZE); + return xip_base <= addr && addr < xip_base + OSPI0_XIP_SIZE; +} + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + mp_int_t cmd = mp_obj_get_int(args[0]); + if (cmd == MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS) { + return MP_OBJ_NEW_SMALL_INT(MP_ARRAY_SIZE(romfs_obj_table)); + } + + if (n_args < 2) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + mp_int_t romfs_id = mp_obj_get_int(args[1]); + if (!(0 <= romfs_id && romfs_id < MP_ARRAY_SIZE(romfs_obj_table))) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + + if (cmd == MP_VFS_ROM_IOCTL_GET_SEGMENT) { + // Return the ROMFS memoryview object. + return MP_OBJ_FROM_PTR(romfs_obj); + } + + uintptr_t romfs_base = (uintptr_t)romfs_obj->items; + uintptr_t romfs_len = romfs_obj->len; + + #if MICROPY_HW_ENABLE_OSPI + const uintptr_t ospi_base = ospi_flash_get_xip_base(); + #endif + + if (cmd == MP_VFS_ROM_IOCTL_WRITE_PREPARE) { + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + if (dest_max > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // No preparation needed for MRAM. + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(MRAM_SECTOR_SIZE); + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Erase OSPI flash. + dest -= ospi_base; + dest_max -= ospi_base; + while (dest < dest_max) { + int ret = ospi_flash_erase_sector(dest); + mp_event_handle_nowait(); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + dest += MICROPY_HW_FLASH_BLOCK_SIZE_BYTES; + } + // Return the minimum write size. + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE) { + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (dest + bufinfo.len > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + if (mram_is_valid_addr(dest)) { + // Write data to MRAM. + mpu_config_mram(false); + const uint8_t *src = bufinfo.buf; + const uint8_t *max = src + bufinfo.len; + while (src < max) { + mram_write_128bit((uint8_t *)dest, src); + dest += MRAM_SECTOR_SIZE; + src += MRAM_SECTOR_SIZE; + } + mpu_config_mram(true); + return MP_OBJ_NEW_SMALL_INT(0); // success + } + + #if MICROPY_HW_ENABLE_OSPI + if (ospi_is_valid_addr(ospi_base, dest)) { + // Write data to OSPI flash. + dest -= ospi_base; + int ret = ospi_flash_write(dest, bufinfo.len, bufinfo.buf); + mp_event_handle_nowait(); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif // MICROPY_VFS_ROM_IOCTL diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8106ab4f3ff03..5c54295028083 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -280,6 +280,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c endif # MICROPY_PY_BLUETOOTH ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 8ac9a8af03d1a..c938dcbda7d6d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -432,6 +432,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += mpbthciport.c +DRIVERS_SRC_C += drivers/cyw43/cywbt.c ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) SRC_C += mpnimbleport.c diff --git a/pyproject.toml b/pyproject.toml index 8053df24dd797..263b120d3fea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.codespell] count = "" ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,shft,synopsys,technic,ure,curren" +ignore-words-list = "ans,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" quiet-level = 3 skip = """ */build*,\ diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index 7022955a3da75..c65095f22a1a5 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -13,7 +13,10 @@ # Configure pins based on the port/board details. # Values are tuples of (spi_id, sck, mosi, miso) -if "pyboard" in sys.platform: +if "alif" in sys.platform: + MAX_DELTA_MS = 20 + spi_instances = ((0, None, None, None),) +elif "pyboard" in sys.platform: spi_instances = ( (1, None, None, None), # "explicit choice of sck/mosi/miso is not implemented" (2, None, None, None), diff --git a/tests/extmod/machine_uart_irq_txidle.py b/tests/extmod/machine_uart_irq_txidle.py index feb95fabaa44f..084e9825768a8 100644 --- a/tests/extmod/machine_uart_irq_txidle.py +++ b/tests/extmod/machine_uart_irq_txidle.py @@ -12,7 +12,10 @@ import time, sys # Configure pins based on the target. -if "rp2" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None +elif "rp2" in sys.platform: uart_id = 0 tx_pin = "GPIO0" rx_pin = "GPIO1" diff --git a/tests/extmod/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py index e9652f3c7ac21..f0cc912da66e0 100644 --- a/tests/extmod/machine_uart_tx.py +++ b/tests/extmod/machine_uart_tx.py @@ -14,7 +14,11 @@ timing_margin_us = 100 # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + pins = {} + bit_margin = 1 +elif "esp32" in sys.platform: uart_id = 1 pins = {} timing_margin_us = 400 diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py index bf34900bd0826..ecc95e62ae570 100644 --- a/tests/extmod_hardware/machine_uart_irq_rx.py +++ b/tests/extmod_hardware/machine_uart_irq_rx.py @@ -15,7 +15,11 @@ byte_by_byte = False # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py index 182ab24ebe0c4..af2412c75eedc 100644 --- a/tests/extmod_hardware/machine_uart_irq_rxidle.py +++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py @@ -14,7 +14,11 @@ import time, sys # Configure pins based on the target. -if "esp32" in sys.platform: +if "alif" in sys.platform: + uart_id = 1 + tx_pin = None + rx_pin = None +elif "esp32" in sys.platform: uart_id = 1 tx_pin = 4 rx_pin = 5 diff --git a/tests/ports/alif_hardware/flash_test.py b/tests/ports/alif_hardware/flash_test.py new file mode 100644 index 0000000000000..5be3fc2d02f3e --- /dev/null +++ b/tests/ports/alif_hardware/flash_test.py @@ -0,0 +1,149 @@ +# MIT license; Copyright (c) 2024 OpenMV LLC. +# +# Note: this test completely erases the filesystem! +# It is intended to be run manually. + +import alif +import hashlib +import os +import sys +import time +import vfs + +hash_algo = "sha256" + + +def flash_make_filesystem(): + try: + vfs.umount("/flash") + except: + pass + bdev = alif.Flash() + vfs.VfsFat.mkfs(bdev) + vfs.mount(vfs.VfsFat(bdev), "/flash") + sys.path.append("/flash") + sys.path.append("/flash/lib") + os.chdir("/flash") + + +def flash_block_test(): + try: + vfs.umount("/flash") + except: + pass + dev = alif.Flash() + + data512 = os.urandom(512) + buf512 = bytearray(512) + block_numbers = tuple(range(32)) + tuple(range(250, 266)) + + print("Block read/write integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.writeblocks(block_n, data512) + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + print("Block read back integrity: ", end="") + ok = True + for block_n in block_numbers: + dev.readblocks(block_n, buf512) + if buf512 != data512: + ok = False + print(ok) + + N = 16 * 1024 + data_big = os.urandom(N) + t0 = time.ticks_us() + dev.writeblocks(0, data_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block write speed: {len(data_big) / 1024 / dt * 1_000_000} KiB/sec") + + buf_big = bytearray(N) + t0 = time.ticks_us() + dev.readblocks(0, buf_big) + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"Block read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("big block read-back failed") + + try: + import uctypes + + xip = memoryview(dev) + except: + xip = None + if xip is not None: + t0 = time.ticks_us() + buf_big[:] = xip[: len(buf_big)] + dt = time.ticks_diff(time.ticks_us(), t0) + print(f"XIP read speed: {len(buf_big) / 1024 / dt * 1_000_000} KiB/sec") + + if buf_big != data_big: + raise RuntimeError("XIP read-back failed") + for i in range(len(buf_big)): + if xip[i] != data_big[i]: + raise RuntimeError("XIP byte-wise read-back failed") + + +def flash_write_verify(path, size=1024 * 1024, block_size=1024): + hash_sum = getattr(hashlib, hash_algo)() + block_count = size // block_size + + print("-" * 16) + print(f"Writing file {size=} {block_size=}") + t0 = time.ticks_ms() + total_size = 0 + with open(path, "wb") as file: + for i in range(block_count): + buf = os.urandom(block_size) + # Update digest + hash_sum.update(buf) + total_size += file.write(buf) + if i % (block_count // 16) == 0: + print(f"{i}/{block_count}", end="\r") + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"Flash write finished: {total_size / 1024 / dt * 1000} KiB/sec") + digest = hash_sum.digest() + + print("Reading file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + buf = bytearray(block_size) + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + total_size += file.readinto(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec") + + print("Verifying file... ", end="") + hash_sum = getattr(hashlib, hash_algo)() + t0 = time.ticks_ms() + total_size = 0 + with open(path, "rb") as file: + for i in range(block_count): + buf = file.read(block_size) + total_size += len(buf) + # Update digest + hash_sum.update(buf) + dt = time.ticks_diff(time.ticks_ms(), t0) + print(f"finished: {total_size / 1024 / dt * 1000} KiB/sec; ", end="") + + if digest != hash_sum.digest(): + raise RuntimeError(f"{hash_algo} checksum verify failed") + + print(f"{hash_algo} checksum verified") + + +if __name__ == "__main__": + flash_block_test() + flash_make_filesystem() + flash_write_verify("test0.bin", size=64 * 1024, block_size=1024) + flash_write_verify("test1.bin", size=128 * 1024, block_size=1024) + flash_write_verify("test2.bin", size=64 * 1024, block_size=2048) + flash_write_verify("test4.bin", size=256 * 1024, block_size=4096) + flash_write_verify("test4.bin", size=512 * 1024, block_size=16384) diff --git a/tools/ci.sh b/tools/ci.sh index 948eeeaef2d22..cfc9754837f76 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -871,3 +871,18 @@ function ci_zephyr_run_tests { # - inf_nan_arith fails pow(-1, nan) test (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) } + +######################################################################################## +# ports/alif + +function ci_alif_setup { + ci_gcc_arm_setup +} + +function ci_alif_ae3_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HP submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_HE submodules + make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_DUAL + make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy