SYSTEM = soc_system

TCL = $(SYSTEM).tcl
QSYS = $(SYSTEM).qsys
SOPCINFO = $(SYSTEM).sopcinfo
QIP = $(SYSTEM)/synthesis/$(SYSTEM).qip
HPS_PIN_TCL = $(SYSTEM)/synthesis/submodules/hps_sdram_p0_pin_assignments.tcl
HPS_PIN_MAP = hps_sdram_p0_all_pins.txt
QPF = $(SYSTEM).qpf
QSF = $(SYSTEM).qsf
SDC = $(SYSTEM).sdc

BOARD_INFO = $(SYSTEM)_board_info.xml
DTS = $(SYSTEM).dts
DTB = $(SYSTEM).dtb

SOF = output_files/$(SYSTEM).sof
RBF = output_files/$(SYSTEM).rbf

SOFTWARE_DIR = software

BSP_DIR = $(SOFTWARE_DIR)/spl_bsp
BSP_SETTINGS = $(BSP_DIR)/settings.bsp
PRELOADER_SETTINGS_DIR = hps_isw_handoff/soc_system_hps_0
PRELOADER_MAKEFILE = $(BSP_DIR)/Makefile
PRELOADER_MKPIMAGE = $(BSP_DIR)/preloader-mkpimage.bin

UBOOT_IMAGE = $(BSP_DIR)/uboot-socfpga/u-boot.img

KERNEL_REPO = https://github.com/altera-opensource/linux-socfpga.git
KERNEL_BRANCH = socfpga-4.19
DEFAULT_CONFIG = socfpga_defconfig
CROSS = env CROSS_COMPILE=arm-altera-eabi- ARCH=arm

KERNEL_DIR = $(SOFTWARE_DIR)/linux-socfpga
KERNEL_CONFIG = $(KERNEL_DIR)/.config
ZIMAGE = $(KERNEL_DIR)/arch/arm/boot/zImage

TARFILES = Makefile \
	$(TCL) \
	$(QSYS) \
	$(SYSTEM)_top.sv \
	$(BOARD_INFO) \
	ip/intr_capturer/intr_capturer.v \
	ip/intr_capturer/intr_capturer_hw.tcl \
	orderbook_core.sv \
	orderbook_core_hw.tcl \
	rtl/buffered_ingest.sv \
	rtl/itch_parser.sv

TARFILE = fpga-hft-hw.tar.gz

# project
#
# Run the topmost tcl script to generate the initial project files

.PHONY : project
project : $(QPF) $(QSF) $(SDC)

$(QPF) $(QSF) $(SDC) : $(TCL)
	quartus_sh -t $(TCL)

# qsys
#
# From the .qsys file, generate the .sopcinfo, .qip, and directory
# (named according to the system) with all the Verilog files, etc.

.PHONY : qsys
qsys : $(SOPCINFO)

$(SOPCINFO) $(QIP) $(HPS_PIN_TCL) $(SYSTEM)/ $(PRELOADER_SETTINGS_DIR) : $(QSYS)
	rm -rf $(SOPCINFO) $(SYSTEM)/
	qsys-generate $(QSYS) --synthesis=VERILOG --search-path="$$,$(CURDIR)"

# qsys_edit
#
# Open the .qsys file in Platform Designer (Qsys GUI) for editing.
# After saving in the GUI, run "make qsys" to regenerate synthesis files.

.PHONY : qsys_edit
qsys_edit :
	qsys-edit $(QSYS) &

# quartus
#
# Run Quartus on the Qsys-generated files
#
#    Build netlist
# quartus_map soc_system
#
#    Use netlist information to determine HPS stuff
# quartus_sta -t hps_sdram_p0_pin_assignments.tcl soc_system
#
#    Do the rest
#   FIXME: this is wasteful.  Really want not to repeat the "map" step
# quartus_sh --flow compile 
#
# quartus_fit
# quartus_asm
# quartus_sta

.PHONY : quartus
quartus : $(SOF)

$(SOF) $(HPS_PIN_MAP) : $(QIP) $(QPF) $(QSF) $(HPS_PIN_TCL)
	quartus_map $(SYSTEM)
	quartus_sta -t $(HPS_PIN_TCL) $(SYSTEM)
	quartus_fit $(SYSTEM)
	quartus_asm $(SYSTEM)
#	quartus_sh --flow compile $(QPF)

# rbf
#
# Convert the .sof file (for programming through the USB blaster)
# to an .rbf file to be placed on an SD card and written by u-boot
.PHONY : rbf
rbf : $(RBF)

$(RBF) : $(SOF)
	quartus_cpf -c $(SOF) $(RBF)

# dtb
#
# Compile the hand-maintained soc_system.dts to a device tree blob.
#
# NOTE: sopc2dts is intentionally NOT used here.  sopc2dts (Quartus 20.1)
# does not have component plugins for the University Program VGA IP
# (altera_up_avalon_video_pixel_buffer_dma, altera_up_avalon_video_vga_controller,
# altera_up_avalon_video_dual_clock_buffer, altera_up_avalon_video_rgb_resampler,
# altera_pll) and generates a broken DTS with unresolved &vga_pll references.
# soc_system.dts is committed to git and maintained manually; the altvipfb
# framebuffer node for /dev/fb0 has been added by hand (see pixel_buffer_dma node).
#
# To regenerate from scratch (base only, without VGA):
#   sopc2dts --input soc_system.sopcinfo --output soc_system.dts \
#            --type dts --board soc_system_board_info.xml --clocks
# then manually re-add the altvipfb node and extend hps_0_bridges ranges.
.PHONY : dtb
dtb : $(DTB)

$(DTB) : $(DTS)
	@which dtc || (echo "dtc not found.  Did you run embedded_command_shell.sh?"; exit 1)
	dtc -I dts -O dtb -o $(DTB) $(DTS)

# $(DTS) has no build rule — it is hand-maintained in git.

# preloader
#
# Builds the SPL's preloader-mkpimage.bin image file, which should
# be written to the "magic" 3rd parition on the SD card
# in software/spl_bsp
#
# Requires the embedded_command_shell.sh script to have run so the compiler,
# etc is available
#
.PHONY : preloader
preloader : $(PRELOADER_MKPIMAGE)

$(PRELOADER_MKPIMAGE) : $(PRELOADER_MAKEFILE) $(BSP_SETTINGS)
	$(MAKE) -C $(BSP_DIR)

$(BSP_SETTINGS) $(PRELOADER_MAKEFILE) : $(PRELOADER_SETTINGS_DIR)
	mkdir -p $(BSP_DIR)
	bsp-create-settings \
	  --type spl \
	  --bsp-dir $(BSP_DIR) \
	  --settings $(BSP_SETTINGS) \
	  --preloader-settings-dir $(PRELOADER_SETTINGS_DIR) \
	  --set spl.boot.FAT_SUPPORT 1

# uboot
#
# Build the bootloader

.PHONY : uboot
uboot : $(UBOOT_IMAGE)

$(UBOOT_IMAGE) : $(PRELOADER_MAKEFILE) $(BSP_SETTINGS)
	$(MAKE) -C $(BSP_DIR) uboot

# kernel-download
#
# Clone the Linux kernel repository
#
# kernel-config
#
#   Set up the default kernel configuration
#
# kernel-menuconfig
#
#   (Optional) Access the kernel configuration menu to make further
#   adjustments about which modules are included
#
# zimage
#
#   Compile the kernel

.PHONY : download-kernel config-kernel zimage
kernel-download : $(KERNEL_DIR)
kernel-config : $(KERNEL_CONFIG)
kernel-menuconfig :
	$(CROSS) $(MAKE) -C $(KERNEL_DIR) menuconfig
zimage : $(ZIMAGE)

$(KERNEL_DIR) :
	mkdir -p $(KERNEL_DIR)
	git clone --branch $(KERNEL_BRANCH) $(KERNEL_REPO) $(KERNEL_DIR)

# Configure the kernel.  Start from a provided default,
# 
# Turn off version checking (makes it easier to compile kernel
# modules and not have them complain about version)
#
# Turn on large file (+2TB) support, which the ext4 filesystem
# requires by default (it will not be able to mount the root
# filesystem read/write otherwise)
$(KERNEL_CONFIG) : $(KERNEL_DIR)
	$(CROSS) $(MAKE) -C $(KERNEL_DIR) $(DEFAULT_CONFIG)
	$(KERNEL_DIR)/scripts/config --file $(KERNEL_CONFIG) \
	  --disable CONFIG_LOCALVERSION_AUTO \
	  --enable CONFIG_LBDAF \
	  --disable CONFIG_XFS_FS \
	  --disable CONFIG_GFS2_FS \
	  --disable CONFIG_TEST_KMOD

# Compile the kernel

$(ZIMAGE) : $(KERNEL_CONFIG)
	$(CROSS) $(MAKE) -C $(KERNEL_DIR) LOCALVERSION= zImage

# tar
#
# Build soc_system.tar.gz

.phony : tar
tar : $(TARFILE)

$(TARFILE) : $(TARFILES)
	tar zcfC $(TARFILE) .. $(TARFILES:%=lab3-hw/%)

# simulation
#
# Verilator-based unit tests for selected RTL modules.

VERILATOR ?= verilator
VERILATOR_WARNINGS = -Wall

.PHONY : sim-hash-table lint-hash-table sim-heap lint-heap \
	sim-serial-rx lint-serial-rx sim-itch-parser lint-itch-parser \
	sim-buffered-ingest lint-buffered-ingest sim-clean

sim-hash-table :
	rm -rf obj_dir
	$(VERILATOR) --cc --sv $(VERILATOR_WARNINGS) \
		--Mdir obj_dir \
		--top-module hash_table \
		rtl/hash_table.sv \
		--exe sim/tb_hash_table.cpp
	$(MAKE) -C obj_dir -f Vhash_table.mk Vhash_table
	./obj_dir/Vhash_table

lint-hash-table :
	$(VERILATOR) --lint-only --timing --sv $(VERILATOR_WARNINGS) \
		rtl/hash_table.sv \
		sim/tb_hash_table.sv

sim-heap :
	rm -rf obj_dir
	$(VERILATOR) --cc --sv $(VERILATOR_WARNINGS) --Wno-fatal \
		--trace \
		--Mdir obj_dir \
		--top-module tb_heap \
		sim/tb_heap.sv \
		rtl/heap.sv \
		--exe sim/tb_heap_main.cpp
	$(MAKE) -C obj_dir -f Vtb_heap.mk Vtb_heap
	./obj_dir/Vtb_heap

lint-heap :
	$(VERILATOR) --lint-only --sv $(VERILATOR_WARNINGS) --Wno-fatal \
		sim/tb_heap.sv \
		rtl/heap.sv

sim-serial-rx :
	rm -rf obj_dir
	$(VERILATOR) --cc --sv $(VERILATOR_WARNINGS) \
		--trace \
		--Mdir obj_dir \
		--top-module tb_serial_rx \
		sim/tb_serial_rx.sv \
		rtl/serial_rx.sv \
		--exe sim/tb_serial_rx_main.cpp
	$(MAKE) -C obj_dir -f Vtb_serial_rx.mk Vtb_serial_rx
	./obj_dir/Vtb_serial_rx

lint-serial-rx :
	$(VERILATOR) --lint-only --timing --sv $(VERILATOR_WARNINGS) \
		sim/tb_serial_rx.sv \
		rtl/serial_rx.sv

sim-itch-parser :
	rm -rf obj_dir
	$(VERILATOR) --cc --sv $(VERILATOR_WARNINGS) \
		--trace \
		--Mdir obj_dir \
		--top-module tb_itch_parser \
		sim/tb_itch_parser.sv \
		rtl/itch_parser.sv \
		--exe sim/tb_itch_parser_main.cpp
	$(MAKE) -C obj_dir -f Vtb_itch_parser.mk Vtb_itch_parser
	./obj_dir/Vtb_itch_parser

lint-itch-parser :
	$(VERILATOR) --lint-only --timing --sv $(VERILATOR_WARNINGS) \
		sim/tb_itch_parser.sv \
		rtl/itch_parser.sv

sim-buffered-ingest :
	rm -rf obj_dir
	$(VERILATOR) --cc --sv $(VERILATOR_WARNINGS) --Wno-fatal \
		--Mdir obj_dir \
		--top-module tb_buffered_ingest \
		sim/tb_buffered_ingest.sv \
		rtl/buffered_ingest.sv \
		--exe sim/tb_buffered_ingest_main.cpp
	$(MAKE) -C obj_dir -f Vtb_buffered_ingest.mk Vtb_buffered_ingest
	./obj_dir/Vtb_buffered_ingest

lint-buffered-ingest :
	$(VERILATOR) --lint-only --timing --sv $(VERILATOR_WARNINGS) \
		sim/tb_buffered_ingest.sv \
		rtl/buffered_ingest.sv

sim-clean :
	rm -rf obj_dir tb_serial_rx.vcd tb_itch_parser.vcd \
		tb_buffered_ingest.vcd tb_heap.vcd

# clean
#
# Remove all generated files

.PHONY : clean quartus-clean qsys-clean qsys_clean project-clean
clean : quartus-clean qsys-clean project-clean dtb-clean preloader-clean \
	uboot-clean

project-clean :
	rm -rf $(QPF) $(QSF) $(SDC)

# Remove Qsys / Platform Designer generated artifacts only (not Quartus DB/SOF).
# Use before "make qsys" when the system is stale or after editing .qsys by hand.
qsys-clean :
	rm -rf $(SOPCINFO) $(QIP) $(SYSTEM)/ .qsys_edit \
	hps_isw_handoff/ hps_sdram_p0_summary.csv

# Alias (underscore) for the same target as qsys-clean
.PHONY : qsys_clean
qsys_clean : qsys-clean

quartus-clean :
	rm -rf  $(SOF) output_files db incremental_db $(SYSTEM).qdf \
	c5_pin_model_dump.txt $(HPS_PIN_MAP)

dtb-clean :
	rm -f $(DTB)
	# $(DTS) is hand-maintained — never auto-deleted

preloader-clean :
	rm -rf $(BSP_DIR)

uboot-clean :
	rm -rf $(BSP_DIR)/uboot-socfpga

kernel-clean :
	rm -rf $(KERNEL_DIR)

config-clean :
	rm -rf $(KERNEL_CONFIG)
