# servant (SERV RISC-V SoC) for GW1NZ-1, QFN48 -- the first design for the Gowin fabric model.
# Tools are built from source into $(TOOLS) (see ../../plan.md); SERV is a clone in $(SERV).
TOOLS ?= $(HOME)/opt/gowin-tools
SERV  ?= $(HOME)/git/serv
FW    ?= hello_uart
MEMSIZE ?= 4096
WITH_CSR ?= 1
OUT   ?= out
FAMILY ?= GW1NZ-1
DEVICE ?= GW1NZ-LV1QN48C6/I5
CST ?= servant_gw1nz.cst
# servant-sized crop: make FAMILY=GW1NZ-1-trim12-s CST=servant_crop.cst WITH_CSR=0 MEMSIZE=1024
# the trimmed virtual chip: make FAMILY=GW1NZ-1-trim8 (same pins and part, fewer pips)

YOSYS   = $(TOOLS)/bin/yosys
NEXTPNR = LD_LIBRARY_PATH=$(TOOLS)/lib $(TOOLS)/bin/nextpnr-himbaechel
PACK    = $(TOOLS)/venv/bin/gowin_pack

# serv_rf_ram is replaced by serv_rf_ram_sdpb.v: one SDPB (GW1NZ-1 has no dual port at width 2, UG285)
RTL = $(addprefix $(SERV)/rtl/serv_,bufreg bufreg2 alu csr ctrl decode immdec mem_if rf_if rf_ram_if \
        state debug top rf_top aligner compdec) \
      $(addprefix $(SERV)/servile/servile_,rf_mem_if mux arbiter) $(SERV)/servile/servile \
      $(addprefix $(SERV)/servant/servant_,timer gpio mux ram) $(SERV)/servant/servant
SRC = $(addsuffix .v,$(RTL)) serv_rf_ram_sdpb.v servant_gw1nz.v

all: $(OUT)/servant_gw1nz.fs

# firmware from source (never the shipped .hex): clang/lld rv32i_zicsr, SERV's makehex.py
$(OUT)/%.hex: $(SERV)/sw/%.S $(SERV)/sw/link.ld
	@mkdir -p $(OUT)
	clang --target=riscv32-unknown-elf -march=rv32i_zicsr -mabi=ilp32 -nostdlib -nostartfiles \
	  -fuse-ld=lld -Wl,-T$(SERV)/sw/link.ld -o $(OUT)/$*.elf $<
	llvm-objcopy -O binary $(OUT)/$*.elf $(OUT)/$*.bin
	python3 -I $(SERV)/sw/makehex.py $(OUT)/$*.bin > $@

$(OUT)/servant.json: $(SRC) $(OUT)/$(FW).hex
	cd $(OUT) && $(YOSYS) -q -l synth.log -p "read_verilog -defer -I$(SERV)/rtl $(abspath $(SRC)); \
	  chparam -set memsize $(MEMSIZE) -set with_csr $(WITH_CSR) -set memfile \"$(FW).hex\" servant_gw1nz; hierarchy -top servant_gw1nz; \
	  synth_gowin -nolutram -top servant_gw1nz -json servant.json"

$(OUT)/pnr.json: $(OUT)/servant.json $(CST)
	$(NEXTPNR) --json $< --write $@ --device $(DEVICE) --vopt family=$(FAMILY) \
	  --vopt cst=$(CST) --freq 27 > $(OUT)/pnr.log 2>&1 || (tail -5 $(OUT)/pnr.log; false)

$(OUT)/servant_gw1nz.fs: $(OUT)/pnr.json
	$(PACK) -d $(FAMILY) -o $@ $<

# the RTL golden: servant at RTL under iverilog (sim/sdpb_sim.v for the SDPB), the UART pin decoded
IVERILOG = $(TOOLS)/bin/iverilog
VVP      = $(TOOLS)/bin/vvp
CYCLES  ?= 400000
rtl: $(OUT)/$(FW).hex
	cd $(OUT) && $(IVERILOG) -g2012 -o rtl.vvp -DMEMFILE='"$(FW).hex"' -DCYCLES=$(CYCLES) -I$(SERV)/rtl \
	  $(abspath $(SRC)) $(abspath sim/sdpb_sim.v sim/tb.v) && $(VVP) -n rtl.vvp > rtl.log
	python3 -I sim/uart_decode.py $(OUT)/uart.log

# serving (SERV + one shared RAM, no timer): make serving [FAMILY=... CST=...]; firmware one byte a line
SERVING_SRC = $(addsuffix .v,$(addprefix $(SERV)/rtl/serv_,bufreg bufreg2 alu csr ctrl decode immdec mem_if rf_if \
        rf_ram_if state debug top rf_top aligner compdec) $(SERV)/servile/servile_rf_mem_if $(SERV)/servile/servile_mux \
        $(SERV)/servile/servile_arbiter $(SERV)/servile/servile $(SERV)/serving/serving_ram $(SERV)/serving/serving) \
        serving_gw1nz.v
$(OUT)/%.b8.hex: $(OUT)/%.hex
	python3 -I -c "import sys; [print('%02x' % ((int(w, 16) >> (8 * k)) & 255)) for w in open(sys.argv[1]).read().split() for k in range(4)]" $< > $@
serving: $(OUT)/$(FW).b8.hex
	cd $(OUT) && $(YOSYS) -q -l synth.log -p "read_verilog -defer -I$(SERV)/rtl $(abspath $(SERVING_SRC)); \
	  chparam -set memfile \"$(FW).b8.hex\" serving_gw1nz; hierarchy -top serving_gw1nz; \
	  synth_gowin -nolutram -top serving_gw1nz -json servant.json"
	$(NEXTPNR) --json $(OUT)/servant.json --write $(OUT)/pnr.json --device $(DEVICE) --vopt family=$(FAMILY) \
	  --vopt cst=$(CST) --freq 27 > $(OUT)/pnr.log 2>&1 || (tail -5 $(OUT)/pnr.log; false)
	$(PACK) -d $(FAMILY) -o $(OUT)/serving_gw1nz.fs $(OUT)/pnr.json
	grep -E "LUT4:|ALU:|DFF:|BSRAM:|Max freq" $(OUT)/pnr.log | tail -5

# servmin (servile + RAM + RF on one SDPB + 1-bit GPIO; no timer, no mux): make servmin [FAMILY=... CST=...]
SERVMIN_SRC = $(addsuffix .v,$(addprefix $(SERV)/rtl/serv_,bufreg bufreg2 alu csr ctrl decode immdec mem_if rf_if \
        rf_ram_if state debug top rf_top aligner compdec) $(addprefix $(SERV)/servile/servile_,rf_mem_if mux arbiter) \
        $(SERV)/servile/servile $(SERV)/servant/servant_ram) serv_rf_ram_sdpb.v servmin_gw1nz.v
servmin: $(OUT)/$(FW).hex
	cd $(OUT) && $(YOSYS) -q -l synth.log -p "read_verilog -defer -I$(SERV)/rtl $(abspath $(SERVMIN_SRC)); \
	  chparam -set memsize $(MEMSIZE) -set memfile \"$(FW).hex\" servmin_gw1nz; hierarchy -top servmin_gw1nz; \
	  synth_gowin -nolutram -top servmin_gw1nz -json servant.json"
	$(NEXTPNR) --json $(OUT)/servant.json --write $(OUT)/pnr.json --device $(DEVICE) --vopt family=$(FAMILY) \
	  --vopt cst=$(CST) --freq 27 > $(OUT)/pnr.log 2>&1 || (tail -5 $(OUT)/pnr.log; false)
	$(PACK) -d $(FAMILY) -o $(OUT)/servant_gw1nz.fs $(OUT)/pnr.json
	grep -E "LUT4:|ALU:|DFF:|BSRAM:|Max freq" $(OUT)/pnr.log | tail -5

# servtiny (serv_top + our glue: one shared RAM, a 1-bit GPIO; PRE=0|1 decode pre-register):
#   make servtiny [FAMILY=... CST=... PRE=1]
PRE ?= 1
SEED ?=           # nextpnr's seed: the s2 crops route servtiny only with some (GW1NZ-1-trim10-s2: SEED=2, 52 MHz)
SERVTINY_SRC = $(addsuffix .v,$(addprefix $(SERV)/rtl/serv_,bufreg bufreg2 alu csr ctrl decode immdec mem_if rf_if \
        rf_ram_if state debug top aligner compdec)) serv_rf_ram_sdpb.v servtiny_gw1nz.v
servtiny: $(OUT)/$(FW).hex
	cd $(OUT) && $(YOSYS) -q -l synth.log -p "read_verilog -defer -I$(SERV)/rtl $(abspath $(SERVTINY_SRC)); \
	  chparam -set memsize $(MEMSIZE) -set pre_register $(PRE) -set memfile \"$(FW).hex\" servtiny_gw1nz; \
	  hierarchy -top servtiny_gw1nz; synth_gowin -nolutram -top servtiny_gw1nz -json servant.json"
	$(NEXTPNR) --json $(OUT)/servant.json --write $(OUT)/pnr.json --device $(DEVICE) --vopt family=$(FAMILY) \
	  --vopt cst=$(CST) --freq 27 $(if $(strip $(SEED)),--seed $(strip $(SEED))) > $(OUT)/pnr.log 2>&1 || (tail -5 $(OUT)/pnr.log; false)
	$(PACK) -d $(FAMILY) -o $(OUT)/servant_gw1nz.fs $(OUT)/pnr.json
	grep -E "LUT4:|ALU:|DFF:|BSRAM:|Max freq" $(OUT)/pnr.log | tail -5

clean:
	rm -rf $(OUT)
.PHONY: all clean rtl serving servmin servtiny
