Skip to content

Commit

Permalink
Merge pull request #9667 from Brandon-Hurst/port/max32690
Browse files Browse the repository at this point in the history
Add support for ADI MAX32690 microcontroller
  • Loading branch information
tannewt authored Nov 27, 2024
2 parents 67a818b + 24b53f5 commit 9659673
Show file tree
Hide file tree
Showing 52 changed files with 3,406 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,6 @@
[submodule "frozen/Adafruit_CircuitPython_Wiznet5k"]
path = frozen/Adafruit_CircuitPython_Wiznet5k
url = https://github.com/adafruit/Adafruit_CircuitPython_Wiznet5k
[submodule "ports/analog/msdk"]
path = ports/analog/msdk
url = https://github.com/analogdevicesinc/msdk.git
1 change: 1 addition & 0 deletions docs/shared_bindings_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from concurrent.futures import ThreadPoolExecutor

SUPPORTED_PORTS = [
"analog",
"atmel-samd",
"broadcom",
"cxd56",
Expand Down
1 change: 1 addition & 0 deletions docs/supported_ports.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Additional testing is limited.
.. toctree::
:maxdepth: 2

../ports/analog/README
../ports/atmel-samd/README
../ports/broadcom/README
../ports/cxd56/README
Expand Down
304 changes: 304 additions & 0 deletions ports/analog/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
# This file is part of the CircuitPython project: https://circuitpython.org
#
# SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc.
#
# SPDX-License-Identifier: MIT

# Includes mpconfigboard.mk & mpconfigport.mk,
# along with numerous other shared environment makefiles.
include ../../py/circuitpy_mkenv.mk

CROSS_COMPILE = arm-none-eabi-

# MCU_SERIES e.g. "max32"
# MCU_VARIANT e.g. "max32690"
# defined in mpconfigboard.mk
MCU_SERIES_LOWER := $(shell echo $(MCU_SERIES) | tr '[:upper:]' '[:lower:]')
MCU_SERIES_UPPER := $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]')
MCU_VARIANT_LOWER := $(shell echo $(MCU_VARIANT) | tr '[:upper:]' '[:lower:]')
MCU_VARIANT_UPPER := $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]')

# *******************************************************************************
#### MSDK INCLUDES ####
# Necessary for msdk makefiles
TARGET := $(MCU_VARIANT_UPPER)
TARGET_UC := $(MCU_VARIANT_UPPER)
TARGET_LC := $(MCU_VARIANT_LOWER)

MSDK_ROOT = ./msdk
MSDK_LIBS = $(MSDK_ROOT)/Libraries
CMSIS_ROOT = $(MSDK_LIBS)/CMSIS
ADI_PERIPH = $(MSDK_ROOT)/Libraries/PeriphDrivers
ADI_MISC_DRIVERS_DIR ?= $(MSDK_LIBS)/MiscDrivers
ADI_BOARD_DIR = $(MSDK_LIBS)/Boards/$(MCU_VARIANT_UPPER)/$(BOARD)

# For debugging the build
ifneq ($(BUILD_VERBOSE),"")
$(info MSDK_ROOT is $(MSDK_ROOT))
$(info MSDK_LIBS is $(MSDK_LIBS))
$(info CMSIS_ROOT is $(CMSIS_ROOT))
$(info ADI_PERIPH is $(ADI_PERIPH))
$(info ADI_MISC_DRIVERS_DIR is $(ADI_MISC_DRIVERS_DIR))
$(info ADI_BOARD_DIR is $(ADI_BOARD_DIR))
$(info MAXIM_PATH is $(MAXIM_PATH))
endif

# -----------------
# Sources & Include
# -----------------
# Define max32 die type for PeriphDriver Includes
# default to me18 for max32690
# more info:
# https://analogdevicesinc.github.io/msdk//USERGUIDE/#die-types-to-part-numbers
ifeq ($(MCU_VARIANT_LOWER), "max32690")
DIE_TYPE=me18
else
DIE_TYPE=me18
endif

PERIPH_SRC = $(ADI_PERIPH)/Source

INC += -I.
INC += -I../..
INC += -I$(BUILD)
INC += -I$(BUILD)/genhdr
INC += -I./../../lib/cmsis/inc
INC += -I./boards/
INC += -I./boards/$(BOARD)
INC += -I./peripherals/
INC += -I../../lib/mp-readline

INC += \
-I$(TOP)/$(BOARD_PATH) \
-I$(TOP)/lib/cmsis/inc \
-I$(CMSIS_ROOT)/Include \
-I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Include \
-I$(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) \
-I$(PERIPH_SRC)/SYS \
-I$(PERIPH_SRC)/CTB \
-I$(PERIPH_SRC)/DMA \
-I$(PERIPH_SRC)/FLC \
-I$(PERIPH_SRC)/GPIO \
-I$(PERIPH_SRC)/ICC \
-I$(PERIPH_SRC)/TMR \
-I$(PERIPH_SRC)/RTC \
-I$(PERIPH_SRC)/UART

INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC

SRC_MAX32 += \
$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/heap.c \
$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/system_$(MCU_VARIANT_LOWER).c \
$(PERIPH_SRC)/SYS/mxc_assert.c \
$(PERIPH_SRC)/SYS/mxc_delay.c \
$(PERIPH_SRC)/SYS/mxc_lock.c \
$(PERIPH_SRC)/SYS/nvic_table.c \
$(PERIPH_SRC)/SYS/pins_$(DIE_TYPE).c \
$(PERIPH_SRC)/SYS/sys_$(DIE_TYPE).c \
$(PERIPH_SRC)/CTB/ctb_$(DIE_TYPE).c \
$(PERIPH_SRC)/CTB/ctb_reva.c \
$(PERIPH_SRC)/CTB/ctb_common.c \
$(PERIPH_SRC)/DMA/dma_reva.c \
$(PERIPH_SRC)/DMA/dma_$(DIE_TYPE).c \
$(PERIPH_SRC)/FLC/flc_common.c \
$(PERIPH_SRC)/FLC/flc_$(DIE_TYPE).c \
$(PERIPH_SRC)/FLC/flc_reva.c \
$(PERIPH_SRC)/GPIO/gpio_common.c \
$(PERIPH_SRC)/GPIO/gpio_$(DIE_TYPE).c \
$(PERIPH_SRC)/GPIO/gpio_reva.c \
$(PERIPH_SRC)/ICC/icc_$(DIE_TYPE).c \
$(PERIPH_SRC)/ICC/icc_reva.c \
$(PERIPH_SRC)/RTC/rtc_$(DIE_TYPE).c \
$(PERIPH_SRC)/RTC/rtc_reva.c \
$(PERIPH_SRC)/TMR/tmr_common.c \
$(PERIPH_SRC)/TMR/tmr_revb.c \
$(PERIPH_SRC)/TMR/tmr_$(DIE_TYPE).c \
$(PERIPH_SRC)/UART/uart_common.c \
$(PERIPH_SRC)/UART/uart_$(DIE_TYPE).c \
$(PERIPH_SRC)/UART/uart_revb.c

SRC_C += $(SRC_MAX32) \
boards/$(BOARD)/board.c \
boards/$(BOARD)/pins.c \
peripherals/$(MCU_VARIANT_LOWER)/pins.c \
peripherals/$(MCU_VARIANT_LOWER)/gpios.c

# *******************************************************************************
### Compiler & Linker Flags ###
COMPILER ?= GCC

ifeq ($(COMPILER), GCC)

STARTUPFILE = $(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC/startup_$(MCU_VARIANT_LOWER).s
# STARTUPFILE = $(ADI_BOARD_DIR)/Source/startup_$(MCU_VARIANT_LOWER).s

# CircuitPython custom linkerfile (necessary for build steps & filesystems)
LINKERFILE = linking/$(MCU_VARIANT_LOWER)_cktpy.ld
LDFLAGS += -nostartfiles -specs=nano.specs
endif

SRC_S += supervisor/cpu.s \
$(STARTUPFILE)

# Needed to compile some MAX32 headers
CFLAGS += -D$(MCU_VARIANT_UPPER) \
-DTARGET_REV=0x4131 \
-DTARGET=$(MCU_VARIANT_UPPER) \
-DIAR_PRAGMAS=0 \
-DRISCV_LOAD=0 \
-DCONFIG_TRUSTED_EXECUTION_SECURE=0

# todo: add these for linkerfiles later on so that it's easier to add new boards
# -DFLASH_ORIGIN \
# -DFLASH_SIZE \
# -DSRAM_ORIGIN \
# -DSRAM_SIZE

CPU_CORE=cortex-m4
CFLAGS += -mthumb -mcpu=$(CPU_CORE) -mfloat-abi=softfp -mfpu=fpv4-sp-d16

# NOTE: Start with DEBUG=1 defaults for now
ifeq ($(DEBUG),)
DEBUG ?= 1
endif

ifeq ($(DEBUG),1)
COPT = -ggdb3 -Og -Os
else
COPT += -Os
endif

# TinyUSB CFLAGS
CFLAGS += \
-DCFG_TUSB_MCU=OPT_MCU_$(MCU_VARIANT_UPPER) \
-DBOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED \
-DCFG_TUSB_OS=OPT_OS_NONE \
-DCFG_TUD_CDC_TX_BUFSIZE=1024 \
-DCFG_TUD_CDC_RX_BUFSIZE=1024 \
-DCFG_TUD_MSC_BUFSIZE=4096 \
-DCFG_TUD_MIDI_RX_BUFSIZE=128 \
-DCFG_TUD_MIDI_TX_BUFSIZE=128 \
-DCFG_TUD_VENDOR_RX_BUFSIZE=1024 \
-DCFG_TUD_VENDOR_TX_BUFSIZE=1024

# Add TinyUSB sources
INC += -I../../lib/tinyusb/src
INC += -I../../supervisor/shared/usb
SRC_C += lib/tinyusb/src/portable/mentor/musb/dcd_musb.c

# Add port sources incl. any board functions
SRC_C += \
boards/$(BOARD)/board.c \
background.c \
mphalport.c \

CFLAGS += $(INC) -Werror -Wall -std=gnu11 -nostartfiles $(BASE_CFLAGS) $(COPT)

# Suppress some errors for MSDK
# cast-align warning will be suppressed;
# it gets generated by CircuitPy's TLSF memory allocator lib
CFLAGS += -Wno-error=unused-parameter \
-Wno-error=old-style-declaration \
-Wno-error=sign-compare \
-Wno-error=strict-prototypes \
-Wno-error=cast-qual \
-Wno-error=unused-variable \
-Wno-error=lto-type-mismatch \
-Wno-error=cast-align \
-Wno-error=nested-externs \
-Wno-error=sign-compare \
-Wno-cast-align \
-Wno-sign-compare \

ENTRY = Reset_Handler
LDFLAGS += $(CFLAGS) --entry $(ENTRY) -Wl,-nostdlib -Wl,-T,$(LINKERFILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections
LIBS := -lgcc -lc

# If not using CKTPY mathlib, use toolchain mathlib
ifndef INTERNAL_LIBM
LIBS += -lm
endif

# *******************************************************************************
### PORT-DEFINED BUILD RULES ###
# This section attempts to build the Python core, the supervisor, and any
# port-provided source code.
#
# QSTR sources are provided for the initial build step, which generates
# Python constants to represent C data which gets passed into the GC.

SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
$(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \
$(addprefix common-hal/, $(SRC_COMMON_HAL))

SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))

# There are duplicates between SRC_COMMON_HAL_EXPANDED and SRC_SHARED_MODULE_EXPANDED,
# because a few modules have files both in common-hal/ and shared-module/.
# Doing a $(sort ...) removes duplicates as part of sorting.
SRC_COMMON_HAL_SHARED_MODULE_EXPANDED = $(sort $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED))

# OBJ includes
OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o))
ifeq ($(INTERNAL_LIBM),1)
OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o))
endif
OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o))

# List of sources for qstr extraction
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_CIRCUITPY_COMMON) \
$(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED) $(SRC_MOD)
# Sources that only hold QSTRs after pre-processing.
SRC_QSTR_PREPROCESSOR +=

# Default build target
all: $(BUILD)/firmware.elf $(BUILD)/firmware.hex $(BUILD)/firmware.bin

clean-all:
rm -rf build-*

# Optional flash option when running within an installed MSDK to use OpenOCD
# Mainline OpenOCD does not yet have the MAX32's flash algorithm integrated.
# If the MSDK is installed, flash-msdk can be run to utilize the the modified
# openocd with the algorithms
MAXIM_PATH := $(subst \,/,$(MAXIM_PATH))
OPENOCD ?= $(MAXIM_PATH)/Tools/OpenOCD/openocd
OPENOCD_SCRIPTS ?= $(MAXIM_PATH)/Tools/OpenOCD/scripts
flash-msdk:
$(OPENOCD) -s $(OPENOCD_SCRIPTS) \
-f interface/cmsis-dap.cfg -f target/$(MCU_VARIANT_LOWER).cfg \
-c "program $(BUILD)/firmware.elf verify; init; reset; exit"

# flash target using JLink
JLINK_DEVICE = $(MCU_VARIANT_LOWER)

JLINKEXE ?= JLink.exe
JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 10000
COMMAND_FILE := tools/flash_max32.jlink

flash-jlink: $(BUILD)/firmware.bin
@$(JLINKEXE) -device $(MCU_VARIANT_UPPER) -NoGui 1 -CommandFile ${COMMAND_FILE}

$(BUILD)/firmware.elf: $(OBJ)
$(STEPECHO) "LINK $@"
$(Q)echo $^ > $(BUILD)/firmware.objs
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LINKERFILE) $(BUILD)

$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
$(STEPECHO) "Create $@"
$(Q)$(OBJCOPY) -O ihex $^ $@

$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(STEPECHO) "Create $@"
$(Q)$(OBJCOPY) -O binary $^ $@

# *******************************************************************************
### CKTPY BUILD RULES ###
include $(TOP)/py/mkrules.mk
59 changes: 59 additions & 0 deletions ports/analog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Analog Devices "MAX32" MCUs

This port brings CircuitPython to ADI's "MAX32" series of microcontrollers. These devices are mostly ARM Cortex-M4-based and focus on delivering performance at low-power levels. Currently this port only supports MAX32690.

## Structure of this port

- **`boards/:`** Board-specific definitions including pins, board initialization, etc.
- **`common-hal/:`** Port-specific implementations of CircuitPython common-hal APIs. When a new module is enabled, this is often where the implementation is found. Expected functions for modules in `common-hal` are usually found in `shared-bindings/` or `shared-module/` in the CircuitPy root directory.
- **`linking/:`** Linkerfiles customized for CircuitPython. These are distinct from the linkerfiles used in MSDK as they adopt the structure required by CircuitPython. They may also omit unused features and memory sections, e.g. Mailboxes, RISC-V Flash, & Hyperbus RAM for MAX32690.
- **`msdk:/`** SDK for MAX32 devices. More info on our GitHub: [Analog Devices MSDK GitHub](https://github.com/analogdevicesinc/msdk)
- **`peripherals:/`** Helper files for peripherals such as clocks, gpio, etc. These files tend to be specific to vendor SDKs and provide some useful functions for the common-hal interfaces.
- **`supervisor/:`** Implementation files for the CircuitPython supervisor. This includes port setup, usb, and a filesystem on a storage medium such as SD Card/eMMC, QSPI Flash, or internal flash memory. Currently the internal flash is used. This folder is the most important part of a port's core functionality for CircuitPython.
- **`supervisor/port.c:`** Port-specific startup code including clock initialization, console startup, etc.

- `. :` Build system and high-level interface to the CircuitPython core for the ADI port.

## Building for MAX32 devices

Ensure CircuitPython dependencies are up-to-date by following the CircuitPython introduction on Adafruit's Website: [Building CircuitPython - Introduction](https://learn.adafruit.com/building-circuitpython/introduction). You will require the [ARM GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads), with ARM GCC >=13.x. It is also necessary to fetch all submodules and build the `mpy-cross` compiler, per the "Building CircuitPython" guide.

Ensure the ARM toolchain is contained on your PATH. This can be done in MinGW or WSL by exporting a prefix to the PATH variable. The author's path is included below as an example:

$ export ARM_GNU_PATH=C:/x-tools/arm-win/arm-none-eabi-w64-i686-13.3rel1/bin
$ export PATH=$ARM_GNU_PATH:$PATH

This needs to be done each time you open a command environment to build CircuitPython. It can be useful to set up a simple shell script for this.

Once you have built `mpy-cross` and set up your build system for CircuitPython, you can build for MAX32 devices using the following commands:

$ cd ports/analog
$ make BOARD=<board from boards/ directory>

Be aware the build may take a long time without parallelizing via the `-jN` flag, where N is the # of cores on your machine.

## Flashing the board

Universal instructions on flashing MAX32 devices this project can be found in the **[MSDK User Guide](https://analogdevicesinc.github.io/msdk/USERGUIDE/)**.

In addition, a user may flash the device by calling `make` with the `flash-msdk` target from within the `ports/analog` directory, as below:

```
$ make BOARD=<target board> flash-msdk
```

This requires the following:
- A MAX32625PICO is connected to the PC via USB
- The PICO board shows up as a "DAPLINK" drive which implements the CMSIS-DAP interface.
- The PICO board is connected to the target board via a 10-pin SWD ribbon cable.
- If SWD connectors are not keyed, the P1 indicator (red line) on the SWD ribbon cable should match the P1 indicator on the board silkscreen near the 10-pin SWD connector.

## Using the REPL

Once the device is plugged in, it will enumerate via USB as both a USB Serial Device (CDC) and a Mass Storage Device (MSC). You can connect to the Python REPL with your favorite Serial Monitor program e.g. TeraTerm, VS Code, Putty, etc. Use any buadrate with 8-bit, No Parity, 1 Stop Bit (8N1) settings. From this point forward, you can run Python code on the MCU! If you want help with learning CircuitPython-specific code or learning Python in general, a good place to start is Adafruit's ["Welcome to CircuitPython"](https://learn.adafruit.com/welcome-to-circuitpython/) guide.

## Editing code.py

Python code may be executed from `code.py` the `CIRCUITPY:` drive. When editing this file, please be aware that some text editors will work better than others. A list of suggested text editors can be found at Adafruit's guide here: https://learn.adafruit.com/welcome-to-circuitpython/recommended-editors

Once you save `code.py`, it gets written back to the device you are running Circuitpython on, and will automatically run and output it's result to the REPL. You can also automatically reload and run code.py any time from the REPL by pressing CTRL+D.
Loading

0 comments on commit 9659673

Please sign in to comment.