SHELL := /bin/bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c

ASIC_DIR := $(abspath .)
REPO_DIR := $(abspath ../../..)
TOOLS_DIR := $(ASIC_DIR)/tools
SOC_DIR := $(REPO_DIR)/soc
SOC_SW_DIR := $(SOC_DIR)/software
SOC_OUT_DIR := $(SOC_DIR)/out
HW_DIR := $(REPO_DIR)/hardware
UPSTREAM_RESOURCES_DIR := $(HW_DIR)/upstream/rocket-chip/src/main/resources
SOC_SCALA_DIR := $(HW_DIR)/soc/scala
HW_GENERATED_DIR := $(HW_DIR)/generated
ASIC_SIM_DIR := $(HW_DIR)/sim/asic/rocket
OUT_DIR := $(ASIC_DIR)/out
LOG_DIR := $(OUT_DIR)/logs
FILELIST_DIR := $(OUT_DIR)/filelists
FLASH_DIR := $(SOC_OUT_DIR)/flash

include $(REPO_DIR)/tools/project.mk

VLOG ?= vlog
VSIM ?= vsim
CXX ?= g++

TOP_CLASS ?= freechips.rocketchip.fpga.VCU118RocketSystem
ASIC_CONFIG ?= freechips.rocketchip.fpga.VCU118RocketJtagConfig
FIRRTL_DIR ?= $(HW_GENERATED_DIR)/asic-rocket-soc
VERILOG_DIR ?= $(HW_GENERATED_DIR)/asic-rocket-soc-verilog
SIM_VERILOG_DIR ?= $(HW_GENERATED_DIR)/asic-rocket-soc-sim-verilog

ASIC_TOP ?= $(OUT_DIR)/generated/asic_rocket_top.sv
SIM_ASIC_TOP ?= $(OUT_DIR)/generated/asic_rocket_top_sim.sv
app ?=
ifneq ($(strip $(app)),)
APP ?= $(app)
else
APP ?= hello_uart
endif
SIM_APP ?= $(APP)
APP_FLASH_OFFSET ?= 0x06000000
APP_BUILD_DIR ?= $(SOC_OUT_DIR)/software/$(SIM_APP)
APP_ELF ?= $(APP_BUILD_DIR)/$(SIM_APP).elf
APP_FLASH_BIN ?= $(FLASH_DIR)/$(SIM_APP).flash.bin
APP_FLASH_HEX ?= $(FLASH_DIR)/$(SIM_APP).flash.hex
BOOTLOADER_IMG ?= $(SOC_OUT_DIR)/software/bootloader/bootloader.img
GPS_DEBUG ?= 1
GPS_DEBUG_LEVEL ?= 4
BSP_UART_CLOCK_HZ ?= 100000000
BSP_UART_BAUDRATE ?= 115200
BSP_CLINT_TIMEBASE_HZ ?= 1000000
RTL_FILELIST := $(FILELIST_DIR)/asic_rtl.f
TB_FILELIST := $(FILELIST_DIR)/asic_tb.f
SIM_DPI := $(OUT_DIR)/dpi/libsimjtag.so
GPS_SYSTEM_DIR ?= $(HW_DIR)/external/gps/rtl
GPS_RTL_LIST ?= $(HW_DIR)/external/gps/rtl/filelist.f
GPS_INC_DIRS := $(GPS_SYSTEM_DIR)/trck_src/pulp_fifo
GPS_RTL := $(addprefix $(GPS_SYSTEM_DIR)/,$(shell sed '/^[[:space:]]*$$/d; /^[[:space:]]*#/d' "$(GPS_RTL_LIST)"))
ASIC_PLL_MODEL := $(ASIC_SIM_DIR)/tb/models/clock/asic_pll_model.sv
CHISEL_SRCS := $(shell find $(SOC_SCALA_DIR) -type f -name '*.scala' 2>/dev/null)
APP_SW_DIR := $(SOC_SW_DIR)/$(SIM_APP)
APP_SW_SRCS := $(wildcard $(APP_SW_DIR)/*)
SIM_APP_MAKE_ARGS :=
ifeq ($(SIM_APP),pmod_irq_test)
SIM_APP_MAKE_ARGS += PMOD_IRQ_TEST_QUIET=1 BSP_UART_CLOCK_HZ=$(BSP_UART_CLOCK_HZ) BSP_CLINT_TIMEBASE_HZ=$(BSP_CLINT_TIMEBASE_HZ)
endif
BOOTLOADER_SW_SRCS := $(wildcard $(SOC_SW_DIR)/bootloader/*)
MAKE_FLASH_IMAGE := $(SOC_SW_DIR)/tools/make_flash_image.py
OBJCOPY := $(CROSS)objcopy
READELF := $(CROSS)readelf

VLOG_FLAGS ?= +define+PRINTF_COND=0
SIM_VLOG_FLAGS ?= +define+PRINTF_COND=0 +define+RANDOMIZE_REG_INIT +define+RANDOMIZE_MEM_INIT +define+RANDOM=0 +define+INIT_RANDOM_PROLOG_
GUI_VOPTARGS ?= +acc
SIM_PLUSARGS ?=

.PHONY: help all software chisel rtl sim-rtl filelist vlog-soc vlog-tb dpi sim sim-gui sim-pmod-irq sim-pmod-irq-rtl sanity clean FORCE

help:
	@printf '%s\n' \
	  'Targets:' \
	  '  make software   Build bootloader, hello app, and SPI flash hex' \
	  '  make chisel     Emit ASIC/JTAG Rocket FIRRTL' \
	  '  make rtl        Emit synthesis-style SystemVerilog and ASIC top' \
	  '  make sim-rtl    Emit simulation SystemVerilog and simulation ASIC top' \
	  '  make filelist   Generate RTL/TB filelists' \
	  '  make vlog-soc   Compile synthesis-style SoC RTL with Questa vlog' \
	  '  make vlog-tb    Compile simulation RTL, models, SimJTAG, and TB' \
	  '  make sim        Run UART/SPI flash sanity simulation' \
	  '  make sim-pmod-irq Run PMOD IRQ square-wave/PLIC/ISR simulation' \
	  '  make sim-pmod-irq-rtl Run fast PMOD IRQ square-wave/PLIC simulation' \
	  '  make sim GUI=1  Open Questa/ModelSim GUI, load wave.do, run 0 ns' \
	  '  make sim-gui    Alias for make sim GUI=1' \
	  '  make clean      Remove generated ASIC outputs'

all: rtl vlog-tb

software: $(BOOTLOADER_IMG) $(APP_FLASH_HEX)

$(BOOTLOADER_IMG): FORCE $(BOOTLOADER_SW_SRCS)
		$(MAKE) -C "$(SOC_SW_DIR)" TOOLCHAIN="$(TOOLCHAIN)" APP="$(SIM_APP)" \
		  APP_FLASH_OFFSET="$(APP_FLASH_OFFSET)" \
		  BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" \
		  BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
		  BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" bootloader

$(APP_ELF): $(APP_SW_SRCS)
		test -d "$(APP_SW_DIR)"
		$(MAKE) -C "$(APP_SW_DIR)" TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(APP_BUILD_DIR)" \
		  GPS_DEBUG="$(GPS_DEBUG)" GPS_DEBUG_LEVEL="$(GPS_DEBUG_LEVEL)" \
		  BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" \
		  BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
		  BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" \
		  $(SIM_APP_MAKE_ARGS) all

$(APP_FLASH_BIN): $(APP_ELF)
		mkdir -p "$(FLASH_DIR)"
		python3 "$(MAKE_FLASH_IMAGE)" \
		  --elf "$(APP_ELF)" \
		  --out "$(APP_FLASH_BIN)" \
		  --flash-offset "$(APP_FLASH_OFFSET)" \
		  --objcopy "$(OBJCOPY)" \
		  --readelf "$(READELF)"

$(APP_FLASH_HEX): $(APP_FLASH_BIN)
		mkdir -p "$(FLASH_DIR)"
		od -An -tx1 -v "$(APP_FLASH_BIN)" | tr -s ' ' '\n' | sed '/^$$/d' > "$(APP_FLASH_HEX)"

chisel: $(FIRRTL_DIR)/VCU118RocketSystem.fir

$(FIRRTL_DIR)/VCU118RocketSystem.fir: $(BOOTLOADER_IMG) $(CHISEL_SRCS)
	cd "$(REPO_DIR)"
	"$(MILL)" -i 'rocketchip[6.7.0].assembly'
	rm -rf "$(FIRRTL_DIR)"
	mkdir -p "$(FIRRTL_DIR)"
	"$(JAVA_HOME)/bin/java" -jar "$(REPO_DIR)/out/rocketchip/6.7.0/assembly.dest/out.jar" \
	  --dir "$(FIRRTL_DIR)" \
	  --top "$(TOP_CLASS)" \
	  --config "$(ASIC_CONFIG)"

rtl: $(ASIC_TOP) $(RTL_FILELIST)

$(ASIC_TOP): $(FIRRTL_DIR)/VCU118RocketSystem.fir $(TOOLS_DIR)/gen_asic_wrapper.py
	cd "$(REPO_DIR)"
	rm -rf "$(VERILOG_DIR)"
	mkdir -p "$(VERILOG_DIR)" "$(OUT_DIR)/generated"
	$(FIRTOOL_ENV) "$(FIRTOOL)" \
	  "$(FIRRTL_DIR)/VCU118RocketSystem.fir" \
	  --split-verilog -o "$(VERILOG_DIR)" \
	  --preserve-aggregate=none \
	  --disable-annotation-unknown \
	  --disable-all-randomization \
	  --strip-debug-info
	find "$(VERILOG_DIR)" -maxdepth 1 -type f \( -name '*.sv' -o -name '*.v' \) -printf '%p\n' | sort > "$(VERILOG_DIR)/filelist.abs.f"
	perl -0pi -e 's/^([ \t]*)if \([^\n]*\)\n[ \t]*assert__[^\n]*:\s*assert\([^\n]*\);\n//mg; s/^[ \t]*assert__[^\n]*:\s*assert\([^\n]*\);\n//mg' "$(VERILOG_DIR)"/*.sv
	python3 "$(TOOLS_DIR)/gen_asic_wrapper.py" \
	  --rocket-top "$(VERILOG_DIR)/VCU118RocketSystem.sv" \
	  --out "$@"

sim-rtl: $(SIM_ASIC_TOP) $(TB_FILELIST)

$(SIM_ASIC_TOP): $(FIRRTL_DIR)/VCU118RocketSystem.fir $(TOOLS_DIR)/gen_asic_wrapper.py
	cd "$(REPO_DIR)"
	rm -rf "$(SIM_VERILOG_DIR)"
	mkdir -p "$(SIM_VERILOG_DIR)" "$(OUT_DIR)/generated"
	$(FIRTOOL_ENV) "$(FIRTOOL)" \
	  "$(FIRRTL_DIR)/VCU118RocketSystem.fir" \
	  --split-verilog -o "$(SIM_VERILOG_DIR)" \
	  --preserve-aggregate=none \
	  --disable-annotation-unknown \
	  --strip-debug-info
	find "$(SIM_VERILOG_DIR)" -maxdepth 1 -type f \( -name '*.sv' -o -name '*.v' \) -printf '%p\n' | sort > "$(SIM_VERILOG_DIR)/filelist.abs.f"
	perl -0pi -e 's/^([ \t]*)if \([^\n]*\)\n[ \t]*assert__[^\n]*:\s*assert\([^\n]*\);\n//mg; s/^[ \t]*assert__[^\n]*:\s*assert\([^\n]*\);\n//mg' "$(SIM_VERILOG_DIR)"/*.sv
	python3 "$(TOOLS_DIR)/gen_asic_wrapper.py" \
	  --rocket-top "$(SIM_VERILOG_DIR)/VCU118RocketSystem.sv" \
	  --out "$@"

filelist: $(RTL_FILELIST) $(TB_FILELIST)

$(RTL_FILELIST): $(ASIC_TOP)
	mkdir -p "$(FILELIST_DIR)"
	{
	  echo "$(UPSTREAM_RESOURCES_DIR)/vsrc/plusarg_reader.v"
	  echo "+incdir+$(GPS_INC_DIRS)"
	  printf '%s\n' $(GPS_RTL)
	  echo "$(ASIC_PLL_MODEL)"
	  cat "$(VERILOG_DIR)/filelist.abs.f"
	  echo "$(ASIC_TOP)"
	} > "$@"
	echo "Wrote $@"

$(TB_FILELIST): $(SIM_ASIC_TOP)
	mkdir -p "$(FILELIST_DIR)"
	{
	  echo "$(UPSTREAM_RESOURCES_DIR)/vsrc/SimJTAG.v"
	  echo "$(UPSTREAM_RESOURCES_DIR)/vsrc/plusarg_reader.v"
	  echo "+incdir+$(GPS_INC_DIRS)"
	  printf '%s\n' $(GPS_RTL)
	  cat "$(SIM_VERILOG_DIR)/filelist.abs.f"
	  echo "$(SIM_ASIC_TOP)"
	  find "$(ASIC_SIM_DIR)/tb/models" -type f \( -name '*.sv' -o -name '*.v' \) | sort
	  echo "$(ASIC_SIM_DIR)/tb/asic_rocket_tb.sv"
	} > "$@"
	{
	  echo "$(UPSTREAM_RESOURCES_DIR)/csrc/SimJTAG.cc"
	  echo "$(UPSTREAM_RESOURCES_DIR)/csrc/remote_bitbang.cc"
	} > "$(FILELIST_DIR)/sim_dpi_sources.f"
	echo "Wrote $@"

vlog-soc: $(RTL_FILELIST)
	mkdir -p "$(LOG_DIR)"
	[[ -d "$(ASIC_DIR)/work" ]] || vlib "$(ASIC_DIR)/work"
	cd "$(ASIC_DIR)"
	"$(VLOG)" $(VLOG_FLAGS) -sv -work work -f "$(RTL_FILELIST)" | tee "$(LOG_DIR)/vlog_soc.log"

vlog-tb: $(TB_FILELIST) $(APP_FLASH_HEX)
	mkdir -p "$(LOG_DIR)"
	[[ -d "$(ASIC_DIR)/work" ]] || vlib "$(ASIC_DIR)/work"
	cd "$(ASIC_DIR)"
	"$(VLOG)" $(SIM_VLOG_FLAGS) -sv -work work -f "$(TB_FILELIST)" | tee "$(LOG_DIR)/vlog_tb.log"

dpi: $(SIM_DPI)

$(SIM_DPI): $(UPSTREAM_RESOURCES_DIR)/csrc/SimJTAG.cc $(UPSTREAM_RESOURCES_DIR)/csrc/remote_bitbang.cc
	mkdir -p "$(OUT_DIR)/dpi"
	"$(CXX)" -fPIC -shared -std=c++17 \
	  -I/tools/altera/altera_pro26/questa_fse/include \
	  -I"$(UPSTREAM_RESOURCES_DIR)/csrc" \
	  "$(UPSTREAM_RESOURCES_DIR)/csrc/SimJTAG.cc" \
	  "$(UPSTREAM_RESOURCES_DIR)/csrc/remote_bitbang.cc" \
	  -o "$@"

sim sanity: vlog-tb $(SIM_DPI)
	mkdir -p "$(LOG_DIR)"
	cd "$(REPO_DIR)"
	if [[ "$(GUI)" == "1" ]]; then
	  "$(VSIM)" -suppress 3009 -lib "$(ASIC_DIR)/work" -sv_lib "$(OUT_DIR)/dpi/libsimjtag" asic_rocket_tb \
	    -voptargs="$(GUI_VOPTARGS)" \
	    +flash_hex="$(APP_FLASH_HEX)" \
	    -do "onfinish stop; do $(ASIC_SIM_DIR)/wave.do; run 0ns"
	else
	  "$(VSIM)" -c -suppress 3009 -lib "$(ASIC_DIR)/work" -sv_lib "$(OUT_DIR)/dpi/libsimjtag" asic_rocket_tb \
	    +flash_hex="$(APP_FLASH_HEX)" \
	    $(SIM_PLUSARGS) \
	    -do "run -all; quit -f" | tee "$(LOG_DIR)/vsim_sanity.log"
	  grep -q "ASIC sanity PASS" "$(LOG_DIR)/vsim_sanity.log"
	fi

sim-gui:
	$(MAKE) sim GUI=1

sim-pmod-irq:
	$(MAKE) sim SIM_APP=pmod_irq_test

sim-pmod-irq-rtl:
	$(MAKE) sim SIM_APP=pmod_irq_test SIM_PLUSARGS=+pmod_irq_rtl_only

clean:
	rm -rf "$(OUT_DIR)" "$(FIRRTL_DIR)" "$(VERILOG_DIR)" "$(SIM_VERILOG_DIR)"

FORCE:
