# UniversalisOS Microkernel — Multi-target Makefile # # Usage: # make (default: QEMU lm3s6965evb) # make TARGET=stm32f0 (STM32F0 Nucleo boards) # make TARGET=stm32g0 (STM32G0 Nucleo boards) # make TARGET=stm32l0 (STM32L0 Nucleo/Discovery) # make TARGET=nxp_kl (NXP Kinetis KL25Z/KL43Z/KL46Z/KW41Z) # make TARGET=nxp_lpc (NXP LPC1114) # make TARGET=nuvoton_nano (Nuvoton NANO130) # make run (run in QEMU — ARMv6m only) # make clean (clean build artifacts) # === Target selection === TARGET ?= lm3s6965evb # Target → port directory mapping ifeq ($(TARGET),lm3s6965evb) PORT_DIR = ports/armv6m CPU = -mcpu=cortex-m0 -mthumb QEMU = qemu-system-arm QEMU_MACH = lm3s6965evb QEMU_CPU = cortex-m3 QEMU_MEM = 16K else ifeq ($(TARGET),stm32f0) PORT_DIR = ports/stm32f0 CPU = -mcpu=cortex-m0 -mthumb QEMU = qemu-system-arm QEMU_MACH = stm32f072b-discovery QEMU_CPU = cortex-m0 QEMU_MEM = 16K else ifeq ($(TARGET),stm32g0) PORT_DIR = ports/stm32g0 CPU = -mcpu=cortex-m0plus -mthumb QEMU = qemu-system-arm QEMU_MACH = stm32g071b-st QEMU_CPU = cortex-m0plus QEMU_MEM = 36K else ifeq ($(TARGET),stm32l0) PORT_DIR = ports/stm32l0 CPU = -mcpu=cortex-m0plus -mthumb QEMU = qemu-system-arm QEMU_MACH = stm32l073rz QEMU_CPU = cortex-m0plus QEMU_MEM = 20K else ifeq ($(TARGET),nxp_kl) PORT_DIR = ports/nxp_kl CPU = -mcpu=cortex-m0plus -mthumb QEMU = qemu-system-arm QEMU_MACH = frdm-kl25z QEMU_CPU = cortex-m0plus QEMU_MEM = 16K else ifeq ($(TARGET),nxp_lpc) PORT_DIR = ports/nxp_lpc CPU = -mcpu=cortex-m0 -mthumb QEMU = qemu-system-arm QEMU_MACH = lpc1114 QEMU_CPU = cortex-m0 QEMU_MEM = 4K else ifeq ($(TARGET),nuvoton_nano) PORT_DIR = ports/nuvoton_nano CPU = -mcpu=cortex-m0 -mthumb QEMU = qemu-system-arm QEMU_MACH = numaker-pfm-nano130 QEMU_CPU = cortex-m0 QEMU_MEM = 16K else ifeq ($(TARGET),lpc55s69) PORT_DIR = ports/lpc55s69 CPU = -mcpu=cortex-m33 -mthumb -mfloat-abi=hard QEMU = echo QEMU_MACH = mps2-an505 QEMU_CPU = cortex-m33 QEMU_MEM = 256K TIER_CFG = -DUOS_TIER=2 -DUOS_HAS_TZ=1 -DUOS_HAS_MPU=1 else ifeq ($(TARGET),mps2_m4) PORT_DIR = ports/mps2_m4 CPU = -mcpu=cortex-m4 -mthumb QEMU = qemu-system-arm QEMU_MACH = mps2-an386 QEMU_CPU = cortex-m4 QEMU_MEM = 512K TIER_CFG = -DUOS_TIER=1 -DUOS_HAS_MPU=1 CFLAGS_TIER = $(TIER_CFG) else ifeq ($(TARGET),nordic_nrf51) PORT_DIR = ports/nordic_nrf51 CPU = -mcpu=cortex-m0 -mthumb QEMU = qemu-system-arm QEMU_MACH = microbit QEMU_CPU = cortex-m0 QEMU_MEM = 16K else ifeq ($(TARGET),silabs_efm32) PORT_DIR = ports/silabs_efm32 CPU = -mcpu=cortex-m3 -mthumb QEMU = qemu-system-arm QEMU_MACH = lm3s811evb QEMU_CPU = cortex-m3 QEMU_MEM = 32K else ifeq ($(TARGET),ambiq_apollo) PORT_DIR = ports/ambiq_apollo CPU = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard QEMU = qemu-system-arm QEMU_MACH = ast1030-evb QEMU_CPU = cortex-m4 QEMU_MEM = 256K else ifeq ($(TARGET),esp32) XTENSA_ROOT = $(HOME)/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20260121/xtensa-esp-elf PORT_DIR = ports/esp32 override CC = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-gcc override AS = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-as override LD = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-ld override OBJCOPY = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-objcopy override SIZE = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-size CPU = -mlongcalls -mtext-section-literals -mabi=call0 CFLAGS = $(TIER_CFG) $(CPU) -Iinclude -Iports/esp32 -Iports/common -DUOS_GDBSTUB=1 LDFLAGS = -T ports/esp32/linker.ld QEMU = echo QEMU_MACH = esp32 QEMU_CPU = esp32 else ifeq ($(TARGET),esp32s3) XTENSA_ROOT = $(HOME)/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20260121/xtensa-esp-elf PORT_DIR = ports/esp32 override CC = $(XTENSA_ROOT)/bin/xtensa-esp32s3-elf-gcc override AS = $(XTENSA_ROOT)/bin/xtensa-esp32s3-elf-as override LD = $(XTENSA_ROOT)/bin/xtensa-esp32s3-elf-ld override OBJCOPY = $(XTENSA_ROOT)/bin/xtensa-esp32s3-elf-objcopy override SIZE = $(XTENSA_ROOT)/bin/xtensa-esp32s3-elf-size CPU = -mlongcalls -mtext-section-literals -mabi=call0 CFLAGS = $(TIER_CFG) $(CPU) -Iinclude -Iports/esp32 -Iports/common -DUOS_GDBSTUB=1 LDFLAGS = -T ports/esp32/linker_s3.ld QEMU = echo QEMU_MACH = esp32s3 QEMU_CPU = esp32s3 else ifeq ($(TARGET),esp32idf) XTENSA_ROOT = $(HOME)/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20260121/xtensa-esp-elf PORT_DIR = ports/esp32 override CC = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-gcc override AS = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-as override LD = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-ld override OBJCOPY = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-objcopy override SIZE = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-size CPU = -mlongcalls -mtext-section-literals -mabi=call0 CFLAGS = $(TIER_CFG) $(CPU) -Iinclude -Iports/esp32 -Iports/common -DUOS_GDBSTUB=1 -DUOS_FLASH_XIP=1 -DUOS_GDBSTUB=1 -DUOS_SYSTEM_CLOCK=80000000 LDFLAGS = -T ports/esp32/linker_idf.ld -N QEMU = qemu-system-xtensa QEMU_MACH = esp32 QEMU_CPU = esp32 else ifeq ($(TARGET),esp32hw) XTENSA_ROOT = $(HOME)/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20260121/xtensa-esp-elf PORT_DIR = ports/esp32 override CC = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-gcc override AS = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-as override LD = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-ld override OBJCOPY = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-objcopy override SIZE = $(XTENSA_ROOT)/bin/xtensa-esp32-elf-size CPU = -mlongcalls -mtext-section-literals -mabi=call0 HAL_ROOT = $(CURDIR)/../hal/espressif/components CFLAGS = $(TIER_CFG) $(CPU) -Iinclude -Iports/esp32 -Iports/common -DUOS_GDBSTUB=1 \ -I$(CURDIR)/../hal/espressif \ -I$(HAL_ROOT)/hal/esp32/include \ -I$(HAL_ROOT)/hal/include \ -I$(HAL_ROOT)/hal/platform_port/include \ -I$(HAL_ROOT)/esp_hal_uart/esp32/include \ -I$(HAL_ROOT)/esp_hal_uart/include \ -I$(HAL_ROOT)/esp_hal_wdt/esp32/include \ -I$(HAL_ROOT)/soc/esp32/include \ -I$(HAL_ROOT)/soc/esp32/register \ -I$(HAL_ROOT)/xtensa/esp32/include \ -I$(HAL_ROOT)/xtensa/include \ -I$(HAL_ROOT)/esp_common/include \ -I$(HAL_ROOT)/esp_rom/esp32 \ -DUOS_SYSTEM_CLOCK=80000000 -DUOS_FLASH_XIP=1 -DUOS_GDBSTUB=1 LDFLAGS = -T ports/esp32/linker_flash.ld -N QEMU = qemu-system-xtensa QEMU_MACH = esp32 QEMU_CPU = esp32 else $(error Unknown target: $(TARGET). Valid: lm3s6965evb stm32f0 stm32g0 stm32l0 nxp_kl nxp_lpc nuvoton_nano) endif # === Build configuration === CC = arm-none-eabi-gcc AS = arm-none-eabi-gcc LD = arm-none-eabi-gcc OBJCOPY = arm-none-eabi-objcopy SIZE ?= arm-none-eabi-size INCLUDES = -Iinclude -I$(PORT_DIR) -Iports/common TIER_CFG ?= ifeq ($(filter esp32 esp32s3 esp32hw esp32idf,$(TARGET)),) CFLAGS = $(TIER_CFG) $(CPU) -std=c99 -Os -Wall -Wextra -Werror \ -ffreestanding -fno-builtin -fno-exceptions \ -ffunction-sections -fdata-sections $(INCLUDES) endif ASFLAGS = -mabi=call0 $(INCLUDES) LIBGCC = $(shell $(CC) $(CPU) -print-libgcc-file-name) ifeq ($(filter esp32 esp32s3 esp32hw esp32idf,$(TARGET)),) LDFLAGS = $(CPU) -nostartfiles -Wl,--gc-sections -T $(PORT_DIR)/linker.ld endif BUILD_DIR = build/$(TARGET) # === Source files === CORE_DIR = src/core PERSONALITY_DIR = src/personality TEST_DIR = test # Architecture directory: PikeOS-style, one dir per ISA ifeq ($(filter esp32 esp32s3 esp32hw esp32idf,$(TARGET)),) ARCH_DIR = arch/arm else ARCH_DIR = arch/xtensa endif CORE_SRC = $(wildcard $(CORE_DIR)/*.c) PERSONALITY_SRC = $(wildcard $(PERSONALITY_DIR)/*.c) PORT_SRC = $(wildcard $(PORT_DIR)/*.c) PORT_ASM = $(wildcard $(PORT_DIR)/*.S) ARCH_SRC = $(wildcard $(ARCH_DIR)/src/*.c) ARCH_ASM = $(wildcard $(ARCH_DIR)/src/*.S) TEST_SRC = test/test_uos_esp32.c CORE_OBJ = $(patsubst $(CORE_DIR)/%.c,$(BUILD_DIR)/core/%.o,$(CORE_SRC)) PERSONALITY_OBJ = $(patsubst $(PERSONALITY_DIR)/%.c,$(BUILD_DIR)/pers/%.o,$(PERSONALITY_SRC)) PORT_OBJ = $(patsubst $(PORT_DIR)/%.c,$(BUILD_DIR)/port/%.o,$(PORT_SRC)) PORT_AOBJ = $(patsubst $(PORT_DIR)/%.S,$(BUILD_DIR)/port/%.o,$(PORT_ASM)) ARCH_OBJ = $(patsubst $(ARCH_DIR)/src/%.c,$(BUILD_DIR)/arch/%.o,$(ARCH_SRC)) ARCH_AOBJ = $(patsubst $(ARCH_DIR)/src/%.S,$(BUILD_DIR)/arch/%.o,$(ARCH_ASM)) TEST_OBJ = $(patsubst $(TEST_DIR)/%.c,$(BUILD_DIR)/test/%.o,$(TEST_SRC)) OBJS = $(CORE_OBJ) $(PERSONALITY_OBJ) $(PORT_OBJ) $(PORT_AOBJ) $(ARCH_OBJ) $(ARCH_AOBJ) $(TEST_OBJ) TARGET_ELF = $(BUILD_DIR)/universalisos.elf TARGET_BIN = $(BUILD_DIR)/universalisos.bin # === Rules === .PHONY: all clean run size all: $(TARGET_ELF) size $(TARGET_ELF): $(OBJS) $(LD) $(LDFLAGS) $(OBJS) $(LIBGCC) -o $@ $(OBJCOPY) -O binary $@ $(TARGET_BIN) $(BUILD_DIR)/core/%.o: $(CORE_DIR)/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/port/%.o: $(PORT_DIR)/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/pers/%.o: $(PERSONALITY_DIR)/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/port/%.o: $(PORT_DIR)/%.S @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/arch/%.o: $(ARCH_DIR)/src/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/arch/%.o: $(ARCH_DIR)/src/%.S @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/test/%.o: $(TEST_DIR)/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ size: $(TARGET_ELF) @$(SIZE) $(TARGET_ELF) # esp32hw: generate ESP32 flash image (requires esptool --chip esp32) ifeq ($(TARGET),esp32hw) flash-hw: $(TARGET_ELF) esptool --chip esp32 elf2image --flash-mode dio --flash-size 2MB --flash-freq 40m \ $(TARGET_ELF) -o $(BUILD_DIR)/uos_hw.bin @echo "ESP32 image: $(BUILD_DIR)/uos_hw.bin" @echo "Flash: esptool --chip esp32 --port /dev/ttyACM0 --baud 460800 write_flash 0x10000 $(BUILD_DIR)/uos_hw.bin" endif # === Debug / GDB === GDB ?= xtensa-esp32-elf-gdb GDB_BIN = $(HOME)/.espressif/tools/xtensa-esp-elf-gdb/16.3_20250913/xtensa-esp-elf-gdb/bin/xtensa-esp32-elf-gdb # QEMU with GDB stub (port 1234, halt at startup) debug-qemu: $(TARGET_ELF) $(QEMU) -M $(QEMU_MACH) -cpu $(QEMU_CPU) -m $(QEMU_MEM) \ -nographic -s -S -kernel $(TARGET_ELF) # GDB session against running QEMU stub debug: $(TARGET_ELF) $(GDB_BIN) -ex "target remote :1234" \ -ex "set confirm off" \ -ex "file $(TARGET_ELF)" \ -ex "symbol-file $(TARGET_ELF)" \ -ex "hb _start" \ -ex "c" $(TARGET_ELF) run: $(TARGET_ELF) $(QEMU) -M $(QEMU_MACH) -cpu $(QEMU_CPU) -m $(QEMU_MEM) \ -nographic -kernel $(TARGET_ELF) clean: rm -rf build