# ------------------------------------------------
# Generic Makefile (based on gcc)
# Copyright (c) 2022, All Rights Reserved.
# @file    Makefile
# @author  Wei-Lun Hsu
# @version 1.0
# ------------------------------------------------

RED="\033[0;31m"
GREEN="\033[0;32m"
LIGHT_GREEN="\033[1;32m"
YELLOW="\033[0;33m"
LIGHT_YELLOW="\033[1;33m"
GREY="\033[0;37m"
BWHITE="\033[1;37m"
MAGENTA="\033[1;35m"
CYAN="\033[1;36m"
NC="\033[0m"


PHONY := all flash flash_nvr clean help
PHONY += gdb
PHONY += tags TAGS cscope gtags
PHONY += upload
######################################
# target
######################################
ifeq ("$(TARGET)","")
TARGET = Project
endif

SOC ?= pec930

ifeq (,$(filter $(MAKECMDGOALS),clean help))

ifeq ("$(SOC)","pec930")
	SOC_DRIVER = PEC930
else
$(error "Unknown SoC, User MUST define 'SOC' name !")
endif

endif

srctree=$(shell pwd)

export SOC_CPU_NAME TARGET srctree SOC_DRIVER


######################################
# building variables
######################################
# debug build?
DEBUG = y
OPT =
ifeq ("$(D)","0")
	DEBUG = n
	OPT = -O2
endif

V ?= $(VERBOSE)
ifeq ("$(V)","1")
    Q =
else
    Q = @
endif

PLATFORM = $(shell uname -o)
ifeq ("$(findstring Linux, $(PLATFORM))","Linux")
	ECHO = echo
	TUI = -tui
else
	ECHO = echo -e
	TUI =
endif

PYTHON := python3
PYTHON_VER := $(wordlist 2,4,$(subst ., ,$(shell $(PYTHON) --version 2>&1)))
PYTHON_VER := $(word 1,${PYTHON_VER})$(word 2,${PYTHON_VER})

export PLATFORM ECHO Q PYTHON PYTHON_VER TUI DEBUG OPT
#######################################
# paths
#######################################
ifeq ("$(O)","")
	OUT = out
else
	OUT = $(O)
endif

ifeq ("$(P)","")
	PROJ = Examples/template/demo_project
else
	PROJ = $(P)
endif

$(info Active SoC=$(SOC) @$(PROJ))

export OUT PROJ LIBBIN
#######################################
# binaries
#######################################
PREFIX = riscv64-unknown-elf-

ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
SZ = $(GCC_PATH)/$(PREFIX)size
GDB= $(GCC_PATH)/$(PREFIX)gdb
OBJCOPY = $(GCC_PATH)/$(PREFIX)objcopy
OBJDUMP = $(GCC_PATH)/$(PREFIX)objdump
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
SZ = $(PREFIX)size
GDB= $(PREFIX)gdb
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
endif

HEX = $(OBJCOPY) -O ihex
BIN = $(OBJCOPY) -O binary -S

ifeq ("$(findstring Linux, $(PLATFORM))","Linux")
	OPENOCD = openocd
else
	OPENOCD = openocd.exe
endif

OPENOCDCFG := $(srctree)/Tools/openocd/scripts/target/$(SOC)
#######################################
# CFLAGS
#######################################
# mcu
MCU =

# AS defines
AS_DEFS =

# C defines
C_DEFS =

# AS includes
AS_INCLUDES =

# C includes
C_INCLUDES =

ifeq ("$(DEBUG)","y")
	OPT += -g3 -ggdb -gdwarf-2 -O0
else
	CFLAGS += -DNDEBUG
endif

ASFLAGS = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections -fno-common
CFLAGS = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections -fno-common

CFLAGS += -std=gnu99


CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

export CFLAGS ASFLAGS C_INCLUDES AS_INCLUDES C_DEFS
#######################################
# LDFLAGS
#######################################
# link script
ifeq ("$(L)","")
	LDSCRIPT = $(srctree)/Drivers/$(SOC_DRIVER)/gcc_flashxip.ld
else
	LDSCRIPT = $(L)
endif

# libraries
LIBS =
LIBDIR =

LDFLAGS = $(MCU) -nodefaultlibs -nostartfiles -Wl,--cref \
	$(LIBDIR) \
	-Wl,-T$(LDSCRIPT) \
	-Wl,-Map=$(OUT)/$(TARGET).map

export LDFLAGS LDSCRIPT LIBS
######################################
# source
######################################
# C sources
C_SOURCES :=

# ASM sources
ASM_SOURCES :=

include $(srctree)/Drivers/drivers.mk

include $(srctree)/Common/common.mk

ifeq ("$(PROJ)","")
	$(error "Unknown project !!!")
else
	include $(srctree)/$(PROJ)/component.mk
endif


OBJECTS = $(addprefix $(OUT)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))

OBJECTS += $(addprefix $(OUT)/,$(notdir $(ASM_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASM_SOURCES)))

RELT_SOURCES = $(foreach srcpath,$(C_SOURCES),$(subst $(srctree), ,$(srcpath)))
RELT_SOURCES += $(foreach srcpath,$(ASM_SOURCES),$(subst $(srctree),,$(srcpath)))
RELT_INCLUDES = $(foreach incpath,$(C_INCLUDES),$(subst $(srctree),,$(incpath)))


TARGET_GDB_SCRIPT :=
#######################################
# build the application
#######################################
ASFLAGS += $(AS_DEFS) $(AS_INCLUDES)
CFLAGS += $(C_DEFS) $(C_INCLUDES)

LDFLAGS += \
	-Wl,-gc-sections \
	-Wl,--check-sections \
	-Wl,--start-group \
	-lc_nano -lgcc -lm \
	-Wl,--end-group

LDFLAGS += \
	-Wl,--no-warn-rwx-segments

# default action: build all
all: $(OUT) $(OUT)/$(TARGET).elf $(OUT)/$(TARGET).hex $(OUT)/$(TARGET).bin

$(OUT)/%.o: %.c Makefile | $(OUT)
	@$(ECHO) "  CC $@"
	$(Q)$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(OUT)/$(notdir $(<:.c=.lst)) $< -o $@

$(OUT)/%.o: %.S Makefile | $(OUT)
	@$(ECHO) "    CC $@"
	$(Q)$(AS) -c $(ASFLAGS) $(CFLAGS) $< -o $@

$(OUT)/$(TARGET).elf: $(OBJECTS) Makefile
	$(Q)$(CC) -Wl,--whole-archive $(OBJECTS) $(LIBS) -Wl,--no-whole-archive $(LDFLAGS) -o $@
	$(Q)$(OBJDUMP) -Sx $(OUT)/$(TARGET).elf > $(OUT)/$(TARGET).disa
	@$(ECHO) $(GREEN)"\nSize $(OUT)/$(TARGET).elf" $(NC)
	$(Q)$(SZ) $@

$(OUT)/%.hex: $(OUT)/%.elf | $(OUT)
	@$(ECHO) $(YELLOW) "Generate $(OUT)/$(TARGET).hex" $(NC)
	$(Q)$(HEX) $< $@

$(OUT)/%.bin: $(OUT)/%.elf | $(OUT)
	@$(ECHO) $(YELLOW) "Generate $(OUT)/$(TARGET).bin" $(NC)
	$(Q)$(BIN) $< $@
	$(Q)od -A n -t x4 --width=4 -v $@ > $(OUT)/$(TARGET).txt
	$(Q)$(ECHO) $(CYAN) "Project SoC=$(SOC) @$(PROJ)" $(NC)

$(OUT):
	@mkdir -p $@

flash: $(OUT)/$(TARGET).bin
	@$(ECHO) $(YELLOW) "Program $(OUT)/$(TARGET).elf to flash of DUT" $(NC)
	@$(OPENOCD) -s Tools/openocd/scripts -f Tools/openocd/scripts/target/$(SOC).cfg \
		-c "program $(OUT)/$(TARGET).bin verify 0; exit" 2>&1 | tee openocd.log


gdb: $(OUT)/$(TARGET).elf
ifeq ("$(DEBUG)","y")
	@$(ECHO) $(YELLOW) "Download and Run GDB" $(NC)
	@$(ECHO) "  load $(OUT)/$(TARGET).elf"
	@$(GDB) $< $(TUI) --command=$(TARGET_GDB_SCRIPT)  \
		-ex "set remotetimeout 240" \
		-ex "monitor reset halt resume" \
		-ex "set mem inaccessible-by-default off" \
		-ex "target remote | $(OPENOCD) -c \"gdb_port pipe; log_output openocd.log\" \
			-s Tools/openocd/scripts \
			-f Tools/openocd/scripts/target/$(SOC).cfg" \
		-ex "j _start"
else
	@$(ECHO) $(RED) "It MUST be compiled with debug mode !" $(NC)
endif


upload: $(OUT)/$(TARGET).bin
	@$(ECHO) $(YELLOW) "Download $(OUT)/$(TARGET).elf to flash of DUT and Run" $(NC)
	@$(OPENOCD) -s Tools/openocd/scripts -f Tools/openocd/scripts/target/$(SOC).cfg \
		-c "program $(OUT)/$(TARGET).elf verify reset exit" 2>&1 | tee openocd.log

clean:
	@$(ECHO) $(YELLOW) "Clean target" $(NC)
	@$(ECHO) "  Remove $(OUT)"
	@rm -fR $(OUT)
	@$(ECHO) "  Done\n"

tags TAGS cscope gtags: $(OUT)
	@rm -f GPATH GRTAGS GTAGS
	@$(srctree)/Tools/scripts/tags.sh $@


help:
	@$(ECHO) "----------------------------------------------------------------------"
	@$(ECHO) "Useful make targets:"
	@$(ECHO) ""
	@$(ECHO) "Options:"
	@$(ECHO) "    SOC: $(SOC)"
	@$(ECHO) "    P: project path (root: $(srctree))"
	@$(ECHO) "    D: debug mode (0: release, default: 1)"
	@$(ECHO) "    O: output path (default: out)"
	@$(ECHO) "    L: link-script path (default: gcc_flashxip.ld in Drivers)"
	@$(ECHO) ""
	@$(ECHO) "e.g. make P=Examples/template/demo_project"
	@$(ECHO) ""
	@$(ECHO) "  make clean                 - Remove most generated files"
	@$(ECHO) ""
	@$(ECHO) "  make flash                 - Program ELF file to flash of DUT"
	@$(ECHO) ""
	@$(ECHO) "  make upload                 - Program ELF file to flash of DUT and free-run"
	@$(ECHO) ""
	@$(ECHO) "  make gdb                   - Download elf to flash and run GDB for development"
	@$(ECHO) ""
	@$(ECHO) "  make tags/TAGS             - Generate tags file for editors"
	@$(ECHO) "  make cscope                - Generate cscope index"
	@$(ECHO) ""
	@$(ECHO) "  make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build"
	@$(ECHO) "----------------------------------------------------------------------"

#######################################
# dependencies
#######################################

-include $(wildcard $(OUT)/*.d)

.PHONY: $(PHONY)
