##############################################################################
# Makefile to build an Altera FPGA system designed in Qsys and Quartus
##############################################################################
# Updated and tested on Quartus 12.1
#
# Notes:
# - Use the IDEs (Quartus and Qsys) to setup the system including
#   various parameters.
# - Use Quartus IDE to generate the programming chain file (.cdf) using
#   the graphical download tool
# - This makefile is more of a build script, i.e. it does not do incremental
#   builds but is useful to build everything

# The following six envrionment variables are project specific
QUARTUS_PROJECT=DE4_BERI
QUARTUS_PROJECT_VERSION=DE4_BERI
QSYS_PROJECT=DE4_SOC
FPGA="Stratix IV"
# Version of Quartus this has been tested on:
QUARTUS_VERSION='13.0'
# User specific temporary area to avoid collisions
TEMP=/tmp/$(USER)
TMP=$(TEMP)
# Bluespec standard Verilog libraries from BLUESPECDIR environment variable
BLUESPEC_VERILOG=$(BLUESPECDIR)/Verilog
# CHERI build specific:
CHERI_DIR=../../
# project specific peripherals for Qsys
PERIPHERALS_DIR=../../../../cherilibs/trunk/peripherals/
# software for boot rom
MINIBOOT_DIR=../../../../cheribsd/trunk/miniboot/
# software for reset rom
EXCEPTIONROM_DIR=../../../../cherilibs/trunk/peripherals/ExceptionRom/

# define MICRO to build a TLB-less, L2-less CHERI
# MICRO=Yes

# configuration files critical to the design:
QUARTUS_CONFIG=$(QUARTUS_PROJECT).qpf $(wildcard *.sdc)
# Qsys project source
QSYS_PROJECT_SRCS=$(QSYS_PROJECT).qsys

# derived variables
QSYS_GENERATED_FILES=$(QSYS_PROJECT).sopcinfo
QSYS_SYNTHESIS_DIR=./$(QSYS_PROJECT)/synthesis/

QUARTUS_GENERATED_DIRS=incremental_db db output_files

.PHONY : all
all :  start_msg check_env temp build_cheri build_peripherals build_miniboot build_exceptionrom build_qsys build_fpga report_critical report_error build_nodip_wait end_msg

.PHONY : start_msg
start_msg:
	@echo "**********************************************************************"
	@echo "* Build started at `date`"
	@echo "**********************************************************************"

.PHONY : end_msg
end_msg:
	@echo "**********************************************************************"
	@echo "* Build ended at `date`"
	@echo "**********************************************************************"

.PHONY : check_env
check_env:
	@(/usr/bin/xdpyinfo > /dev/null || (echo "ERROR: X11 display must be active for this build to complete." && false))
	@((quartus_map --version | grep 'Version '$(QUARTUS_VERSION)) || ((echo "ERROR: this build has been tested against Quartus "$(QUARTUS_VERSION)" but you have version"; quartus_map --version) && false))

.PHONY : temp
temp:
	mkdir -p $(TEMP)

.PHONY : build_cheri
build_cheri:
	@echo "**********************************************************************"
	@echo "* Build CHERI Bluespec in " $(CHERI_DIR)
	@echo "**********************************************************************"
ifdef MICRO
	$(MAKE) -C $(CHERI_DIR) MICRO=$(MICRO) verilog
else
	$(MAKE) -C $(CHERI_DIR) verilog_cap
endif

.PHONY : build_peripherals
build_peripherals:
	@echo "**********************************************************************"
	@echo "* Build Peripherals in " $(PERIPHERALS_DIR)
	@echo "**********************************************************************"
	$(MAKE) -C $(PERIPHERALS_DIR)

.PHONY : build_miniboot
build_miniboot:
ifdef MICRO
	# Micro CHERI without TLB won't run FreeBSD, so include bare metal code instead
	# of miniboot
	@echo "**********************************************************************"
	@echo "* Build bare metal code ROM (initial.hex) in " $(CHERI_DIR)/sw
	@echo "**********************************************************************"
	$(MAKE) -C $(CHERI_DIR)/sw verilog
	cp $(CHERI_DIR)/sw/initial.hex ./
else
	@echo "**********************************************************************"
	@echo "* Build miniboot ROM (initial.hex) in " $(MINIBOOT_DIR)
	@echo "**********************************************************************"
	$(MAKE) -C $(MINIBOOT_DIR)
	cp $(MINIBOOT_DIR)/initial.hex ./
	$(MAKE) -C $(MINIBOOT_DIR) clean
	$(MAKE) -C $(MINIBOOT_DIR) ALWAYS_WAIT=yesplease
	cp $(MINIBOOT_DIR)/initial.hex ./initial-nodip.hex
endif

.PHONY : build_exceptionrom
build_exceptionrom:
	@echo "**********************************************************************"
	@echo "* Build exception ROM (resetROM.hex) in " $(EXCEPTIONROM_DIR)
	@echo "**********************************************************************"
	$(MAKE) -C $(EXCEPTIONROM_DIR) verilog
	cp $(EXCEPTIONROM_DIR)/resetROM.hex ./

.PHONY : build_qsys
build_qsys: check_bash
	@echo "**********************************************************************"
	@echo "* Qsys generation"
	@echo "**********************************************************************"
	ip-generate \
	  --report-file=bsf:./output_files/$(QSYS_PROJECT).bsf \
	  --standard-reports \
	  --system-info=DEVICE_FAMILY=$(FPGA) \
	  --output-directory=$(QSYS_SYNTHESIS_DIR) \
	  --project-directory=./ \
	  --file-set=QUARTUS_SYNTH \
	  --component-file=$(QSYS_PROJECT).qsys

.PHONY : check_bash
check_bash:
	@[ -L /bin/sh ] && [ `/bin/readlink /bin/sh` = bash ] && \
	(echo "Info: /bin/sh -> bash") || \
	(echo "**** WARNING /bin/sh isn't a link to bash which may cause Qsys scripts to fail")

.PHONY : build_fpga
.DELETE_ON_ERROR: output_files/$(QSYS_PROJECT_VERSION).sof
build_fpga: build_exceptionrom build_miniboot build_qsys build_peripherals
	@echo "**********************************************************************"
	@echo "* Synthesis run started to generate" output_files/$(QUARTUS_PROJECT_VERSION).sof
	@echo "**********************************************************************"
	quartus_map --read_settings_files=on --write_settings_files=off $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION) -l $(BLUESPEC_VERILOG)
# REMOVE:  -l $(QSYS_PROJECT)/synthesis -l $(QSYS_PROJECT_SUBMODULES) -l $(BLUESPEC_VERILOG)
	quartus_cdb --read_settings_files=off --write_settings_files=off $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION) --merge=on
	quartus_fit --read_settings_files=off --write_settings_files=off $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION)
	quartus_sta $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION)
	quartus_asm --read_settings_files=off --write_settings_files=off $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION)
	cp $(QSYS_SYNTHESIS_DIR)/*.sopcinfo .

# REMOVE: quartus_sh --flow compile $(QUARTUS_PROJECT_VERSION)

.PHONY : report_critical
report_critical:
	@echo "**********************************************************************"
	@echo "* Reporting Critical Warnings During Synthesis"
	@echo "**********************************************************************"
	@/bin/egrep '^Critical' output_files/*.rpt || echo "No Critical Warnings"

.PHONY : report_error
report_error:
	@echo "**********************************************************************"
	@echo "* Reporting Errors During Synthesis"
	@echo "**********************************************************************"
	@/bin/egrep '^Error' output_files/*.rpt || echo "No Errors"

.PHONY : update_mif
update_mif: _update_mif report_critical report_error

.PHONY : _update_mif
_update_mif: build_exceptionrom build_miniboot
	quartus_cdb $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION) --update_mif
	quartus_asm --read_settings_files=on --write_settings_files=off $(QUARTUS_PROJECT) -c $(QUARTUS_PROJECT_VERSION)
	cp $(QSYS_SYNTHESIS_DIR)/*.sopcinfo .

.PHONY : build_nodip_wait
build_nodip_wait: _always_nodip_msg _always_nodip update_mif _always_nodip_restore_dir

.PHONY : _always_nodip_restore_dir
_always_nodip_restore_dir:
	# Make sure the default bitfile stays in the expected output dir.
	mv output_files output_files_nodip
	mv output_files_default output_files
	# Also restore intial.hex.
	mv initial.hex initial-nodip.hex
	mv initial-default.hex initial.hex

.PHONY : _always_nodip_msg
_always_nodip_msg:
	@echo "**********************************************************************"
	@echo "* Rebuilding bitfile with miniboot ingoring DIP switch for waiting"
	@echo "**********************************************************************"

.PHONY : _always_nodip
_always_nodip: build_fpga
	# Swap miniboot ROMs.
	mv initial.hex initial-default.hex
	mv initial-nodip.hex initial.hex
	# Save previously built files.
	cp -pr output_files output_files_default

.PHONY : download
download :
	@echo "**********************************************************************"
	@echo "* Downloading the FPGA image to the FPGA "
	@echo "**********************************************************************"
	@echo "* If this fails you may need to check if the FPGA can be seen using: "
	@echo "*   jtagconfig "
	@echo "* to see what devices are present.  If this still doesn't work, "
	@echo "* fire up the Quartus IDE and use the graphical programming tool "
	@echo "* to diagnose the problem and create a new programming chain "
	@echo "* ("$(QUARTUS_PROJECT_VERSION)".cdf) file. "
	@echo "**********************************************************************"
	quartus_pgm $(QUARTUS_PROJECT_VERSION).cdf
	@echo "**********************************************************************"
	@echo "* Download of hardware ("$(QUARTUS_PROJECT_VERSION).sof") complete"
	@echo "**********************************************************************"

.PHONY : cleanq
cleanq:
	rm -f  $(QSYS_GENERATED_FILES)
	rm -rf $(QSYS_SYNTHESIS_DIR)
	rm -rf $(QUARTUS_GENERATED_DIRS)

.PHONY : cleanall
cleanall: cleanq
	$(MAKE) -C $(CHERI_DIR) clean
	$(MAKE) -C $(PERIPHERALS_DIR) clean
	$(MAKE) -C $(MINIBOOT_DIR) clean

.PHONY : clean
clean: cleanq
	@echo "WARNING: this did not clean CHERI, peripherals or miniboot"
	@echo "         use 'make cleanall' to clean CHERI, peripherals and miniboot"

.PHONY : help
help :
	@echo "Summary of makefile targets"
	@echo "               all - builds everything using the following steps (except download)"
	@echo "       build_cheri - CHERI processor"
	@echo " build_peripherals - peripherals"
	@echo "    build_miniboot - build miniboot ROM and copy initial.hex here"
	@echo "build_exceptionrom - build exception ROM and copy resetROM.hex here"
	@echo "        build_qsys - build Qsys project containing CHERI, etc."
	@echo "        build_fpga - synthesis, map, fit, timing analyse and generate FPGA image"
	@echo "  build_nodip_wait - build_fpga with modified miniboot to ignore wait DIP switch"
	@echo "   report_critical - scans build_fpga reports for critical warnings"
	@echo "      report_error - scans build_fpga reports for errors"
	@echo "        update_mif - Update memory intialization files"
	@echo "          download - attempts to download the FPGA (.sof) image to the FPGA"
	@echo "                     but the chain file (.cdf) may need to be updated for"
	@echo "                     your configuration (e.g. USB port number)"
	@echo "             clean - remove Quartus and Qsys build files"
	@echo "          cleanall - clean + clean peripherals, CHERI and miniboot"
		
