mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
ports/alif: Add Alif Ensemble port.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
This commit is contained in:
parent
4e7849438d
commit
f5dd7de21a
@ -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 \
|
||||
|
||||
28
src/omv/ports/alif/LICENSE
Normal file
28
src/omv/ports/alif/LICENSE
Normal 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.
|
||||
0
src/omv/ports/alif/LICENSE.uncrustify
Normal file
0
src/omv/ports/alif/LICENSE.uncrustify
Normal file
169
src/omv/ports/alif/alif.ld.S
Normal file
169
src/omv/ports/alif/alif.ld.S
Normal 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
659
src/omv/ports/alif/alif.mk
Normal 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
|
||||
35
src/omv/ports/alif/alif_cfg.json
Normal file
35
src/omv/ports/alif/alif_cfg.json
Normal 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
|
||||
}
|
||||
}
|
||||
223
src/omv/ports/alif/alif_dave.c
Normal file
223
src/omv/ports/alif/alif_dave.c
Normal 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;
|
||||
}
|
||||
}
|
||||
714
src/omv/ports/alif/alif_dma.c
Normal file
714
src/omv/ports/alif/alif_dma.c
Normal 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]);
|
||||
}
|
||||
89
src/omv/ports/alif/alif_dma.h
Normal file
89
src/omv/ports/alif/alif_dma.h
Normal 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__
|
||||
532
src/omv/ports/alif/alif_hal.c
Normal file
532
src/omv/ports/alif/alif_hal.c
Normal 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);
|
||||
}
|
||||
44
src/omv/ports/alif/alif_hal.h
Normal file
44
src/omv/ports/alif/alif_hal.h
Normal 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__
|
||||
213
src/omv/ports/alif/alif_npu.c
Normal file
213
src/omv/ports/alif/alif_npu.c
Normal 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(ðosu_driver);
|
||||
}
|
||||
|
||||
int alif_npu_init(void) {
|
||||
// Initialize Ethos-U.
|
||||
if (ethosu_init(ðosu_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(ðosu_driver);
|
||||
ethosu_deinit(ðosu_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(ðosu_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;
|
||||
}
|
||||
1136
src/omv/ports/alif/alif_pins.h
Normal file
1136
src/omv/ports/alif/alif_pins.h
Normal file
File diff suppressed because it is too large
Load Diff
280
src/omv/ports/alif/main.c
Normal file
280
src/omv/ports/alif/main.c
Normal 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
|
||||
454
src/omv/ports/alif/modules/py_audio.c
Normal file
454
src/omv/ports/alif/modules/py_audio.c
Normal 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
|
||||
36
src/omv/ports/alif/modules/py_audio.h
Normal file
36
src/omv/ports/alif/modules/py_audio.h
Normal 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__
|
||||
511
src/omv/ports/alif/omv_csi.c
Normal file
511
src/omv/ports/alif/omv_csi.c
Normal 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
|
||||
104
src/omv/ports/alif/omv_gpio.c
Normal file
104
src/omv/ports/alif/omv_gpio.c
Normal 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);
|
||||
}
|
||||
}
|
||||
226
src/omv/ports/alif/omv_gpu.c
Normal file
226
src/omv/ports/alif/omv_gpu.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
506
src/omv/ports/alif/omv_i2c.c
Normal file
506
src/omv/ports/alif/omv_i2c.c
Normal 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, ®_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, ®_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;
|
||||
}
|
||||
56
src/omv/ports/alif/omv_mpconfigport.h
Normal file
56
src/omv/ports/alif/omv_mpconfigport.h
Normal 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>
|
||||
134
src/omv/ports/alif/omv_portconfig.h
Normal file
134
src/omv/ports/alif/omv_portconfig.h
Normal 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__
|
||||
73
src/omv/ports/alif/omv_portconfig.mk
Normal file
73
src/omv/ports/alif/omv_portconfig.mk
Normal 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
|
||||
264
src/omv/ports/alif/omv_spi.c
Normal file
264
src/omv/ports/alif/omv_spi.c
Normal 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
76
tools/alif_pins_gen.py
Normal 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()
|
||||
Loading…
Reference in New Issue
Block a user