MAKEFLAGS      += -R -r

PROJECT		= max80
REVISIONS	= v1 v2
EXTRAREVS       = bypass
VARIANTS        =
QU		= quartus

ALLREVS         = $(REVISIONS) $(EXTRAREVS)

# Common options for all Quartus tools
QPRI		= --lower_priority
QCPF		= $(QU)_cpf $(QPRI)
QSH		= $(QU)_sh  $(QPRI)
QSTA		= $(QU)_sta $(QPRI)
QPGM		= $(QU)_pgm $(QPRI)

# Common options for Quartus in-flow tools
QOPT		= --write_settings_files=off $(QPRI)
QMAP		= $(QU)_map $(QOPT)
QFIT		= $(QU)_fit $(QOPT)
QCDB		= $(QU)_cdb $(QOPT)
QASM		= $(QU)_asm $(QOPT)
QPOW		= $(QU)_pow $(QOPT)

outdir		= output
mifdir		= mif

PERL		= perl
PYTHON		= python
SED		= sed
GZIP		= gzip

BUILDDATE      := $(shell LC_ALL=C date)

SUBDIRS     = usb
PREREQFILES = $(mifdir)/sram.mif \
	      $(foreach rev,$(ALLREVS),$(foreach coffmt,jic pof, \
		$(outdir)/$(rev).$(coffmt).cof))

alltarg := sof pof jic svf svf.gz xsvf xsvf.gz rbf rbf.gz rpf rpf.gz \
	   rpd rpd.gz pow.rpt sta.rpt
allout   = $(foreach o,$(alltarg),$(outdir)/$(1).$(o))
#vartarg := sof svf svf.gz rbf rbf.gz
#varout   = $(foreach o,$(vartarg),$(outdir)/$(1).$(o))
#uptarg  := update.svf update.svf.gz update.xsvf update.xsvf.gz
#upout    = $(foreach o,$(uptarg),$(outdir)/$(1).$(o))

sram_src = ../rv32/

MKFW		  = ../tools/mkfwimage.pl

RPF_ADDR	 := 0

DRAM_ADDR        := 0x100000
DRAM_IMAGE       := ../rv32/dram.bin

ESP_ADDR         := 0x10000
ESP_IMAGE        := ../esp32/output/max80.ino.bin
ESP_BOOT_ADDR    := 0x1000
ESP_BOOT_IMAGE   := ../esp32/output/max80.ino.bootloader.bin
ESP_PART_ADDR    := 0x8000
ESP_PART_IMAGE   := ../esp32/output/max80.ino.partitions.bin
ESP_OTACTL_ADDR  := 0xe000
ESP_OTACTL_IMAGE := ../esp32/boot_app0.bin
ESP_TOOL_OPT     := ../esp32/esptool.opt

IDCODE     := 0x020f20dd		# JTAG IDCODE of FPGA

BOARDINFO_ADDR   := 0xff000

FWDEPS = $(MKFW) $(DRAM_IMAGE) $(ESP_IMAGE) $(ESP_BOOT_IMAGE) \
	 $(ESP_PART_IMAGE) $(ESP_OTACTL_IMAGE) 

.SUFFIXES:

.SECONDARY:

.DELETE_ON_ERROR:

all: prereq
	$(MAKE) $(ALLREVS:=.targets)
	$(MAKE) fwimages
#	$(MAKE) $(VARIANTS)
#	$(MAKE) $(ALLREVS:=.update)

-include $(ALLREVS:=.deps)

.PHONY: fwimages
fwimages: $(patsubst %,$(outdir)/%.fw,$(REVISIONS) $(PROJECT))

.PHONY: $(ALLREVS)
$(ALLREVS): prereq
	$(MAKE) $@.targets

.PHONY: %.targets
%.targets:
	$(MAKE) $(call allout,$*)

.PHONY: %.update
%.update:
	$(MAKE) $(call upout,$*)

.PHONY: $(VARIANTS)
$(VARIANTS):
	$(MAKE) $(outdir)/$@/variant.stamp

.PHONY: %.vtargets
%.vtargets:
	$(MAKE) --old-file=$(outdir)/$*.fit.rpt $(call varout,$*)

$(outdir)/%/variant.stamp: mif/%.mif \
			   $(foreach rev,$(ALLREVS),$(outdir)/$(rev).fit.rpt)
	-rm -rf var/$* $(outdir)/$*
	mkdir -p var/$* var/$*/mif $(outdir)/$*
	( cd $(outdir)/$* && ln -sf ../*.asm.rpt ../*.fit.rpt . )
	cp -alf Makefile *.deps *.qpf *.qsf *.pins scripts \
		db incremental_db var/$*/
	ln -f $< var/$*/mif/sram.mif
	( cd var/$* && ln -sf ../../$(outdir)/$* ./output )
	$(MAKE) -C var/$* $(ALLREVS:=.vtargets)
	echo '$(BUILDDATE)' > $@

$(outdir)/%.map.rpt: %.qsf | $(mifdir)/sram.mif
	$(QMAP) $(PROJECT) -c $*

$(outdir)/%.fit.rpt: $(outdir)/%.map.rpt
	$(QFIT) $(PROJECT) -c $*

$(mifdir)/%.bin: $(sram_src)/%.bin $(all_map_deps) $(mifdir)
	cp -f $< $@
	printf '%s\0' '$(BUILDDATE)' >> $@

sram_depth  := 8192
sram_width  := 32
sram_stride := 1
$(mifdir)/%.mif: $(mifdir)/%.bin ../tools/bin2mif.pl
	$(PERL) ../tools/bin2mif.pl $< $@ \
		$(sram_depth) $(sram_width) $(sram_stride)

$(outdir)/%.mif_update.rpt: $(outdir)/%.fit.rpt
	[ -z '$($*_asm_deps)' ] || $(QCDB) --update_mif $(PROJECT) -c $*

$(outdir)/%.sof: $(outdir)/%.mif_update.rpt
	$(QASM) $(PROJECT) -c $*

$(outdir)/%.sta.rpt: $(outdir)/%.fit.rpt | $(outdir)/%.sof
	$(QSTA) $(PROJECT) -c $*

$(outdir)/%.pow.rpt: $(outdir)/%.sta.rpt | $(outdir)/%.sof
	$(QPOW) $(PROJECT) -c $*

$(foreach rev,$(ALLREVS),$(outdir)/$(rev).%.cof): %.cof.xml $(outdir)
	$(SED) -e 's/@@PROJECT@@/$(@F:.$*.cof=)/g' $< > $@

$(outdir)/%.jic: $(outdir)/%.jic.cof $(outdir)/%.sof ../rv32/dram.hex
	$(QCPF) -o bitstream_compression=on -c $<

$(outdir)/%.pof: $(outdir)/%.pof.cof $(outdir)/%.sof
	$(QCPF) -o bitstream_compression=on -c $<

# This produces a transient-load .svf file
# 6 MHz is the speed of USB-Blaster...
$(outdir)/%.svf: $(outdir)/%.sof
	$(QCPF) -c -q 6.0MHz -g 3.3 -n p $< $@

# xsvf: compact representation of .svf; more or less a wrapper around
# the raw binary file.
$(outdir)/%.xsvf: $(outdir)/%.svf ../tools/svf2xsvf.py
	$(PYTHON) ../tools/svf2xsvf.py $< $@

# Raw Binary File, compact data for transient programming.
$(outdir)/%.rbf: $(outdir)/%.sof
	$(QCPF) -c $< $@

# Raw Programmer Data, for loading into flash, includes all contents
# but is rather large if not compressed. Adjust bit ordering to match
# SPI...
$(outdir)/%.rpd: $(outdir)/%.pof
	$(QCPF) -c -o bitstream_compression=on -o rpd_little_endian=off $< $@

# RPD file for the FPGA only. The necessary length can be gotten from
# generating a compressed .rbf, but that file is not valid for flashing
# even if bit-reversed due to header(?) differences.
$(outdir)/%.z.rbf: $(outdir)/%.sof
	$(QCPF) -c -o bitstream_compression=on $< $@

$(outdir)/%.rpf: $(outdir)/%.rpd $(outdir)/%.z.rbf
	dd if=$< of=$@ bs=$$(wc -c < $(@:.rpf=.z.rbf)) count=1

$(outdir)/%.gz: $(outdir)/%
	$(GZIP) -9 < $< > $@

# New-style combined firmware image
ALLRPFS   = $(foreach rev,$(REVISIONS),$(outdir)/$(rev).rpf)
everyrev  = $(shell n=1; for r in $(REVISIONS); do echo "$(1)" -ver 0,0,$$n,$$n "$(2)"; n=$$((n+1)); done)
$(outdir)/$(PROJECT).fw: $(ALLRPFS) $(FWDEPS)
	$(PERL) $(MKFW) -o - -fwmin 2 \
		$(call everyrev,-type target,-str 'MAX80 $$r') \
		-type fpgainit -addr $(IDCODE) -file $(outdir)/bypass.rbf \
		$(call everyrev,-type data -addr $(RPF_ADDR),-file $(outdir)/$$r.rpf) \
		-type data -addr $(DRAM_ADDR) -file $(DRAM_IMAGE) \
		-type boardinfo -addr $(BOARDINFO_ADDR) -empty \
		-type esptool -optional -file $(ESP_TOOL_OPT) \
		-type espota -addr $(ESP_ADDR) -file $(ESP_IMAGE) \
		-type esppart -addr $(ESP_PART_ADDR) -file $(ESP_PART_IMAGE) \
		-type espsys -addr $(ESP_BOOT_ADDR) -file $(ESP_BOOT_IMAGE) \
		-type espsys -addr $(ESP_OTACTL_ADDR) -file $(ESP_OTACTL_IMAGE) \
	| $(GZIP) -9 > $@

# Old-style single version firmware images
$(outdir)/%.fw: $(outdir)/%.rpf $(outdir)/bypass.rbf \
		$(DRAM_IMAGE) $(ESP_IMAGE) \
		../tools/mkfwimage.pl
	$(PERL) ../tools/mkfwimage.pl -o - \
		-type target -str 'MAX80 $*' \
		-type fpgainit -addr $(IDCODE) -optional -file $(outdir)/bypass.rbf \
		-type data -addr $(RPF_ADDR) -file $< \
		-type data -addr $(DRAM_ADDR) -file $(DRAM_IMAGE) \
		-type boardinfo -optional -addr $(BOARDINFO_ADDR) -empty \
		-type esptool -optional -file $(ESP_TOOL_OPT) \
		-type espota -addr $(ESP_ADDR) -file $(ESP_IMAGE) \
		-type esppart -optional -addr $(ESP_PART_ADDR) -file $(ESP_PART_IMAGE) \
		-type espsys -optional -addr $(ESP_BOOT_ADDR) -file $(ESP_BOOT_IMAGE) \
		-type espsys -optional -addr $(ESP_OTACTL_ADDR) -file $(ESP_OTACTL_IMAGE) \
	| $(GZIP) -9 > $@

$(outdir)/%.update.svf: ./scripts/flashsvf.pl \
	$(outdir)/jtagupd/%.svf $(outdir)/%.map.rpt $(outdir)/%.fw
	$(PERL) $^ $@

# Prerequisite directories and files
$(outdir) $(mifdir):
	mkdir -p $@

.PHONY: prereq
prereq: $(outdir) $(mifdir)
	for d in $(SUBDIRS); do $(MAKE) -C $$d; done
	$(MAKE) $(PREREQFILES)

# Clean out SignalTap
signalclean:
	for f in *.qsf; do \
		$(PERL) -ne 'print unless (/(SIGNALTAP_FILE\b|\bENABLE_SIGNALTAP\b|\bSLD_FILE\b|SLD_NODE_)/);' < $$f > $$f.tmp && \
		mv -f $$f.tmp $$f ; \
	done

# Programming targets. Environment JTAG_CABLE can override the default,
# which is otherwise the first cable found.
rpar := )
JTAG_CABLE ?= $(shell jtagconfig --enum | sed -ne 's/^1$(rpar) //p')

# Transient programming
program-%: $(outdir)/%.sof
	$(QPGM) -c '$(JTAG_CABLE)' -m JTAG -o 'p;$(outdir)/$*.sof'

# Permanent programming in flash
flash-%: $(outdir)/%.jic
	$(QPGM) -c '$(JTAG_CABLE)' -m JTAG -i -o 'pvbi;$(outdir)/$*.jic'

%.deps: %.qsf scripts/qsfdeps.pl
	$(PERL) scripts/qsfdeps.pl $* $@ $<

clean:
	for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
	rm -rf db incremental_db var simulation/modelsim \
	greybox_tmp */greybox_tmp iodevs.vh output_files $(mifdir)
	for d in $(ALLREVS); do \
		rm -f $$d/*.rpt $$d/*.rpt $$d/*.summary $$d/*.smsg  \
		$$d/*.htm $$d/*.htm_files $$d/*.map $$d/*.eqn $$d/*.sld \
		$$d/*.done ; \
	done

spotless: clean
	for d in $(SUBDIRS); do $(MAKE) -C $$d spotless; done
	rm -rf $(outdir) $(mifdir) *.deps *~ \#*~

iodevs.vh: ../iodevs.conf ../tools/iodevs.pl
	$(PERL) ../tools/iodevs.pl v $< $@

deps: Makefile

# Verilog header dependencies
max80.sv: iodevs.vh
