# Use standard shell for executing commands
SHELL = /bin/sh

# Get directory containing this file
MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

# Overwrite with empty String for verbose output
VERBOSE ?= @

# Xplained Mini 
ATMEGA_MODEL = 328pb

# Xplained Mini USB ID
USB_VENDORID ?= 03eb
USB_PRODUCTID ?= 2145

# Directory containing libspicboard
LIBSPICBOARDDIR ?= $(MAKEFILE_DIR)
# Path to Java SPiCsim
SPICSIMJAR      ?= $(addprefix $(wildcard $(LIBSPICBOARDDIR)/doc/),SPiCsim.jar)

# AVR Toolchain
CC      := avr-gcc
SIZE    := avr-size
GDB     := avr-gdb
OBJCOPY := avr-objcopy
OBJDUMP := avr-objdump

# AVRDUDE for flashing
AVRDUDE ?= avrdude
# Xplained Mini requires special configuration (shipped with libspicboard)
AVRDUDE_CONF ?= $(MAKEFILE_DIR)/doc/atmega$(ATMEGA_MODEL).conf
AVRDUDE_FLAGS := -p m$(ATMEGA_MODEL) -c xplainedmini -P usb

# AVaRICE (new Version required)
AVARICE ?= avarice
AVARICE_PORT ?= 2159
AVARICE_FLAGS ?= -5 -w localhost:$(AVARICE_PORT)
# Binaries in CIP
AVARICE_CIP := /local/avarice-i4spic-gitlab/bin/avarice
AVARICE_PROJ := /proj/i4spic/sys/tools/avarice

# Flags for compiler
# Warning! -fwhole-program breaks the linkage of additional files
CFLAGS = -ffreestanding -mmcu=atmega$(ATMEGA_MODEL) -std=gnu11 -funsigned-char -funsigned-bitfields -fshort-enums -fpack-struct -ffunction-sections -fdata-sections -Wextra -Werror -Wno-error=unused-parameter -Wl,--gc-sections -Wall -pedantic -pedantic-errors -DF_CPU=16000000 -I. -I./libspicboard/ -I$(LIBSPICBOARDDIR)
# Flags for linker
LDFLAGS ?= -L. -L./libspicboard/ -L$(LIBSPICBOARDDIR) -lspicboard -u sb_init

# Append custom AVRDUDE config to flags
ifneq ("$(wildcard $(AVRDUDE_CONF))","")
	AVRDUDE_FLAGS += -C +$(AVRDUDE_CONF)
endif

# Check if avr-gcc supports Atmega model out-of-the-box
ifeq "$(shell $(CC) --target-help | grep atmega$(ATMEGA_MODEL))" ""
	# Otherwise we use the shipped definitions
	CFLAGS += -B$(LIBSPICBOARDDIR) -B. -D__AVR_DEV_LIB_NAME__=m$(ATMEGA_MODEL)
endif

# Phony targets (virtual)
.PHONY: help clean %.flash %.debug %.sim %.dis

# Secondary targets (won't be deleted)
.SECONDARY: %.dbg.elf

help::
	@/bin/echo -e "\n" \
		"\e[1mSPiCboard v3 (generic) Makefile\e[0m\n\n" \
		"\tUsage: \e[4mmake\e[0;3m [target]\e[0m\n\n" \
		"Target could either be\n" \
		"	\e[3mclean\e[0m          (remove all files created by this Makefile)\n" \
		"	\e[3mhelp\e[0m           (show this)\n" \
		"	\e[3mconsole\e[0m        (connect with serial console)\n" \
		"or consist of the name of a source file and one of the following options:\n" \
		"	\e[2;3m[name]\e[0;3m.flash\e[0m   (flash to connected SPiCboard)\n" \
		"	\e[2;3m[name]\e[0;3m.sim\e[0m     (execute in SPiCboard simulator)\n" \
		"	\e[2;3m[name]\e[0;3m.debug\e[0m   (start a debug session)\n" \
		"	\e[2;3m[name]\e[0;3m.dis\e[0m     (disassemble the binary)\n" \
		"Compilation, linkage and translation of the object file is done automatically for the options above, but can also called explicitly:\n" \
		"	\e[2;3m[name]\e[0;3m.elf\e[0m     (compile and link to a GNU executable)\n" \
		"	\e[2;3m[name]\e[0;3m.dbg.elf\e[0m (compile and link a debuggable executable)\n" \
		"	\e[2;3m[name]\e[0;3m.hex\e[0m     (translate the GNU executable to intel hex)\n\n" \
		"Example: In case you want to compile and flash \e[3maufgabe1.c\e[0m, connect your SPiCboard and run\n" \
		"	\e[4mmake\e[0;3m \e[2;3maufgabe1\e[0;3m.flash\e[0m\n\n" \
		"This generic version is for single source files only. If you have a more advanced project (e.g. utilizing several source files or modules) you have to create your own Makefile.\n" \
		"Copy the contents of $(realpath $(lastword $(MAKEFILE_LIST))) into a \e[3mMakefile\e[0m located in your project directory and add a custom target.\n" \
		"In case you are not familar with GNU Make, have a look at \e[4mhttps://www.gnu.org/software/make/manual/make.html\e[0m for further information.\n\n"

clean::
	@echo "Cleaning..."
	$(VERBOSE) rm -f *.hex *.elf *.bin

%.flash: %.hex
	@echo "Flashing..."
	$(VERBOSE) if lsusb -d $(USB_VENDORID):$(USB_PRODUCTID) >/dev/null 2>&1 ; then \
		$(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$< ; \
	else \
		echo "SPiCboard not found - abort!" ; \
		echo "Please connect your SPiCboard v3 via USB and retry." ; \
		exit 1 ; \
	fi

%.debug: %.dbg.elf %.dbg.flash
	$(VERBOSE) if $(AVARICE) -h 2>&1 | grep "\-\-medbg" >/dev/null 2>&1 ; then \
		echo "Starting AVaRICE (listening on port $(AVARICE_PORT))" ; \
		$(AVARICE) $(AVARICE_FLAGS) & \
	elif [ -x $(AVARICE_CIP) ] ; then \
		echo "Starting CIP-AVaRICE (listening on port $(AVARICE_PORT))" ; \
		$(AVARICE_CIP) $(AVARICE_FLAGS) & \
	elif [ -x $(AVARICE_PROJ) ] ; then \
		echo "Starting i4spic-AVaRICE (listening on port $(AVARICE_PORT))" ; \
		$(AVARICE_PROJ) $(AVARICE_FLAGS) & \
	else \
		echo "This AVaRICE version does not support mEDBG - abort!" ; \
		echo "Please use the AVaRICE fork from https://gitlab.cs.fau.de/i4/spic/tools/AVaRICE" ; \
		exit 1 ; \
	fi ; \
	echo -e "Starting GDB (listening on port $(AVARICE_PORT))" ; \
	$(GDB) $< -ex "target remote :$(AVARICE_PORT)"

%.sim: %.elf
	@echo "Launching SPiCsim..."
	$(VERBOSE) java -jar $(SPICSIMJAR) $<

%.hex: %.elf
	$(VERBOSE) $(OBJCOPY) -O ihex -R .eeprom $< $@

%.bin: %.elf
	$(VERBOSE) $(OBJCOPY) -O binary -R .eeprom $< $@

%.dis: %.elf
	@echo "Disassembling..."
	$(VERBOSE) $(OBJDUMP) -d $< | less

%.elf: %.c
	@echo "Compiling $< (optimized for size)..."
	$(VERBOSE) $(CC) -Os $(CFLAGS) -o $@ $^ $(LDFLAGS)
	$(VERBOSE) $(SIZE) $@

%.dbg.elf: %.c
	@echo "Compiling $< (without optimizations)..."
	$(VERBOSE) $(CC) -O0 -ggdb3 $(CFLAGS) -o $@ $^ $(LDFLAGS)
	$(VERBOSE) $(SIZE) $@

console:
	$(VERBOSE) for dev in /sys/bus/usb/devices/* ; do \
		if [ -f "$$dev/idProduct" -a -f "$$dev/idVendor" -a "$$(cat $$dev/idProduct 2>/dev/null)" = "$(USB_PRODUCTID)" -a "$$(cat $$dev/idVendor 2>/dev/null)" = "$(USB_VENDORID)" ] ;  then \
			echo "Found SPiCboard v3 with serial $$(cat $$dev/serial) at $$dev - using the first TTY that comes along with default speed (38400 baud)..." ; \
			if tty=$$(cat $${dev}/*/tty/*/uevent | grep "^DEVNAME=" | head -n 1 | sed -e "s/^DEVNAME=/\/dev\//" ) && head -c 0 "$$tty" 2> /dev/null ; then \
				screen $$tty 38400 ; \
				exit 0 ; \
			else \
				echo "Not able to access/read $$tty" ; \
				echo "You might consider running" ; \
				echo "	sudo usermod -a -G $$(stat -c "%G" $$tty) $$(whoami)" ; \
				echo "	newgrp $$(stat -c "%G" $$tty)" ; \
				exit 1 ; \
			fi \
		fi \
	done ; \
	echo "No SPiCboard found - abort!" ; \
	exit 1
