# ================================================================================ #
# NEORV32 Main Software Makefile                                                   #
# -------------------------------------------------------------------------------- #
# The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32              #
# Copyright (c) NEORV32 contributors.                                              #
# Copyright (c) 2020 - 2026 Stephan Nolting. All rights reserved.                  #
# Licensed under the BSD-3-Clause license, see LICENSE for details.                #
# SPDX-License-Identifier: BSD-3-Clause                                            #
# ================================================================================ #

# -----------------------------------------------------------------------------
# Default configuration (do not edit this file! redefine / override the default
# configuration when including this makefile in the project-specific makefile)
# -----------------------------------------------------------------------------

# User's application sources (*.c, *.cpp, *.s, *.S)
APP_SRC ?= $(wildcard ./*.c) $(wildcard ./*.s) $(wildcard ./*.cpp) $(wildcard ./*.S)

# User's application object files (*.o)
APP_OBJ ?=

# User's application include folders (don't forget the '-I' before each entry)
APP_INC ?= -I .
# User's application include folders - for assembly files only (don't forget the '-I' before each entry)
ASM_INC ?= -I .

# Build folder for output
BUILD_DIR ?= build

# Optimization
EFFORT ?= -Os

# Compiler toolchain prefix
RISCV_PREFIX ?= riscv-none-elf-

# CPU architecture and ABI
MARCH ?= rv32i_zicsr_zifencei
MABI  ?= ilp32

# User flags for additional configuration (will be added to compiler flags)
USER_FLAGS ?=

# User-defined libraries
USER_LIBS ?=

# Relative or absolute path to the NEORV32 home folder
NEORV32_HOME ?= ../../..

# GDB arguments
GDB_ARGS ?= -ex "target extended-remote localhost:3333"

# GHDL simulation run arguments
GHDL_RUN_FLAGS ?=

# Serial device for UART bootloader upload
UART_TTY ?= /dev/ttyUSB1

# -----------------------------------------------------------------------------
# NEORV32 framework
# -----------------------------------------------------------------------------

# Path to NEORV32 linker script and startup file
NEORV32_COM_PATH = $(NEORV32_HOME)/sw/common
# Path to main NEORV32 library include files
NEORV32_INC_PATH = $(NEORV32_HOME)/sw/lib/include
# Path to main NEORV32 library source files
NEORV32_SRC_PATH = $(NEORV32_HOME)/sw/lib/source
# Path to NEORV32 executable generator
NEORV32_EXG_PATH = $(NEORV32_HOME)/sw/image_gen
# Path to NEORV32 rtl folder
NEORV32_RTL_PATH = $(NEORV32_HOME)/rtl
# Path to NEORV32 sim folder
NEORV32_SIM_PATH = $(NEORV32_HOME)/sim

# Core libraries (peripheral and CPU drivers)
CORE_SRC = $(wildcard $(NEORV32_SRC_PATH)/*.c)
# Application start-up code
CORE_SRC += $(NEORV32_COM_PATH)/crt0.S
# Linker script
LD_SCRIPT ?= $(NEORV32_COM_PATH)/neorv32.ld

# Main output files
APP_ELF ?= main.elf
APP_FLT ?= elf.bin
APP_ASM ?= main.asm
APP_EXE ?= neorv32_exe.bin
APP_VHD ?= neorv32_imem_image.vhd
APP_BIN ?= neorv32_raw_exe.bin
APP_HEX ?= neorv32_raw_exe.hex
APP_COE ?= neorv32_raw_exe.coe
APP_MEM ?= neorv32_raw_exe.mem
APP_MIF ?= neorv32_raw_exe.mif
BLD_VHD ?= neorv32_bootrom_image.vhd

# Define all sources
SRC  = $(APP_SRC)
SRC += $(CORE_SRC)

# Define search path for prerequisites
VPATH = $(sort $(dir $(SRC)))

# Define all object files
OBJ := $(patsubst %,$(BUILD_DIR)/%.o,$(notdir $(SRC)))
OBJ += $(APP_OBJ)

# -----------------------------------------------------------------------------
# Tools and flags
# -----------------------------------------------------------------------------

# Host OS
ifeq ($(OS),Windows_NT)
  PLATFORM := windows
else
  UNAME_S := $(shell uname -s)
  ifeq ($(UNAME_S),Darwin)
    PLATFORM := macos
  else ifeq ($(UNAME_S),Linux)
    PLATFORM := linux
  else
    PLATFORM := unknown
  endif
endif

# Compiler suite
CC      = $(RISCV_PREFIX)gcc
CXX     = $(RISCV_PREFIX)g++
OBJDUMP = $(RISCV_PREFIX)objdump
OBJCOPY = $(RISCV_PREFIX)objcopy
READELF = $(RISCV_PREFIX)readelf
SIZE    = $(RISCV_PREFIX)size
GDB     = $(RISCV_PREFIX)gdb

# Host's native compiler
CC_HOST = gcc -Wall -O -g

# System tools
ECHO  = @echo
CP    = cp
SED   = sed
RM    = rm
MKDIR = mkdir -p
CHMOD = chmod

# NEORV32 executable image generator
IMAGE_GEN = $(NEORV32_EXG_PATH)/image_gen
ifeq ($(PLATFORM),windows)
  IMAGE_GEN := $(IMAGE_GEN).exe
endif

# Compiler & linker flags
CC_FLAGS  = -march=$(MARCH) -mabi=$(MABI) $(EFFORT) -Wall -Wextra -ffunction-sections -fdata-sections
CC_FLAGS += -mno-fdiv -mstrict-align -mbranch-cost=10 -ffp-contract=off -g
CC_FLAGS += $(USER_FLAGS)
LD_LIBS   = -lm -lc -lgcc -nostartfiles -Wl,--gc-sections
LD_LIBS  += $(USER_LIBS)

# Allow users to use tool-specific flags
# Uses naming from https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
NEO_CFLAGS   = $(CC_FLAGS) $(CFLAGS)
NEO_CXXFLAGS = $(CC_FLAGS) $(CXXFLAGS)
NEO_LDFLAGS  = $(CC_FLAGS) $(LDFLAGS)
NEO_ASFLAGS  = $(CC_FLAGS) $(ASFLAGS)

# Add CC_FLAGS as string-define
NEO_CFLAGS   += -DCC_FLAGS_STR='"$(CC_FLAGS)"'
NEO_CXXFLAGS += -DCC_FLAGS_STR='"$(CC_FLAGS)"'

# -----------------------------------------------------------------------------
# Application output definitions
# -----------------------------------------------------------------------------

.PHONY: check info help elf_info elf_sections clean clean_all \
        elf asm exe bin hex coe mem mif image install all \
        sim upload gdb bootloader bl_image hdl_lists elf_symbols

.DEFAULT_GOAL := help

elf:     $(APP_ELF)
asm:     $(APP_ASM)
exe:     $(APP_EXE)
bin:     $(APP_BIN)
hex:     $(APP_HEX)
coe:     $(APP_COE)
mem:     $(APP_MEM)
mif:     $(APP_MIF)
image:   $(APP_VHD)
install: install-$(APP_VHD)
all:     elf asm exe bin hex coe mem mif image install

# -----------------------------------------------------------------------------
# Verbosity
# -----------------------------------------------------------------------------

ifeq ("$(origin V)", "command line")
  BUILD_VERBOSE=$(V)
endif
ifndef BUILD_VERBOSE
  BUILD_VERBOSE = 0
endif
ifeq ($(BUILD_VERBOSE),0)
  Q = @
else
  Q =
endif

# -----------------------------------------------------------------------------
# Image generator targets
# -----------------------------------------------------------------------------

# Compile image generator
$(IMAGE_GEN): $(NEORV32_EXG_PATH)/image_gen.c
	$(ECHO) Compiling image generator...
	$(Q)$(CC_HOST) $< -o $(IMAGE_GEN)
	$(Q)$(CHMOD) +rx $(IMAGE_GEN)

# -----------------------------------------------------------------------------
# Build targets: Assemble, compile, link
# -----------------------------------------------------------------------------

# Create the build directories if they don't exist
$(BUILD_DIR):
	$(Q)$(MKDIR) $(BUILD_DIR)

# Compile app *.s sources (assembly)
$(BUILD_DIR)/%.s.o: %.s | $(BUILD_DIR)
	$(Q)$(CC) -c $(NEO_ASFLAGS) -MMD -MP -MF $(BUILD_DIR)/$*.s.d -MT $(BUILD_DIR)/$*.s.o -I $(NEORV32_INC_PATH) $(ASM_INC) $< -o $@

# Compile app *.S sources (assembly + C pre-processor)
$(BUILD_DIR)/%.S.o: %.S | $(BUILD_DIR)
	$(Q)$(CC) -c $(NEO_ASFLAGS) -MMD -MP -MF $(BUILD_DIR)/$*.S.d -MT $(BUILD_DIR)/$*.S.o -I $(NEORV32_INC_PATH) $(ASM_INC) $< -o $@

# Compile app *.c sources
$(BUILD_DIR)/%.c.o: %.c | $(BUILD_DIR)
	$(Q)$(CC) -c $(NEO_CFLAGS) -MMD -MP -MF $(BUILD_DIR)/$*.c.d -MT $(BUILD_DIR)/$*.c.o -I $(NEORV32_INC_PATH) $(APP_INC) $< -o $@

# Compile app *.cpp sources
$(BUILD_DIR)/%.cpp.o: %.cpp | $(BUILD_DIR)
	$(Q)$(CXX) -c $(NEO_CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)/$*.cpp.d -MT $(BUILD_DIR)/$*.cpp.o -I $(NEORV32_INC_PATH) $(APP_INC) $< -o $@

# Link object files and show memory utilization
-include $(OBJ:.o=.d)
$(APP_ELF): $(OBJ)
	$(Q)$(CC) $(NEO_LDFLAGS) -T $(LD_SCRIPT) $^ $(LD_LIBS) -o $@
	$(ECHO) "Memory utilization:"
	$(Q)$(SIZE) $(APP_ELF)

# Generate flat binary
$(APP_FLT): $(APP_ELF)
	$(Q)$(OBJCOPY) -O binary $^ $@

# Assembly listing file (for debugging)
$(APP_ASM): $(APP_ELF)
	$(Q)$(OBJDUMP) -d -S -z $< > $@

# -----------------------------------------------------------------------------
# Image file targets: Generate executable formats
# -----------------------------------------------------------------------------

# Generate NEORV32 executable image for upload via bootloader
$(APP_EXE): $(APP_ELF) $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_EXE)"
	$(Q)$(IMAGE_GEN) -t exe -b $(shell $(READELF) -h $(APP_ELF) | $(SED) -n 's/.*Entry point address: *//p') -i $(APP_FLT) -o $@

# Generate NEORV32 RAW executable VHDL boot image
$(APP_VHD): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_VHD)"
	$(Q)$(IMAGE_GEN) -t vhd -i $< -o $@

# Generate NEORV32 RAW executable image in binary format
$(APP_BIN): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_BIN)"
	$(Q)$(IMAGE_GEN) -t bin -i $< -o $@

# Generate NEORV32 RAW executable image in hex format
$(APP_HEX): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_HEX)"
	$(Q)$(IMAGE_GEN) -t hex -i $< -o $@

# Generate NEORV32 RAW executable image in COE format
$(APP_COE): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_COE)"
	$(Q)$(IMAGE_GEN) -t coe -i $< -o $@

# Generate NEORV32 RAW executable image in MIF format
$(APP_MIF): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_MIF)"
	$(Q)$(IMAGE_GEN) -t mif -i $< -o $@

# Generate NEORV32 RAW executable image in MEM format
$(APP_MEM): $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(APP_MEM)"
	$(Q)$(IMAGE_GEN) -t mem -i $< -o $@

# Create local VHDL BOOTROM image
bl_image: $(APP_FLT) $(IMAGE_GEN)
	$(ECHO) "Generating $(BLD_VHD)"
	$(Q)$(IMAGE_GEN) -t vhd -i $< -o $(BLD_VHD)

# Install BOOTROM image to VHDL source directory
bootloader: bl_image
	$(ECHO) "Installing bootloader image to $(NEORV32_RTL_PATH)/core/$(BLD_VHD)"
	$(Q)$(CP) $(BLD_VHD) $(NEORV32_RTL_PATH)/core/.

# -----------------------------------------------------------------------------
# In-console simulation using default testbench and GHDL
# -----------------------------------------------------------------------------

sim: $(APP_VHD)
	$(ECHO) "Simulating processor using default testbench..."
	$(Q)$(CHMOD) +rx $(NEORV32_SIM_PATH)/ghdl.sh
	$(Q)$(NEORV32_SIM_PATH)/ghdl.sh $(GHDL_RUN_FLAGS)

# Install VHDL memory initialization file
install-$(APP_VHD): $(APP_VHD)
	$(ECHO) "Installing application image to $(NEORV32_RTL_PATH)/core/$(APP_VHD)"
	$(Q)$(CP) $(APP_VHD) $(NEORV32_RTL_PATH)/core/.

# -----------------------------------------------------------------------------
# Regenerate HDL file list file(s)
# -----------------------------------------------------------------------------

hdl_lists:
	$(Q)$(CHMOD) +rx $(NEORV32_RTL_PATH)/generate_file_lists.sh
	$(Q)$(NEORV32_RTL_PATH)/generate_file_lists.sh

# -----------------------------------------------------------------------------
# Show final ELF details (just for debugging)
# -----------------------------------------------------------------------------

elf_info: $(APP_ELF)
	$(Q)$(READELF) -h $(APP_ELF)

elf_symbols: $(APP_ELF)
	$(Q)$(OBJDUMP) -x $(APP_ELF)

elf_sections: $(APP_ELF)
	$(Q)$(READELF) -S $(APP_ELF)

# -----------------------------------------------------------------------------
# Upload to bootloader via UART
# -----------------------------------------------------------------------------

upload: $(APP_EXE)
	$(Q)$(CHMOD) +rx $(NEORV32_EXG_PATH)/uart_upload.sh
	$(Q)$(NEORV32_EXG_PATH)/uart_upload.sh $(UART_TTY) $(APP_EXE)

# -----------------------------------------------------------------------------
# Run GDB
# -----------------------------------------------------------------------------

gdb: $(APP_ELF)
	$(Q)$(GDB) $(APP_ELF) $(GDB_ARGS)

# -----------------------------------------------------------------------------
# Clean up
# -----------------------------------------------------------------------------

# remove all build artifacts
clean:
	$(Q)$(RM) -rf $(BUILD_DIR)
	$(Q)$(RM) -f $(APP_EXE) $(APP_ELF) $(APP_FLT) $(APP_BIN) $(APP_HEX) $(APP_COE) $(APP_MEM) $(APP_MIF) $(APP_ASM) $(APP_VHD) $(BLD_VHD)
	$(Q)$(RM) -f .gdb_history

# also remove image generator
clean_all: clean
	$(Q)$(RM) -f $(IMAGE_GEN)
	$(Q)$(RM) -rf $(NEORV32_SIM_PATH)/build

# -----------------------------------------------------------------------------
# Check toolchain
# -----------------------------------------------------------------------------

check: $(IMAGE_GEN)
	$(ECHO) "******************************************************"
	$(ECHO) $(CC)
	$(Q)$(CC) -v
	$(Q)$(CC) -march=help
	$(ECHO) "******************************************************"
	$(ECHO) $(OBJDUMP)
	$(Q)$(OBJDUMP) -V
	$(ECHO) "******************************************************"
	$(ECHO) $(OBJCOPY)
	$(Q)$(OBJCOPY) -V
	$(ECHO) "******************************************************"
	$(ECHO) $(READELF)
	$(Q)$(READELF) -v
	$(ECHO) "******************************************************"
	$(ECHO) $(SIZE)
	$(Q)$(SIZE) -V
	$(ECHO) "******************************************************"
	$(ECHO) $(IMAGE_GEN)
	$(Q)$(IMAGE_GEN) -h
	$(ECHO) "******************************************************"
	$(ECHO) $(CC_HOST)
	$(Q)$(CC_HOST) -v
	$(ECHO) ""
	$(ECHO) "Toolchain check OK"

# -----------------------------------------------------------------------------
# Show configuration
# -----------------------------------------------------------------------------

info:
	$(ECHO) "******************************************************"
	$(ECHO) "Project / Makefile Configuration"
	$(ECHO) "******************************************************"
	$(ECHO) "Platform: $(PLATFORM)"
	$(ECHO) "Source files: $(APP_SRC)"
	$(ECHO) "Include folder(s): $(APP_INC)"
	$(ECHO) "ASM include folder(s): $(ASM_INC)"
	$(ECHO) "NEORV32 home folder (NEORV32_HOME): $(NEORV32_HOME)"
	$(ECHO) "IMAGE_GEN: $(IMAGE_GEN)"
	$(ECHO) "Core source files:"
	$(ECHO) "$(CORE_SRC)"
	$(ECHO) "Core include folder:"
	$(ECHO) "$(NEORV32_INC_PATH)"
	$(ECHO) "Search path (VPATH)"
	$(ECHO) "$(VPATH)"
	$(ECHO) "Project object files:"
	$(ECHO) "$(OBJ)"
	$(ECHO) "LIBGCC:"
	$(Q)$(CC) -print-libgcc-file-name
	$(ECHO) "SEARCH-DIRS:"
	$(Q)$(CC) -print-search-dirs
	$(ECHO) "USER_LIBS: $(USER_LIBS)"
	$(ECHO) "LD_LIBS: $(LD_LIBS)"
	$(ECHO) "MARCH: $(MARCH)"
	$(ECHO) "MABI: $(MABI)"
	$(ECHO) "CC: $(CC)"
	$(ECHO) "OBJDUMP: $(OBJDUMP)"
	$(ECHO) "SIZE: $(SIZE)"
	$(ECHO) "DEBUGGER: $(GDB)"
	$(ECHO) "GDB_ARGS: $(GDB_ARGS)"
	$(ECHO) "GHDL_RUN_FLAGS: $(GHDL_RUN_FLAGS)"
	$(ECHO) "USER_FLAGS: $(USER_FLAGS)"
	$(ECHO) "CC_FLAGS: $(CC_FLAGS)"

# -----------------------------------------------------------------------------
# Help
# -----------------------------------------------------------------------------

help::
	$(ECHO) "NEORV32 Software Makefile"
	$(ECHO) "Find more information at https://github.com/stnolting/neorv32"
	$(ECHO) "Use 'make V=1' or set BUILD_VERBOSE to increase build verbosity"
	$(ECHO) ""
	$(ECHO) "Targets:"
	$(ECHO) ""
	$(ECHO) "  help       show this text"
	$(ECHO) "  check      check toolchain and list supported ISA extensions"
	$(ECHO) "  info       show project/makefile configuration"
	$(ECHO) "  gdb        start GNU debugging session"
	$(ECHO) "  asm        build and generate <$(APP_ASM)> assembly listing file"
	$(ECHO) "  elf        build and generate <$(APP_ELF)> ELF file"
	$(ECHO) "  exe        build and generate <$(APP_EXE)> executable file for bootloader upload"
	$(ECHO) "  bin        build and generate <$(APP_BIN)> executable memory image"
	$(ECHO) "  hex        build and generate <$(APP_HEX)> executable memory image"
	$(ECHO) "  coe        build and generate <$(APP_COE)> executable memory image"
	$(ECHO) "  mem        build and generate <$(APP_MEM)> executable memory image"
	$(ECHO) "  mif        build and generate <$(APP_MIF)> executable memory image"
	$(ECHO) "  image      build and generate VHDL IMEM application memory image <$(APP_VHD)> in local folder"
	$(ECHO) "  install    build, generate and install VHDL IMEM application memory image <$(APP_VHD)>"
	$(ECHO) "  clean      clean up project home folder"
	$(ECHO) "  clean_all  clean up project home folder and image generator"
	$(ECHO) "  all        elf + asm + exe + bin + coe + mem + mif + image + install"
	$(ECHO) ""
	$(ECHO) "Additional targets:"
	$(ECHO) ""
	$(ECHO) "  sim           in-console simulation using default testbench (sim folder) and GHDL"
	$(ECHO) "  hdl_lists     regenerate HDL file-lists (*.f) in NEORV32_HOME/rtl"
	$(ECHO) "  upload        upload executable to bootloader via UART ($(UART_TTY))"
	$(ECHO) "  elf_info      show ELF information"
	$(ECHO) "  elf_sections  show ELF sections"
	$(ECHO) "  bl_image      build and generate VHDL BOOTROM bootloader memory image <$(BLD_VHD)> in local folder"
	$(ECHO) "  bootloader    build, generate and install VHDL BOOTROM bootloader memory image <$(BLD_VHD)>"
	$(ECHO) ""
	$(ECHO) "Variables:"
	$(ECHO) ""
	$(ECHO) "  BUILD_VERBOSE   Set to increase build verbosity: \"$(BUILD_VERBOSE)\""
	$(ECHO) "  USER_FLAGS      Custom toolchain flags [append only]: \"$(USER_FLAGS)\""
	$(ECHO) "  USER_LIBS       Custom libraries [append only]: \"$(USER_LIBS)\""
	$(ECHO) "  EFFORT          Optimization level: \"$(EFFORT)\""
	$(ECHO) "  MARCH           Machine architecture: \"$(MARCH)\""
	$(ECHO) "  MABI            Machine binary interface: \"$(MABI)\""
	$(ECHO) "  APP_INC         C include folder(s) [append only]: \"$(APP_INC)\""
	$(ECHO) "  APP_SRC         C source folder(s) [append only]: \"$(APP_SRC)\""
	$(ECHO) "  APP_OBJ         Object file(s) [append only]: \"$(APP_OBJ)\""
	$(ECHO) "  ASM_INC         ASM include folder(s) [append only]: \"$(ASM_INC)\""
	$(ECHO) "  RISCV_PREFIX    Toolchain prefix: \"$(RISCV_PREFIX)\""
	$(ECHO) "  NEORV32_HOME    NEORV32 home folder: \"$(NEORV32_HOME)\""
	$(ECHO) "  GDB_ARGS        GDB arguments: \"$(GDB_ARGS)\""
	$(ECHO) "  UART_TTY        Serial port for upload to bootloader: \"$(UART_TTY)\""
	$(ECHO) "  GHDL_RUN_FLAGS  GHDL simulation run arguments: \"$(GHDL_RUN_FLAGS)\""
	$(ECHO) ""
