ports/alif: Add Alif Ensemble port.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
This commit is contained in:
iabdalkader 2024-01-20 10:29:21 +02:00
parent 4e7849438d
commit f5dd7de21a
25 changed files with 6563 additions and 0 deletions

View File

@ -34,6 +34,7 @@ SRCS += \
se_services/source/services_host_maintenance.c \
Device/common/source/clk.c \
Device/common/source/dcd.c \
Device/common/source/pm.c \
Device/common/source/mpu_M55.c \
Device/common/source/tgu_M55.c \
Device/common/source/system_M55.c \

View File

@ -0,0 +1,28 @@
Copyright (C) 2023-2024 OpenMV, LLC.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Any redistribution, use, or modification in source or binary form
is done solely for personal benefit and not for any commercial
purpose or for monetary gain. For commercial licensing options,
please contact openmv@openmv.io
THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif port linker script.
*/
// Entry Point
ENTRY(Reset_Handler)
#include "omv_boardconfig.h"
MEMORY
{
FLASH_TEXT (rx) : ORIGIN = OMV_FLASH_ORIGIN, LENGTH = OMV_FLASH_LENGTH
ITCM (rx) : ORIGIN = OMV_ITCM_ORIGIN, LENGTH = OMV_ITCM_LENGTH
DTCM (rw) : ORIGIN = OMV_DTCM_ORIGIN, LENGTH = OMV_DTCM_LENGTH
#if defined(OMV_SRAM0_ORIGIN)
SRAM0 (rw) : ORIGIN = OMV_SRAM0_ORIGIN, LENGTH = OMV_SRAM0_LENGTH
#endif
#if defined(OMV_SRAM1_ORIGIN)
SRAM1 (rw) : ORIGIN = OMV_SRAM1_ORIGIN, LENGTH = OMV_SRAM1_LENGTH
#endif
#if defined(OMV_SRAM6_A_ORIGIN)
SRAM6_A (rw) : ORIGIN = OMV_SRAM6_A_ORIGIN, LENGTH = OMV_SRAM6_A_LENGTH
#endif
#if defined(OMV_SRAM6_B_ORIGIN)
SRAM6_B (rw) : ORIGIN = OMV_SRAM6_B_ORIGIN, LENGTH = OMV_SRAM6_B_LENGTH
#endif
#if defined(OMV_SRAM7_ORIGIN)
SRAM7 (rw) : ORIGIN = OMV_SRAM7_ORIGIN, LENGTH = OMV_SRAM7_LENGTH
#endif
#if defined(OMV_SRAM8_ORIGIN)
SRAM8 (rw) : ORIGIN = OMV_SRAM8_ORIGIN, LENGTH = OMV_SRAM8_LENGTH
#endif
#if defined(OMV_SRAM9_A_ORIGIN)
SRAM9_A (rw) : ORIGIN = OMV_SRAM9_A_ORIGIN, LENGTH = OMV_SRAM9_A_LENGTH
#endif
#if defined(OMV_SRAM9_B_ORIGIN)
SRAM9_B (rw) : ORIGIN = OMV_SRAM9_B_ORIGIN, LENGTH = OMV_SRAM9_B_LENGTH
#endif
}
SECTIONS
{
.text : ALIGN(16)
{
KEEP(*(.vectors))
. = ALIGN(4);
*(.text*)
. = ALIGN(4);
*(EXCLUDE_FILE("*/imlib/*.o") .rodata*)
. = ALIGN(16);
} > FLASH_TEXT
.copy.table : ALIGN(4)
{
__copy_table_start__ = .;
LONG ( LOADADDR(.data) )
LONG ( ADDR(.data) )
LONG ( SIZEOF(.data)/4 )
__copy_table_end__ = .;
. = ALIGN(16);
} > FLASH_TEXT
.zero.table : ALIGN(4)
{
__zero_table_start__ = .;
LONG (ADDR(.bss))
LONG (SIZEOF(.bss)/4)
LONG (ADDR(.bss.sram))
LONG (SIZEOF(.bss.sram)/4)
__zero_table_end__ = .;
. = ALIGN(16);
} > FLASH_TEXT
.data : ALIGN(8)
{
*(.data)
. = ALIGN(8);
*(.data.*)
. = ALIGN(8);
"*/imlib/*.o"(.rodata*)
. = ALIGN(16);
} > OMV_MAIN_MEMORY AT > FLASH_TEXT
/* Peripherals in expansion master 0 (such as USB, Ethernet, SD/MMC)
are by default configured as non-secure, so they don't have access
to DTCMs. This can be fixed in the ToC by allowing access to DTCMs
to all bus masters, for now these peripherals should place buffers
in regular SRAM */
.bss.sram (NOLOAD) : ALIGN(32)
{
*(.bss.sram*)
. = ALIGN(32);
/* EthosU cache area */
*(.bss.ethos_u_cache)
. = ALIGN (32);
} > OMV_BSS_SRAM_MEMORY
.bss : ALIGN(4)
{
__bss_start__ = .;
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > OMV_MAIN_MEMORY
.init_fini_arrays : ALIGN(16)
{
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.eh_frame*))
. = ALIGN(16);
} > FLASH_TEXT
#include "common.ld.S"
}

659
src/omv/ports/alif/alif.mk Normal file
View File

@ -0,0 +1,659 @@
#Copyright (C) 2023-2024 OpenMV, LLC.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Any redistribution, use, or modification in source or binary form
# is done solely for personal benefit and not for any commercial
# purpose or for monetary gain. For commercial licensing options,
# please contact openmv@openmv.io
#
# THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
# OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Include OpenMV board config.
include $(OMV_BOARD_CONFIG_DIR)/omv_boardconfig.mk
LDSCRIPT ?= alif
BUILD := $(BUILD)/$(MCU_CORE)
FIRMWARE := $(FIRMWARE)_$(MCU_CORE)
DAVE2D_DIR=drivers/dave2d
CORE_M55_HP := $(if $(filter M55_HP,$(MCU_CORE)),1,0)
CMSIS_MCU_H := '<system_utils.h>'
ROMFS_CONFIG := $(OMV_BOARD_CONFIG_DIR)/romfs.json
ROMFS_PART := $(if $(filter M55_HE,$(MCU_CORE)),0,1)
ROMFS_IMAGE := $(FW_DIR)/romfs$(ROMFS_PART).stamp
# Compiler Flags
CFLAGS += -std=gnu99 \
-Wall \
-Werror \
-Warray-bounds \
-nostartfiles \
-fdata-sections \
-ffunction-sections \
-fno-inline-small-functions \
-mfloat-abi=hard \
-mthumb \
-mcpu=$(CPU) \
-mtune=$(CPU) \
-mfpu=$(FPU) \
-march=armv8.1-m.main+fp+mve.fp \
-fsingle-precision-constant \
-Wdouble-promotion
CFLAGS += -D__VFP_FP__ \
-D$(TARGET) \
-D$(MCU_CORE) \
-DCORE_$(MCU_CORE)=1 \
-D$(ARM_MATH) \
-DARM_NN_TRUNCATE \
-DETHOS_U \
-DPINS_AF_H=$(PINS_AF_H) \
-DCMSIS_MCU_H=$(CMSIS_MCU_H) \
-DOMV_NOSYS_STUBS_ENABLE=1 \
-DTUSB_ALIF_NO_IRQ_CFG \
-DWITH_MM_FIXED_RANGE #WITH_MM_DYNAMIC -DNO_MSIZE
ifeq ($(MCU_CORE),M55_HP)
CFLAGS += -DRTE_LPUART_SELECT_DMA0=1 \
-DRTE_LPSPI_SELECT_DMA0=1 \
-DRTE_LPSPI_SELECT_DMA0_GROUP=1 \
-DRTE_LPI2S_SELECT_DMA0=1 \
-DRTE_LPSPI_SELECT_DMA0=1 \
-DRTE_LPPDM_SELECT_DMA0=1
endif
CFLAGS_H += -DALIF_CMSIS_H=$(CMSIS_MCU_H)
CLANG_FLAGS = -fshort-enums \
--target=armv8.1m-none-eabi \
-march=armv8.1-m.main+mve.fp+fp.dp \
-Wno-shift-count-overflow \
-Wno-ignored-optimization-argument \
-Wno-unused-command-line-argument \
-D__ARMCC_VERSION=6100100 \
-DALIF_CMSIS_H=$(CMSIS_MCU_H) \
$(filter-out -march%,$(CFLAGS))
HAL_CFLAGS += -I$(TOP_DIR)/$(CMSIS_DIR)/include/
HAL_CFLAGS += -I$(TOP_DIR)/$(CMSIS_DIR)/include/alif
HAL_CFLAGS += -I$(OMV_BOARD_CONFIG_DIR)
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/drivers/include/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/ospi_xip/source/ospi/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/se_services/include
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/Device/common/config/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/Device/common/include/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/Device/core/$(MCU_CORE)/config/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/Device/core/$(MCU_CORE)/include/
HAL_CFLAGS += -I$(TOP_DIR)/$(HAL_DIR)/Device/$(MCU_SERIES)/$(MCU_VARIANT)/
MPY_CFLAGS += -I$(MP_BOARD_CONFIG_DIR)
MPY_CFLAGS += -I$(MP_BOARD_CONFIG_DIR)
MPY_CFLAGS += -I$(BUILD)/$(MICROPY_DIR)
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/py
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/oofatfs
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/tinyusb/src
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/lwip/src/include
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/mbedtls/include
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/shared/runtime
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif/tinyusb_port
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif/lwip_inc
MPY_CFLAGS += -DMICROPY_PY_CSI=$(MICROPY_PY_CSI)
MPY_CFLAGS += -DMICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
MPY_CFLAGS += -DMICROPY_PY_SSL=$(MICROPY_PY_SSL)
MPY_CFLAGS += -DMICROPY_SSL_MBEDTLS=$(MICROPY_SSL_MBEDTLS)
MPY_CFLAGS += -DMICROPY_PY_NETWORK_CYW43=$(MICROPY_PY_NETWORK_CYW43)
MPY_CFLAGS += -DMICROPY_PY_BLUETOOTH=$(MICROPY_PY_BLUETOOTH)
MPY_CFLAGS += -DMICROPY_BLUETOOTH_NIMBLE=$(MICROPY_BLUETOOTH_NIMBLE)
MPY_MKARGS += MCU_CORE=$(MCU_CORE)
MPY_MKARGS += MICROPY_VFS_LFS2=0
MPY_MKARGS += MICROPY_FLOAT_IMPL=float
MPY_MKARGS += ALIF_DFP_REL_HERE=$(TOP_DIR)/$(HAL_DIR)
MPY_MKARGS += CMSIS_DIR=$(TOP_DIR)/$(HAL_DIR)/cmsis/inc
MPY_MKARGS += MICROPY_PY_CSI=$(MICROPY_PY_CSI)
MPY_MKARGS += MICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
MPY_MKARGS += MICROPY_PY_SSL=$(MICROPY_PY_SSL)
MPY_MKARGS += MICROPY_SSL_MBEDTLS=$(MICROPY_SSL_MBEDTLS)
MPY_MKARGS += MICROPY_PY_NETWORK_CYW43=$(MICROPY_PY_NETWORK_CYW43)
MPY_MKARGS += MICROPY_PY_BLUETOOTH=$(MICROPY_PY_BLUETOOTH)
MPY_MKARGS += MICROPY_BLUETOOTH_NIMBLE=$(MICROPY_BLUETOOTH_NIMBLE)
MPY_MKARGS += MICROPY_PY_OPENAMP=$(MICROPY_PY_OPENAMP)
MPY_MKARGS += MICROPY_PY_OPENAMP_REMOTEPROC=$(MICROPY_PY_OPENAMP_REMOTEPROC)
MPY_MKARGS += MICROPY_MANIFEST_MCU_CORE=$(shell echo $(MCU_CORE) | awk -F'_' '{print tolower($$2)}')
ifeq ($(MCU_CORE),M55_HP)
MPY_MKARGS += MICROPY_PY_OPENAMP_HOST=1
else ifeq ($(MCU_CORE),M55_HE)
MPY_MKARGS += MICROPY_PY_OPENAMP_DEVICE=1
else
$(error Invalid MCU core specified))
endif
ifeq ($(MCU_CORE),M55_HP)
VELA_ARGS="--system-config RTSS_HP_DTCM_MRAM \
--accelerator-config ethos-u55-256 \
--memory-mode Shared_Sram"
else
VELA_ARGS="--system-config RTSS_HE_SRAM_MRAM \
--accelerator-config ethos-u55-128 \
--memory-mode Shared_Sram"
endif
OMV_CFLAGS += -I$(OMV_BOARD_CONFIG_DIR)
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/alloc/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/common/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/imlib/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/modules/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/sensors/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/templates/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/ports/$(PORT)/
OMV_CFLAGS += -I$(TOP_DIR)/$(OMV_DIR)/ports/$(PORT)/modules/
OMV_CFLAGS += -I$(TOP_DIR)/$(LEPTON_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(LSM6DSM_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(VL53L5CX_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(VL53L8CX_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(WINC1500_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90621_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90640_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(MLX90641_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(PIXART_DIR)/include/
OMV_CFLAGS += -I$(TOP_DIR)/$(DAVE2D_DIR)/include
CFLAGS += $(HAL_CFLAGS) $(MPY_CFLAGS) $(OMV_CFLAGS)
# Linker Flags
LDFLAGS = -mthumb \
-mcpu=$(CPU) \
-mfpu=$(FPU) \
-mfloat-abi=hard \
-mabi=aapcs-linux \
-z noexecstack \
-Wl,--print-memory-usage \
-Wl,--gc-sections \
-Wl,--no-warn-rwx-segment \
-Wl,-Map=$(BUILD)/$(FIRMWARE).map \
-Wl,-T$(BUILD)/$(LDSCRIPT).lds
ifeq ($(MCU_CORE),M55_HP)
# Linker Flags
LDFLAGS += -Wl,--wrap=mp_usbd_task \
-Wl,--wrap=tud_cdc_rx_cb \
-Wl,--wrap=mp_hal_stdio_poll \
-Wl,--wrap=mp_hal_stdout_tx_strn
endif
# CMSIS Objects
FIRM_OBJ += $(wildcard $(BUILD)/$(CMSIS_DIR)/src/dsp/CommonTables/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(CMSIS_DIR)/src/dsp/FastMathFunctions/*.o)
# HAL Objects
FIRM_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/drivers/source/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/Device/common/source/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/Device/core/$(MCU_CORE)/source/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/ospi_xip/source/ospi/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/se_services/source/*.o)
# Drivers
FIRM_OBJ += $(wildcard $(BUILD)/$(LEPTON_DIR)/src/*.o)
ifeq ($(MICROPY_PY_IMU), 1)
FIRM_OBJ += $(wildcard $(BUILD)/$(LSM6DSM_DIR)/src/*.o)
endif
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90621_DIR)/src/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90640_DIR)/src/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MLX90641_DIR)/src/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(VL53L5CX_DIR)/src/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(VL53L8CX_DIR)/src/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(PIXART_DIR)/src/*.o)
ifeq ($(MCU_CORE),M55_HP)
FIRM_OBJ += $(wildcard $(BUILD)/$(DAVE2D_DIR)/src/*.o)
endif
# OpenMV Objects
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/alloc/, \
xalloc.o \
fb_alloc.o \
umm_malloc.o \
unaligned_memcpy.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/common/, \
array.o \
ringbuf.o \
trace.o \
mutex.o \
vospi.o \
pendsv.o \
usbdbg.o \
tinyusb_debug.o \
file_utils.o \
mp_utils.o \
nosys_stubs.o \
omv_csi.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \
ov2640.o \
ov5640.o \
ov7670.o \
ov7690.o \
ov7725.o \
ov9650.o \
mt9v0xx.o \
mt9m114.o \
lepton.o \
hm01b0.o \
hm0360.o \
gc2145.o \
pag7920.o \
pag7936.o \
paj6100.o \
frogeye2020.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \
agast.o \
apriltag.o \
bayer.o \
binary.o \
blob.o \
bmp.o \
clahe.o \
collections.o \
dmtx.o \
draw.o \
edge.o \
eye.o \
fast.o \
fft.o \
filter.o \
fmath.o \
font.o \
framebuffer.o \
fsort.o \
gif.o \
haar.o \
hog.o \
hough.o \
imlib.o \
integral.o \
integral_mw.o \
isp.o \
jpegd.o \
jpege.o \
lodepng.o \
png.o \
kmeans.o \
lab_tab.o \
lbp.o \
line.o \
lsd.o \
mathop.o \
mjpeg.o \
orb.o \
phasecorrelation.o \
point.o \
ppm.o \
qrcode.o \
qsort.o \
rainbow_tab.o \
rectangle.o \
selective_search.o \
sincos_tab.o \
stats.o \
stereo.o \
template.o \
xyz_tab.o \
yuv.o \
zbar.o \
)
FIRM_OBJ += $(wildcard $(BUILD)/$(OMV_DIR)/ports/$(PORT)/*.o)
#------------- MicroPy Objects -------------------#
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/modules/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/ports/$(PORT)/modules/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/py/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/boards/$(TARGET)/*.o)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
alif_flash.o \
cyw43_port_spi.o \
fatfs_port.o \
frozen_content.o \
machine_pin.o \
machine_i2c.o \
machine_spi.o \
machine_rtc.o \
modalif.o \
mphalport.o \
mpnetworkport.o \
mpuart.o \
msc_disk.o \
ospi_ext.o \
ospi_flash.o \
system_tick.o \
usbd.o \
pins_board.o \
se_services.o \
vfs_rom_ioctl.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/, \
lib/tinyusb/src/tusb.o \
lib/tinyusb/src/class/cdc/cdc_device.o \
lib/tinyusb/src/class/msc/msc_device.o \
lib/tinyusb/src/common/tusb_fifo.o \
lib/tinyusb/src/device/usbd.o \
lib/tinyusb/src/device/usbd_control.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/shared/,\
libc/printf.o \
libc/string0.o \
readline/readline.o \
runtime/gchelper_thumb2.o \
runtime/gchelper_native.o \
runtime/interrupt_char.o \
runtime/mpirq.o \
runtime/pyexec.o \
runtime/softtimer.o \
runtime/stdout_helpers.o \
runtime/sys_stdio_mphal.o \
timeutils/timeutils.o \
tinyusb/mp_usbd.o \
tinyusb/mp_usbd_cdc.o \
tinyusb/mp_usbd_descriptor.o \
)
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/lib/mbedtls/library/*.o)
FIRM_OBJ += $(wildcard $(BUILD)/$(MICROPY_DIR)/lib/mbedtls_errors/*.o)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/extmod/,\
machine_adc.o \
machine_adc_block.o \
machine_bitstream.o \
machine_i2c.o \
machine_i2s.o \
machine_mem.o \
machine_pinbase.o \
machine_pulse.o \
machine_pwm.o \
machine_signal.o \
machine_spi.o \
machine_timer.o \
machine_uart.o \
machine_usb_device.o \
machine_wdt.o \
modasyncio.o \
modbinascii.o \
modbtree.o \
modcryptolib.o \
moddeflate.o \
modframebuf.o \
modhashlib.o \
modheapq.o \
modjson.o \
modmachine.o \
modmarshal.o \
modnetwork.o \
modonewire.o \
modopenamp.o \
modopenamp_remoteproc.o \
modopenamp_remoteproc_store.o \
modos.o \
modplatform.o \
modrandom.o \
modre.o \
modselect.o \
modsocket.o \
modtls_axtls.o \
modtls_mbedtls.o \
modtime.o \
moductypes.o \
modvfs.o \
network_esp_hosted.o \
network_ninaw10.o \
network_wiznet5k.o \
os_dupterm.o \
vfs.o \
vfs_blockdev.o \
vfs_fat.o \
vfs_fat_diskio.o \
vfs_fat_file.o \
vfs_lfs.o \
vfs_rom.o \
vfs_rom_file.o \
vfs_posix.o \
vfs_posix_file.o \
vfs_reader.o \
virtpin.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/lib/oofatfs/,\
ff.o \
ffunicode.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/drivers/,\
bus/softspi.o \
dht/dht.o \
)
ifeq ($(MICROPY_PY_ULAB), 1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/modules/ulab/code/,\
ndarray.o \
ndarray_operators.o \
ndarray_properties.o \
numpy/approx.o \
numpy/bitwise.o \
numpy/carray/carray.o \
numpy/carray/carray_tools.o \
numpy/compare.o \
numpy/create.o \
numpy/fft/fft.o \
numpy/fft/fft_tools.o \
numpy/filter.o \
numpy/io/io.o \
numpy/linalg/linalg.o \
numpy/linalg/linalg_tools.o \
numpy/ndarray/ndarray_iter.o \
numpy/numerical.o \
numpy/numpy.o \
numpy/poly.o \
numpy/random/random.o \
numpy/stats.o \
numpy/transform.o \
numpy/vector.o \
scipy/integrate/integrate.o \
scipy/linalg/linalg.o \
scipy/optimize/optimize.o \
scipy/scipy.o \
scipy/signal/signal.o \
scipy/special/special.o \
ulab.o \
ulab_tools.o \
user/user.o \
utils/utils.o \
)
endif
ifeq ($(MICROPY_PY_LWIP), 1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
lib/lwip/src/core/*.o \
lib/lwip/src/core/*/*.o \
lib/lwip/src/netif/*.o \
lib/lwip/src/apps/*/*.o \
extmod/modlwip.o \
extmod/modwebsocket.o \
extmod/modwebrepl.o \
extmod/network_lwip.o \
)
endif
ifeq ($(MICROPY_PY_NETWORK), 1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/shared/,\
netutils/dhcpserver.o \
netutils/netutils.o \
netutils/trace.o \
)
endif
ifeq ($(MICROPY_SSL_MBEDTLS), 1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
mbedtls/mbedtls_port.o \
)
endif
ifeq ($(MICROPY_PY_NETWORK_CYW43), 1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/lib/cyw43-driver/,\
src/cyw43_bthci_uart.o \
src/cyw43_ctrl.o \
src/cyw43_lwip.o \
src/cyw43_ll.o \
src/cyw43_sdio.o \
src/cyw43_spi.o \
src/cyw43_stats.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
extmod/network_cyw43.o \
)
endif
ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/lib/mynewt-nimble/,\
ext/tinycrypt/src/*.o \
nimble/host/services/gap/src/*.o \
nimble/host/services/gatt/src/*.o \
nimble/host/src/*.o \
nimble/host/util/src/*.o \
nimble/transport/uart/src/*.o \
porting/nimble/src/*.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
mpbthciport.o \
mpnimbleport.o \
extmod/nimble/modbluetooth_nimble.o \
extmod/nimble/nimble/nimble_npl_os.o \
extmod/nimble/hal/hal_uart.o \
extmod/modbluetooth.o \
)
endif
ifeq ($(MICROPY_PY_OPENAMP),1)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/openamp/metal/,\
device.o \
dma.o \
init.o \
io.o \
irq.o \
log.o \
shmem.o \
softirq.o \
version.o \
system/micropython/condition.o \
system/micropython/device.o \
system/micropython/io.o \
system/micropython/irq.o \
system/micropython/shmem.o \
system/micropython/time.o \
)
FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/, \
mpmetalport.o \
mpremoteprocport.o \
lib/open-amp/lib/virtio/virtio.o \
lib/open-amp/lib/virtio/virtqueue.o \
lib/open-amp/lib/virtio_mmio/virtio_mmio_drv.o \
lib/open-amp/lib/rpmsg/rpmsg.o \
lib/open-amp/lib/rpmsg/rpmsg_virtio.o \
lib/open-amp/lib/remoteproc/elf_loader.o \
lib/open-amp/lib/remoteproc/remoteproc.o \
lib/open-amp/lib/remoteproc/remoteproc_virtio.o \
lib/open-amp/lib/remoteproc/rsc_table_parser.o \
)
endif
# Libraries
ifeq ($(MICROPY_PY_ML_TFLM), 1)
OMV_CFLAGS += -I$(BUILD)/$(TENSORFLOW_DIR)/
OMV_CFLAGS += -I$(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/include
OMV_CFLAGS += -I$(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/include/third_party/ethos_u_core_driver/include/
FIRM_OBJ += $(addprefix $(BUILD)/$(TENSORFLOW_DIR)/, \
tflm_backend.o \
)
LIBS += $(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/lib/libtflm-$(CPU)-u55-release.a
endif
export VELA_ARGS
export CLANG_FLAGS
export USE_CLANG=1
all: $(FIRMWARE) .WAIT $(ROMFS_IMAGE)
$(SIZE) $(FW_DIR)/$(FIRMWARE).elf
FIRMWARE_OBJS:
$(MAKE) -C $(CMSIS_DIR) BUILD=$(BUILD)/$(CMSIS_DIR) CFLAGS="$(CFLAGS) -MMD"
$(MAKE) -C $(HAL_DIR) BUILD=$(BUILD)/$(HAL_DIR) CFLAGS="$(CFLAGS) -MMD"
ifeq ($(MCU_CORE),M55_HP)
$(MAKE) -C $(DAVE2D_DIR) BUILD=$(BUILD)/$(DAVE2D_DIR) CFLAGS="$(CFLAGS) -MMD"
endif
$(MAKE) -C $(MICROPY_DIR)/ports/$(PORT) -f alif.mk BUILD=$(BUILD)/$(MICROPY_DIR) $(MPY_MKARGS) obj
ifeq (0, 1)
$(MAKE) -C $(LEPTON_DIR) BUILD=$(BUILD)/$(LEPTON_DIR) CFLAGS="$(CFLAGS) -MMD"
endif
ifeq ($(MICROPY_PY_IMU), 1)
$(MAKE) -C $(LSM6DSM_DIR) BUILD=$(BUILD)/$(LSM6DSM_DIR) CFLAGS="$(CFLAGS) -MMD"
endif
$(MAKE) -C $(MLX90621_DIR) BUILD=$(BUILD)/$(MLX90621_DIR) CFLAGS="$(CFLAGS) -MMD"
$(MAKE) -C $(MLX90640_DIR) BUILD=$(BUILD)/$(MLX90640_DIR) CFLAGS="$(CFLAGS) -MMD"
$(MAKE) -C $(MLX90641_DIR) BUILD=$(BUILD)/$(MLX90641_DIR) CFLAGS="$(CFLAGS) -MMD"
$(MAKE) -C $(VL53L5CX_DIR) BUILD=$(BUILD)/$(VL53L5CX_DIR) CFLAGS="$(CFLAGS) $(CFLAGS_H) -MMD"
$(MAKE) -C $(VL53L8CX_DIR) BUILD=$(BUILD)/$(VL53L8CX_DIR) CFLAGS="$(CFLAGS) $(CFLAGS_H) -MMD"
ifeq ($(MICROPY_PY_ML_TFLM), 1)
$(MAKE) -C $(TENSORFLOW_DIR) BUILD=$(BUILD)/$(TENSORFLOW_DIR) CFLAGS="$(CFLAGS) -MMD"
endif
$(MAKE) -C $(OMV_DIR) BUILD=$(BUILD)/$(OMV_DIR) CFLAGS="$(CFLAGS) $(CFLAGS_H) -MMD"
$(ROMFS_IMAGE): $(ROMFS_CONFIG)
$(ECHO) "GEN $(FW_DIR)/romfs_$(MCU_CORE).img"
$(PYTHON) $(TOOLS)/$(MKROMFS) --top-dir $(TOP_DIR) --build-dir $(BUILD) --out-dir $(FW_DIR) \
--partition $(ROMFS_PART) --vela-args $(VELA_ARGS) --config $(ROMFS_CONFIG)
touch $@
$(FIRMWARE): FIRMWARE_OBJS
$(CPP) -P -E -DLINKER_SCRIPT -DCORE_$(MCU_CORE) -I$(OMV_COMMON_DIR) -I$(OMV_BOARD_CONFIG_DIR) \
$(OMV_DIR)/ports/$(PORT)/$(LDSCRIPT).ld.S > $(BUILD)/$(LDSCRIPT).lds
$(CC) $(LDFLAGS) $(FIRM_OBJ) -o $(FW_DIR)/$(FIRMWARE).elf $(LIBS) -lm
$(OBJCOPY) -Obinary $(FW_DIR)/$(FIRMWARE).elf $(FW_DIR)/$(FIRMWARE).bin
BIN_SIZE=$$(stat -c%s "$(FW_DIR)/$(FIRMWARE).bin"); \
PADDED_SIZE=$$(( (BIN_SIZE + 15) / 16 * 16 )); \
if [ $$BIN_SIZE -lt $$PADDED_SIZE ]; then \
dd if=/dev/zero bs=1 count=$$((PADDED_SIZE - BIN_SIZE)) >> $(FW_DIR)/$(FIRMWARE).bin; \
fi

View File

@ -0,0 +1,35 @@
{
"DEVICE": {
"disabled" : false,
"binary": "app-device-config-ae3.json",
"version" : "0.5.00",
"signed": false
},
"HP_BOOT": {
"disabled" : false,
"binary": "bootloader.bin",
"mramAddress": "0x80000000",
"version": "1.0.0",
"cpu_id": "M55_HP",
"flags": ["boot"],
"signed": false
},
"HP_APP": {
"disabled" : false,
"binary": "firmware_M55_HP.bin",
"mramAddress": "0x80020000",
"version": "1.0.0",
"cpu_id": "M55_HP",
"flags": ["deferred"],
"signed": false
},
"HE_APP": {
"disabled" : false,
"binary": "firmware_M55_HE.bin",
"mramAddress": "0x80320000",
"version": "1.0.0",
"cpu_id": "M55_HE",
"flags": ["deferred"],
"signed": false
}
}

View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2023 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 <string.h>
#include CMSIS_MCU_H
#include "global_map.h"
#include "dave_base.h"
#include "dave_d0lib.h"
#include "dave_registermap.h"
#include "dave_driver.h"
#include "irq.h"
#define D1_REG(index) ((long *) GPU2D_BASE)[index]
#define D1_DEV(handle) ((d1_device_intern *) handle)
#define D1_IRQCTL_ENABLE (D2IRQCTL_CLR_FINISH_DLIST | D2IRQCTL_CLR_FINISH_ENUM \
| D2IRQCTL_ENABLE_FINISH_DLIST)
#define D1_IRQCTL_CLEAR D1_IRQCTL_ENABLE
#define D1_IRQCTL_DISABLE (D2IRQCTL_CLR_FINISH_DLIST | D2IRQCTL_CLR_FINISH_ENUM)
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define D1_VERSION ((D1_VERSION_MAJOR << 16) | D1_VERSION_MINOR)
#define D1_VERSION_STRING "v" TOSTRING(D1_VERSION_MAJOR) "." TOSTRING(D1_VERSION_MINOR)
typedef struct _d1_device_intern {
long flags;
int dlist_indirect;
volatile long *dlist_start;
} d1_device_intern;
static volatile d1_device_intern *s_isr_context;
static volatile unsigned int s_dlists_done = 0;
int d1_getversion() {
return D1_VERSION;
}
const char *d1_getversionstring() {
return D1_VERSION_STRING;
}
void d1_irq_enable() {
// Clear all interrupts and enable DLIST IRQ
D1_REG(D2_IRQCTL) = D1_IRQCTL_ENABLE;
// Configure and enable GPU interrupt
NVIC_SetPriority(GPU2D_IRQ_IRQn, IRQ_PRI_GPU);
NVIC_ClearPendingIRQ(GPU2D_IRQ_IRQn);
NVIC_EnableIRQ(GPU2D_IRQ_IRQn);
}
void d1_irq_disable() {
// Clear all interrupts and disable DLIST IRQ
D1_REG(D2_IRQCTL) = D1_IRQCTL_DISABLE;
// Disable GPU interrupt
NVIC_DisableIRQ(GPU2D_IRQ_IRQn);
NVIC_ClearPendingIRQ(GPU2D_IRQ_IRQn);
}
int d1_queryirq(d1_device *handle, int irqmask, int timeout) {
if (timeout == d1_to_no_wait) {
return 1;
}
for (; timeout > 0; --timeout) {
if (s_dlists_done) {
--s_dlists_done;
return GPU2D_IRQ_IRQn;
}
}
return 0;
}
d1_device *d1_opendevice(long flags) {
d1_device *handle = d1_allocmem(sizeof(d1_device_intern));
D1_DEV(handle)->flags = flags;
#if (D1_DLIST_INDIRECT == 1)
D1_DEV(handle)->dlist_indirect = 1;
#endif
// Enable D/AVE2D clock
REG32_SET_ONE_BIT(CLKCTL_PER_MST->PERIPH_CLK_ENA, 8);
// Enable interrupts
d1_irq_enable();
s_isr_context = handle;
return handle;
}
int d1_closedevice(d1_device *handle) {
// Disable interrupts
d1_irq_disable();
s_isr_context = NULL;
// Disable clock
REG32_CLR_ONE_BIT(CLKCTL_PER_MST->PERIPH_CLK_ENA, 8);
d1_freemem(handle);
return 1;
}
int d1_devicesupported(d1_device *handle, int deviceid) {
switch (deviceid) {
case D1_DAVE2D:
#if (D1_DLIST_INDIRECT == 1)
case D1_DLISTINDIRECT:
#endif
return 1;
default:
return 0;
}
}
long d1_getregister(d1_device *handle, int deviceid, int index) {
switch (deviceid) {
case D1_DAVE2D:
return D1_REG(index);
#if (D1_DLIST_INDIRECT == 1)
case D1_DLISTINDIRECT:
return D1_DEV(handle)->dlist_indirect;
#endif
default:
return 0;
}
}
void d1_setregister(d1_device *handle, int deviceid, int index, long value) {
switch (deviceid) {
case D1_DAVE2D:
#if (D1_DLIST_INDIRECT == 1)
if (index == D2_DLISTSTART && D1_DEV(handle)->dlist_indirect) {
long *dlist_ptr = (long *) value;
D1_DEV(handle)->dlist_start = dlist_ptr + 1;
D1_REG(index) = *dlist_ptr;
} else {
D1_REG(index) = value;
}
#else
D1_REG(index) = value;
#endif
break;
#if (D1_DLIST_INDIRECT == 1)
case D1_DLISTINDIRECT:
D1_DEV(handle)->dlist_indirect = value;
#endif
default:
break;
}
}
void *d1_allocmem(unsigned int size) {
return d0_allocmem(size);
}
void d1_freemem(void *ptr) {
d0_freemem(ptr);
}
unsigned int d1_memsize(void *ptr) {
return d0_memsize(ptr);
}
void *d1_allocvidmem(d1_device *handle, int memtype, unsigned int size) {
return d0_allocvidmem(size);
}
void d1_freevidmem(d1_device *handle, int memtype, void *ptr) {
return d0_freevidmem(ptr);
}
int d1_queryarchitecture(d1_device *handle) {
return d1_ma_unified;
}
void *d1_maptovidmem(d1_device *handle, void *ptr) {
return ptr;
}
void *d1_mapfromvidmem(d1_device *handle, void *ptr) {
return ptr;
}
int d1_copytovidmem(d1_device *handle, void *dst, const void *src, unsigned int size, int flags) {
memcpy(dst, src, size);
return 1;
}
int d1_cacheblockflush(d1_device *handle, int memtype, const void *ptr, unsigned int size) {
SCB_CleanDCache_by_Addr((void *) ptr, size);
return 1;
}
void GPU2D_IRQHandler(void) {
// Get D/AVE2D interrupt status
long dave_status = D1_REG(D2_STATUS);
// Clear all interrupts triggered
D1_REG(D2_IRQCTL) = D1_IRQCTL_CLEAR;
// Check if DLIST IRQ triggered
if (dave_status & D2C_IRQ_DLIST) {
#if (D1_DLIST_INDIRECT == 1)
if (s_isr_context->dlist_indirect
&& ((void *) *s_isr_context->dlist_start) != NULL) {
D1_REG(D2_DLISTSTART) = *s_isr_context->dlist_start;
++s_isr_context->dlist_start;
}
#endif
++s_dlists_done;
}
}

View File

@ -0,0 +1,714 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif DMA driver.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "irq.h"
#include CMSIS_MCU_H
#include "evtrtr.h"
#include "dma_op.h"
#include "dma_ctrl.h"
#include "dma_config.h"
#include "dma_opcode.h"
#include "dma_mapping.h"
#include "sys_ctrl_i2s.h"
#include "sys_ctrl_dma.h"
#include "py/mphal.h"
#include "runtime.h"
#include "alif_hal.h"
#include "alif_dma.h"
#define DMA_SHARED_INDEX (0)
#define DMA_LOCAL_INDEX (1)
#define DMA_SHARED_FILTER (0)
#define DMA_LOCAL_FILTER (0)
#define DMA_IS_LOCAL(dma) ((dma == ((DMA_Type *) DMALOCAL_SEC_BASE)) || (dma == ((DMA_Type *) DMALOCAL_NS_BASE)))
#define DMA_IS_SECURE(dma) ((dma == ((DMA_Type *) DMALOCAL_SEC_BASE)) || (dma == ((DMA_Type *) DMA0_SEC_BASE)))
#define DMA_DESCR_INDEX(dma) (DMA_IS_LOCAL(dma) ? DMA_LOCAL_INDEX : DMA_SHARED_INDEX)
#define IRQ_PRI_DMA NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 2, 0)
typedef struct {
DMA_Type *dma_inst;
bool is_local;
bool is_secure;
bool initialized;
IRQn_Type irqn;
uint32_t channels;
dma_callback_t callback[DMA_MAX_CHANNELS];
dma_config_info_t config;
} dma_descr_t;
typedef dma_channel_info_t chn_descr_t;
static dma_descr_t dma_descr_all[2] __attribute__((section(".bss.sram0")));
// DMA helper functions.
static dma_descr_t *dma_get_dma_descr(DMA_Type *inst) {
return &dma_descr_all[DMA_DESCR_INDEX(inst)];
}
static chn_descr_t *dma_get_chn_descr(DMA_Type *inst, int8_t index) {
dma_descr_t *dma_descr = dma_get_dma_descr(inst);
return &dma_descr->config.channel_thread[index].channel_info;
}
static bool dma_thread_stopped(DMA_Type *inst, uint8_t index) {
return (dma_get_channel_status(inst, index) == DMA_THREAD_STATUS_STOPPED);
}
static bool dma_thread_allocated(DMA_Type *inst, uint8_t index) {
dma_descr_t *dma_descr = dma_get_dma_descr(inst);
return dma_descr->config.channel_thread[index].in_use;
}
static void dma_event_handler(dma_descr_t *dma_descr, uint8_t event) {
uint8_t index = dma_descr->config.event_map[event];
dma_clear_interrupt(dma_descr->dma_inst, event);
// Call event callback if set.
if (dma_descr->callback[index]) {
dma_descr->callback[index](DMA_EVENT_COMPLETE);
}
}
static void dma_fault_handler(dma_descr_t *dma_descr) {
if (dma_manager_is_faulting(dma_descr->dma_inst)) {
// Requires a software reset.
}
for (uint8_t index = 0; index < DMA_MAX_CHANNELS; index++) {
if (dma_get_channel_fault_status(dma_descr->dma_inst, index)) {
dma_channel_t channel = {
.index = index,
.inst = dma_descr->dma_inst
};
// Abort channel.
dma_abort(&channel, true);
// Call event callback if set.
if (dma_descr->callback[index]) {
dma_descr->callback[index](DMA_EVENT_ABORTED);
}
}
}
}
static int dma_execute_opcode(DMA_Type *dma, const dma_opcode_buf *opcode, uint8_t channel, uint8_t dthread) {
uint8_t *opcode_buf = opcode->buf;
uint8_t opcode_len = opcode->buf_size;
dma_dbginst0_t dma_dbginst0 = { .dbginst0 = 0 };
dma_dbginst0.dbginst0_b.chn_num = channel;
dma_dbginst0.dbginst0_b.dbg_thrd = dthread;
dma_dbginst0.dbginst0_b.ins_byte0 = opcode_buf[0];
if (opcode_len > DMA_OP_1BYTE_LEN) {
dma_dbginst0.dbginst0_b.ins_byte1 = opcode_buf[1];
}
dma_dbginst1_t dma_dbginst1 = { .dbginst1 = 0 };
if (opcode_len > DMA_OP_2BYTE_LEN) {
dma_dbginst1.dbginst1_b.ins_byte2 = opcode_buf[2];
}
if (opcode_len > DMA_OP_3BYTE_LEN) {
dma_dbginst1.dbginst1_b.ins_byte3 = opcode_buf[3];
}
if (opcode_len == DMA_OP_6BYTE_LEN) {
dma_dbginst1.dbginst1_b.ins_byte4 = opcode_buf[4];
dma_dbginst1.dbginst1_b.ins_byte5 = opcode_buf[5];
}
dma_execute(dma, dma_dbginst0.dbginst0, dma_dbginst1.dbginst1);
return 0;
}
static int dma_init(dma_descr_t *dma_descr, DMA_Type *inst) {
bool is_local = DMA_IS_LOCAL(inst);
bool is_secure = DMA_IS_SECURE(inst);
uint32_t ns_mask = is_secure ? 0 : 0xFFFFFFFF;
// Reset events and chans_descr.
dma_reset_all_events(&dma_descr->config);
dma_reset_all_channels(&dma_descr->config);
// Initialize DMA
if (is_local) {
dmalocal_set_glitch_filter(DMA_LOCAL_FILTER);
dmalocal_enable_periph_clk();
evtrtrlocal_enable_dma_req();
if (is_secure) {
dmalocal_set_boot_manager_secure();
} else {
dmalocal_set_boot_manager_nonsecure();
}
dmalocal_set_boot_irq_ns_mask(ns_mask);
dmalocal_set_boot_periph_ns_mask(ns_mask);
dmalocal_reset();
} else {
dma0_set_glitch_filter(DMA_SHARED_FILTER);
dma0_enable_periph_clk();
evtrtr0_enable_dma_req();
if (is_secure) {
dma0_set_boot_manager_secure();
} else {
dma0_set_boot_manager_nonsecure();
}
dma0_set_boot_irq_ns_mask(ns_mask);
dma0_set_boot_periph_ns_mask(ns_mask);
dma0_reset();
}
dma_descr->dma_inst = inst;
dma_descr->is_local = is_local;
dma_descr->is_secure = is_secure;
dma_descr->irqn = (IRQn_Type) (is_local ? DMALOCAL_IRQ0_IRQn : DMA0_IRQ0_IRQn);
dma_descr->initialized = true;
// Configure DMA abort IRQ
NVIC_ClearPendingIRQ(dma_descr->irqn + DMA_IRQ_ABORT_OFFSET);
NVIC_SetPriority(dma_descr->irqn + DMA_IRQ_ABORT_OFFSET, IRQ_PRI_DMA);
NVIC_EnableIRQ(dma_descr->irqn + DMA_IRQ_ABORT_OFFSET);
return 0;
}
static int dma_deinit(dma_descr_t *dma_descr) {
if (!dma_descr->initialized) {
return 0;
}
// Deinitialize DMA
if (dma_descr->is_local) {
dmalocal_reset();
evtrtrlocal_disable_dma_req();
dmalocal_disable_periph_clk();
} else {
dma0_reset();
evtrtr0_disable_dma_req();
dma0_disable_periph_clk();
}
// Disable DMA abort IRQ
NVIC_DisableIRQ(dma_descr->irqn + DMA_IRQ_ABORT_OFFSET);
NVIC_ClearPendingIRQ(dma_descr->irqn + DMA_IRQ_ABORT_OFFSET);
// Clear DMA descriptor.
memset(dma_descr, 0, sizeof(dma_descr_t));
return 0;
}
int dma_deinit_all() {
dma_deinit(&dma_descr_all[0]);
dma_deinit(&dma_descr_all[1]);
return 0;
}
int dma_alloc(dma_channel_t *channel, dma_config_t *config) {
dma_descr_t *dma_descr = dma_get_dma_descr(config->inst);
chn_descr_t *chn_descr = dma_get_chn_descr(config->inst, channel->index);
// Set DMA instance.
channel->inst = config->inst;
// Initialize DMA descriptor (if not initialized yet).
if (!dma_descr->initialized) {
dma_init(dma_descr, channel->inst);
}
// Check if DMA instance is configured in a different mode.
if (dma_descr->dma_inst != channel->inst) {
return -1;
}
// Allocate a DMA channel.
if ((channel->index = dma_allocate_channel(&dma_descr->config)) < 0) {
;
return -1;
}
// Allocate a DMA event.
int8_t event;
if ((event = dma_allocate_event(&dma_descr->config, channel->index)) < 0) {
;
dma_release_channel(&dma_descr->config, channel->index);
return -1;
}
#if CORE_M55_HP
// Select DMA0 for LP peripherals on HP core.
// The HE core uses local DMA to access these.
if (!dma_descr->is_local) {
switch (config->request & 0xFF) {
case LPPDM_DMA_PERIPH_REQ:
lppdm_select_dma0();
break;
case LPI2S_DMA_TX_PERIPH_REQ:
case LPI2S_DMA_RX_PERIPH_REQ:
lpi2s_select_dma0();
break;
case LPUART_DMA_TX_PERIPH_REQ:
case LPUART_DMA_RX_PERIPH_REQ:
lpuart_select_dma0();
break;
case LPSPI_DMA_TX_PERIPH_REQ:
case LPSPI_DMA_RX_PERIPH_REQ:
lpspi_select_dma0(RTE_LPSPI_SELECT_DMA0_GROUP);
break;
}
}
#endif
// Configure event router.
if (dma_descr->is_local) {
evtrtrlocal_enable_dma_channel(config->request & 0xFF, DMA_ACK_COMPLETION_PERIPHERAL);
} else {
evtrtr0_enable_dma_channel(config->request & 0xFF, config->request >> 8, DMA_ACK_COMPLETION_PERIPHERAL);
evtrtr0_enable_dma_handshake(config->request & 0xFF, config->request >> 8);
}
// Update active channels count.
dma_descr->channels++;
// Copy channel config to internal channel descriptor.
chn_descr->flags |= config->flags;
chn_descr->desc_info.direction = config->direction;
chn_descr->desc_info.periph_num = config->request & 0xFF;
chn_descr->desc_info.src_bsize = config->burst_size;
chn_descr->desc_info.dst_bsize = config->burst_size;
chn_descr->desc_info.src_blen = config->burst_blen;
chn_descr->desc_info.dst_blen = config->burst_blen;
chn_descr->desc_info.sec_state = dma_descr->is_secure == false;
chn_descr->desc_info.dst_prot_ctrl = dma_descr->is_secure ? 0x00 : 0x02;
chn_descr->desc_info.src_prot_ctrl = dma_descr->is_secure ? 0x00 : 0x02;
chn_descr->desc_info.src_cache_ctrl = (config->direction == DMA_TRANSFER_DEV_TO_MEM) ? 0x0 : 0x02;
chn_descr->desc_info.dst_cache_ctrl = (config->direction == DMA_TRANSFER_MEM_TO_DEV) ? 0x0 : 0x02;
chn_descr->desc_info.endian_swap_size = config->byte_swap;
// Configure the DMA channel's IRQ priority.
NVIC_SetPriority(dma_descr->irqn + chn_descr->event_index, config->priority);
return 0;
}
int dma_start(dma_channel_t *channel, void *src, void *dst0, void *dst1, uint32_t size, dma_callback_t callback) {
dma_descr_t *dma_descr = dma_get_dma_descr(channel->inst);
chn_descr_t *chn_descr = dma_get_chn_descr(channel->inst, channel->index);
uint8_t go_opcode_buf[DMA_OP_6BYTE_LEN] = {0};
dma_opcode_buf go_opcode = { .off = 0, .buf = go_opcode_buf, .buf_size = DMA_OP_6BYTE_LEN };
if (dma_debug_is_busy(dma_descr->dma_inst)) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("DMA debug is busy"));
}
if (!dma_thread_stopped(dma_descr->dma_inst, channel->index)) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("DMA channel is busy"));
}
if (!dma_thread_allocated(dma_descr->dma_inst, channel->index)) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("DMA channel not allocated"));
}
if (((uint32_t) src | (uint32_t) dst0 | size)
& ((1 << (uint32_t) chn_descr->desc_info.dst_bsize) - 1)) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Unaligned DMA transfer"));
}
// Set transfer source, destination and and total size.
chn_descr->desc_info.src_addr = LocalToGlobal(src);
chn_descr->desc_info.dst_addr0 = LocalToGlobal(dst0);
chn_descr->desc_info.dst_addr1 = LocalToGlobal(dst1);
chn_descr->desc_info.total_len = size;
if (!dma_generate_opcode(&dma_descr->config, channel->index)) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Generate OP code failed"));
}
// Flush opcode buffer.
uint8_t *opcode_buf = dma_get_opcode_buf(&dma_descr->config, channel->index);
RTSS_CleanDCache_by_Addr(opcode_buf, DMA_MICROCODE_SIZE);
// Construct go microcode.
dma_construct_go(!dma_descr->is_secure, channel->index, LocalToGlobal(opcode_buf), &go_opcode);
dma_execute_opcode(dma_descr->dma_inst, &go_opcode, channel->index, DMA_THREAD_MANAGER);
// Set channel callback
dma_descr->callback[channel->index] = callback;
// Configure and enable DMA channel IRQ
IRQn_Type irqn = (IRQn_Type) (dma_descr->irqn + chn_descr->event_index);
NVIC_DisableIRQ(irqn);
NVIC_ClearPendingIRQ(irqn);
NVIC_EnableIRQ(irqn);
dma_clear_interrupt(dma_descr->dma_inst, chn_descr->event_index);
dma_enable_interrupt(dma_descr->dma_inst, chn_descr->event_index);
return 0;
}
int dma_abort(dma_channel_t *channel, bool dealloc) {
dma_descr_t *dma_descr = dma_get_dma_descr(channel->inst);
chn_descr_t *chn_descr = dma_get_chn_descr(channel->inst, channel->index);
uint8_t opcode_buf[DMA_OP_1BYTE_LEN] = { 0 };
dma_opcode_buf kill_opcode = { .off = 0, .buf = opcode_buf, .buf_size = DMA_OP_1BYTE_LEN };
if (!dma_descr->initialized ||
!dma_descr->channels ||
dma_debug_is_busy(dma_descr->dma_inst) ||
dma_thread_stopped(dma_descr->dma_inst, channel->index) ||
!dma_thread_allocated(dma_descr->dma_inst, channel->index)) {
return -1;
}
// Construct kill microcode.
dma_construct_kill(&kill_opcode);
dma_execute_opcode(dma_descr->dma_inst, &kill_opcode, channel->index, DMA_THREAD_CHANNEL);
for (size_t i = 0, max_retry = 1000; i < max_retry; i++) {
if (dma_thread_stopped(dma_descr->dma_inst, channel->index)) {
break;
}
if ((i + 1) == max_retry) {
return -1;
}
}
// Disable and clear DMA channel IRQ
IRQn_Type irqn = (IRQn_Type) (dma_descr->irqn + chn_descr->event_index);
NVIC_DisableIRQ(irqn);
NVIC_ClearPendingIRQ(irqn);
dma_disable_interrupt(dma_descr->dma_inst, chn_descr->event_index);
dma_clear_interrupt(dma_descr->dma_inst, chn_descr->event_index);
printf("DMA channel: %d event: %d stopped\n", channel->index, chn_descr->event_index);
// Deallocate channel
if (dealloc) {
dma_descr->channels--;
dma_release_event(&dma_descr->config, chn_descr->event_index);
dma_release_channel(&dma_descr->config, channel->index);
}
// Deinit DMA if last channel.
if (dma_descr->channels == 0) {
dma_deinit(dma_descr);
}
// Clear callback
dma_descr->callback[channel->index] = NULL;
return 0;
}
void *dma_target_address(dma_channel_t *channel) {
chn_descr_t *chn_descr = dma_get_chn_descr(channel->inst, channel->index);
uint32_t target = dma_get_channel_dest_addr(channel->inst, channel->index);
if (chn_descr->desc_info.dst_addr1 == 0) {
// Single buffer mode.
return GlobalToLocal(target);
} else if (target == chn_descr->desc_info.dst_addr0) {
return GlobalToLocal(chn_descr->desc_info.dst_addr1);
} else {
return GlobalToLocal(chn_descr->desc_info.dst_addr0);
}
}
// DMA0 Handlers
void DMA0_IRQ0Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 0);
}
void DMA0_IRQ1Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 1);
}
void DMA0_IRQ2Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 2);
}
void DMA0_IRQ3Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 3);
}
void DMA0_IRQ4Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 4);
}
void DMA0_IRQ5Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 5);
}
void DMA0_IRQ6Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 6);
}
void DMA0_IRQ7Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 7);
}
void DMA0_IRQ8Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 8);
}
void DMA0_IRQ9Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 9);
}
void DMA0_IRQ10Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 10);
}
void DMA0_IRQ11Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 11);
}
void DMA0_IRQ12Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 12);
}
void DMA0_IRQ13Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 13);
}
void DMA0_IRQ14Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 14);
}
void DMA0_IRQ15Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 15);
}
void DMA0_IRQ16Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 16);
}
void DMA0_IRQ17Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 17);
}
void DMA0_IRQ18Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 18);
}
void DMA0_IRQ19Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 19);
}
void DMA0_IRQ20Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 20);
}
void DMA0_IRQ21Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 21);
}
void DMA0_IRQ22Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 22);
}
void DMA0_IRQ23Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 23);
}
void DMA0_IRQ24Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 24);
}
void DMA0_IRQ25Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 25);
}
void DMA0_IRQ26Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 26);
}
void DMA0_IRQ27Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 27);
}
void DMA0_IRQ28Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 28);
}
void DMA0_IRQ29Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 29);
}
void DMA0_IRQ30Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 30);
}
void DMA0_IRQ31Handler(void) {
dma_event_handler(&dma_descr_all[DMA_SHARED_INDEX], 31);
}
void DMA0_IRQ_ABORT_Handler(void) {
dma_fault_handler(&dma_descr_all[DMA_SHARED_INDEX]);
}
// DMALOCAL Handlers
void DMALOCAL_IRQ0Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 0);
}
void DMALOCAL_IRQ1Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 1);
}
void DMALOCAL_IRQ2Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 2);
}
void DMALOCAL_IRQ3Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 3);
}
void DMALOCAL_IRQ4Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 4);
}
void DMALOCAL_IRQ5Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 5);
}
void DMALOCAL_IRQ6Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 6);
}
void DMALOCAL_IRQ7Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 7);
}
void DMALOCAL_IRQ8Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 8);
}
void DMALOCAL_IRQ9Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 9);
}
void DMALOCAL_IRQ10Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 10);
}
void DMALOCAL_IRQ11Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 11);
}
void DMALOCAL_IRQ12Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 12);
}
void DMALOCAL_IRQ13Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 13);
}
void DMALOCAL_IRQ14Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 14);
}
void DMALOCAL_IRQ15Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 15);
}
void DMALOCAL_IRQ16Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 16);
}
void DMALOCAL_IRQ17Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 17);
}
void DMALOCAL_IRQ18Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 18);
}
void DMALOCAL_IRQ19Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 19);
}
void DMALOCAL_IRQ20Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 20);
}
void DMALOCAL_IRQ21Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 21);
}
void DMALOCAL_IRQ22Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 22);
}
void DMALOCAL_IRQ23Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 23);
}
void DMALOCAL_IRQ24Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 24);
}
void DMALOCAL_IRQ25Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 25);
}
void DMALOCAL_IRQ26Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 26);
}
void DMALOCAL_IRQ27Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 27);
}
void DMALOCAL_IRQ28Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 28);
}
void DMALOCAL_IRQ29Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 29);
}
void DMALOCAL_IRQ30Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 30);
}
void DMALOCAL_IRQ31Handler(void) {
dma_event_handler(&dma_descr_all[DMA_LOCAL_INDEX], 31);
}
void DMALOCAL_IRQ_ABORT_Handler(void) {
dma_fault_handler(&dma_descr_all[DMA_LOCAL_INDEX]);
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif DMA driver.
*/
#ifndef __ALIF_DMA_H__
#define __ALIF_DMA_H__
#include <stdint.h>
#include <stdbool.h>
#include "dma_ctrl.h"
#include "dma_op.h"
#include "dma_mapping.h"
typedef enum {
DMA_BSWAP_NONE,
DMA_BSWAP_16BIT,
DMA_BSWAP_32BIT,
DMA_BSWAP_64BIT,
DMA_BSWAP_128BIT,
} dma_swap_t;
typedef enum {
DMA_BURST_SIZE_1,
DMA_BURST_SIZE_2,
DMA_BURST_SIZE_4,
DMA_BURST_SIZE_8,
DMA_BURST_SIZE_16,
} dma_burst_size_t;
typedef enum {
DMA_EVENT_ABORTED = (1 << 0),
DMA_EVENT_COMPLETE = (1 << 1),
} dma_event_t;
typedef enum {
DMA_FLAGS_SINGLE_CH = DMA_CHANNEL_FLAG_I2S_MONO_MODE,
} dma_flags_t;
typedef struct {
DMA_Type *inst;
int8_t index;
} dma_channel_t;
typedef struct {
DMA_Type *inst;
uint32_t request;
uint32_t priority;
uint8_t direction;
uint8_t burst_size;
uint8_t burst_blen;
uint8_t byte_swap;
uint32_t flags;
} dma_config_t;
typedef void (*dma_callback_t) (dma_event_t event);
int dma_deinit_all();
int dma_alloc(dma_channel_t *channel, dma_config_t *config);
int dma_start(dma_channel_t *channel, void *src, void *dst0, void *dst1, uint32_t size, dma_callback_t callback);
int dma_abort(dma_channel_t *channel, bool dealloc);
void *dma_target_address(dma_channel_t *channel);
#endif //__ALIF_DMA_H__

View File

@ -0,0 +1,532 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif HAL.
*/
#include <stdio.h>
#include <stdint.h>
#include "irq.h"
#include CMSIS_MCU_H
#include "py/mphal.h"
#include "alif_hal.h"
#include "sys_ctrl_i3c.h"
#include "sys_ctrl_spi.h"
#include "sys_ctrl_cpi.h"
#include "sys_ctrl_pdm.h"
#include "se_services.h"
#include "omv_boardconfig.h"
// Define pin objects in this file.
#define OMV_GPIO_DEFINE_PINS (1)
#include "omv_gpio.h"
#define MPU_ATTR_NORMAL_WT_RA_TRANSIENT (0)
#define MPU_ATTR_DEVICE_nGnRE (1)
#define MPU_ATTR_NORMAL_WB_RA_WA (2)
#define MPU_ATTR_NORMAL_WT_RA (3)
#define MPU_ATTR_NORMAL_NON_CACHEABLE (4)
#define MPU_REGION_SRAM0 (0)
#define MPU_REGION_SRAM1 (1)
#define MPU_REGION_HOST_PERIPHERALS (2)
#define MPU_REGION_MRAM (3)
#define MPU_REGION_OSPI_REGISTERS (4)
#define MPU_REGION_OSPI0_XIP (5)
#define MPU_REGION_OPENAMP (6)
#define MPU_REGION_FIRST_FREE (7) // Reserve the first 8 regions.
uint8_t OMV_BOARD_UID_ADDR[12];
void alif_hal_mpu_init(void);
extern int alif_npu_init(void);
extern void __fatal_error(const char *msg);
void alif_hal_init(void) {
// Disable BOR.
ANA_REG->VBAT_ANA_REG1 |= (1 << 25);
// Disable USB mux.
#if CORE_M55_HP && defined(OMV_USB_SWITCH_PIN)
omv_gpio_config(OMV_USB_SWITCH_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_USB_SWITCH_PIN, 1);
#endif
// Init services.
se_services_init();
// Configures run/off profiles and SRAM clock source.
board_early_init();
// Set board unique ID.
se_services_get_unique_id(OMV_BOARD_UID_ADDR);
// To match the USB serial.
for (size_t i = 0, j = sizeof(OMV_BOARD_UID_ADDR) - 1; i < j; i++, j--) {
OMV_BOARD_UID_ADDR[i] ^= OMV_BOARD_UID_ADDR[j];
OMV_BOARD_UID_ADDR[j] ^= OMV_BOARD_UID_ADDR[i];
OMV_BOARD_UID_ADDR[i] ^= OMV_BOARD_UID_ADDR[j];
}
// load MPU regions and enable the MPU.
alif_hal_mpu_init();
// Configure and initialize the NPU.
alif_npu_init();
// Configure and enable USB IRQs.
NVIC_ClearPendingIRQ(USB_IRQ_IRQn);
NVIC_SetPriority(USB_IRQ_IRQn, IRQ_PRI_USB);
}
int alif_hal_i2c_init(uint32_t bus_id) {
omv_gpio_t scl_pin = NULL;
omv_gpio_t sda_pin = NULL;
switch (bus_id) {
#if defined(OMV_I3C0_ID)
case OMV_I3C0_ID: {
enable_i3c_clock();
scl_pin = OMV_I3C0_SCL_PIN;
sda_pin = OMV_I3C0_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C0_ID)
case OMV_I2C0_ID: {
scl_pin = OMV_I2C0_SCL_PIN;
sda_pin = OMV_I2C0_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C1_ID)
case OMV_I2C1_ID: {
scl_pin = OMV_I2C1_SCL_PIN;
sda_pin = OMV_I2C1_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C2_ID)
case OMV_I2C2_ID: {
scl_pin = OMV_I2C2_SCL_PIN;
sda_pin = OMV_I2C2_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C3_ID)
case OMV_I2C3_ID: {
scl_pin = OMV_I2C3_SCL_PIN;
sda_pin = OMV_I2C3_SDA_PIN;
break;
}
#endif
default:
return -1;
}
omv_gpio_config(scl_pin, OMV_GPIO_MODE_ALT_OD, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_config(sda_pin, OMV_GPIO_MODE_ALT_OD, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1);
return 0;
}
int alif_hal_spi_init(uint32_t bus_id, bool nss_enable, uint32_t nss_pol) {
typedef struct {
omv_gpio_t sclk_pin;
omv_gpio_t miso_pin;
omv_gpio_t mosi_pin;
omv_gpio_t ssel_pin;
} spi_pins_t;
spi_pins_t spi_pins = { NULL, NULL, NULL, NULL };
switch (bus_id) {
#if defined(OMV_SPI0_ID)
case OMV_SPI0_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI0_SCLK_PIN, OMV_SPI0_MISO_PIN, OMV_SPI0_MOSI_PIN, OMV_SPI0_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI1_ID)
case OMV_SPI1_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI1_SCLK_PIN, OMV_SPI1_MISO_PIN, OMV_SPI1_MOSI_PIN, OMV_SPI1_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI2_ID)
case OMV_SPI2_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI2_SCLK_PIN, OMV_SPI2_MISO_PIN, OMV_SPI2_MOSI_PIN, OMV_SPI2_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI3_ID)
case OMV_SPI3_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI3_SCLK_PIN, OMV_SPI3_MISO_PIN, OMV_SPI3_MOSI_PIN, OMV_SPI3_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI4_ID)
case OMV_SPI4_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI4_SCLK_PIN, OMV_SPI4_MISO_PIN, OMV_SPI4_MOSI_PIN, OMV_SPI4_SSEL_PIN
};
enable_lpspi_clk();
break;
}
#endif
default:
return -1;
}
omv_gpio_config(spi_pins.sclk_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_MED, -1);
omv_gpio_config(spi_pins.miso_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_MED, -1);
omv_gpio_config(spi_pins.mosi_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_MED, -1);
if (nss_enable) {
omv_gpio_config(spi_pins.ssel_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MED, -1);
} else {
omv_gpio_config(spi_pins.ssel_pin, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MED, 0);
if (nss_pol == OMV_SPI_NSS_LOW) {
omv_gpio_write(spi_pins.ssel_pin, 1);
} else {
omv_gpio_write(spi_pins.ssel_pin, 0);
}
}
return 0;
}
int alif_hal_spi_deinit(uint32_t bus_id) {
typedef struct {
omv_gpio_t sclk_pin;
omv_gpio_t miso_pin;
omv_gpio_t mosi_pin;
omv_gpio_t ssel_pin;
} spi_pins_t;
spi_pins_t spi_pins = { NULL, NULL, NULL, NULL };
switch (bus_id) {
#if defined(OMV_SPI0_ID)
case OMV_SPI0_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI0_SCLK_PIN, OMV_SPI0_MISO_PIN, OMV_SPI0_MOSI_PIN, OMV_SPI0_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI1_ID)
case OMV_SPI1_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI1_SCLK_PIN, OMV_SPI1_MISO_PIN, OMV_SPI1_MOSI_PIN, OMV_SPI1_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI2_ID)
case OMV_SPI2_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI2_SCLK_PIN, OMV_SPI2_MISO_PIN, OMV_SPI2_MOSI_PIN, OMV_SPI2_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI3_ID)
case OMV_SPI3_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI3_SCLK_PIN, OMV_SPI3_MISO_PIN, OMV_SPI3_MOSI_PIN, OMV_SPI3_SSEL_PIN
};
break;
}
#endif
#if defined(OMV_SPI4_ID)
case OMV_SPI4_ID: {
spi_pins = (spi_pins_t) {
OMV_SPI4_SCLK_PIN, OMV_SPI4_MISO_PIN, OMV_SPI4_MOSI_PIN, OMV_SPI4_SSEL_PIN
};
disable_lpspi_clk();
break;
}
#endif
default:
return -1;
}
omv_gpio_deinit(spi_pins.sclk_pin);
omv_gpio_deinit(spi_pins.miso_pin);
omv_gpio_deinit(spi_pins.mosi_pin);
omv_gpio_deinit(spi_pins.ssel_pin);
return 0;
}
int alif_hal_pdm_init(uint32_t pdm_id) {
typedef struct {
omv_gpio_t clk_pin;
omv_gpio_t dat_pin;
} pdm_pins_t;
pdm_pins_t pdm_pins = { NULL, NULL };
switch (pdm_id) {
#if defined(OMV_PDM0_ID)
case OMV_PDM0_ID: {
enable_pdm_periph_clk();
pdm_pins = (pdm_pins_t) {
OMV_PDM0_C0_PIN, OMV_PDM0_D0_PIN
};
break;
}
#endif
#if defined(OMV_PDM1_ID)
case OMV_PDM1_ID: {
enable_lppdm_periph_clk();
pdm_pins = (pdm_pins_t) {
OMV_PDM1_C0_PIN, OMV_PDM1_D0_PIN
};
break;
}
#endif
default:
return -1;
}
omv_gpio_config(pdm_pins.clk_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(pdm_pins.dat_pin, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
return 0;
}
int alif_hal_pdm_deinit(uint32_t pdm_id) {
typedef struct {
omv_gpio_t clk_pin;
omv_gpio_t dat_pin;
} pdm_pins_t;
pdm_pins_t pdm_pins = { NULL, NULL };
switch (pdm_id) {
#if defined(OMV_PDM0_ID)
case OMV_PDM0_ID: {
disable_pdm_periph_clk();
pdm_pins = (pdm_pins_t) {
OMV_PDM0_C0_PIN, OMV_PDM0_D0_PIN
};
break;
}
#endif
#if defined(OMV_PDM1_ID)
case OMV_PDM1_ID: {
disable_lppdm_periph_clk();
pdm_pins = (pdm_pins_t) {
OMV_PDM1_C0_PIN, OMV_PDM1_D0_PIN
};
break;
}
#endif
default:
return -1;
}
omv_gpio_deinit(pdm_pins.clk_pin);
omv_gpio_deinit(pdm_pins.dat_pin);
return 0;
}
int alif_hal_csi_init(CPI_Type *cpi, uint32_t mode) {
if (mode == 0) {
enable_cpi_periph_clk();
} else {
enable_lpcpi_periph_clk();
}
// Configure camera sensor interface pins
omv_gpio_config(OMV_CSI_D0_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D1_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D2_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D3_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D4_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D5_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D6_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_D7_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_HSYNC_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_VSYNC_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_PXCLK_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
omv_gpio_config(OMV_CSI_MXCLK_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_HIGH, -1);
// Configure DCMI GPIOs
#if defined(OMV_CSI_RESET_PIN)
omv_gpio_config(OMV_CSI_RESET_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_DOWN, OMV_GPIO_SPEED_LOW, -1);
#endif
#if defined(OMV_CSI_FSYNC_PIN)
omv_gpio_config(OMV_CSI_FSYNC_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_DOWN, OMV_GPIO_SPEED_LOW, -1);
#endif
#if defined(OMV_CSI_POWER_PIN)
omv_gpio_config(OMV_CSI_POWER_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1);
#endif
NVIC_SetPriority(CAM_IRQ_IRQn, IRQ_PRI_CSI);
return 0;
}
void alif_hal_mpu_init(void) {
static const ARM_MPU_Region_t MPU_CONFIG_TABLE[] __STARTUP_RO_DATA_ATTRIBUTE = {
[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, MPU_ATTR_NORMAL_WT_RA_TRANSIENT)
},
[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, MPU_ATTR_NORMAL_WB_RA_WA)
},
[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, MPU_ATTR_DEVICE_nGnRE)
},
[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, MPU_ATTR_NORMAL_WT_RA)
},
[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, MPU_ATTR_DEVICE_nGnRE)
},
[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, MPU_ATTR_NORMAL_NON_CACHEABLE)
},
};
// Disable IRQs.
uint32_t primask = __get_PRIMASK();
__disable_irq();
// Disable MPU
ARM_MPU_Disable();
// Clear all regions.
for (size_t i = 0; i < (MPU->TYPE >> 8); i++) {
ARM_MPU_ClrRegion(i);
}
// Mem Attribute for 0th index
ARM_MPU_SetMemAttr(MPU_ATTR_NORMAL_WT_RA_TRANSIENT, ARM_MPU_ATTR(
/* NT=0, WB=0, RA=1, WA=0 */
ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0),
ARM_MPU_ATTR_MEMORY_(0, 0, 1, 0)));
// Mem Attribute for 1st index
ARM_MPU_SetMemAttr(MPU_ATTR_DEVICE_nGnRE, ARM_MPU_ATTR(
/* Device Memory */
ARM_MPU_ATTR_DEVICE,
ARM_MPU_ATTR_DEVICE_nGnRE));
// Mem Attribute for 2nd index
ARM_MPU_SetMemAttr(MPU_ATTR_NORMAL_WB_RA_WA, ARM_MPU_ATTR(
/* NT=1, WB=1, RA=1, WA=1 */
ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1),
ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1)));
// Mem Attribute for 3th index
ARM_MPU_SetMemAttr(MPU_ATTR_NORMAL_WT_RA, ARM_MPU_ATTR(
/* NT=1, WB=0, RA=1, WA=0 */
ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0),
ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0)));
// Mem Attribute for 4th index
ARM_MPU_SetMemAttr(MPU_ATTR_NORMAL_NON_CACHEABLE, ARM_MPU_ATTR(
ARM_MPU_ATTR_NON_CACHEABLE,
ARM_MPU_ATTR_NON_CACHEABLE));
// Load the MPU regions.
ARM_MPU_Load(0, MPU_CONFIG_TABLE, sizeof(MPU_CONFIG_TABLE) / sizeof(ARM_MPU_Region_t));
// Configure MPU regions.
typedef struct {
uint32_t addr;
uint32_t size;
} dma_memory_table_t;
// Start after the last region used by the startup code.
uint8_t region_number = MPU_REGION_FIRST_FREE;
extern const dma_memory_table_t _dma_memory_table_start;
extern const dma_memory_table_t _dma_memory_table_end;
for (dma_memory_table_t const *buf = &_dma_memory_table_start; buf < &_dma_memory_table_end; buf++) {
uint32_t region_base = buf->addr;
uint32_t region_size = buf->size;
if (region_size) {
MPU->RNR = region_number++;
MPU->RBAR = ARM_MPU_RBAR(region_base, ARM_MPU_SH_NON, 0, 1, 0); // RO-0, NP-1, XN-0
MPU->RLAR = ARM_MPU_RLAR(region_base + region_size - 1, MPU_ATTR_NORMAL_NON_CACHEABLE);
}
}
// Re-enable MPU
ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
// Re-enable IRQs.
__set_PRIMASK(primask);
}
// Bypass startup MPU defaults.
void MPU_Setup(void) {
}
// Bypass startup MPU defaults.
void MPU_Load_Regions(void) {
}
// Used by MicroPython VFS.
void mpu_config_mram(bool read_only) {
// Disable IRQs.
uint32_t primask = __get_PRIMASK();
__disable_irq();
// Disable MPU
ARM_MPU_Disable();
MPU->RNR = 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, MPU_ATTR_NORMAL_WT_RA);
// Re-enable MPU
ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
// Re-enable IRQs.
__set_PRIMASK(primask);
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ALIF HAL.
*/
#ifndef __ALIF_HAL_H__
#define __ALIF_HAL_H__
#include "cpi.h"
void alif_hal_init(void);
int alif_hal_i2c_init(uint32_t bus_id);
int alif_hal_spi_init(uint32_t bus_id, bool nss_enable, uint32_t nss_pol);
int alif_hal_spi_deinit(uint32_t bus_id);
int alif_hal_pdm_init(uint32_t pdm_id);
int alif_hal_pdm_deinit(uint32_t pdm_id);
int alif_hal_csi_init(CPI_Type *cpi, uint32_t mode);
#endif //__ALIF_HAL_H__

View File

@ -0,0 +1,213 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* EthosU HAL layer.
*/
#include <stdio.h>
#include "irq.h"
#include CMSIS_MCU_H
#include "py/mphal.h"
#include "alif_hal.h"
#include "omv_boardconfig.h"
#include "ethosu_driver.h"
#define ETHOSU_SEC_ENABLED (1)
#define ETHOSU_PRIV_ENABLED (1)
#define ETHOSU_CACHE_ALIGN (16)
//#define ETHOSU_CACHE_SIZE (393216U)
#define ETHOSU_CACHE_SIZE (0)
#define ETHOSU_CACHE_SECTION section(".bss.ethosu_cache")
#define ETHOSU_CACHE_ATTRIBUTE __attribute__((aligned(ETHOSU_CACHE_ALIGN), ETHOSU_CACHE_SECTION))
#define IRQ_PRI_NPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0)
#if CORE_M55_HP
#define ETHOSU_IRQ_NUMBER NPU_HP_IRQ_IRQn
#define ETHOSU_IRQ_HANDLER NPU_HP_IRQHandler
#define ETHOSU_BASE_ADDRESS (void *) NPU_HP_BASE
#else
#define ETHOSU_IRQ_NUMBER NPU_HE_IRQ_IRQn
#define ETHOSU_IRQ_HANDLER NPU_HE_IRQHandler
#define ETHOSU_BASE_ADDRESS (void *) NPU_HE_BASE
#endif
#if (ETHOSU_CACHE_SIZE > 0)
static uint8_t ETHOSU_CACHE_BUFFER[ETHOSU_CACHE_SIZE] ETHOSU_CACHE_ATTRIBUTE;
#else
static uint8_t *ETHOSU_CACHE_BUFFER = NULL;
#endif
static struct ethosu_driver ethosu_driver;
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 },
{ .base = OSPI0_XIP_BASE, .limit = OSPI0_XIP_BASE + OSPI0_XIP_SIZE },
{ .base = OSPI1_XIP_BASE, .limit = OSPI1_XIP_BASE + OSPI1_XIP_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 ethosu_semaphore_t {
uint8_t count;
};
void *ethosu_semaphore_create(void) {
// The default implementation allocates this dynamically,
// but there's no need for more than one semaphore, so a
// static one can be used.
static struct ethosu_semaphore_t sem;
sem.count = 0;
return &sem;
}
int ethosu_semaphore_take(void *sem, uint64_t timeout) {
struct ethosu_semaphore_t *s = sem;
while (s->count == 0) {
__WFE();
}
s->count--;
return 0;
}
int ethosu_semaphore_give(void *sem) {
struct ethosu_semaphore_t *s = sem;
s->count++;
__SEV();
return 0;
}
void ethosu_semaphore_destroy(void *sem) {
// The Semaphore is statically allocated.
}
void ethosu_flush_dcache(uint32_t *p, size_t bytes) {
if (!p) {
SCB_CleanDCache();
} else if (is_cacheable(p, bytes)) {
SCB_CleanDCache_by_Addr(p, bytes);
}
}
void ethosu_invalidate_dcache(uint32_t *p, size_t bytes) {
if (!p) {
SCB_CleanInvalidateDCache();
} else if (is_cacheable(p, bytes)) {
SCB_InvalidateDCache_by_Addr(p, bytes);
}
}
uint64_t ethosu_address_remap(uint64_t address, int index) {
return LocalToGlobal((void *) (uint32_t) address);
}
void ethosu_inference_begin(struct ethosu_driver *drv, void *user_arg) {
}
void ethosu_inference_end(struct ethosu_driver *drv, void *user_arg) {
}
void ETHOSU_IRQ_HANDLER(void) {
ethosu_irq_handler(&ethosu_driver);
}
int alif_npu_init(void) {
// Initialize Ethos-U.
if (ethosu_init(&ethosu_driver, /* Ethos-U driver device pointer */
ETHOSU_BASE_ADDRESS, /* Ethos-U NPU's base address. */
ETHOSU_CACHE_BUFFER, /* Pointer to fast mem area - NULL for U55. */
ETHOSU_CACHE_SIZE, /* Fast mem region size. */
ETHOSU_SEC_ENABLED, /* Security enable. */
ETHOSU_PRIV_ENABLED)) {
/* Privilege enable. */
return -1;
}
NVIC_SetPriority(ETHOSU_IRQ_NUMBER, IRQ_PRI_NPU);
NVIC_ClearPendingIRQ(ETHOSU_IRQ_NUMBER);
NVIC_EnableIRQ(ETHOSU_IRQ_NUMBER);
return 0;
}
int alif_npu_deinit(void) {
ethosu_soft_reset(&ethosu_driver);
ethosu_deinit(&ethosu_driver);
// Disable NPU interrupt
NVIC_DisableIRQ(ETHOSU_IRQ_NUMBER);
NVIC_ClearPendingIRQ(ETHOSU_IRQ_NUMBER);
return 0;
}
int alif_npu_info() {
struct ethosu_hw_info hw_info;
ethosu_get_hw_info(&ethosu_driver, &hw_info);
struct ethosu_driver_version driver_version;
ethosu_get_driver_version(&driver_version);
printf("Ethos-U version info:\n");
printf("\tArch: %ld %ld %ld \n",
hw_info.version.arch_major_rev,
hw_info.version.arch_minor_rev,
hw_info.version.arch_patch_rev);
printf("\tDriver: %d %d %d\n",
driver_version.major,
driver_version.minor,
driver_version.patch);
printf("\tMACs/cc: %ld \n", (uint32_t) (1 << hw_info.cfg.macs_per_cc));
printf("\tCmd stream: %ld \n", hw_info.cfg.cmd_stream_version);
return 0;
}

File diff suppressed because it is too large Load Diff

280
src/omv/ports/alif/main.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "py/compile.h"
#include "py/runtime.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "py/stackctrl.h"
#include "shared/readline/readline.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/pyexec.h"
#include "shared/runtime/softtimer.h"
#include "tusb.h"
#include "mpuart.h"
#include "ospi_flash.h"
#include "pendsv.h"
#include "se_services.h"
#include "system_tick.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#if MICROPY_PY_LWIP
#include "lwip/init.h"
#include "lwip/apps/mdns.h"
#endif
#if MICROPY_PY_NETWORK
#include "extmod/modnetwork.h"
#endif
#if MICROPY_PY_BLUETOOTH
#include "mpbthciport.h"
#include "extmod/modbluetooth.h"
#endif
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#endif
#include "omv_boardconfig.h"
#include "framebuffer.h"
#include "usbdbg.h"
#include "tinyusb_debug.h"
#include "fb_alloc.h"
#include "file_utils.h"
#include "mp_utils.h"
#include "omv_csi.h"
#include "omv_gpu.h"
#include "alif_hal.h"
#include "alif_dma.h"
#include "py_audio.h"
#include "py_imu.h"
NORETURN void __fatal_error(const char *msg);
int main(void) {
bool first_soft_reset = true; (void) first_soft_reset;
system_tick_init();
alif_hal_init();
pendsv_init();
#if MICROPY_HW_ENABLE_UART_REPL
mp_uart_init_repl();
#endif
#if MICROPY_HW_ENABLE_OSPI
ospi_flash_init();
#endif
soft_reset:
// Initialise stack extents and GC heap.
extern uint8_t _estack, _sstack, _heap_start, _heap_end;
mp_init_gc_stack(&_sstack, &_estack, &_heap_start, &_heap_end, 1024);
// Initialise MicroPython runtime.
mp_init();
// Initialise sub-systems.
readline_init0();
fb_alloc_init0();
framebuffer_init0();
#if MICROPY_PY_CSI
omv_csi_init0();
#endif
#ifdef IMLIB_ENABLE_IMAGE_FILE_IO
file_buffer_init0();
#endif
#if CORE_M55_HP
usbdbg_init();
#endif
#if MICROPY_PY_IMU
py_imu_init();
#endif
imlib_init_all();
#if MICROPY_PY_CSI
if (first_soft_reset) {
omv_csi_init();
}
#endif
#if MICROPY_PY_LWIP
// lwIP can only be initialized once, because the system timeout
// list (next_timeout), is only ever reset by BSS clearing.
if (first_soft_reset) {
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
#if MICROPY_PY_NETWORK
mod_network_init();
#endif
// TODO: _boot on HP uses OSPI which is disabled right now.
#if MICROPY_HW_ENABLE_OSPI || CORE_M55_HE
// Execute _boot.py.
pyexec_frozen_module("_boot.py", false);
#endif
#if MICROPY_HW_ENABLE_OSPI
// Mark the filesystem as an OpenMV storage.
file_ll_touch(".openmv_disk");
#endif
// Initialize TinyUSB after the filesystem is mounted.
#if MICROPY_HW_ENABLE_USBDEV
if (!tusb_inited()) {
tusb_init();
}
#endif
// Run boot.py script.
bool interrupted = mp_exec_bootscript("boot.py", true);
// Run main.py script on first soft-reset.
if (first_soft_reset && !interrupted && mp_vfs_import_stat("main.py")) {
mp_exec_bootscript("main.py", true);
goto soft_reset_exit;
}
#if CORE_M55_HE
goto soft_reset_exit;
#endif
// If there's no script ready, just re-exec REPL
while (!usbdbg_script_ready()) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
// enable IDE interrupt
usbdbg_set_irq_enabled(true);
// run REPL
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
if (pyexec_raw_repl() != 0) {
break;
}
} else {
if (pyexec_friendly_repl() != 0) {
break;
}
}
nlr_pop();
}
}
if (usbdbg_script_ready()) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
// Enable IDE interrupt
usbdbg_set_irq_enabled(true);
// Execute the script.
pyexec_str(usbdbg_get_script(), true);
// Disable IDE interrupts
usbdbg_set_irq_enabled(false);
nlr_pop();
} else {
mp_obj_print_exception(&mp_plat_print, (mp_obj_t) nlr.ret_val);
}
}
soft_reset_exit:
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
#if MICROPY_PY_AUDIO
py_audio_deinit();
#endif
#if MICROPY_PY_BLUETOOTH
mp_bluetooth_deinit();
#endif
#if MICROPY_PY_NETWORK
mod_network_deinit();
#endif
#if MICROPY_PY_NETWORK_CYW43
cyw43_deinit(&cyw43_state);
#endif
imlib_deinit_all();
soft_timer_deinit();
dma_deinit_all();
gc_sweep_all();
mp_deinit();
first_soft_reset = false;
goto soft_reset;
}
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();
}
}
NORETURN void __fatal_error(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++) {
}
}
}
#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);
__fatal_error("Assertion failed");
}
#endif

View File

@ -0,0 +1,454 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Audio Python module.
*/
#if MICROPY_PY_AUDIO
#include <stdio.h>
#include "py/obj.h"
#include "py/objarray.h"
#include "py/nlr.h"
#include "py/mphal.h"
#include "py/binary.h"
#include "runtime.h"
#include "py_assert.h"
#include "py_helper.h"
#include "fb_alloc.h"
#include "omv_boardconfig.h"
#include "omv_common.h"
#include "omv_gpio.h"
#include "sys_ctrl_pdm.h"
#include "pdm.h"
#include "alif_dma.h"
#include "alif_hal.h"
#define PCM_DEFAULT_SAMPLES (512)
#define PDM_DEFAULT_BUFFERS (16) // Number of PCM samples buffers.
#define PDM_FIFO_LEVEL (5)
#define PDM_MODE_16K (0x04UL)
#define PDM_MODE_32K (0x05UL)
#define PDM_MODE_48K (0x07UL)
#define PDM_MODE_96K (0x08UL)
#define IRQ_PRI_PDM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0)
typedef struct _audio_state_t {
uint32_t n_samples;
uint32_t n_buffers;
uint32_t t_samples;
uint32_t buffer_size;
int16_t *pcm_buffer;
int16_t *dma_buffer[2];
mp_obj_array_t *user_buffer;
volatile uint32_t head;
volatile uint32_t tail;
volatile bool streaming;
volatile bool overflow;
bool abort_on_overflow;
mp_obj_t user_callback;
PDM_Type *pdm_inst;
dma_channel_t dma_channel;
} audio_state_t;
typedef struct pdm_descr {
PDM_Type *pdm_inst;
DMA_Type *dma_inst;
uint16_t dma_request;
bool is_lp;
} pdm_descr_t;
static const pdm_descr_t pdm_descr_all[] = {
#if defined(OMV_PDM0_ID)
{ (PDM_Type *) PDM_BASE, (DMA_Type *) DMA0_NS_BASE, (PDM_DMA_GROUP << 8) | PDM_DMA_PERIPH_REQ, false },
#else
{ NULL, NULL, 0, false },
#endif
#if defined(OMV_PDM1_ID)
#if CORE_M55_HP
{ (PDM_Type *) LPPDM_BASE, (DMA_Type *) DMA0_NS_BASE, (LPPDM_DMA_GROUP << 8) | LPPDM_DMA_PERIPH_REQ, true },
#else
{ (PDM_Type *) LPPDM_BASE, (DMA_Type *) DMALOCAL_NS_BASE, (LPPDM_DMA_GROUP << 8) | LPPDM_DMA_PERIPH_REQ, true },
#endif
#else
{ NULL, NULL, 0, false },
#endif
};
#define audio_state MP_STATE_PORT(_audio_state)
#define NEXT_BUFFER(x) (((x) + 1) % (audio_state->n_buffers))
static bool audio_initialized = false;
static mp_sched_node_t audio_task_sched_node;
static volatile bool audio_task_scheduled = false;
static void audio_task_callback(mp_sched_node_t *node);
// Note two separate buffers instead of an array so that each is cache-aligned.
// An extra sample must be added due to the way DMA transfer works in mono channel mode.
// TODO we can add 4 to align the next buffer.
static int32_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(DMA_BUFFER0[PCM_DEFAULT_SAMPLES + 1], 32), ".bss.sram0");
static int32_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(DMA_BUFFER1[PCM_DEFAULT_SAMPLES + 1], 32), ".bss.sram0");
void PDM_ERROR_IRQHandler(void) {
pdm_error_detect_irq_handler(audio_state->pdm_inst);
}
uint16_t gain_to_u84(float gain_db) {
// Convert gain from dB to linear scale: gain = 10^(gain_db / 20)
float gain_linear = powf(10.0f, gain_db / 20.0f);
// Multiply by 16 to convert to 8.4 format (shifting 4 bits to the left)
return (uint16_t) (gain_linear * 16.0f);
}
static inline int16_t *audio_get_buffer(size_t index) {
return audio_state->pcm_buffer + (index * audio_state->n_samples);
}
static void dma_transfer_callback(dma_event_t event) {
if (event & DMA_EVENT_ABORTED) {
dma_abort(&audio_state->dma_channel, true);
}
if (event & DMA_EVENT_COMPLETE) {
int16_t *pcm_buffer = audio_get_buffer(audio_state->head);
int32_t *dma_buffer = dma_target_address(&audio_state->dma_channel);
// Invalidate DMA buffer and copy to PCM buffer.
SCB_InvalidateDCache_by_Addr(dma_buffer, audio_state->buffer_size);
for (size_t i = 0; i < audio_state->n_samples; i++) {
pcm_buffer[i] = dma_buffer[i] >> 16;
}
if (NEXT_BUFFER(audio_state->head) != audio_state->tail) {
// Advance buffer.
audio_state->head = NEXT_BUFFER(audio_state->head);
audio_state->t_samples += audio_state->n_samples;
} else {
// Use current buffer and set overflow flag.
audio_state->overflow = true;
}
// Schedule user callback
if (audio_state->user_callback != mp_const_none) {
mp_sched_schedule_node(&audio_task_sched_node, audio_task_callback);
}
}
}
static void audio_task_callback(mp_sched_node_t *node) {
if (audio_state->streaming == false) {
return;
}
if (audio_state->tail != audio_state->head) {
audio_state->user_buffer->items = audio_get_buffer(audio_state->tail);
// Advance tail to next buffer.
audio_state->tail = NEXT_BUFFER(audio_state->tail);
// Call user callback.
mp_call_function_1(audio_state->user_callback, MP_OBJ_FROM_PTR(audio_state->user_buffer));
} else if (audio_state->overflow == true && audio_state->abort_on_overflow) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Audio buffer overflow."));
}
}
static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_channels, ARG_frequency, ARG_gain_db, ARG_buffers, ARG_samples, ARG_overflow, ARG_highpass };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_channels, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = OMV_PDM_CHANNELS } },
{ MP_QSTR_frequency, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16000 } },
{ MP_QSTR_gain_db, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 24 } },
{ MP_QSTR_buffers, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PDM_DEFAULT_BUFFERS} },
{ MP_QSTR_samples, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_overflow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = true} },
{ MP_QSTR_highpass, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = false } },
};
// 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);
// Read Args.
uint32_t n_channels = args[ARG_channels].u_int;
uint32_t frequency = args[ARG_frequency].u_int;
int32_t gain_db = args[ARG_gain_db].u_int;
uint16_t gain_u84 = gain_to_u84((float) gain_db);
// Sanity checks
if (frequency != 16000 && frequency != 32000 &&
frequency != 48000 && frequency != 96000) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid frequency!"));
}
if (n_channels != 1 && n_channels > OMV_PDM_CHANNELS) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid number of channels!"));
}
// Default/max PCM buffer size;
uint32_t n_samples = PCM_DEFAULT_SAMPLES;
if (args[ARG_samples].u_int > 0) {
if (args[ARG_samples].u_int % 16 != 0 ||
args[ARG_samples].u_int > n_samples) {
mp_raise_msg_varg(&mp_type_ValueError,
MP_ERROR_TEXT("Invalid number of samples. The number of samples" \
"must be a multiple of 16 and a maximum of %d"),
n_samples);
}
n_samples = args[ARG_samples].u_int;
}
audio_state = m_new_obj(audio_state_t);
memset(audio_state, 0, sizeof(audio_state_t));
const pdm_descr_t *pdm_descr = &pdm_descr_all[OMV_PDM_ID];
audio_state->pdm_inst = pdm_descr->pdm_inst;
audio_state->user_callback = mp_const_none;
audio_state->abort_on_overflow = args[ARG_overflow].u_int;
audio_state->n_samples = n_samples * n_channels;
audio_state->n_buffers = args[ARG_buffers].u_int;
audio_state->buffer_size = n_samples * sizeof(int32_t);
audio_state->dma_buffer[0] = (void *) DMA_BUFFER0;
audio_state->dma_buffer[1] = (void *) DMA_BUFFER1;
audio_state->pcm_buffer = m_new(int16_t, audio_state->n_samples * audio_state->n_buffers);
// Instead of returning a new heap object for every buffer, which will result in heap
// fragmentation, reuse the same object and point to PCM buffers returned to the user.
audio_state->user_buffer = mp_obj_new_bytearray_by_ref(audio_state->n_samples * sizeof(int16_t),
audio_state->pcm_buffer);
if (audio_state->pcm_buffer == NULL || audio_state->user_buffer == NULL) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to allocate memory for PCM buffers."));
}
// Initialize GPIOs and clocks.
alif_hal_pdm_init(OMV_PDM_ID);
// Disable and reset PDM state.
pdm_clear_modes(audio_state->pdm_inst);
pdm_clear_channel(audio_state->pdm_inst);
pdm_disable_error_irq(audio_state->pdm_inst);
pdm_enable_fifo_clear(audio_state->pdm_inst);
// Enable and configure PDM channels
pdm_enable_multi_ch(audio_state->pdm_inst, PDM_CHANNEL_3);
// Configure PDM mode.
switch (frequency) {
case 16000:
pdm_enable_modes(audio_state->pdm_inst, PDM_MODE_16K);
break;
case 32000:
pdm_enable_modes(audio_state->pdm_inst, PDM_MODE_32K);
break;
case 48000:
pdm_enable_modes(audio_state->pdm_inst, PDM_MODE_48K);
break;
default:
pdm_enable_modes(audio_state->pdm_inst, PDM_MODE_96K);
}
// Set gain
pdm_set_ch_gain(audio_state->pdm_inst, 3, gain_u84);
// Store the FIR/IIR coefficient if enabled.
if (!args[ARG_highpass].u_int) {
pdm_bypass_iir(audio_state->pdm_inst, true);
pdm_bypass_fir(audio_state->pdm_inst, true);
} else {
uint32_t ch_iir_coef = 0x00000004;
uint32_t ch_fir_coef[18] = {
0x000FFFFA, 0x000FFFFA, 0x00000000,
0x00000005, 0x000FFFE4, 0x000FFFA8,
0x000FFFCC, 0x00000096, 0x0000016F,
0x0000016F, 0x00000096, 0x000FFFCC,
0x000FFFA8, 0x000FFFE4, 0x00000005,
0x00000000, 0x000FFFFA, 0x000FFFFA
};
pdm_set_fir_coeff(audio_state->pdm_inst, 3, ch_fir_coef);
pdm_set_ch_iir_coef(audio_state->pdm_inst, 3, ch_iir_coef);
}
// Enable the DMA handshake.
pdm_dma_handshake(pdm_descr->pdm_inst, true);
// Set FIFO watermark level.
pdm_set_fifo_watermark(audio_state->pdm_inst, PDM_FIFO_LEVEL);
// Allocate and configure a DMA channel for PDM
dma_config_t dma_config;
dma_config.inst = pdm_descr->dma_inst;
dma_config.request = pdm_descr->dma_request;
dma_config.priority = IRQ_PRI_PDM;
dma_config.direction = DMA_TRANSFER_DEV_TO_MEM;
dma_config.burst_size = DMA_BURST_SIZE_4;
dma_config.burst_blen = 1;
dma_config.byte_swap = DMA_BSWAP_NONE;
dma_config.flags = DMA_FLAGS_SINGLE_CH;
if (dma_alloc(&audio_state->dma_channel, &dma_config) != 0) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to allocate DMA channel"));
}
// Start the DMA channel. Transfer will not start until later.
if (dma_start(&audio_state->dma_channel,
(void *) pdm_get_ch2_3_addr(audio_state->pdm_inst),
audio_state->dma_buffer[0], audio_state->dma_buffer[1],
audio_state->buffer_size, dma_transfer_callback) != 0) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to start the DMA"));
}
// Configure and enable PDM IRQs.
NVIC_ClearPendingIRQ(PDM_ERROR_IRQ_IRQn);
NVIC_EnableIRQ(PDM_ERROR_IRQ_IRQn);
audio_initialized = true;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_audio_init_obj, 0, py_audio_init);
void py_audio_deinit() {
if (audio_initialized) {
// Disable NVIC IRQs.
NVIC_DisableIRQ(PDM_ERROR_IRQ_IRQn);
NVIC_ClearPendingIRQ(PDM_ERROR_IRQ_IRQn);
// Abort DMA channel.
dma_abort(&audio_state->dma_channel, true);
// De-initialize PDM.
pdm_clear_modes(audio_state->pdm_inst);
pdm_clear_channel(audio_state->pdm_inst);
pdm_disable_error_irq(audio_state->pdm_inst);
pdm_enable_fifo_clear(audio_state->pdm_inst);
pdm_dma_handshake(audio_state->pdm_inst, false);
// De-initialize clocks and GPIOs.
alif_hal_pdm_init(OMV_PDM_ID);
memset(audio_state, 0, sizeof(audio_state_t));
}
audio_state = MP_OBJ_NULL;
audio_initialized = false;
}
static mp_obj_t py_audio_start_streaming(mp_obj_t callback_obj) {
audio_state->head = 0;
audio_state->tail = 0;
audio_state->t_samples = 0;
audio_state->overflow = false;
audio_state->streaming = true;
if (mp_obj_is_callable(callback_obj)) {
audio_state->user_callback = callback_obj;
} else {
audio_state->user_callback = mp_const_none;
}
pdm_disable_fifo_clear(audio_state->pdm_inst);
pdm_dma_enable_irq(audio_state->pdm_inst);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_audio_start_streaming_obj, py_audio_start_streaming);
static mp_obj_t py_audio_stop_streaming() {
if (audio_state->streaming) {
audio_state->streaming = false;
for (mp_uint_t start = mp_hal_ticks_ms();
(mp_hal_ticks_ms() - start) < 100;
mp_hal_delay_ms(10)) {
;
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_audio_stop_streaming_obj, py_audio_stop_streaming);
static mp_obj_t py_audio_get_buffer(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_timeout
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
// 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);
if (audio_state->streaming == false) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Audio streaming is not enabled."));
}
if (audio_state->overflow == true && audio_state->abort_on_overflow) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Audio buffer overflow."));
}
if (mp_obj_is_callable(audio_state->user_callback)) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("Audio streaming with callback function is enabled."));
}
for (mp_uint_t start = mp_hal_ticks_ms(); (audio_state->tail == audio_state->head);) {
if (args[ARG_timeout].u_int && (mp_hal_ticks_ms() - start) >= args[ARG_timeout].u_int) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("Timeout waiting for audio buffer."));
}
}
audio_state->user_buffer->items = audio_get_buffer(audio_state->tail);;
// Advance head to next buffer.
audio_state->tail = NEXT_BUFFER(audio_state->tail);
// Return PCM buffer.
return MP_OBJ_FROM_PTR(audio_state->user_buffer);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_audio_get_buffer_obj, 0, py_audio_get_buffer);
static const mp_rom_map_elem_t globals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audio) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&py_audio_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_start_streaming), MP_ROM_PTR(&py_audio_start_streaming_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_streaming), MP_ROM_PTR(&py_audio_stop_streaming_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_buffer), MP_ROM_PTR(&py_audio_get_buffer_obj) },
};
static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
const mp_obj_module_t audio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_t) &globals_dict,
};
MP_REGISTER_MODULE(MP_QSTR_audio, audio_module);
MP_REGISTER_ROOT_POINTER(struct _audio_state_t *_audio_state);
#endif //MICROPY_PY_AUDIO

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Audio Python module.
*/
#ifndef __PY_AUDIO_H__
#define __PY_AUDIO_H__
void py_audio_deinit();
#endif // __PY_AUDIO_H__

View File

@ -0,0 +1,511 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif CSI driver.
*/
#if MICROPY_PY_CSI
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "py/mphal.h"
#include "alif_hal.h"
#include "cpi.h"
#include "sys_ctrl_cpi.h"
#include "system_utils.h"
#include "omv_boardconfig.h"
#include "omv_gpio.h"
#include "omv_gpu.h"
#include "omv_i2c.h"
#include "omv_csi.h"
#include "framebuffer.h"
#include "unaligned_memcpy.h"
// Bits missing from cpi.h
#define CAM_CFG_INTERFACE_Pos (0U)
#define CAM_CFG_CSI_HALT_EN_Pos (1U)
#define CAM_CFG_RW_ROUNDUP_Pos (8U)
#define CAM_CFG_PXCLK_POL_Pos (12U)
#define CAM_CFG_ENDIANNESS_Pos (20U)
#define CPI_VSYNC_MODE_DISABLE (0)
#define CPI_VSYNC_MODE_ENABLE (1)
#define CPI_VSYNC_WAIT_DISABLE (0)
#define CPI_VSYNC_WAIT_ENABLE (1)
#define CPI_IRQ_FLAGS (CAM_INTR_STOP | CAM_INTR_VSYNC | CAM_INTR_HSYNC | \
CAM_INTR_INFIFO_OVERRUN | CAM_INTR_OUTFIFO_OVERRUN | \
CAM_INTR_BRESP_ERR)
#define CPI_ERROR_FLAGS (CAM_INTR_INFIFO_OVERRUN | \
CAM_INTR_OUTFIFO_OVERRUN | \
CAM_INTR_BRESP_ERR)
// csi struct.
omv_csi_t csi = {};
static CPI_Type *cpi_get_base_addr(omv_csi_t *csi) {
return ((CPI_Type *) CPI_BASE);
}
void omv_csi_init0() {
omv_csi_abort(true, false);
// Re-init I2C to reset the bus state after soft reset, which
// could have interrupted the bus in the middle of a transfer.
if (csi.i2c_bus.initialized) {
// Reinitialize the bus using the last used id and speed.
// TODO: Causes Alif's I3C to lock up.
//omv_i2c_init(&csi.i2c_bus, csi.i2c_bus.id, csi.i2c_bus.speed);
}
csi.disable_delays = false;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
}
int omv_csi_init() {
int init_ret = 0;
CPI_Type *cpi = cpi_get_base_addr(&csi);
alif_hal_csi_init(cpi, 0);
#if defined(OMV_CSI_POWER_PIN)
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
#endif
#if defined(OMV_CSI_RESET_PIN)
omv_gpio_write(OMV_CSI_RESET_PIN, 0);
#endif
// Reset the csi state
memset(&csi, 0, sizeof(omv_csi_t));
// Set default snapshot function.
// Some sensors need to call snapshot from init.
csi.snapshot = omv_csi_snapshot;
// Configure the CSI external clock.
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
// Failed to initialize the csi clock.
return OMV_CSI_ERROR_TIM_INIT_FAILED;
}
// Detect and initialize the image csi.
if ((init_ret = omv_csi_probe_init(OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED)) != 0) {
// csi probe/init failed.
return init_ret;
}
// Configure the CSI interface.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
// CSI config failed
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
// Set default color palette.
csi.color_palette = rainbow_table;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
// All good!
csi.detected = true;
return 0;
}
int omv_csi_config(omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) {
CPI_Type *cpi = cpi_get_base_addr(&csi);
// Configure the FIFO.
cpi->CAM_FIFO_CTRL &= ~CAM_FIFO_CTRL_RD_WMARK_Msk;
cpi->CAM_FIFO_CTRL = 0x08;
cpi->CAM_FIFO_CTRL &= ~CAM_FIFO_CTRL_WR_WMARK_Msk;
cpi->CAM_FIFO_CTRL |= (0x18 << CAM_FIFO_CTRL_WR_WMARK_Pos);
cpi->CAM_CFG = 0;
// Configure the capture interface (CPI, LPCPI or CSI).
cpi->CAM_CFG |= (CPI_INTERFACE_PARALLEL << CAM_CFG_INTERFACE_Pos);
cpi->CAM_CFG |= (CPI_VSYNC_MODE_DISABLE << CAM_CFG_VSYNC_MODE_Pos);
cpi->CAM_CFG |= (CPI_VSYNC_WAIT_DISABLE << CAM_CFG_VSYNC_WAIT_Pos);
// Set VSYNC, HSYNC and PIXCLK polarities.
cpi->CAM_CFG |= (csi.vsync_pol << CAM_CFG_VSYNC_POL_Pos);
cpi->CAM_CFG |= (csi.hsync_pol << CAM_CFG_HSYNC_POL_Pos);
cpi->CAM_CFG |= (!csi.pixck_pol << CAM_CFG_PXCLK_POL_Pos);
// Configure the data bus width, mode, endianness.
cpi->CAM_CFG |= (CPI_ROW_ROUNDUP_DISABLE << CAM_CFG_RW_ROUNDUP_Pos);
cpi->CAM_CFG |= (CPI_DATA_MODE_BIT_8 << CAM_CFG_DATA_MODE_Pos);
cpi->CAM_CFG |= (CPI_CODE10ON8_CODING_DISABLE << CAM_CFG_CODE10ON8_Pos);
cpi->CAM_CFG |= (CPI_DATA_ENDIANNESS_LSB_FIRST << CAM_CFG_ENDIANNESS_Pos);
// Configure the data mask (for 16-bits mode only).
cpi->CAM_CFG &= ~CAM_CFG_DATA_MASK_Msk;
cpi->CAM_CFG |= (0 << CAM_CFG_DATA_MASK_Pos);
// Configure IPI color mode (for CSI mode only).
cpi->CAM_CSI_CMCFG = CPI_COLOR_MODE_CONFIG_IPI48_RGB565;
}
return 0;
}
int omv_csi_abort(bool fifo_flush, bool in_irq) {
CPI_Type *cpi = cpi_get_base_addr(&csi);
cpi->CAM_CTRL = 0;
NVIC_DisableIRQ(CAM_IRQ_IRQn);
cpi_disable_interrupt(cpi, CPI_IRQ_FLAGS);
cpi_irq_handler_clear_intr_status(cpi, CPI_IRQ_FLAGS);
csi.first_line = false;
csi.drop_frame = false;
csi.last_frame_ms = 0;
csi.last_frame_ms_valid = false;
if (fifo_flush) {
framebuffer_flush_buffers(true);
} else if (!csi.disable_full_flush) {
framebuffer_flush_buffers(false);
}
return 0;
}
int omv_csi_set_clk_frequency(uint32_t frequency) {
// Configure CPI clock source (400MHz or 480MHz) and divider.
if (frequency >= 24000000) {
set_cpi_pixel_clk(CPI_PIX_CLKSEL_480MZ, 20);
} else if (frequency >= 12000000) {
set_cpi_pixel_clk(CPI_PIX_CLKSEL_480MZ, 40);
} else if (frequency >= 6000000) {
set_cpi_pixel_clk(CPI_PIX_CLKSEL_480MZ, 80);
} else {
set_cpi_pixel_clk(CPI_PIX_CLKSEL_400MZ, 100);
}
return 0;
}
uint32_t omv_csi_get_clk_frequency() {
uint32_t div = (CLKCTL_PER_MST->CAMERA_PIXCLK_CTRL & CAMERA_PIXCLK_CTRL_DIVISOR_Msk) >>
CAMERA_PIXCLK_CTRL_DIVISOR_Pos;
if (CLKCTL_PER_MST->CAMERA_PIXCLK_CTRL & CAMERA_PIXCLK_CTRL_CLK_SEL) {
return 480000000 / div;
} else {
return 400000000 / div;
}
}
uint32_t omv_csi_get_fb_offset(omv_csi_t *csi) {
uint32_t offset = 0;
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Offset the pixels buffer for debayering.
if (csi->raw_output && csi->pixformat == PIXFORMAT_RGB565) {
offset += line_size_bytes * resolution[csi->framesize][1];
}
return offset;
}
// This is the default snapshot function, which can be replaced in omv_csi_init functions.
int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
static uint32_t frames = 0;
static uint32_t r_stat, gb_stat, gr_stat, b_stat;
// Used to restore MAIN_FB's width and height.
uint32_t w = MAIN_FB()->u;
uint32_t h = MAIN_FB()->v;
CPI_Type *cpi = cpi_get_base_addr(csi);
if (csi->pixformat == PIXFORMAT_INVALID) {
return OMV_CSI_ERROR_INVALID_PIXFORMAT;
}
if (csi->framesize == OMV_CSI_FRAMESIZE_INVALID) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
if (omv_csi_check_framebuffer_size() != 0) {
return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW;
}
// Compress the framebuffer for the IDE preview.
framebuffer_update_jpeg_buffer();
// Free the current FB head.
framebuffer_free_current_buffer();
// Reconfigure and restart the CSI transfer if it's not running.
if (!(cpi->CAM_CTRL & CAM_CTRL_BUSY)) {
framebuffer_setup_buffers();
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Error out if the transfer size is not compatible with DMA transfer restrictions.
if ((!line_size_bytes) ||
(line_size_bytes % sizeof(uint64_t)) ||
csi->transpose ||
(csi->pixformat == PIXFORMAT_JPEG)) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
}
// Get the destination buffer address.
vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK);
// Check if buffer is not ready or is not 64-bit aligned.
if ((!buffer) || (LocalToGlobal(buffer->data) & 0x7)) {
return OMV_CSI_ERROR_FRAMEBUFFER_ERROR;
}
if (!csi->raw_output &&
((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) ||
(csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap))) {
cpi->CAM_CFG |= (CPI_DATA_ENDIANNESS_MSB_FIRST << CAM_CFG_ENDIANNESS_Pos);
} else {
cpi->CAM_CFG &= ~(CPI_DATA_ENDIANNESS_MSB_FIRST << CAM_CFG_ENDIANNESS_Pos);
}
// Find maximum burst size that perfectly fits the line size.
cpi->CAM_FIFO_CTRL &= ~CAM_FIFO_CTRL_RD_WMARK_Msk;
for (uint32_t i = 16; i >= 4; i--) {
if (!(line_size_bytes % (i * 8))) {
cpi->CAM_FIFO_CTRL |= (i << CAM_FIFO_CTRL_RD_WMARK_Pos);
break;
}
}
cpi->CAM_VIDEO_FCFG &= ~CAM_VIDEO_FCFG_DATA_Msk;
cpi->CAM_VIDEO_FCFG = line_size_bytes;
cpi->CAM_VIDEO_FCFG &= ~CAM_VIDEO_FCFG_ROW_Msk;
cpi->CAM_VIDEO_FCFG |= ((resolution[csi->framesize][1] - 1) << CAM_VIDEO_FCFG_ROW_Pos);
cpi->CAM_FRAME_ADDR = LocalToGlobal(buffer->data + omv_csi_get_fb_offset(csi));
// Configure and enable CSI interrupts.
cpi_irq_handler_clear_intr_status(cpi, CPI_IRQ_FLAGS);
cpi_enable_interrupt(cpi, CPI_IRQ_FLAGS);
NVIC_ClearPendingIRQ(CAM_IRQ_IRQn);
NVIC_EnableIRQ(CAM_IRQ_IRQn);
// Reset CSI and start the capture.
cpi->CAM_CTRL = 0;
cpi->CAM_CTRL |= CAM_CTRL_SW_RESET;
cpi->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL);
//printf("==== reconfigured ===\n");
}
// Let the camera know we want to trigger it now.
#if defined(OMV_CSI_FSYNC_PIN)
if (csi->frame_sync) {
omv_gpio_write(OMV_CSI_FSYNC_PIN, 1);
}
#endif
vbuffer_t *buffer = framebuffer_get_head(FB_INVALIDATE);
// Wait for the DMA to finish the transfer.
for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) {
MICROPY_EVENT_POLL_HOOK
if ((mp_hal_ticks_ms() - ticks) > 3000) {
omv_csi_abort(true, false);
#if defined(OMV_CSI_FSYNC_PIN)
if (csi->frame_sync) {
omv_gpio_write(OMV_CSI_FSYNC_PIN, 0);
}
#endif
return OMV_CSI_ERROR_CAPTURE_TIMEOUT;
}
buffer = framebuffer_get_head(FB_INVALIDATE);
}
// We're done receiving data.
#if defined(OMV_CSI_FSYNC_PIN)
if (csi->frame_sync) {
omv_gpio_write(OMV_CSI_FSYNC_PIN, 0);
}
#endif
if (!csi->transpose) {
MAIN_FB()->w = w;
MAIN_FB()->h = h;
} else {
MAIN_FB()->w = h;
MAIN_FB()->h = w;
}
// Reset the frame buffer's pixel format.
switch (csi->pixformat) {
case PIXFORMAT_GRAYSCALE:
MAIN_FB()->pixfmt = PIXFORMAT_GRAYSCALE;
break;
case PIXFORMAT_RGB565:
MAIN_FB()->pixfmt = PIXFORMAT_RGB565;
break;
case PIXFORMAT_YUV422: {
MAIN_FB()->pixfmt = PIXFORMAT_YUV;
MAIN_FB()->subfmt_id = csi->yuv_format;
MAIN_FB()->pixfmt = imlib_yuv_shift(MAIN_FB()->pixfmt, MAIN_FB()->x);
break;
case PIXFORMAT_BAYER:
MAIN_FB()->pixfmt = PIXFORMAT_BAYER;
MAIN_FB()->subfmt_id = csi->cfa_format;
MAIN_FB()->pixfmt = imlib_bayer_shift(MAIN_FB()->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose);
break;
}
default:
break;
}
// Initialize a frame using the frame buffer.
framebuffer_init_image(dst_image);
// Set the frame's pixel format to bayer for raw sensors.
if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) {
dst_image->pixfmt = PIXFORMAT_BAYER;
dst_image->subfmt_id = csi->cfa_format;
dst_image->pixfmt = imlib_bayer_shift(dst_image->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose);
}
// Crop first to reduce the frame size before debayering.
if (omv_csi_get_cropped()) {
image_t src_cimage = *dst_image;
image_t dst_cimage = *dst_image;
src_cimage.w = resolution[csi->framesize][0];
src_cimage.h = resolution[csi->framesize][1];
// Offset the pixels buffer for the debayer code.
if (csi->pixformat == PIXFORMAT_RGB565) {
src_cimage.pixels += omv_csi_get_fb_offset(csi);
dst_cimage.pixels += omv_csi_get_fb_offset(csi);
}
rectangle_t srect = { MAIN_FB()->x, MAIN_FB()->y, MAIN_FB()->u, MAIN_FB()->v };
rectangle_t drect = { 0, 0, MAIN_FB()->u, MAIN_FB()->v };
if (omv_gpu_draw_image(&src_cimage, &srect, &dst_cimage, &drect, 255, NULL, NULL, 0) != 0) {
return OMV_CSI_ERROR_IO_ERROR;
}
}
// Debayer the frame to match the target pixel format.
if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) {
image_t src_image = *dst_image;
// Offset the pixels buffer for the debayer code.
if (csi->pixformat == PIXFORMAT_RGB565) {
src_image.pixels += omv_csi_get_fb_offset(csi);
}
// Set the target pixel format before debayer.
dst_image->pixfmt = MAIN_FB()->pixfmt;
// Update AWB stats every n frames.
if ((frames++ % 100) == 0) {
omv_csi_ioctl(OMV_CSI_IOCTL_GET_RGB_STATS, &r_stat, &gb_stat, &gr_stat, &b_stat);
}
// Debayer frame.
imlib_debayer_image_awb(dst_image, &src_image, false, r_stat, (gb_stat + gr_stat) / 2, b_stat);
}
return 0;
}
void CAM_IRQHandler(void) {
uint32_t mask = 0;
CPI_Type *cpi = cpi_get_base_addr(&csi);
uint32_t status = cpi_get_interrupt_status(cpi);
if (status & CAM_INTR_VSYNC) {
mask |= CAM_INTR_VSYNC;
}
if (status & CAM_INTR_HSYNC) {
mask |= CAM_INTR_HSYNC;
}
if (status & CAM_INTR_INFIFO_OVERRUN) {
mask |= CAM_INTR_INFIFO_OVERRUN;
omv_csi_abort(true, true);
printf("INFIFO_OVERRUN\n");
}
if (status & CAM_INTR_OUTFIFO_OVERRUN) {
mask |= CAM_INTR_OUTFIFO_OVERRUN;
omv_csi_abort(true, true);
printf("OUTFIFO_OVERRUN\n");
}
if (status & CAM_INTR_BRESP_ERR) {
mask |= CAM_INTR_BRESP_ERR;
omv_csi_abort(true, true);
printf("BRESP_ERR %lu\n", cpi->CAM_AXI_ERR_STAT);
}
if (status & CAM_INTR_STOP) {
mask |= CAM_INTR_STOP;
cpi->CAM_CTRL = 0;
if (!(status & CPI_ERROR_FLAGS)) {
// Release the current framebuffer.
framebuffer_get_tail(FB_NO_FLAGS);
}
// Get the current framebuffer (or new tail).
vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK);
if (buffer != NULL) {
cpi->CAM_CTRL = 0;
cpi->CAM_CTRL |= CAM_CTRL_SW_RESET;
cpi->CAM_FRAME_ADDR = LocalToGlobal(buffer->data + omv_csi_get_fb_offset(&csi));
cpi_irq_handler_clear_intr_status(cpi, mask);
cpi->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL);
}
if (!(status & CPI_ERROR_FLAGS)) {
if (csi.frame_callback) {
csi.frame_callback();
}
}
}
// Clear interrupts.
cpi_irq_handler_clear_intr_status(cpi, mask);
}
#endif // MICROPY_PY_CSI

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* GPIO port for Alif.
*/
#include <stdint.h>
#include <stdint.h>
#include <stdbool.h>
#include "py/mphal.h"
#include "omv_boardconfig.h"
#include "omv_gpio.h"
#include "omv_common.h"
static inline GPIO_Type *omv_gpio_get_base(omv_gpio_t pin) {
GPIO_Type *gpio = NULL;
if (pin->port == 15) {
gpio = (GPIO_Type *) LPGPIO_BASE;
} else {
gpio = (GPIO_Type *) (GPIO0_BASE + (pin->port * (GPIO1_BASE - GPIO0_BASE)));
}
return gpio;
}
void omv_gpio_config(omv_gpio_t pin, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t af) {
GPIO_Type *gpio = omv_gpio_get_base(pin);
uint16_t pad_config = (mode | pull | speed | pin->ren) & 0xFF;
af = (af == -1) ? pin->af : af;
pinconf_set(pin->port, pin->pin, af, pad_config);
// If pad was configured for GPIO module, set the pin config.
switch (mode) {
case OMV_GPIO_MODE_ALT:
case OMV_GPIO_MODE_ALT_OD: {
// No GPIO config.
break;
}
case OMV_GPIO_MODE_INPUT: {
gpio_set_direction_input(gpio, pin->pin);
break;
}
case OMV_GPIO_MODE_OUTPUT:
case OMV_GPIO_MODE_OUTPUT_OD: {
gpio_set_direction_output(gpio, pin->pin);
break;
}
case OMV_GPIO_MODE_IT_FALL: {
break;
}
case OMV_GPIO_MODE_IT_RISE: {
break;
}
case OMV_GPIO_MODE_IT_BOTH: {
break;
}
}
}
void omv_gpio_deinit(omv_gpio_t pin) {
}
bool omv_gpio_read(omv_gpio_t pin) {
GPIO_Type *gpio = omv_gpio_get_base(pin);
return gpio_get_value(gpio, pin->pin);
}
void omv_gpio_write(omv_gpio_t pin, bool value) {
GPIO_Type *gpio = omv_gpio_get_base(pin);
if (value) {
gpio_set_value_high(gpio, pin->pin);
} else{
gpio_set_value_low(gpio, pin->pin);
}
}

View File

@ -0,0 +1,226 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* GPU driver for Alif port.
*/
#include <stdio.h>
#include <assert.h>
#include "py/mphal.h"
#include "py/runtime.h"
#include "dave_d0lib.h"
#include "dave_registermap.h"
#include "dave_driver.h"
#include "omv_gpu.h"
static d2_device *dev;
extern char _gpu_memory_start;
extern char _gpu_memory_end;
#define OMV_GPU_CHECK_ERROR(err) \
omv_gpu_check_error(err, __LINE__)
static void omv_gpu_check_error(uint32_t error, uint32_t line);
#define D2_WIDTH(x) (((uint16_t) (x) << 4) & 0x3FF0) // 10:4 fixed-point number (4 bits fraction)
#define D2_POINT(x) (((uint16_t) (x) << 4) & 0x7FF0) // 1:11:4 FP (1 bit sign, 11 bits integer, 4 bits fraction)
#define D2_BLITP(x) (((uint16_t) (x) << 0) & 0x03FF) // unsigned short. The allowed range is 0 to 1023.
extern NORETURN void __fatal_error(const char *msg);
int omv_gpu_init() {
if (dev != NULL) {
return 0;
}
#if defined(WITH_MM_DYNAMIC)
if (d0_initdefaultheapmanager() != 1) {
__fatal_error("Failed to init GPU heap manager");
}
#elif defined(WITH_MM_FIXED_RANGE)
if (d0_initheapmanager(&_gpu_memory_start,
&_gpu_memory_end - &_gpu_memory_start,
d0_mm_fixed_range,
NULL, // unified memory.
0,
0,
d0_mm_fixed_range,
d0_ma_unified) != 1) {
__fatal_error("Failed to init GPU heap manager");
}
#else
#error "GPU heap manager is not defined"
#endif
dev = d2_opendevice(d2_df_no_irq);
if (dev == NULL) {
__fatal_error("Failed to init GPU device");
}
if (d2_inithw(dev, 0) != D2_OK) {
__fatal_error("Failed to init GPU hardware");
}
#if 0
d2_setblendmode(dev, d2_bm_alpha, d2_bm_one_minus_alpha);
d2_setalphamode(dev, d2_am_constant); // default in d2_newcontext
d2_setlinecap(dev, d2_lc_butt);
d2_setantialiasing(dev, 1);
d2_setblur(dev, 0);
d2_settextureoperation(dev, d2_to_one, d2_to_copy, d2_to_copy, d2_to_copy);
d2_setfillmode(dev, d2_fm_texture);
d2_settexturemode(dev, d2_tm_filter);
#endif
return 0;
}
void omv_gpu_deinit() {
if (dev) {
d2_closedevice(dev);
dev = NULL;
}
}
static d2_u32 omv_gpu_pixfmt(uint32_t omv_pixfmt) {
switch (omv_pixfmt) {
case PIXFORMAT_BINARY:
return d2_mode_alpha1;
case PIXFORMAT_BAYER_ANY:
case PIXFORMAT_GRAYSCALE:
return d2_mode_alpha8;
case PIXFORMAT_RGB565:
return d2_mode_rgb565;
}
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Pixel format is not supported"));
}
int omv_gpu_draw_image(image_t *src_img,
rectangle_t *src_rect,
image_t *dst_img,
rectangle_t *dst_rect,
int alpha,
const uint16_t *color_palette,
const uint8_t *alpha_palette,
image_hint_t hint) {
// Belnding is not supported yet.
if (color_palette || alpha_palette) {
return -1;
}
OMV_PROFILE_START();
d2_s32 err;
d2_u32 blit_flags = 0;
d2_renderbuffer *rbuffer = d2_getrenderbuffer(dev, 0);
err = d2_selectrenderbuffer(dev, rbuffer);
OMV_GPU_CHECK_ERROR(err);
d2_u32 dst_pixfmt = omv_gpu_pixfmt(dst_img->pixfmt);
err = d2_framebuffer(dev, dst_img->data, dst_img->w, dst_img->w, dst_img->h, dst_pixfmt);
OMV_GPU_CHECK_ERROR(err);
d2_u32 src_pixfmt = omv_gpu_pixfmt(src_img->pixfmt);
err = d2_setblitsrc(dev, src_img->data, src_img->w, src_img->w, src_img->h, src_pixfmt);
OMV_GPU_CHECK_ERROR(err);
if (hint & IMAGE_HINT_BILINEAR) {
blit_flags |= d2_bf_filter;
}
if (src_pixfmt == d2_mode_alpha8 || dst_pixfmt == d2_mode_alpha8) {
blit_flags |= d2_bf_usealpha;
}
// Flush source image
SCB_CleanDCache_by_Addr(src_img->data, image_size(src_img));
// Copy source image to the framebuffer.
err = d2_blitcopy(dev,
src_rect->w,
src_rect->h,
D2_BLITP(src_rect->x),
D2_BLITP(src_rect->y),
D2_WIDTH(dst_rect->w),
D2_WIDTH(dst_rect->h),
D2_POINT(dst_rect->x),
D2_POINT(dst_rect->y),
blit_flags | d2_bf_no_blitctxbackup);
OMV_GPU_CHECK_ERROR(err);
err = d2_executerenderbuffer(dev, rbuffer, d2_ef_default);
OMV_GPU_CHECK_ERROR(err);
err = d2_flushframe(dev);
OMV_GPU_CHECK_ERROR(err);
// Invalidate the framebuffer image.
SCB_InvalidateDCache_by_Addr(dst_img->data, image_size(dst_img));
OMV_PROFILE_PRINT();
return 0;
}
static void omv_gpu_check_error(uint32_t error, uint32_t line) {
static const char *errors[] = {
"success",
"memory allocation failed %d",
"invalid device %d",
"invalid rendering context %d",
"invalid renderbuffer context %d",
"hardware device already in use %d",
"device already assigned %d",
"cannot operate on default context %d",
"index is out of bounds %d",
"rendermode not supported %d",
"width out of legal range %d",
"height out of legal range %d",
"illegal framebuffer address %d",
"parameter too close to zero %d",
"parameter is negative %d",
"parameter value is too large %d",
"unsupported mode %d",
"source pointer may not be null %d",
"operation cannot execute while hardware is busy %d",
"cannot operate on default buffer %d",
"d2_df_no_dlist is not supported in low_localmemmode %d",
"not enough dlistblocks. please adjust in d2_lowlocalmemmode(...) %d",
};
if (error == D2_OK) {
return;
}
if (error > (sizeof(errors) / sizeof(errors[0]))) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Unknown GPU error."));
}
mp_raise_msg_varg(&mp_type_RuntimeError, (mp_rom_error_text_t) errors[error], line);
}

View File

@ -0,0 +1,506 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif I2C driver.
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "py/mphal.h"
#include "py/runtime.h"
#include "omv_portconfig.h"
#include "omv_boardconfig.h"
#include "alif_hal.h"
#include "omv_gpio.h"
#include "omv_common.h"
#include "omv_i2c.h"
#include "sys_ctrl_i3c.h"
#define I2C_SCAN_TIMEOUT (10)
#define I2C_XFER_TIMEOUT (1000)
#define I2C_DAT_INDEX (0)
#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_IC_CON_MASTER_TX_EMPTY_CTRL (1 << 8)
#define I2C_CON_SPEED(speed) \
((speed == I2C_SPEED_STANDARD) ? I2C_IC_CON_SPEED_STANDARD : I2C_IC_CON_SPEED_FAST)
#define I2C_TX_FIFO_LEN (I2C_FIFO_DEPTH / 2)
#define I2C_RX_FIFO_LEN (I2C_FIFO_DEPTH / 2)
#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)
#ifdef NDEBUG
#define I2C_CHECK_ERRORS(base) \
if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \
(void) base->I2C_RAW_INTR_STAT; \
(void) base->I2C_CLR_TX_ABRT; \
return -1; \
}
#else
#define I2C_CHECK_ERRORS(base) \
if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \
uint32_t status = base->I2C_RAW_INTR_STAT; \
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) base->I2C_CLR_TX_ABRT; \
return -1; \
}
#endif
typedef struct {
uint8_t *data;
size_t size;
uint32_t flags;
uint32_t direction;
uint8_t address;
} i2c_transfer_t;
typedef enum {
I2C_TRANSFER_READ = (1 << 0),
I2C_TRANSFER_WRITE = (1 << 1),
} i2c_transfer_direction_t;
static int omv_ixc_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32_t timeout);
int omv_i2c_init(omv_i2c_t *i2c, uint32_t bus_id, uint32_t speed) {
i2c->id = bus_id;
i2c->initialized = false;
i2c->is_i3c = (bus_id == OMV_I3C0_ID);
i2c->cw_size = 0;
i2c->speed = speed;
switch (speed) {
case OMV_I2C_SPEED_STANDARD:
speed = i2c->is_i3c ? I3C_I2C_SPEED_MODE_SS_100_KBPS : I2C_SPEED_STANDARD; // 100 kbit/s
break;
case OMV_I2C_SPEED_FULL:
speed = i2c->is_i3c ? I3C_I2C_SPEED_MODE_FM_400_KBPS : I2C_SPEED_FAST; // 400 kbit/s
break;
case OMV_I2C_SPEED_FAST:
speed = i2c->is_i3c ? I3C_I2C_SPEED_MODE_FMP_1_MBPS : I2C_SPEED_FASTPLUS; // 1000 kbit/s
break;
default:
return -1;
}
switch (bus_id) {
#if defined(OMV_I3C0_ID)
case OMV_I3C0_ID: {
i2c->inst = (I3C_Type *) I3C_BASE;
i2c->scl_pin = OMV_I3C0_SCL_PIN;
i2c->sda_pin = OMV_I3C0_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C0_ID)
case OMV_I2C0_ID: {
i2c->inst = (I2C_Type *) I2C0_BASE;
i2c->scl_pin = OMV_I2C0_SCL_PIN;
i2c->sda_pin = OMV_I2C0_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C1_ID)
case OMV_I2C1_ID: {
i2c->inst = (I2C_Type *) I2C1_BASE;
i2c->scl_pin = OMV_I2C1_SCL_PIN;
i2c->sda_pin = OMV_I2C1_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C2_ID)
case OMV_I2C2_ID: {
i2c->inst = (I2C_Type *) I2C2_BASE;
i2c->scl_pin = OMV_I2C2_SCL_PIN;
i2c->sda_pin = OMV_I2C2_SDA_PIN;
break;
}
#endif
#if defined(OMV_I2C3_ID)
case OMV_I2C3_ID: {
i2c->inst = (I2C_Type *) I2C3_BASE;
i2c->scl_pin = OMV_I2C3_SCL_PIN;
i2c->sda_pin = OMV_I2C3_SDA_PIN;
break;
}
#endif
default:
return -1;
}
alif_hal_i2c_init(bus_id);
if (i2c->is_i3c) {
// I3C controller in I2C/legacy mode.
I3C_Type *inst = i2c->inst;
// Core soft-reset.
inst->I3C_RESET_CTRL = 0x1;
while (inst->I3C_RESET_CTRL & 0x1) {
}
// Initialize I2C controller
i3c_master_init(inst);
// Configure clock and speed.
i2c_clk_cfg(inst, SystemAPBClock, speed);
} else {
// I2C controller.
I2C_Type *inst = i2c->inst;
// Disable I2C.
i2c_disable(inst);
// Initialize I2C controller
inst->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE |
I2C_IC_CON_MASTER_RESTART_EN |
I2C_IC_CON_MASTER_TX_EMPTY_CTRL |
I2C_CON_SPEED(speed);
// Set TX/RX FIFO threshold (i2c must be disabled)
inst->I2C_TX_TL = I2C_TX_FIFO_LEN;
inst->I2C_RX_TL = I2C_RX_FIFO_LEN;
// Configure clock.
i2c_master_set_clock(inst, SystemAPBClock / 1000, speed);
// Re-enable I2C.
i2c_enable(inst);
}
i2c->initialized = true;
return 0;
}
int omv_i2c_deinit(omv_i2c_t *i2c) {
if (i2c->initialized) {
// TODO
i2c->initialized = false;
}
return 0;
}
int omv_i2c_scan(omv_i2c_t *i2c, uint8_t *list, uint8_t size) {
uint32_t idx = 0;
for (uint8_t addr = 0x29, data = 0; addr < 0x78; addr++) {
i2c_transfer_t xfer = {
.data = &data,
.size = 1,
.flags = 0,
.direction = I2C_TRANSFER_READ,
.address = addr
};
if (omv_ixc_transfer_timeout(i2c, &xfer, I2C_SCAN_TIMEOUT) == 0) {
if (list == NULL || size == 0) {
idx = (addr << 1);
break;
} else if (idx < size) {
list[idx++] = (addr << 1);
} else {
break;
}
}
mp_event_handle_nowait();
}
return idx;
}
int omv_i2c_enable(omv_i2c_t *i2c, bool enable) {
if (i2c->is_i3c) {
//TODO: For I3C this causes a lockup.
I3C_Type *inst = i2c->inst;
if (enable) {
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL & ~DEV_CTRL_ENABLE;
while (inst->I3C_DEVICE_CTRL & DEV_CTRL_ENABLE) {
}
} else {
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL | DEV_CTRL_ENABLE;
while (!(inst->I3C_DEVICE_CTRL & DEV_CTRL_ENABLE)) {
}
i3c_resume(inst);
}
} else {
I2C_Type *inst = i2c->inst;
if (enable) {
i2c_enable(inst);
} else {
i2c_disable(inst);
}
}
return 0;
}
int omv_i2c_gencall(omv_i2c_t *i2c, uint8_t cmd) {
int ret = 0;
ret |= omv_i2c_write_bytes(i2c, 0, &cmd, 1, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_readb(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t reg_addr, uint8_t *reg_data) {
int ret = 0;
ret |= omv_i2c_write_bytes(i2c, slv_addr, &reg_addr, 1, OMV_I2C_XFER_NO_FLAGS);
ret |= omv_i2c_read_bytes(i2c, slv_addr, reg_data, 1, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_writeb(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t reg_addr, uint8_t reg_data) {
int ret = 0;
uint8_t buf[] = {reg_addr, reg_data};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 2, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_readb2(omv_i2c_t *i2c, uint8_t slv_addr, uint16_t reg_addr, uint8_t *reg_data) {
int ret = 0;
uint8_t buf[] = {(reg_addr >> 8), reg_addr};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 2, OMV_I2C_XFER_NO_STOP);
ret |= omv_i2c_read_bytes(i2c, slv_addr, reg_data, 1, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_writeb2(omv_i2c_t *i2c, uint8_t slv_addr, uint16_t reg_addr, uint8_t reg_data) {
int ret = 0;
uint8_t buf[] = {(reg_addr >> 8), reg_addr, reg_data};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 3, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_readw(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t reg_addr, uint16_t *reg_data) {
int ret = 0;
ret |= omv_i2c_write_bytes(i2c, slv_addr, &reg_addr, 1, OMV_I2C_XFER_NO_STOP);
ret |= omv_i2c_read_bytes(i2c, slv_addr, (uint8_t *) reg_data, 2, OMV_I2C_XFER_NO_FLAGS);
*reg_data = (*reg_data << 8) | (*reg_data >> 8);
return ret;
}
int omv_i2c_writew(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t reg_addr, uint16_t reg_data) {
int ret = 0;
uint8_t buf[] = {reg_addr, (reg_data >> 8), reg_data};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 3, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
int omv_i2c_readw2(omv_i2c_t *i2c, uint8_t slv_addr, uint16_t reg_addr, uint16_t *reg_data) {
int ret = 0;
uint8_t buf[] = {(reg_addr >> 8), reg_addr};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 2, OMV_I2C_XFER_NO_STOP);
ret |= omv_i2c_read_bytes(i2c, slv_addr, (uint8_t *) reg_data, 2, OMV_I2C_XFER_NO_FLAGS);
*reg_data = (*reg_data << 8) | (*reg_data >> 8);
return ret;
}
int omv_i2c_writew2(omv_i2c_t *i2c, uint8_t slv_addr, uint16_t reg_addr, uint16_t reg_data) {
int ret = 0;
uint8_t buf[] = {(reg_addr >> 8), reg_addr, (reg_data >> 8), reg_data};
ret |= omv_i2c_write_bytes(i2c, slv_addr, buf, 4, OMV_I2C_XFER_NO_FLAGS);
return ret;
}
static int i2c_poll_flags(I2C_Type *base, uint32_t flags, uint32_t timeout) {
mp_uint_t tick_start = mp_hal_ticks_ms();
while (!(base->I2C_STATUS & flags)) {
I2C_CHECK_ERRORS(base);
if ((mp_hal_ticks_ms() - tick_start) >= timeout) {
// Should not raise exception as we're not always in nlr context.
return -1;
}
mp_event_handle_nowait();
}
return 0;
}
static int omv_i2c_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32_t timeout) {
I2C_Type *base = i2c->inst;
i2c_clear_all_interrupt(base);
i2c_set_target_addr(base, xfer->address, I2C_7BIT_ADDRESS, 0);
// Write buffered transfer (if any) first.
for (size_t cw_idx = 0; cw_idx < i2c->cw_size;) {
// Write data to FIFO
if (base->I2C_STATUS & I2C_IC_STATUS_TFNF) {
base->I2C_DATA_CMD = (uint16_t) i2c->cw_buf[cw_idx++];
I2C_CHECK_ERRORS(base);
}
}
size_t tx_size = (xfer->direction == I2C_TRANSFER_WRITE) ? xfer->size : 0;
for (size_t tx_idx = 0; tx_idx < tx_size; ) {
// Write data to FIFO
if (base->I2C_STATUS & I2C_IC_STATUS_TFNF) {
base->I2C_DATA_CMD = (uint16_t) xfer->data[tx_idx++];
I2C_CHECK_ERRORS(base);
}
// Wait for TX FIFO empty
if (tx_idx == tx_size && i2c_poll_flags(base, I2C_IC_STATUS_TFE, 10) != 0) {
return -1;
}
}
size_t rx_size = (xfer->direction == I2C_TRANSFER_READ) ? xfer->size : 0;
for (size_t tx_idx = 0, rx_idx = 0; rx_idx < rx_size; ) {
// Write command to FIFO
if ((base->I2C_STATUS & I2C_IC_STATUS_TFNF) &&
!(base->I2C_STATUS & I2C_IC_STATUS_RFNE) && tx_idx++ < rx_size) {
base->I2C_DATA_CMD = I2C_IC_DATA_CMD_READ_REQ;
I2C_CHECK_ERRORS(base);
}
// Wait for RX FIFO not empty
if (i2c_poll_flags(base, I2C_IC_STATUS_RFNE, timeout) != 0) {
return -1;
}
// Read data from FIFO
while ((base->I2C_STATUS & I2C_IC_STATUS_RFNE) && rx_idx < rx_size) {
xfer->data[rx_idx++] = base->I2C_DATA_CMD & 0xFF;
}
}
return 0;
}
static int omv_i3c_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32_t timeout) {
int ret = 0;
I3C_XFER i3c_xfer = {0};
I3C_Type *base = i2c->inst;
i3c_add_slv_to_dat(base, I2C_DAT_INDEX, 0, xfer->address);
if (xfer->direction == I2C_TRANSFER_READ) {
i3c_xfer.rx_buf = xfer->data;
i3c_xfer.rx_len = xfer->size;
i3c_master_rx(base, &i3c_xfer, I2C_DAT_INDEX, i3c_xfer.rx_len);
} else {
i3c_xfer.tx_buf = xfer->data;
i3c_xfer.tx_len = xfer->size;
i3c_master_tx(base, &i3c_xfer, I2C_DAT_INDEX, i3c_xfer.tx_len);
}
// Wait for the transfer to finish.
mp_uint_t tick_start = mp_hal_ticks_ms();
while (base->I3C_INTR_STATUS == 0) {
if ((mp_hal_ticks_ms() - tick_start) >= timeout) {
// Should not raise exception as we're not always in nlr context.
ret = -1;
goto cleanup;
}
mp_event_handle_nowait();
}
uint32_t status = base->I3C_INTR_STATUS;
// See Table 15-81 Response Data Structure
uint32_t resp = base->I3C_RESPONSE_QUEUE_PORT;
if ((status & INTR_TRANSFER_ERR_STAT) || RESPONSE_PORT_ERR_STATUS(resp)) {
ret = -1;
goto cleanup;
}
if (xfer->direction == I2C_TRANSFER_READ && xfer->data) {
for (uint32_t i = 0, dr = 0; i < xfer->size; i++, dr >>= 8) {
if ((i % 4) == 0) {
dr = base->I3C_RX_DATA_PORT;
}
xfer->data[i] = dr & 0xFF;
}
}
cleanup:
i3c_clear_xfer_error(base);
i3c_resume(base);
return ret;
}
static int omv_ixc_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32_t timeout) {
int ret = 0;
if (i2c->is_i3c) {
ret = omv_i3c_transfer_timeout(i2c, xfer, timeout);
} else {
uint32_t xfer_cont = (OMV_I2C_XFER_NO_STOP | OMV_I2C_XFER_SUSPEND);
if (xfer->direction == I2C_TRANSFER_WRITE && (xfer->flags & xfer_cont)) {
if (xfer->size > sizeof(i2c->cw_buf)) {
return -1;
}
i2c->cw_size = xfer->size;
memcpy(i2c->cw_buf, xfer->data, xfer->size);
} else {
ret = omv_i2c_transfer_timeout(i2c, xfer, timeout);
i2c->cw_size = 0;
}
}
return ret;
}
int omv_i2c_read_bytes(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t *buf, int len, uint32_t flags) {
i2c_transfer_t xfer = {
.data = buf,
.size = len,
.flags = flags,
.direction = I2C_TRANSFER_READ,
.address = (slv_addr >> 1)
};
return omv_ixc_transfer_timeout(i2c, &xfer, I2C_XFER_TIMEOUT);
}
int omv_i2c_write_bytes(omv_i2c_t *i2c, uint8_t slv_addr, uint8_t *buf, int len, uint32_t flags) {
i2c_transfer_t xfer = {
.data = buf,
.size = len,
.flags = flags,
.direction = I2C_TRANSFER_WRITE,
.address = (slv_addr >> 1)
};
return omv_ixc_transfer_timeout(i2c, &xfer, I2C_XFER_TIMEOUT);
}
int omv_i2c_pulse_scl(omv_i2c_t *i2c) {
if (i2c->initialized && i2c->scl_pin) {
omv_i2c_deinit(i2c);
omv_gpio_config(i2c->scl_pin, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
// Pulse SCL to recover stuck device.
for (int i = 0; i < 1000; i++) {
omv_gpio_write(i2c->scl_pin, 1);
mp_hal_delay_us(10);
omv_gpio_write(i2c->scl_pin, 0);
mp_hal_delay_us(10);
}
omv_i2c_init(i2c, i2c->id, i2c->speed);
}
return 0;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* MicroPython port configuration.
*/
#define MICROPY_NLR_RAISE_HOOK \
do { \
extern void fb_alloc_free_till_mark(); \
fb_alloc_free_till_mark(); \
} while (0);
#ifndef MICROPY_EVENT_POLL_HOOK
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
mp_handle_pending(true); \
} while (0);
#endif
#define MICROPY_ENABLE_VM_ABORT (1)
#define MICROPY_GC_SPLIT_HEAP (1)
#define MICROPY_PY_MARSHAL (1)
#define MICROPY_BANNER_NAME_AND_VERSION "OpenMV " OPENMV_GIT_TAG "; MicroPython " MICROPY_GIT_TAG
void __fatal_error(const char *);
#define MICROPY_BOARD_FATAL_ERROR __fatal_error
#include <mpconfigport.h>

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* OpenMV MIMXRT port abstraction layer.
*/
#ifndef __OMV_PORTCONFIG_H__
#define __OMV_PORTCONFIG_H__
#include <stdlib.h>
#include "clk.h"
#include "i2c.h"
#include "i3c.h"
#include "spi.h"
#include "gpio.h"
#include "pinconf.h"
#include PINS_AF_H
#include CMSIS_MCU_H
// *INDENT-OFF*
// omv_gpio_t definitions
// GPIO speeds.
#define OMV_GPIO_SPEED_LOW ((1 << 8) | 0)
#define OMV_GPIO_SPEED_MED ((2 << 8) | 0)
#define OMV_GPIO_SPEED_HIGH ((3 << 8) | PADCTRL_SLEW_RATE_FAST)
#define OMV_GPIO_SPEED_MAX ((4 << 8) | PADCTRL_SLEW_RATE_FAST)
// GPIO pull.
#define OMV_GPIO_PULL_NONE ((1 << 8) | PADCTRL_DRIVER_DISABLED_HIGH_Z)
#define OMV_GPIO_PULL_UP ((2 << 8) | PADCTRL_DRIVER_DISABLED_PULL_UP)
#define OMV_GPIO_PULL_DOWN ((3 << 8) | PADCTRL_DRIVER_DISABLED_PULL_DOWN)
// GPIO modes.
#define OMV_GPIO_MODE_INPUT ((1 << 8) | PADCTRL_SCHMITT_TRIGGER_ENABLE | PADCTRL_READ_ENABLE)
#define OMV_GPIO_MODE_OUTPUT ((2 << 8) | PADCTRL_OUTPUT_DRIVE_STRENGTH_8MA)
#define OMV_GPIO_MODE_OUTPUT_OD ((3 << 8) | PADCTRL_OUTPUT_DRIVE_STRENGTH_8MA | PADCTRL_DRIVER_OPEN_DRAIN)
#define OMV_GPIO_MODE_ALT ((4 << 8)) // No settings for AF modes.
#define OMV_GPIO_MODE_ALT_OD ((5 << 8)) // No settings for AF modes.
// GPIO IT modes.
#define OMV_GPIO_MODE_IT_FALL ((6 << 8) | PADCTRL_SCHMITT_TRIGGER_ENABLE | PADCTRL_READ_ENABLE)
#define OMV_GPIO_MODE_IT_RISE ((7 << 8) | PADCTRL_SCHMITT_TRIGGER_ENABLE | PADCTRL_READ_ENABLE)
#define OMV_GPIO_MODE_IT_BOTH ((8 << 8) | PADCTRL_SCHMITT_TRIGGER_ENABLE | PADCTRL_READ_ENABLE)
// *INDENT-OFF*
typedef struct {
uint8_t port;
uint8_t pin;
uint8_t af;
uint8_t ren; // Receiver enable.
} alif_gpio_t;
typedef const alif_gpio_t *omv_gpio_t;
// For board config files.
#if OMV_GPIO_DEFINE_PINS
#define OMV_GPIO_DEFINE(name, port, pin, af, ren) \
const alif_gpio_t omv_pin_##name = {port, pin, port##_##pin##_AF_##af, ren};
#else
#define OMV_GPIO_DEFINE(name, port, pin, af, ren) \
extern const alif_gpio_t omv_pin_##name;
#endif
#include "omv_pins.h"
// omv_i2c_t definitions
typedef void *omv_i2c_dev_t;
#define OMV_I2C_PORT_BITS \
struct { \
bool is_i3c; \
size_t cw_size; \
uint8_t cw_buf[256]; \
};
#define OMV_I2C_MAX_8BIT_XFER (65536U - 16U)
#define OMV_I2C_MAX_16BIT_XFER (65536U - 8U)
// omv_spi_t definitions
#define OMV_SPI_MODE_SLAVE (0)
#define OMV_SPI_MODE_MASTER (1)
#define OMV_SPI_LSB_FIRST (0)
#define OMV_SPI_MSB_FIRST (1)
#define OMV_SPI_BUS_TX (1 << 0)
#define OMV_SPI_BUS_RX (1 << 1)
#define OMV_SPI_BUS_TX_RX (OMV_SPI_BUS_TX | OMV_SPI_BUS_RX)
#define OMV_SPI_CPOL_LOW (0)
#define OMV_SPI_CPOL_HIGH (1)
#define OMV_SPI_CPHA_1EDGE (0)
#define OMV_SPI_CPHA_2EDGE (1)
#define OMV_SPI_NSS_LOW (0)
#define OMV_SPI_NSS_HIGH (1)
#define OMV_SPI_MAX_8BIT_XFER (32768U - 32U)
#define OMV_SPI_MAX_16BIT_XFER (32768U - 16U)
#define OMV_SPI_MAX_TIMEOUT (0xFFFFFFFF)
#define OMV_SPI_PORT_BITS \
struct { \
SPI_Type *inst; \
bool is_lp; \
};
#endif // __OMV_PORTCONFIG_H__

View File

@ -0,0 +1,73 @@
# Copyright (C) 2023-2024 OpenMV, LLC.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Any redistribution, use, or modification in source or binary form
# is done solely for personal benefit and not for any commercial
# purpose or for monetary gain. For commercial licensing options,
# please contact openmv@openmv.io
#
# THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
# OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
all: $(FW_DIR)/firmware.toc
ifeq ($(MCU_CORE),M55_HP)
ALIF_TOC_APPS = $(FW_DIR)/firmware_M55_HP.bin
else ifeq ($(MCU_CORE),M55_HE)
ALIF_TOC_APPS = $(FW_DIR)/firmware_M55_HE.bin
else
ALIF_TOC_APPS = $(FW_DIR)/bootloader.bin $(FW_DIR)/firmware_M55_HP.bin $(FW_DIR)/firmware_M55_HE.bin
endif
# Force make commands to run the targets every time regardless of whether
# firmware.toc already exists to detect changes in the source files and rebuild.
.PHONY: $(FW_DIR)/bootloader.bin
.PHONY: $(FW_DIR)/firmware_M55_HE.bin
.PHONY: $(FW_DIR)/firmware_M55_HP.bin
SERIAL_PORT ?= /dev/ttyUSB0
ALIF_TOOLS = $(TOOLS)/alif/toolkit
ALIF_TOC_CONFIG = $(OMV_PORT_DIR)/alif_cfg.json
$(FW_DIR):
$(MKDIR) -p $@
$(FW_DIR)/bootloader.bin: | $(FW_DIR)
make -C $(TOP_DIR)/$(BOOT_DIR) MCU_CORE=M55_HP
$(FW_DIR)/firmware_M55_HP.bin: | $(FW_DIR)
make -f $(OMV_PORT_DIR)/alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0
$(FW_DIR)/firmware_M55_HE.bin: | $(FW_DIR)
make -f $(OMV_PORT_DIR)/alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1
$(FW_DIR)/firmware.toc: $(ALIF_TOC_APPS)
$(ALIF_TOOLS)/app-gen-toc.py \
--filename $(abspath $(ALIF_TOC_CONFIG)) \
--output-dir $(BUILD) \
--firmware-dir $(FW_DIR) \
--output $@
deploy: $(FW_DIR)/firmware.toc
$(ECHO) "Writing $< to the board"
$(ALIF_TOOLS)/app-write-mram.py \
--port $(SERIAL_PORT) \
--pad \
--images file:$(BUILD)/application_package.ds

View File

@ -0,0 +1,264 @@
/*
* Copyright (C) 2023-2024 OpenMV, LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Any redistribution, use, or modification in source or binary form
* is done solely for personal benefit and not for any commercial
* purpose or for monetary gain. For commercial licensing options,
* please contact openmv@openmv.io
*
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alif SPI driver.
*/
#include <stdint.h>
#include <string.h>
#include "omv_boardconfig.h"
#include "py/mphal.h"
#include "spi.h"
#include "alif_hal.h"
#include "omv_common.h"
#include "omv_spi.h"
#include "omv_gpio.h"
typedef struct omv_spi_descr {
SPI_Type *inst;
omv_gpio_t cs;
bool is_lp;
} omv_spi_descr_t;
static const omv_spi_descr_t omv_spi_descr_all[] = {
#if defined(OMV_SPI0_ID)
{ (SPI_Type *) SPI0_BASE, OMV_SPI0_SSEL_PIN, false },
#else
{ NULL, NULL, false },
#endif
#if defined(OMV_SPI1_ID)
{ (SPI_Type *) SPI1_BASE, OMV_SPI1_SSEL_PIN, false },
#else
{ NULL, NULL, false },
#endif
#if defined(OMV_SPI2_ID)
{ (SPI_Type *) SPI2_BASE, OMV_SPI2_SSEL_PIN, false },
#else
{ NULL, NULL, false },
#endif
#if defined(OMV_SPI3_ID)
{ (SPI_Type *) SPI3_BASE, OMV_SPI3_SSEL_PIN, false },
#else
{ NULL, NULL, false },
#endif
#if defined(OMV_SPI4_ID)
{ (SPI_Type *) LPSPI0_BASE, OMV_SPI4_SSEL_PIN, true },
#else
{ NULL, NULL, false },
#endif
};
int omv_spi_transfer_start(omv_spi_t *spi, omv_spi_transfer_t *xfer) {
const omv_spi_descr_t *spi_descr = &omv_spi_descr_all[spi->id];
if (xfer->flags & OMV_SPI_XFER_BLOCKING) {
spi_transfer_t spi_xfer = { 0 };
spi_xfer.tx_buff = xfer->txbuf;
spi_xfer.tx_total_cnt = xfer->size;
spi_xfer.rx_buff = xfer->rxbuf;
spi_xfer.rx_total_cnt = xfer->size;
spi_xfer.tx_default_val = 0xFF;
spi_xfer.tx_default_enable = true;
spi_xfer.mode = SPI_TMOD_TX_AND_RX;
if (!spi_descr->is_lp) {
spi_transfer_blocking(spi->inst, &spi_xfer);
} else {
lpspi_transfer_blocking(spi->inst, &spi_xfer);
}
return 0;
}
return -1;
}
int omv_spi_transfer_abort(omv_spi_t *spi) {
return 0;
}
int omv_spi_init(omv_spi_t *spi, omv_spi_config_t *config) {
memset(spi, 0, sizeof(omv_spi_t));
const omv_spi_descr_t *spi_descr = &omv_spi_descr_all[config->id];
spi->id = config->id;
spi->inst = spi_descr->inst;
spi->cs = spi_descr->cs;
spi->is_lp = spi_descr->is_lp;
if (spi->inst == NULL) {
return -1;
}
if (config->dma_flags) {
// TODO DMA mode not supported.
return -1;
}
// Disable SPI.
spi_disable(spi->inst);
// Initialize GPIOs and clocks.
alif_hal_spi_init(config->id, config->nss_enable, config->nss_pol);
// Disable all interrupts.
spi_mask_interrupts(spi->inst);
// Configure baudrate clock
omv_spi_set_baudrate(spi, config->baudrate);
// Configure FIFOs
spi_set_tx_threshold(spi->inst, 0);
spi_set_rx_threshold(spi->inst, 0);
if (!spi_descr->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 = (config->clk_pol << 1) | config->clk_pha;
if (!spi_descr->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_descr->is_lp) {
spi_set_protocol(spi->inst, spi_proto);
} else{
lpspi_set_protocol(spi->inst, spi_proto);
}
// Configure SPI transfer mode.
if (!spi_descr->is_lp) {
if (config->spi_mode == OMV_SPI_MODE_MASTER) {
spi_mode_master(spi->inst);
//ctrl_ss_in(spi->inst, SS_IN_IO_PIN);
} else {
// TODO not support.
//spi_mode_slave(spi->inst);
}
}
// Configure frame size.
if (!spi_descr->is_lp) {
spi_set_dfs(spi->inst, config->datasize);
} else {
lpspi_set_dfs(spi->inst, config->datasize);
}
// Configure slave select pin
spi_control_ss(spi->inst, 0, true);
if (!spi_descr->is_lp) {
spi_set_sste(spi->inst, false);
} else{
lpspi_set_sste(spi->inst, false);
}
(void) spi->inst->SPI_ICR;
spi_enable(spi->inst);
spi->initialized = true;
return 0;
}
int omv_spi_deinit(omv_spi_t *spi) {
// Disable all interrupts.
spi_mask_interrupts(spi->inst);
// Disable SCLK clock
omv_spi_set_baudrate(spi, 0);
// Disable SS pin.
spi_control_ss(spi->inst, 0, 0);
// Disable SPI.
spi_disable(spi->inst);
// Deinitialize GPIOs and clocks.
alif_hal_spi_deinit(spi->id);
return 0;
}
int omv_spi_set_baudrate(omv_spi_t *spi, uint32_t baudrate) {
omv_spi_transfer_abort(spi);
uint32_t sclk_source = spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock();
spi_set_bus_speed(spi->inst, baudrate, sclk_source);
return 0;
}
int omv_spi_default_config(omv_spi_config_t *config, uint32_t bus_id) {
config->id = bus_id;
config->baudrate = 4000000;
config->datasize = 8;
config->spi_mode = OMV_SPI_MODE_MASTER;
config->bus_mode = OMV_SPI_BUS_TX_RX;
config->bit_order = OMV_SPI_MSB_FIRST;
config->clk_pol = OMV_SPI_CPOL_HIGH;
config->clk_pha = OMV_SPI_CPHA_1EDGE;
config->nss_pol = OMV_SPI_NSS_LOW;
config->nss_enable = true;
config->dma_flags = 0;
config->data_retained = true;
return 0;
}
int omv_spi_test() {
static omv_spi_t spi_bus;
omv_spi_config_t spi_config;
omv_spi_default_config(&spi_config, OMV_IMU_SPI_ID);
spi_config.baudrate = 500000;
spi_config.clk_pol = OMV_SPI_CPOL_LOW;
spi_config.clk_pha = OMV_SPI_CPHA_1EDGE;
spi_config.nss_enable = false; // Soft NSS
omv_spi_init(&spi_bus, &spi_config);
while (true) {
omv_spi_transfer_t spi_xfer = {
.timeout = 0,
.flags = OMV_SPI_XFER_BLOCKING,
.callback = NULL,
.userdata = NULL,
};
uint8_t data[] = {0x7f, 0x8f, 0x9f, 0xAA, 0xBB };
spi_xfer.size = sizeof(data);
spi_xfer.txbuf = data;
spi_xfer.rxbuf = NULL;
omv_gpio_write(spi_bus.cs, 0);
omv_spi_transfer_start(&spi_bus, &spi_xfer);
omv_gpio_write(spi_bus.cs, 1);
mp_hal_delay_ms(500);
}
}

76
tools/alif_pins_gen.py Normal file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env python3
# This file is part of the OpenMV project.
#
# Copyright (c) 2024 Ibrahim Abdelkader <iabdalkader@openmv.io>
# Copyright (c) 2024 Kwabena W. Agyeman <kwagyeman@openmv.io>
#
# This work is licensed under the MIT license, see the file LICENSE for details.
import sys
import re
import argparse
import xml.etree.ElementTree as ET
def parse_svd_file(svd_file):
tree = ET.parse(svd_file)
root = tree.getroot()
pins_data = []
pins_element = root.find(".//pins")
if pins_element is not None:
for pin_elem in pins_element.findall(".//pin"):
pin_name = pin_elem.get("name")
# Use regular expression to check if pin_name matches the pattern P<digit>_<digit>
if re.match(r"^P\d+_\d+$", pin_name):
mux_modes = pin_elem.get("muxModes").split(", ")
# Replace "Reserved" with None and pad to 8 functions with None
mux_modes = [None if mode == "Reserved" else mode for mode in mux_modes]
mux_modes.extend([None] * (8 - len(mux_modes)))
pins_data.append((pin_name,) + tuple(mux_modes))
return pins_data
def main():
parser = argparse.ArgumentParser(description = "Usage: python gen_iomux_pins.py <fsl_iomuxc.h>.")
parser.add_argument("--svd", action = "store", help = "Input SVD file.", required=True)
args = parser.parse_args()
svd_file = sys.argv[1]
pins_data = parse_svd_file(args.svd)
print("// THIS FILE IS AUTO-GENERATED DO NOT EDIT.\n")
print("/*")
print(" * This file is part of the OpenMV project.")
print(" *")
print(" * Copyright (c) 2024 Ibrahim Abdelkader <iabdalkader@openmv.io>")
print(" * Copyright (c) 2024 Kwabena W. Agyeman <kwagyeman@openmv.io>")
print(" *")
print(" * This work is licensed under the MIT license, see the file LICENSE for details.")
print(" */")
for pin_info in pins_data:
pin_name, *mux_modes = pin_info
port, pin = pin_name.split("P")[1].split("_")
pin_name = "PORT_"+port+"_PIN_"+pin
print("// ", pin_name)
for i, function_name in enumerate(mux_modes):
if function_name is not None:
# Remove the GPIO number from the generated mux GPIO function
if i == 0 and function_name.startswith("GPIO"):
function_name = "GPIO"
# Remove the last part of a mux function name if it has 3 parts
parts = function_name.split("_")
if len(parts) == 3:
function_name = "_".join(parts[:2])
print(f"#define {f'{pin_name}_AF_{function_name}'.ljust(30)} PINMUX_ALTERNATE_FUNCTION_{i}")
print("")
if __name__ == "__main__":
main()