From 8214f5dfc88e3135096d0c5fa2bc88953dd585a5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 14 Jun 2025 15:40:30 +1000 Subject: [PATCH] Update port development guide to focus on minimal and STM32 ports - Replace Alif-specific examples with minimal and STM32 port patterns - Add minimal port examples for simplest implementation patterns - Use STM32 as the comprehensive reference for full-featured ports - Keep Alif only as reference for commit progression analysis - Update build system examples to show both minimal and STM32 approaches - Reference established ports (STM32, minimal, RP2) throughout --- MicroPython-New-Port-Development-Guide.md | 536 ++++++++++++++-------- 1 file changed, 347 insertions(+), 189 deletions(-) diff --git a/MicroPython-New-Port-Development-Guide.md b/MicroPython-New-Port-Development-Guide.md index 81539ad..e423151 100644 --- a/MicroPython-New-Port-Development-Guide.md +++ b/MicroPython-New-Port-Development-Guide.md @@ -1,6 +1,6 @@ # MicroPython New Port Development Guide -This comprehensive guide walks through creating a new MicroPython port, using the Alif port development as a case study. The guide is based on analysis of commit `ccc5935234` and subsequent development commits. +This comprehensive guide walks through creating a new MicroPython port. The guide uses the minimal port as the simplest reference implementation and the STM32 port as a comprehensive example. Recent port development patterns were analyzed from the Alif port's commit progression. ## Overview @@ -20,7 +20,7 @@ Before starting, ensure you have: ## Development Phases -The Alif port development followed this logical progression: +Most successful ports follow this logical progression: 1. **Foundation** - Core infrastructure and basic I/O 2. **System Services** - Timing, interrupts, memory management 3. **Peripheral Support** - Progressive addition of hardware interfaces @@ -53,55 +53,59 @@ Create these essential directories: - Use `MICROPY_CONFIG_ROM_LEVEL` for feature sets (MINIMAL/BASIC/EXTRA/FULL) - Define platform string: `#define MICROPY_PY_SYS_PLATFORM "yourport"` -**Template from Alif**: +**Minimal Configuration (from minimal port)**: ```c -// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h +// Minimal port uses the simplest possible configuration +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_MINIMUM) +// Type definitions - minimal port uses standard C types +typedef int32_t mp_int_t; +typedef uint32_t mp_uint_t; +typedef long mp_off_t; + +// Required for all ports +#define MICROPY_HW_BOARD_NAME "minimal" +#define MICROPY_HW_MCU_NAME "unknown-cpu" +``` + +**Full-Featured Configuration (STM32 port pattern)**: +```c +// STM32 port uses feature levels based on available flash/RAM #include #include - -// board specific definitions #include "mpconfigboard.h" +#include "stm32_hal.h" // Your hardware HAL +// Feature level (STM32 uses different levels for different chips) #ifndef MICROPY_CONFIG_ROM_LEVEL -#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) #endif -// Hardware features -#define MICROPY_HW_ENABLE_UART_REPL (1) -#define MICROPY_HW_ENABLE_USBDEV (1) +// Type definitions (STM32 pattern) +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; // type for offsets -// USB configuration -#define MICROPY_HW_USB_CDC (1) -#define MICROPY_HW_USB_VID (0xf055) -#define MICROPY_HW_USB_PID (0x9802) - -// Memory and performance -#define MICROPY_ALLOC_PATH_MAX (128) -#define MICROPY_QSTR_BYTES_IN_HASH (1) - -// MicroPython emitters (for ARM Cortex-M) -#define MICROPY_EMIT_THUMB (1) -#define MICROPY_EMIT_THUMB_ARMV7M (1) -#define MICROPY_EMIT_INLINE_THUMB (1) - -// Core Python features +// Python features (STM32 enables based on flash size) #define MICROPY_ENABLE_GC (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_SCHEDULER_DEPTH (8) -// Platform identification -#define MICROPY_PY_SYS_PLATFORM "yourport" +// Hardware features (STM32 pattern) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_ADC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_HAS_SWITCH (1) -// Extended modules +// Extended modules (STM32 includes many by default) #define MICROPY_PY_MACHINE (1) -#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/yourport/modmachine.c" -#define MICROPY_VFS (1) - -// Type definitions -typedef intptr_t mp_int_t; -typedef uintptr_t mp_uint_t; -typedef intptr_t mp_off_t; +#define MICROPY_PY_MACHINE_PIN_BASE (1) +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/stm32/modmachine.c" // Board hooks (define these as no-ops initially) #ifndef MICROPY_BOARD_STARTUP @@ -153,44 +157,75 @@ MICROPY_FLOAT_IMPL = double - Pin manipulation macros - Timing functions -**Template from Alif**: +**Minimal HAL (from minimal port)**: ```c -#include "py/ringbuf.h" -#include "shared/runtime/interrupt_char.h" -#include "irq.h" +// Minimal port shows the bare minimum required +static inline void mp_hal_set_interrupt_char(char c) {} -// Atomic sections (critical sections) +// Required timing functions (minimal stubs) +static inline mp_uint_t mp_hal_ticks_ms(void) { return 0; } +static inline void mp_hal_delay_ms(mp_uint_t ms) { (void)ms; } +``` + +**Full HAL (STM32 pattern)**: +```c +#include "py/mphal.h" +#include STM32_HAL_H + +// STM32 uses the HAL's critical section functions #define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() #define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) -// Event handling for efficient wait -#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \ - do { \ - if ((TIMEOUT_MS) < 0) { \ - __WFE(); \ - } else { \ - /* TODO: implement timeout */ \ - } \ - } while (0) +// STM32 implements proper timing using SysTick +extern volatile uint32_t systick_ms; +static inline mp_uint_t mp_hal_ticks_ms(void) { + return systick_ms; +} -// Declare pin type and manipulation functions -extern const mp_obj_type_t pin_type; -typedef struct _pin_obj_t { - mp_obj_base_t base; - qstr name; - // ... hardware-specific fields -} pin_obj_t; +// STM32 pin handling +#define mp_hal_pin_obj_t const pin_obj_t* +#define mp_hal_get_pin_obj(o) pin_find(o) +#define mp_hal_pin_od_low(p) mp_hal_pin_low(p) +#define mp_hal_pin_od_high(p) mp_hal_pin_high(p) ``` #### B. `mphalport.c` (HAL Implementation) **Purpose**: Implements the hardware abstraction functions. -**Key Functions** (analyze STM32/RP2 implementations): -- `mp_hal_delay_ms()` / `mp_hal_delay_us()` -- `mp_hal_ticks_ms()` / `mp_hal_ticks_us()` -- `mp_hal_stdout_tx_strn()` (debug output) -- `mp_hal_stdin_rx_chr()` (console input) +**Key Functions to Implement**: + +**From minimal port (bare minimum)**: +```c +void mp_hal_stdout_tx_strn(const char *str, size_t len) { + // Minimal: just write to UART + for (size_t i = 0; i < len; ++i) { + uart_tx_char(str[i]); + } +} +``` + +**From STM32 port (full implementation)**: +```c +void mp_hal_delay_ms(mp_uint_t ms) { + // STM32: proper delay with event handling + mp_uint_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + MICROPY_EVENT_POLL_HOOK + } +} + +int mp_hal_stdin_rx_chr(void) { + // STM32: check multiple input sources + for (;;) { + if (MP_STATE_PORT(pyb_stdio_uart) != NULL && + uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) { + return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart)); + } + MICROPY_EVENT_POLL_HOOK + } +} +``` ### Step 4: Main Entry Point @@ -198,68 +233,105 @@ typedef struct _pin_obj_t { **Purpose**: Main application loop and MicroPython initialization. -**Common Pattern Analysis** (all ports follow this structure): +**Minimal Implementation (from minimal port)**: ```c -#include "py/runtime.h" -#include "py/gc.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/stackctrl.h" -#include "shared/runtime/pyexec.h" - -// Hardware-specific includes -#include "your_hardware_headers.h" - -// Memory layout (defined in linker script) -extern uint8_t __StackTop, __StackLimit; -extern uint8_t __GcHeapStart, __GcHeapEnd; - -void _start(void) { - // 1. Hardware initialization - SystemInit(); // Clock setup, etc. +// Minimal port shows the absolute minimum required +int main(int argc, char **argv) { + mp_stack_ctrl_init(); + mp_stack_set_limit(STACK_LIMIT); - // 2. Board-specific startup - MICROPY_BOARD_STARTUP(); + #if MICROPY_ENABLE_GC + gc_init(heap, heap + sizeof(heap)); + #endif - // 3. Initialize interrupt system - SysTick_Config(SystemCoreClock / 1000); + mp_init(); - // 4. Early hardware init + #if MICROPY_REPL_EVENT_DRIVEN + pyexec_event_repl_init(); + for (;;) { + int c = mp_hal_stdin_rx_chr(); + if (pyexec_event_repl_process_char(c)) { + break; + } + } + #else + pyexec_friendly_repl(); + #endif + + mp_deinit(); + return 0; +} +``` + +**Full Implementation (STM32 pattern)**: + +```c +// STM32 main.c pattern - comprehensive initialization +int main(void) { + // STM32 HAL init + HAL_Init(); + + // Configure clocks + SystemClock_Config(); + + // Basic hardware init + powerctrl_check_enter_bootloader(); + + // Board-specific initialization MICROPY_BOARD_EARLY_INIT(); - // 5. Initialize peripherals - uart_init(); // For REPL - flash_init(); // For filesystem + // Enable caches (STM32-specific) + #if defined(MICROPY_BOARD_STARTUP) + MICROPY_BOARD_STARTUP(); + #endif - // 6. Set up MicroPython memory management - mp_stack_set_top(&__StackTop); - mp_stack_set_limit(&__StackTop - &__StackLimit - 1024); - gc_init(&__GcHeapStart, &__GcHeapEnd); + // Initialize systick for timing + systick_init(); + pendsv_init(); - // 7. Main execution loop + // Initialize storage + #if MICROPY_HW_ENABLE_STORAGE + storage_init(); + #endif + + // GC init with STM32's heap calculation + #if MICROPY_ENABLE_GC + gc_init(&_heap_start, &_heap_end); + #endif + + // Initialize stack pointer for stack checking + mp_stack_set_top(&_estack); + mp_stack_set_limit((char*)&_estack - (char*)&_sstack - STACK_LIMIT); + + // Main MicroPython loop for (;;) { - // Initialize MicroPython runtime + #if MICROPY_HW_ENABLE_STORAGE + storage_init(); + #endif + + // STM32 resets peripherals on soft reset + machine_init(); + + // Run MicroPython mp_init(); - // Initialize subsystems - readline_init0(); + // STM32 runs boot.py and main.py + #if MICROPY_VFS + pyexec_file_if_exists("boot.py"); + pyexec_file_if_exists("main.py"); + #endif - // Execute boot scripts - pyexec_frozen_module("_boot.py", false); - - // Execute user scripts - int ret = pyexec_file_if_exists("boot.py"); - if (ret & PYEXEC_FORCED_EXIT) { - goto soft_reset_exit; - } - - // Main REPL loop + // Run REPL for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { - if (pyexec_raw_repl() != 0) break; + if (pyexec_raw_repl() != 0) { + break; + } } else { - if (pyexec_friendly_repl() != 0) break; + if (pyexec_friendly_repl() != 0) { + break; + } } } @@ -300,7 +372,7 @@ void nlr_jump_fail(void *val) { **Build System Architecture Choices**: -1. **Pure Make** (STM32, SAMD, Alif) +1. **Pure Make** (STM32, SAMD, NRF, minimal) - Traditional approach for bare-metal ports - Direct control over build process - No external build system dependencies @@ -331,52 +403,28 @@ void nlr_jump_fail(void *val) { **Key Insight**: The Make wrapper pattern allows MicroPython to maintain a consistent `make` interface across all ports while accommodating vendor requirements. Users always run `make BOARD=xxx` regardless of underlying build system. -**Template from Alif (Pure Make)**: +**Minimal Makefile (from minimal port)**: ```makefile -################################################################################ -# Initial setup of Makefile environment - -BOARD ?= DEFAULT_BOARD -BOARD_DIR ?= boards/$(BOARD) -BUILD ?= build-$(BOARD) - -ifeq ($(wildcard $(BOARD_DIR)/.),) -$(error Invalid BOARD specified: $(BOARD_DIR)) -endif - +# Minimal port Makefile - simplest possible build include ../../py/mkenv.mk -include mpconfigport.mk -include $(BOARD_DIR)/mpconfigboard.mk -# qstr definitions (must come before including py.mk) -QSTR_DEFS += qstrdefsport.h +# qstr definitions (minimal port keeps it simple) +QSTR_DEFS = qstrdefsport.h # include py core make definitions include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -################################################################################ -# Project specific settings - CROSS_COMPILE ?= arm-none-eabi- -GIT_SUBMODULES += lib/tinyusb -# Include paths INC += -I. INC += -I$(TOP) INC += -I$(BUILD) -INC += -I$(BOARD_DIR) -# Source files SRC_C = \ main.c \ - mphalport.c \ - modmachine.c \ - machine_pin.c \ - -# Hardware-specific sources -SRC_C += \ - your_hardware_drivers.c \ + uart_core.c \ + mphalport.c OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) @@ -385,10 +433,66 @@ all: $(BUILD)/firmware.elf $(BUILD)/firmware.elf: $(OBJ) $(ECHO) "LINK $@" $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(SIZE) $@ include $(TOP)/py/mkrules.mk ``` +**Full-Featured Makefile (STM32 pattern)**: +```makefile +# STM32 Makefile pattern - comprehensive build system +BOARD ?= PYBV10 +BOARD_DIR ?= boards/$(BOARD) + +# If the build directory is not given, make it reflect the board name. +BUILD ?= build-$(BOARD) + +include ../../py/mkenv.mk +-include mpconfigport.mk +include $(BOARD_DIR)/mpconfigboard.mk + +# Configure for STM32 MCU family +CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]') +STARTUP_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).s + +# Select the cross compile prefix +CROSS_COMPILE ?= arm-none-eabi- + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -I$(BOARD_DIR) +INC += -Ilwip_inc + +# Basic source files +SRC_C = \ + main.c \ + system_stm32.c \ + stm32_it.c \ + mphalport.c \ + +SRC_O = \ + $(STARTUP_FILE) \ + gchelper.o \ + +# Add STM32 HAL drivers +SRC_STM32 = \ + $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal.c \ + hal_cortex.c \ + hal_dma.c \ + hal_gpio.c \ + hal_rcc.c \ + ) + +OBJ = $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_O)) +OBJ += $(addprefix $(BUILD)/, $(SRC_STM32:.c=.o)) + +include $(TOP)/py/py.mk +``` + ### Step 6: Board Configuration #### A. `boards/YOUR_BOARD/mpconfigboard.h` @@ -396,14 +500,33 @@ include $(TOP)/py/mkrules.mk **Purpose**: Board-specific hardware definitions. ```c -// Board-specific configuration +// Minimal board configuration (from minimal port) +#define MICROPY_HW_BOARD_NAME "minimal" +#define MICROPY_HW_MCU_NAME "Cortex-M4" -#define MICROPY_HW_BOARD_NAME "Your Board Name" -#define MICROPY_HW_MCU_NAME "Your MCU" +// STM32 board configuration pattern (from PYBV10) +#define MICROPY_HW_BOARD_NAME "PYBv1.0" +#define MICROPY_HW_MCU_NAME "STM32F405RG" -// Pin definitions -#define MICROPY_HW_UART_REPL_TX pin_A9 -#define MICROPY_HW_UART_REPL_RX pin_A10 +// Crystal frequencies +#define MICROPY_HW_CLK_PLLM (12) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (2) +#define MICROPY_HW_CLK_PLLQ (7) + +// UART config for REPL +#define MICROPY_HW_UART_REPL PYB_UART_1 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// Hardware features +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_HAS_SDCARD (1) +#define MICROPY_HW_HAS_LCD (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) // Flash configuration #define MICROPY_HW_FLASH_SIZE_MB (2) @@ -436,12 +559,20 @@ LD_FILES = mcu/your_mcu.ld **Purpose**: Define interrupt priority levels. +**STM32 Pattern** (`ports/stm32/irq.h`): ```c -// IRQ priority definitions -#define IRQ_PRI_SYSTICK (1) -#define IRQ_PRI_UART (6) -#define IRQ_PRI_USB (6) -#define IRQ_PRI_PENDSV (15) // Lowest priority +// STM32 uses NVIC priority grouping with 4 bits for preemption +// Higher number = lower priority +#define IRQ_PRI_SYSTICK NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0) +#define IRQ_PRI_UART NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 12, 0) +#define IRQ_PRI_FLASH NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 12, 0) +#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 14, 0) +#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0) + +// For simpler MCUs, use raw priority values +#define IRQ_PRI_SYSTICK (0x40) // Highest priority +#define IRQ_PRI_UART (0x80) +#define IRQ_PRI_PENDSV (0xc0) // Lowest priority ``` #### B. `pendsv.c` & `pendsv.h` (PendSV Handler) @@ -482,44 +613,52 @@ __GcHeapEnd = __StackLimit; **Purpose**: Implements the `machine` module. -**Common Pattern** (analyze STM32 implementation): +**STM32 Implementation Pattern**: ```c +// STM32's modmachine.c - demonstrates the include file pattern #include "py/runtime.h" #include "extmod/modmachine.h" +#include "drivers/dht/dht.h" -// Forward declarations for machine classes -extern const mp_obj_type_t machine_pin_type; +#if MICROPY_PY_MACHINE +// STM32 resets via NVIC static mp_obj_t machine_reset(void) { NVIC_SystemReset(); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); +MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); +// STM32 uses HAL to get unique ID static mp_obj_t machine_unique_id(void) { - // Return unique device ID - return mp_obj_new_bytes((const byte*)&your_unique_id, sizeof(your_unique_id)); + byte *id = (byte *)MP_HAL_UNIQUE_ID_ADDRESS; + return mp_obj_new_bytes(id, 12); } -static MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); +MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); -static const mp_rom_map_elem_t machine_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) }, - - { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, - { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, - - // Machine classes - { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, -}; -static MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); +// STM32 freq functions +static mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + // Get frequency + return mp_obj_new_int(HAL_RCC_GetSysClockFreq()); + } else { + // Set frequency (STM32 specific) + mp_int_t freq = mp_obj_get_int(args[0]); + if (!set_sys_clock_source(freq)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid freq")); + } + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); -const mp_obj_module_t machine_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&machine_module_globals, -}; +// The actual module uses MICROPY_PY_MACHINE_INCLUDEFILE +// This allows extmod/modmachine.c to handle the standard parts +// while the port adds its own specifics in modmachine.c +#include MICROPY_PY_MACHINE_INCLUDEFILE -MP_REGISTER_MODULE(MP_QSTR_machine, machine_module); +#endif // MICROPY_PY_MACHINE ``` ### Step 10: Pin Support @@ -617,12 +756,30 @@ MP_DEFINE_CONST_OBJ_TYPE( **Purpose**: Implement storage backend for filesystem. -**Common Pattern**: Implement a flash driver that provides: -- Block read/write interface -- Sector erase functionality -- Integration with `extmod/vfs.c` +**STM32 Storage Pattern**: -Study the Alif `ospi_flash.c` and `alif_flash.c` implementations. +STM32 implements storage through `storage.c` and `flashbdev.c`: + +```c +// STM32 storage.c pattern +static const mp_obj_base_t pyb_flash_obj = {&pyb_flash_type}; + +// STM32 defines a block device interface +static mp_obj_t pyb_flash_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); + FLASH_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); + return mp_const_none; +} + +// Register with VFS +MP_DEFINE_CONST_FUN_OBJ_3(pyb_flash_readblocks_obj, pyb_flash_readblocks); +``` + +**Key Points**: +- Implement block device protocol (readblocks, writeblocks, ioctl) +- Handle flash programming constraints (erase before write) +- Register with VFS for filesystem support ### Step 12: USB MSC Support @@ -647,7 +804,8 @@ SRC_C += machine_i2c.c 2. **Add hardware driver** (if needed): ```makefile -ALIF_SRC_C += drivers/source/i2c.c +# STM32 adds HAL drivers +SRC_STM32 += $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_i2c.c ``` #### B. Enable in Configuration @@ -660,23 +818,23 @@ ALIF_SRC_C += drivers/source/i2c.c #### C. Implement Peripheral Class -**Pattern Analysis** (from Alif I2C implementation): +**STM32 Peripheral Pattern** (I2C example): -1. **Define object structure**: +1. **Define object structure** (from STM32): ```c typedef struct _machine_i2c_obj_t { mp_obj_base_t base; - uint32_t i2c_id; - I2C_Type *i2c; // Hardware registers - mp_hal_pin_obj_t scl; // Pin objects - mp_hal_pin_obj_t sda; - uint32_t freq; // Configuration - uint32_t timeout; + I2C_HandleTypeDef *i2c; // STM32 HAL handle + bool is_initialised; + uint8_t scl; + uint8_t sda; } machine_i2c_obj_t; ``` -2. **Create instance table**: +2. **Create instance table** (STM32 pattern): ```c +// STM32 uses a macro to define I2C objects +#define I2C_OBJ(n) &machine_i2c_obj[n] static machine_i2c_obj_t machine_i2c_obj[] = { #if defined(MICROPY_HW_I2C0_SCL) [0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA}, @@ -726,7 +884,7 @@ For Bluetooth-enabled hardware: ### Step 16: Multi-core Support -For multi-core MCUs (like Alif Ensemble): +For multi-core MCUs (RP2 has dual-core support): 1. **Implement Open-AMP backend** (`mpmetalport.c`, `mpremoteprocport.c`) 2. **Add core-specific configurations** @@ -792,7 +950,7 @@ When vendor SDKs require CMake: ### Hardware Integration - **Follow existing peripheral patterns** - don't reinvent APIs -- **Handle hardware quirks gracefully** (see Alif I2C zero-length transfer workaround) +- **Handle hardware quirks gracefully** (see STM32 DMA constraints, RP2 PIO limitations) - **Add proper error handling** and timeouts ### Build System