COMMON_DIR  := common
FPGA_BOARD = zybo_z7_10

TOCLEAN = $(filter %log %.jou %.rpt %.mmi %.dcp %.csv\
                   %.wdb vivado% updatemem% usage_statistics_webtalk%,$(wildcard *))
TOCLEAN += $(wildcard _*) .Xil out RTL build download.bit $(MEM_DIR)
TOCLEAN += ./bench/programs_rust/target/riscv64i/release

# macro de verbosité
VERB     := 0

# macro de vérification de variables d'environnement
check_vars = $(foreach v, $1, $(if $(filter undefined, $(origin $v)),\
		if [ "$v" = "XILINX_VIVADO" ]; then \
			echo "⚠️  Tapez : source /bigsoft/Xilinx/Vivado/2019.1/settings64.sh"; \
			exit 1; \
		else \
			echo "⚠️ ⚡⚡ Variable d'environnement $v manquante ⚡⚡⚠️ "; \
			$(MAKE) --no-print-directory help; \
			exit 1; \
		fi;))
 

all: help 

help:
	@awk 'BEGIN {FS = ":.*##!"; printf "Usage: make \033[32m<commande>\033[0m \
	[PROG=<nom du fichier de test sans extension>] \
	[CYCLES=durée de la simulation] [VERB=1 pour la verbosité]\
	\nCommandes par \033[36mcatégories :\n"} \
	/^[a-zA-Z0-9_-]+:.*##!/ { printf "  \033[32m%-15s\033[0m %s\n", $$1, $$2 } \
	/^##@/ { printf "\n\033[36m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)


###############################################################################


## Partie Compilation 

BENCH_DIR := bench
MEM_DIR := $(BENCH_DIR)/mem
SRC_SW := $(shell find $(BENCH_DIR) -name '*.s' -type f | sed 's/^bench\///g') 
SRC_SW += $(wildcard $(BENCH_DIR)/*.c) # Contient tous les .s et .c du projet
SRC_TESTS_SW := $(shell find $(BENCH_DIR)/tests -name '*.s' -type f | sed 's/^bench\///g')
MEM = $(addprefix $(MEM_DIR)/, $(addsuffix .mem, $(basename $(SRC_SW)))) # Tous les .mem du projet
MEM_TESTS = $(addprefix test-, $(addprefix $(MEM_DIR)/, $(addsuffix .mem, $(basename $(SRC_TESTS_SW))))) # Tous les .mem du projet en test
PREFIX  := $(shell command -v riscv64-unknown-elf-gcc >/dev/null 2>&1 && echo riscv64-unknown-elf- || echo /matieres/3MMFMN/riscv32/bin/riscv32-unknown-elf-)
CC      := $(PREFIX)gcc
OBJDUMP := $(PREFIX)objdump
OBJCOPY := $(PREFIX)objcopy
OBTOMEM := common/objtomem.awk

## Flags
ASFLAGS       :=-march=rv64im -mabi=lp64 -ffreestanding -nostdlib -T $(BENCH_DIR)/link.ld 
CFLAGS        :=$(ASFLAGS)
ELFFLAGS      :=$(ASFLAGS) $(MEM_DIR)/crt.o -Os -fno-unroll-loops 
ODFLAGS       :=-j .text -j .rodata -j .data -s 

## Paramètres de tests
TEST_MAX_RAM := 11000000 ## Limite de ram pour les tests en KB
TEST_MAX_PROCS := $$(($$(nproc) - 2)) ## Garde un processeur libre

.PRECIOUS: $(MEM_DIR)/%.elf

.PHONY: synthese fpga clean compile autotest simulation force
force:
	
compile: $(MEM)
$(MEM_DIR):
	@mkdir -p $@/tests/op
	@mkdir -p $@/programs
	@mkdir -p $@/programs_rust

.PRECIOUS: $(MEM_DIR)/programs_rust/%.elf
$(MEM_DIR)/programs_rust/%.elf: bench/programs_rust/% force $(MEM_DIR)/crt.o |$(MEM_DIR)
	cd bench/programs_rust && cargo build --release --bin=$(basename $(notdir $@))
	cp -f bench/programs_rust/target/riscv64i/release/$(basename $(notdir $@)) $@
$(MEM_DIR)/crt.o: $(BENCH_DIR)/crt.S |$(MEM_DIR)
	$(CC) $(CFLAGS) -c $< -o $@
$(MEM_DIR)/%.elf: $(BENCH_DIR)/%.c $(MEM_DIR)/crt.o |$(MEM_DIR)
	$(CC) $(ELFFLAGS) $< -o $@
$(MEM_DIR)/%.elf: $(BENCH_DIR)/%.s |$(MEM_DIR)
	$(CC) $(CFLAGS) -o $@ $<
$(MEM_DIR)/%.mem: $(MEM_DIR)/%.elf |$(MEM_DIR) 
	$(OBJCOPY) -O binary --verilog-data-width 8 $< $@.bin
	python3 common/swap_endian.py $@.bin $@.bin.swapped 8
	$(OBJCOPY) -I binary -O verilog --verilog-data-width 8 $@.bin.swapped $@
	rm $@.bin $@.bin.swapped

test-$(MEM_DIR)/%.mem: $(MEM_DIR)/%.mem |$(MEM_DIR) 
	@PROG="$(shell echo $< | sed "s/^bench\/mem\///g" | sed "s/\.mem$$//g")" PROOT=$(shell pwd) ./mill TPchisel.test.testOnly $(TOP_REP).$(TOP_TEST) -- -z "$(shell echo $< | sed "s/^bench\/mem\///g" | sed "s/\.mem$$//g")"


###############################################################################


##@ Simulation

TOP         := ZzTop
TOP_REP     := projet
PATH_SRC    := src/main/scala/$(TOP_REP)
TOP_TEST    := $(addsuffix Spec, $(TOP))

CYCLES=500
INSTRUCTIONS=1000
# Pour filtrer les tests dans une simulation 
ifdef PROG
	FILTRE_TEST:= -- -z "$(PROG)"
endif

mill:
	@curl -L https://raw.githubusercontent.com/lefou/millw/0.4.11/millw > mill 
	@chmod +x mill

autotest: mill test-bench/mem/tests/$(PROG).mem ##! Lance la simulation automatique pour tous les tests ou juste celui fourni dans PROG

testall: mill compile ##! Lance la simulation automatique pour tous les tests en parallèle
	@# Limite le nombre de processeurs et la ram utilisée pour les tests parallèle
	@( \
		ulimit -v $(TEST_MAX_RAM); \
		PROOT=$(PWD) taskset -c 0-$(TEST_MAX_PROCS) ./mill TPchisel.test.testOnly $(TOP_REP).ZzTopAllSpec \
	)

$(PROG): $(MEM_DIR)/$(PROG).mem

simulation: mill $(PROG) ##! Lance gtkwave sur le test fourni dans PROG et produit le log du pc
	@$(call check_vars,PROG)
	@PROOT=$(PWD) PROG=$(PROG) CYCLES=$(CYCLES) ./mill TPchisel.test.testOnly $(TOP_REP).$(TOP_TEST) -- -DemitVcd=1 -z "gtkwave"
	@cp -f ./build/chiselsim/ZzTopSpec/Simulation-pour-gtkwave/workdir-verilator/core_pc.log .
	@gtkwave -a common/config.gtkw ./build/chiselsim/ZzTopSpec/Simulation-pour-gtkwave/workdir-verilator/trace.vcd

spike-simulation: mill $(PROG) ##! Simule INSTRUCTIONS instructions de PROG avec spike et produit un log du PC dans spike_pc.log
	spike --log spike.log --instructions=$$(($(INSTRUCTIONS) + 5)) -m0x10000:0x20000,0x80000000:0x00020000 -l $(MEM_DIR)/$(PROG).elf
	cat spike.log | sed "/>>>>/d" | awk '{print $$3}' | tail -n $(INSTRUCTIONS) > spike_pc.log


###############################################################################


##@ Passage sur carte 

SRC         := $(wildcard $(PATH_SRC)/*.scala)
SRC         += $(wildcard src/main/resources/vsrc/*.v)
FPGA	    := xc7z010-clg400-1
SRC_SV      := RTL/$(TOP_REP).$(TOP)/$(TOP).sv


XILINX_PREFIX := $(XILINX_VIVADO)/bin/
MEM_FILE:= $(MEM_DIR)/$(PROG).mem
MMI_FILE:=$(TOP).mmi
MEM_ID=$(shell grep -oP 'InstPath="\K[^"]+' $(MMI_FILE))
PARAM = $(MEM_FILE) false # Variable pour passer des parametres au générateur SV

.PRECIOUS: golden.bit
# Créer un hash unique de la variable
PROG_HASH := $(shell echo "$(PROG)" | md5sum | cut -d' ' -f1)

RTL: $(SRC_SV) ##! Génération du SystemVerilog

$(SRC_SV): mill $(SRC) 
	./mill TPchisel.runMain common.EmitModule $(TOP_REP).$(TOP) $(PARAM)

$(TOP)_utilization.rpt $(TOP)_timing.rpt $(TOP)_summary.rpt: $(COMMON_DIR)/synthese.vivado.tcl RTL 
	@$(call check_vars,XILINX_VIVADO)
	$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $< -tclargs $(FPGA) $(TOP) $(TOP_REP)

synthese: $(TOP)_utilization.rpt $(TOP)_timing.rpt $(TOP)_summary.rpt  ##! Réalise la synthèse grossière pour évaluer les performances
	@echo "Rapport d'utilisation disponible dans $(TOP)_utilization.rpt"
	@echo "Rapport de timing disponible dans $(TOP)_timing.rpt"
	@python3 $(COMMON_DIR)/parse_report.py $^

golden.bit: $(COMMON_DIR)/bitstream.vivado.tcl $(COMMON_DIR)/$(TOP).xdc RTL
	@$(call check_vars,XILINX_VIVADO PROG)
	$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $< -tclargs $(FPGA) $(TOP) $(TOP_REP) $@ $(MEM_FILE)

.prog_$(PROG_HASH):
	@rm -f .prog_*  # Supprimer les anciens marqueurs
	@touch $@

download.bit: golden.bit $(MEM_FILE) $(MMI_FILE) .prog_$(PROG_HASH)
	@$(call check_vars,XILINX_VIVADO PROG)
	$(XILINX_PREFIX)updatemem -debug -force --meminfo $(MMI_FILE) --data $(MEM_FILE) --proc $(MEM_ID) --bit $< --out $@	    

fpga: download.bit $(COMMON_DIR)/programFPGA.vivado.tcl ##! Génére le fichier de configuration du FPGA et le programme
	@$(call check_vars,XILINX_VIVADO)
	$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $(COMMON_DIR)/programFPGA.vivado.tcl -nolog -nojournal \
	                           -tclargs $<

flash: download.bit
	openFPGALoader -b $(FPGA_BOARD) $<

clean: ##! Fais le nettoyage
	rm -rf $(TOCLEAN) 
