#
# Nitro FPGA build system: Project rules file
#
# Copyright (C) 2020 Sylvain Munaut
# SPDX-License-Identifier: BSD-3-Clause
#

# Default tools
YOSYS ?= yosys
YOSYS_READ_ARGS ?=
YOSYS_SYNTH_ARGS ?= -dffe_min_ce_use 4
NEXTPNR ?= nextpnr-ice40
NEXTPNR_ARGS ?= --freq 50
ICEPACK ?= icepack
ICEPROG ?= iceprog
IVERILOG ?= iverilog
DFU_UTIL ?= dfu-util

ifeq ($(PLACER),heap)
NEXTPNR_SYS_ARGS += --placer heap
endif

ICE40_LIBS ?= $(shell yosys-config --datdir/ice40/cells_sim.v)


# Must be first rule and call it 'all' by convention
all: synth

# Base directories
ifeq ($(origin NO2BUILD_DIR), undefined)
NO2BUILD_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
endif

ifeq ($(origin NO2CORES_DIR), undefined)
NO2CORES_DIR := $(abspath $(NO2BUILD_DIR)/../cores)
endif

# Temporary build-directory
BUILD_TMP := $(abspath build-tmp)

$(BUILD_TMP):
	@mkdir -p $(BUILD_TMP)

# Discover all cores
$(foreach core_def, $(wildcard $(NO2CORES_DIR)/*/no2core.mk), $(eval include $(core_def)))

# Resolve dependency tree for project and collect sources
$(BUILD_TMP)/proj-deps.mk: Makefile $(BUILD_TMP) $(addprefix $(BUILD_TMP)/deps-core-,$(PROJ_DEPS))
	@echo "SELF_DIR := \$$(dir \$$(lastword \$$(MAKEFILE_LIST)))" > $@
	@if [ "$(PROJ_DEPS)" != "" ]; then \
		echo "include \$$(SELF_DIR)deps-core-*" >> $@; \
	fi
	@echo "PROJ_ALL_DEPS := \$$(DEPS_SOLVE_TMP)" >> $@
	@echo "PROJ_ALL_RTL_SRCS := \$$(RTL_SRCS_SOLVE_TMP)" >> $@
	@echo "PROJ_ALL_SIM_SRCS := \$$(SIM_SRCS_SOLVE_TMP)" >> $@
	@echo "PROJ_ALL_PREREQ := \$$(PREREQ_SOLVE_TMP)" >> $@

include $(BUILD_TMP)/proj-deps.mk

# Make all sources absolute
PROJ_RTL_SRCS := $(abspath $(PROJ_RTL_SRCS))
PROJ_SIM_SRCS := $(abspath $(PROJ_SIM_SRCS))
PROJ_TOP_SRC  := $(abspath $(PROJ_TOP_SRC))

# Board config
PIN_DEF ?= $(abspath data/$(PROJ_TOP_MOD)-$(BOARD).pcf)

BOARD_DEFINE=BOARD_$(shell echo $(BOARD) | tr a-z\- A-Z_)
YOSYS_READ_ARGS += -D$(BOARD_DEFINE)=1

# Add those to the list
PROJ_ALL_RTL_SRCS += $(PROJ_RTL_SRCS)
PROJ_ALL_SIM_SRCS += $(PROJ_SIM_SRCS)
PROJ_ALL_PREREQ += $(PROJ_PREREQ)

# Include path
PROJ_SYNTH_INCLUDES := -I$(abspath rtl/) $(addsuffix /rtl/, $(addprefix -I$(NO2CORES_DIR)/, $(PROJ_ALL_DEPS)))
PROJ_SIM_INCLUDES   := -I$(abspath sim/) $(addsuffix /sim/, $(addprefix -I$(NO2CORES_DIR)/, $(PROJ_ALL_DEPS)))


# Synthesis & Place-n-route rules

$(BUILD_TMP)/$(PROJ).ys: $(PROJ_TOP_SRC) $(PROJ_ALL_RTL_SRCS)
	@echo "read_verilog $(YOSYS_READ_ARGS) $(PROJ_SYNTH_INCLUDES) $(PROJ_TOP_SRC) $(PROJ_ALL_RTL_SRCS)" > $@
	@echo "synth_ice40 $(YOSYS_SYNTH_ARGS) -top $(PROJ_TOP_MOD) -json $(PROJ).json" >> $@

$(BUILD_TMP)/$(PROJ).synth.rpt $(BUILD_TMP)/$(PROJ).json: $(PROJ_ALL_PREREQ) $(BUILD_TMP)/$(PROJ).ys $(PROJ_ALL_RTL_SRCS)
	cd $(BUILD_TMP) && \
		$(YOSYS) -s $(BUILD_TMP)/$(PROJ).ys \
			 -l $(BUILD_TMP)/$(PROJ).synth.rpt

$(BUILD_TMP)/$(PROJ).pnr.rpt $(BUILD_TMP)/$(PROJ).asc: $(BUILD_TMP)/$(PROJ).json $(PIN_DEF)
	$(NEXTPNR) $(NEXTPNR_ARGS) $(NEXTPNR_SYS_ARGS) \
		--$(DEVICE) --package $(PACKAGE)  \
		-l $(BUILD_TMP)/$(PROJ).pnr.rpt \
		--json $(BUILD_TMP)/$(PROJ).json \
		--pcf $(PIN_DEF) \
		--asc $@

%.bin: %.asc
	$(ICEPACK) -s $< $@


# Simulation
$(BUILD_TMP)/%_tb: sim/%_tb.v $(ICE40_LIBS) $(PROJ_ALL_PREREQ) $(PROJ_ALL_RTL_SRCS) $(PROJ_ALL_SIM_SRCS)
	$(IVERILOG) -Wall -Wno-portbind -Wno-timescale -DSIM=1 -DNO_ICE40_DEFAULT_ASSIGNMENTS -D$(BOARD_DEFINE)=1 -o $@ \
		$(PROJ_SYNTH_INCLUDES) $(PROJ_SIM_INCLUDES) \
		$(addprefix -l, $(ICE40_LIBS) $(PROJ_ALL_RTL_SRCS) $(PROJ_ALL_SIM_SRCS)) \
		$<


# Action targets

synth: $(BUILD_TMP)/$(PROJ).bin

sim: $(addprefix $(BUILD_TMP)/, $(PROJ_TESTBENCHES))

prog: $(BUILD_TMP)/$(PROJ).bin
	$(ICEPROG) $<

sudo-prog: $(BUILD_TMP)/$(PROJ).bin
	@echo 'Executing prog as root!!!'
	sudo $(ICEPROG) $<

dfuprog: $(BUILD_TMP)/$(PROJ).bin
ifeq ($(DFU_SERIAL),)
	$(DFU_UTIL) -e -a 0 -D $<
else
	$(DFU_UTIL) -e -S $(DFU_SERIAL) -a 0 -D $<
endif

clean:
	@rm -Rf $(BUILD_TMP)


.PHONY: all synth sim prog sudo-prog clean