# Makefile -- TSAC on FPGA: Pressing 44.1k audio through an uart line at 9600 baud (the receiver's side; README.md)
#   make prereq    the Python venv and Bellard's model files (~/.cache/tsac)
#   make run       check, prepare (once), 1 program the FPGA, 2 the image into the DDR3, 3 start + the clip,
#                  4 the board's UART bytes, 5 decode them and play them on ffplay
#   make check     what make run needs: the venv (torch/torchaudio CPU, dac), the models, ffplay, vivado, the UART
#   make net IFACE=enx...   the host's interface to the board: 192.168.77.1/24 (sudo ip; README's "The network")
#   make prep      the DDR3 image of CLIP (SEC seconds) into D/;   make prog: bit/top.bit into the FPGA over JTAG
#   make send      the board only: zero DDR3, the image, 'G', the PCM, the bytes back (no playing)
#   make decode    D/got.bin -> D/out.wav in one piece;   make play: ffplay it;   make status: the walker's words
PY      ?= $(CURDIR)/venv/bin/python
H       := $(CURDIR)/host/l8/cpu/tensor/shape3/test/tsac/pipe
CLIP    ?= $(CURDIR)/examples/clip.wav
SEC     ?= 13
D       ?= $(CURDIR)/run
IP      ?= 192.168.77.3
HOSTIP  ?= 192.168.77.1/24
TTY     ?= /dev/ttyUSB1
# Vivado: on the PATH, XILINX_VIVADO (its settings64.sh), or where the installer put it (the newest wins)
VIVADO  ?= $(shell command -v vivado 2>/dev/null || \
             ([ -x "$(XILINX_VIVADO)/bin/vivado" ] && echo "$(XILINX_VIVADO)/bin/vivado") || \
             ls -1d /fast4/xilinx/*/Vivado/bin/vivado /opt/Xilinx/*/Vivado/bin/vivado /tools/Xilinx/*/Vivado/bin/vivado \
                    $(HOME)/Xilinx/*/Vivado/bin/vivado 2>/dev/null | sort -V | tail -1)
# the CPU wheels the image was verified with (bit/top.bit's run): PyPI's torchaudio is a CUDA build
# (libcudart.so.13), and another torch calibrates the scales a little differently (a different, untested image)
TORCH   ?= torch==2.14.1+cpu torchaudio==2.11.0+cpu
.PHONY: help prereq check prep prog run send decode play status net
help:
	@sed -n '2,9p' Makefile
prereq:
	[ -x $(PY) ] || python3 -m venv venv
	$(PY) -m pip install --upgrade pip
	$(PY) -m pip install --force-reinstall --no-deps $(TORCH) --index-url https://download.pytorch.org/whl/cpu
	$(PY) -m pip install $(TORCH) --index-url https://download.pytorch.org/whl/cpu
	$(PY) -m pip install numpy soundfile pyserial descript-audio-codec
	$(PY) -c "import torch, torchaudio, dac; print('prereq: torch', torch.__version__, 'torchaudio', torchaudio.__version__)"
	sh scripts/get_models.sh
	$(MAKE) -s check
check:
	@[ -x $(PY) ] || { echo "check: no $(PY): make prereq"; exit 1; }
	@$(PY) -c "import torch, torchaudio, dac, numpy, soundfile, serial" 2>/dev/null || { $(PY) -c "import torch, torchaudio, dac, numpy, soundfile, serial" 2>&1 | tail -1; echo "check: the venv's packages do not load: make prereq"; exit 1; }
	@M=$${TSAC_MODELS:-$$HOME/.cache/tsac}; [ -s $$M/dac_stereo_q8.bin ] && [ -s $$M/tsac_stereo_q8.bin ] || { echo "check: no models in $$M: make prereq"; exit 1; }
	@command -v ffplay >/dev/null || { echo "check: no ffplay (the ffmpeg package)"; exit 1; }
	@[ -n "$(VIVADO)" ] && command -v $(VIVADO) >/dev/null || { echo "check: no vivado (its settings64.sh, or VIVADO=/path/to/vivado)"; exit 1; }
	@[ -c $(TTY) ] || { echo "check: no $(TTY) (the PE1's micro USB J603; TTY=...)"; exit 1; }
	@$(PY) -c "import torch, torchaudio; print('check: ok (torch', torch.__version__, 'torchaudio', torchaudio.__version__ + ')')"
$(D)/image.bin:
	@echo "== 0/5 prepare (once): the DDR3 image of $(notdir $(CLIP)), $(SEC) s, into $(D)"
	$(PY) $(H)/kx2_host.py prep $(D) --clip $(CLIP) --seconds $(SEC)
prep: $(D)/image.bin
prog:
	@echo "== 1/5 program the FPGA: bit/top.bit over JTAG (Vivado; log in $(D)/prog.log)"
	@mkdir -p $(D); t=$$(date +%s); $(VIVADO) -mode batch -nojournal -nolog -source scripts/jtag_prog.tcl \
	  -tclargs bit/top.bit > $(D)/prog.log 2>&1 && grep -q "DONE_PIN 1" $(D)/prog.log || \
	  { tail -20 $(D)/prog.log; echo "   not programmed (the Platform Cable on J1700, hw_server?)"; exit 1; }; \
	  echo "   programmed in $$(( $$(date +%s) - t )) s"
run: check $(D)/image.bin prog
	@$(PY) $(H)/kx2_host.py run $(D) --ip $(IP) --tty $(TTY) --play
send:
	$(PY) $(H)/kx2_host.py run $(D) --ip $(IP) --tty $(TTY)
decode:
	$(PY) $(H)/kx2_host.py decode $(D)
play:
	ffplay -nodisp -autoexit $(D)/out.wav
status:
	$(PY) $(H)/kx2_host.py status --ip $(IP)
net:
	@[ -n "$(IFACE)" ] || { echo "make net IFACE=<the interface the board's cable is in> (ip -br link)"; exit 1; }
	sudo ip addr replace $(HOSTIP) dev $(IFACE)
	sudo ip link set $(IFACE) up
	ip -br addr show dev $(IFACE)
