SHELL := /bin/bash
.SHELLFLAGS := -euo pipefail -c
.ONESHELL:
.NOTPARALLEL: detect interface map map-locked bitstream bitstream-locked \
	program-fpga program-flash-loader

FPGA_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
REPO_DIR := $(abspath $(FPGA_DIR)/../../..)
SOC_SW_DIR := $(REPO_DIR)/soc/software
SOC_OUT_DIR := $(REPO_DIR)/soc/out
HW_DIR := $(REPO_DIR)/hardware
HW_GENERATED_DIR := $(HW_DIR)/generated
GPS_SYSTEM_DIR ?= $(HW_DIR)/external/gps/rtl
FLOW_DIR := $(FPGA_DIR)/flow/efinity
DEBUG_DIR := $(FPGA_DIR)/flow/debug
TOOL_SCRIPT_DIR := $(FPGA_DIR)/tools
OUT_DIR := $(FPGA_DIR)/out
LOG_DIR := $(OUT_DIR)/logs
BUILD_LOCK ?= $(OUT_DIR)/.bitstream.lock

include $(REPO_DIR)/tools/project.mk

BOARD ?= ti375c529
DEVICE ?= Ti375C529
TIMING_MODEL ?= C4
SOC_CLK_HZ ?= 100000000
GPS_ACQ_CLK_HZ ?= 100000000
GPS_TRCK_CLK_HZ ?= 125000000
GPS_RF_CLK_HZ ?= 16367981
RF_INPUT_MODE ?= pmod
UART_PORT ?= /dev/ttyUSB2
UART_BAUD ?= 460800
OPENOCD ?= $(shell command -v openocd)
OPENOCD_ADAPTER_SPEED ?= 1000
GDB_HOST ?= localhost
GDB_PORT ?= 3333
GDB ?= $(firstword $(wildcard /tools/toolchain/bin/riscv64-unknown-elf-gdb) $(CROSS)gdb)
# Leave empty for Efinity's board-profile auto-detection. Boards without an
# FTDI serial number require a bus/device-qualified URL, which changes after
# re-enumeration; users with multiple boards can override this explicitly.
FTDI_URL ?=
BSP_UART_CLOCK_HZ ?= $(SOC_CLK_HZ)
BSP_UART_BAUDRATE ?= $(UART_BAUD)
BSP_CLINT_TIMEBASE_HZ ?= 1000000
DTIM_BASE ?= 0x80000000
DTIM_SIZE ?= 0x00100000
# The compressed Ti375 configuration currently occupies about 5.2 MB.
# Keep the application on a 64-KiB boundary with ample growth margin.
APP_FLASH_OFFSET ?= 0x00800000
FLASH_SIZE ?= 0x04000000
GPS_DEBUG ?= 1
GPS_DEBUG_LEVEL ?= 4
GPS_ESKF_ENABLE ?= 1
GPS_EPHEMERIS_FLASH_ENABLE ?= 1
GPS_EPHEMERIS_FLASH_SEED_INITIAL ?= 1
GPS_EPHEMERIS_FLASH_BASE ?= 0x03fe0000
GPS_NAV_CONTEXT_ENABLE ?= 1
GPS_NAV_CONTEXT_FLASH_BASE ?= 0x03fc0000
BSP_SPI_FLASH_PROFILE ?= 3
IMU_SENSOR ?= auto
app ?=
ifneq ($(strip $(app)),)
APP ?= $(app)
else
APP ?= hello_uart
endif

FIRRTL_DIR ?= $(HW_GENERATED_DIR)/ti375c529-soc
VERILOG_DIR ?= $(HW_GENERATED_DIR)/ti375c529-soc-verilog
WRAPPER := $(OUT_DIR)/build/generated/ti375c529_rocket_top.sv
GPS_EFINITY_DIR := $(OUT_DIR)/build/generated/gps_efinity
SOURCE_LIST := $(OUT_DIR)/build/efinity_sources.f
PROJECT_XML := $(FLOW_DIR)/ti375c529_rocket.xml
INTERFACE_SOURCE := $(HW_DIR)/fpga/ti375c529/constraints/ti375c529_rocket.isf
INTERFACE_SCRIPT := $(FLOW_DIR)/ti375c529_rocket.isf
BIT_FILE ?= $(OUT_DIR)/bitstream/ti375c529_rocket.bit
CONFIG_HEX ?= $(OUT_DIR)/bitstream/ti375c529_rocket.hex
APP_ELF ?= $(SOC_OUT_DIR)/software/$(APP)/$(APP).elf
APP_FLASH_IMAGE ?= $(SOC_OUT_DIR)/flash/$(APP).flash.bin
FLASH_DIR := $(OUT_DIR)/flash
FLASH_REGION_DIR := $(FLASH_DIR)/regions
APP_HEX ?= $(FLASH_REGION_DIR)/app.hex
FLASH_HEX ?= $(FLASH_DIR)/ti375c529_rocket.hex
FLASH_MANIFEST ?= $(FLASH_DIR)/ti375c529_rocket.json
FLASH_LOADER ?= $(EFINITY_HOME)/pgm/fli/titanium/u006A0A79.bit
FLASH_VERIFY ?= onchipx2
OPENOCD_CFG ?= $(DEBUG_DIR)/openocd_ti375c529_bscan.cfg
GDB_LOAD_SCRIPT ?= $(DEBUG_DIR)/gdb_load_and_run.gdb
GDB_ATTACH_SCRIPT ?= $(DEBUG_DIR)/gdb_attach.gdb

.PHONY: help all fpga-config efinity-build-check efinity-program-check \
	detect software chisel rtl soc interface map map-locked bitstream bitstream-locked \
	program-fpga debug-openocd debug-load debug-gdb uart-listen \
	flash-image flash-regions program-flash program-flash-elf \
	program-flash-bitstream program-flash-regions program-flash-all \
	program-flash-combined program-flash-loader program-app program-bitstream \
	clean clean-sw clean-fpga FORCE

help:
	@printf '%s\n' \
	  'Ti375C529 Efinity targets:' \
	  '  make detect          Scan USB and read the Ti375 JTAG IDCODE' \
	  '  make software        Build the Rocket boot ROM and selected RV64 app' \
	  '  make chisel         Emit Rocket FIRRTL with the existing FPGA config' \
	  '  make rtl            Emit split SystemVerilog, Efinix shell, and file list' \
	  '  make interface      Generate Efinity pin/PLL/JTAG interface data' \
	  '  make map            Run Efinity synthesis only' \
	  '  make bitstream      Run the complete Efinity flow' \
	  '  make program-fpga   Load BIT_FILE into Ti375 SRAM over JTAG' \
	  '  make flash-image    Generate combined Lower-flash Efinity HEX' \
	  '  make program-flash  Program only the application flash region' \
	  '  make program-flash-bitstream' \
	  '                      Program only the configuration region' \
	  '  make program-flash-combined' \
	  '                      Program configuration + application HEX' \
	  '  make debug-openocd Start Rocket DMI/GDB server through JTAG_USER1' \
	  '  make debug-load     Load APP ELF into DTIM through GDB' \
	  '  make uart-listen    Listen to FTDI UART interface 2' \
	  '  make fpga-config    Print resolved tools, clocks, files, and USB config'

all: bitstream

efinity-build-check:
	@if [[ -z "$(strip $(EFINITY_HOME))" ]]; then
	  echo "Efinity installation not found." >&2
	  echo "Install Efinity, then set EFINITY_HOME=/path/to/efinity/<version>." >&2
	  echo "Searched /tools/efinix, /tools/Efinix, /opt, \$$HOME, and tools/local/efinity." >&2
	  exit 1
	fi
	for required in "$(EFINITY_SETUP)" "$(EFX_RUN)"; do
	  if [[ ! -r "$$required" ]]; then
	    echo "Incomplete Efinity installation: missing $$required" >&2
	    echo "Set EFINITY_HOME to the directory containing bin/setup.sh." >&2
	    exit 1
	  fi
	done

efinity-program-check: efinity-build-check
	@if [[ ! -x "$(EFX_PGM)" ]]; then
	  echo "Incomplete Efinity installation: missing $(EFX_PGM)" >&2
	  exit 1
	fi

fpga-config:
	@printf '%s\n' \
	  'Ti375C529 FPGA config:' \
	  '  EFINITY_HOME=$(if $(strip $(EFINITY_HOME)),$(EFINITY_HOME),NOT FOUND)' \
	  '  EFINITY_SETUP=$(if $(strip $(EFINITY_SETUP)),$(EFINITY_SETUP),NOT FOUND)' \
	  '  EFX_RUN=$(if $(strip $(EFX_RUN)),$(EFX_RUN),NOT FOUND)' \
	  '  EFX_PGM=$(if $(strip $(EFX_PGM)),$(EFX_PGM),NOT FOUND)' \
	  '  OPENOCD=$(OPENOCD)' \
	  '  GDB=$(GDB)' \
	  '  DEVICE=$(DEVICE)' \
	  '  TIMING_MODEL=$(TIMING_MODEL)' \
	  '  SOC_CLK_HZ=$(SOC_CLK_HZ)' \
	  '  GPS_ACQ_CLK_HZ=$(GPS_ACQ_CLK_HZ)' \
	  '  GPS_TRCK_CLK_HZ=$(GPS_TRCK_CLK_HZ)' \
	  '  GPS_RF_CLK_HZ=$(GPS_RF_CLK_HZ)' \
	  '  RF_INPUT_MODE=$(RF_INPUT_MODE)' \
	  '  BIT_FILE=$(BIT_FILE)' \
	  '  CONFIG_HEX=$(CONFIG_HEX)' \
	  '  FLASH_HEX=$(FLASH_HEX)' \
	  '  APP_FLASH_OFFSET=$(APP_FLASH_OFFSET)' \
	  '  GPS_EPHEMERIS_FLASH_ENABLE=$(GPS_EPHEMERIS_FLASH_ENABLE)' \
	  '  GPS_EPHEMERIS_FLASH_SEED_INITIAL=$(GPS_EPHEMERIS_FLASH_SEED_INITIAL)' \
	  '  GPS_EPHEMERIS_FLASH_BASE=$(GPS_EPHEMERIS_FLASH_BASE)' \
	  '  GPS_NAV_CONTEXT_ENABLE=$(GPS_NAV_CONTEXT_ENABLE)' \
	  '  GPS_NAV_CONTEXT_FLASH_BASE=$(GPS_NAV_CONTEXT_FLASH_BASE)' \
	  '  BSP_SPI_FLASH_PROFILE=$(BSP_SPI_FLASH_PROFILE)' \
	  '  FLASH_LOADER=$(FLASH_LOADER)' \
	  '  FTDI_URL=$(FTDI_URL)' \
	  '  UART_PORT=$(UART_PORT)' \
	  '  UART_BAUD=$(UART_BAUD)'

detect: efinity-program-check
	@set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	"$(EFX_PGM)" --scan_usb

software:
	@$(MAKE) -C "$(SOC_SW_DIR)" \
	  TOOLCHAIN="$(TOOLCHAIN)" APP="$(APP)" \
	  APP_FLASH_OFFSET="$(APP_FLASH_OFFSET)" \
	  BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" \
	  BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
	  GPS_SYSTEM_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
	  BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" \
	  BSP_DTIM_BASE="$(DTIM_BASE)" BSP_DTIM_SIZE="$(DTIM_SIZE)" \
	  GPS_DEBUG="$(GPS_DEBUG)" GPS_DEBUG_LEVEL="$(GPS_DEBUG_LEVEL)" \
	  GPS_ESKF_ENABLE="$(GPS_ESKF_ENABLE)" \
	  GPS_EPHEMERIS_FLASH_ENABLE="$(GPS_EPHEMERIS_FLASH_ENABLE)" \
	  GPS_EPHEMERIS_FLASH_SEED_INITIAL="$(GPS_EPHEMERIS_FLASH_SEED_INITIAL)" \
	  GPS_EPHEMERIS_FLASH_BASE="$(GPS_EPHEMERIS_FLASH_BASE)" \
	  GPS_NAV_CONTEXT_ENABLE="$(GPS_NAV_CONTEXT_ENABLE)" \
	  GPS_NAV_CONTEXT_FLASH_BASE="$(GPS_NAV_CONTEXT_FLASH_BASE)" \
	  BSP_SPI_FLASH_PROFILE="$(BSP_SPI_FLASH_PROFILE)" \
	  IMU_SENSOR="$(IMU_SENSOR)" \
	  bootloader flash-image

$(APP_ELF): FORCE
	@$(MAKE) -C "$(SOC_SW_DIR)" \
	  TOOLCHAIN="$(TOOLCHAIN)" APP="$(APP)" \
	  BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" \
	  BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
	  GPS_SYSTEM_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" \
	  BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" \
	  BSP_DTIM_BASE="$(DTIM_BASE)" BSP_DTIM_SIZE="$(DTIM_SIZE)" \
	  GPS_DEBUG="$(GPS_DEBUG)" GPS_DEBUG_LEVEL="$(GPS_DEBUG_LEVEL)" \
	  GPS_ESKF_ENABLE="$(GPS_ESKF_ENABLE)" \
	  GPS_EPHEMERIS_FLASH_ENABLE="$(GPS_EPHEMERIS_FLASH_ENABLE)" \
	  GPS_EPHEMERIS_FLASH_SEED_INITIAL="$(GPS_EPHEMERIS_FLASH_SEED_INITIAL)" \
	  GPS_EPHEMERIS_FLASH_BASE="$(GPS_EPHEMERIS_FLASH_BASE)" \
	  GPS_NAV_CONTEXT_ENABLE="$(GPS_NAV_CONTEXT_ENABLE)" \
	  GPS_NAV_CONTEXT_FLASH_BASE="$(GPS_NAV_CONTEXT_FLASH_BASE)" \
	  BSP_SPI_FLASH_PROFILE="$(BSP_SPI_FLASH_PROFILE)" \
	  IMU_SENSOR="$(IMU_SENSOR)" app

chisel: software
	@cd "$(REPO_DIR)"
	"$(MILL)" -i 'rocketchip[6.7.0].assembly'
	rm -rf "$(FIRRTL_DIR)"
	mkdir -p "$(FIRRTL_DIR)"
	"$(JAVA_HOME)/bin/java" \
	  -DROCKET_DTIM_BASE="$(DTIM_BASE)" \
	  -DROCKET_DTIM_SIZE="$(DTIM_SIZE)" \
	  -DROCKET_SOC_CLOCK_HZ="$(SOC_CLK_HZ)" \
	  -jar out/rocketchip/6.7.0/assembly.dest/out.jar \
	  --dir "$(FIRRTL_DIR)" \
	  --top freechips.rocketchip.fpga.VCU118RocketSystem \
	  --config freechips.rocketchip.fpga.VCU118RocketConfig

rtl: chisel
	@cd "$(REPO_DIR)"
	rm -rf "$(VERILOG_DIR)"
	mkdir -p "$(VERILOG_DIR)" "$(dir $(WRAPPER))"
	$(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 './%f\n' | sort > "$(VERILOG_DIR)/filelist.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 "$(TOOL_SCRIPT_DIR)/prepare_efinity_rtl.py" \
	  --verilog-dir "$(VERILOG_DIR)" \
	  --gps-source-dir "$(GPS_SYSTEM_DIR)" \
	  --gps-output-dir "$(GPS_EFINITY_DIR)"
	python3 "$(TOOL_SCRIPT_DIR)/gen_ti375c529_wrapper.py" \
	  --rocket-top "$(VERILOG_DIR)/VCU118RocketSystem.sv" \
	  --out "$(WRAPPER)"
	: > "$(SOURCE_LIST)"
	while IFS= read -r rel; do
	  rel="$${rel#./}"
	  printf '%s\n' "$(VERILOG_DIR)/$$rel" >> "$(SOURCE_LIST)"
	done < "$(VERILOG_DIR)/filelist.f"
	printf '%s\n' "$(HW_DIR)/upstream/rocket-chip/src/main/resources/vsrc/plusarg_reader.v" >> "$(SOURCE_LIST)"
	while IFS= read -r rel; do
	  [[ -z "$$rel" || "$$rel" == \#* ]] && continue
	  case "$$rel" in
	    trck_src/xil_fifo.sv|trck_src/pulp_fifo/top.sv) continue ;;
	    acq_src/acq_nco_carr.sv)
	      printf '%s\n' "$(GPS_EFINITY_DIR)/acq_nco_carr.sv" >> "$(SOURCE_LIST)"
	      continue ;;
	    acq_src/twiddle_ram.sv)
	      printf '%s\n' "$(GPS_EFINITY_DIR)/twiddle_ram.sv" >> "$(SOURCE_LIST)"
	      continue ;;
	    trck_src/NCO_carr.sv)
	      printf '%s\n' "$(GPS_EFINITY_DIR)/NCO_carr.sv" >> "$(SOURCE_LIST)"
	      continue ;;
	  esac
	  printf '%s\n' "$(GPS_SYSTEM_DIR)/$$rel" >> "$(SOURCE_LIST)"
	done < "$(GPS_SYSTEM_DIR)/filelist.f"
	printf '%s\n' "$(HW_DIR)/ip/max2771_spi/rtl/max2771_spi_engine.sv" >> "$(SOURCE_LIST)"
	# Efinix JTAG_USER1 has the same two-bit TDO lead-in as the Kintex-7
	# BSCANE2 path, so use the Genesys2-aligned Rocket DMI tunnel.
	printf '%s\n' "$(HW_DIR)/fpga/genesys2/rtl/rocket_bscan_dmi_tunnel.sv" >> "$(SOURCE_LIST)"
	$(MAKE) -C "$(FPGA_DIR)" "$(INTERFACE_SCRIPT)"

soc: rtl

$(INTERFACE_SCRIPT): $(INTERFACE_SOURCE)
	cp "$<" "$@"

interface: efinity-build-check $(INTERFACE_SCRIPT)
	@mkdir -p "$(LOG_DIR)"
	cd "$(FLOW_DIR)"
	# Efinity prefers a matching generated .peri.xml over the source .isf.
	# Remove it so pin/PLL changes in the checked-in interface script are applied.
	rm -f "$(FLOW_DIR)/ti375c529_rocket.peri.xml"
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	python3 -u "$(EFX_RUN)" "$(PROJECT_XML)" --flow interface --un_flow \
	  --pt_opts "peri_scripts=$(INTERFACE_SCRIPT)" 2>&1 | tee "$(LOG_DIR)/interface.log"

map: efinity-build-check
	@mkdir -p "$(dir $(BUILD_LOCK))"
	set +e
	flock -E 75 -n "$(BUILD_LOCK)" $(MAKE) -C "$(FPGA_DIR)" map-locked
	status=$$?
	set -e
	if [[ $$status -eq 75 ]]; then
	  echo "Another Ti375 bitstream/map build is already running." >&2
	fi
	exit $$status

map-locked: rtl interface
	@mkdir -p "$(LOG_DIR)"
	cd "$(FLOW_DIR)"
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	python3 -u "$(EFX_RUN)" "$(PROJECT_XML)" --flow map 2>&1 | tee "$(LOG_DIR)/map.log"

bitstream: efinity-build-check
	@mkdir -p "$(dir $(BUILD_LOCK))"
	set +e
	flock -E 75 -n "$(BUILD_LOCK)" $(MAKE) -C "$(FPGA_DIR)" bitstream-locked
	status=$$?
	set -e
	if [[ $$status -eq 75 ]]; then
	  echo "Another Ti375 bitstream/map build is already running." >&2
	fi
	exit $$status

bitstream-locked: rtl interface
	@mkdir -p "$(LOG_DIR)" "$(dir $(BIT_FILE))"
	cd "$(FLOW_DIR)"
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	: > "$(LOG_DIR)/full.log"
	python3 -u "$(EFX_RUN)" "$(PROJECT_XML)" --flow map 2>&1 | tee -a "$(LOG_DIR)/full.log"
	python3 -u "$(EFX_RUN)" "$(PROJECT_XML)" --flow pnr --work_dir work_pnr 2>&1 | tee -a "$(LOG_DIR)/full.log"
	python3 -u "$(EFX_RUN)" "$(PROJECT_XML)" --flow pgm --work_dir work_pnr 2>&1 | tee -a "$(LOG_DIR)/full.log"
	generated_bit="$$(find "$(FLOW_DIR)/outflow" "$(OUT_DIR)" -type f -name 'ti375c529_rocket.bit' 2>/dev/null | head -n 1)"
	if [[ -z "$$generated_bit" ]]; then
	  echo "Efinity completed without a discoverable ti375c529_rocket.bit" >&2
	  exit 1
	fi
	if [[ "$$generated_bit" != "$(BIT_FILE)" ]]; then
	  cp "$$generated_bit" "$(BIT_FILE)"
	fi
	generated_hex="$$(find "$(FLOW_DIR)/outflow" "$(OUT_DIR)" -type f -name 'ti375c529_rocket.hex' ! -path '$(FLASH_DIR)/*' 2>/dev/null | head -n 1)"
	if [[ -z "$$generated_hex" ]]; then
	  echo "Efinity completed without a discoverable ti375c529_rocket.hex" >&2
	  exit 1
	fi
	if [[ "$$generated_hex" != "$(CONFIG_HEX)" ]]; then
	  cp "$$generated_hex" "$(CONFIG_HEX)"
	fi
	printf 'Bitstream: %s\nConfiguration HEX: %s\n' "$(BIT_FILE)" "$(CONFIG_HEX)"

program-fpga: efinity-program-check
	@if [[ ! -f "$(BIT_FILE)" ]]; then
	  echo "Missing bitstream: $(BIT_FILE)" >&2
	  echo "Run: make -C $(FPGA_DIR) bitstream" >&2
	  exit 1
	fi
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	ftdi_args=()
	if [[ -n "$(FTDI_URL)" ]]; then
	  ftdi_args+=(--url "$(FTDI_URL)")
	fi
	"$(EFX_PGM)" --mode jtag "$${ftdi_args[@]}" "$(BIT_FILE)"

debug-openocd:
	@"$(OPENOCD)" \
	  -c "set ADAPTER_SPEED $(OPENOCD_ADAPTER_SPEED)" \
	  -c "gdb_port $(GDB_PORT)" \
	  -f "$(OPENOCD_CFG)"

debug-load: $(APP_ELF)
	@"$(GDB)" -q "$(APP_ELF)" \
	  -ex "set confirm off" \
	  -ex "set pagination off" \
	  -ex "target extended-remote $(GDB_HOST):$(GDB_PORT)" \
	  -x "$(GDB_LOAD_SCRIPT)"

debug-gdb: $(APP_ELF)
	@"$(GDB)" -q "$(APP_ELF)" \
	  -ex "set confirm off" \
	  -ex "set pagination off" \
	  -ex "target extended-remote $(GDB_HOST):$(GDB_PORT)" \
	  -x "$(GDB_ATTACH_SCRIPT)"

uart-listen:
	@stty -F "$(UART_PORT)" "$(UART_BAUD)" cs8 -cstopb -parenb -ixon -ixoff raw
	@cat "$(UART_PORT)"

flash-image: flash-regions

flash-regions: software
	@if [[ ! -f "$(CONFIG_HEX)" ]]; then
	  echo "Missing Efinity configuration HEX: $(CONFIG_HEX)" >&2
	  echo "Run: make -C $(FPGA_DIR) bitstream" >&2
	  exit 1
	fi
	mkdir -p "$(FLASH_DIR)" "$(FLASH_REGION_DIR)"
	python3 "$(TOOL_SCRIPT_DIR)/make_flash_hex.py" \
	  --config-hex "$(CONFIG_HEX)" \
	  --app-bin "$(APP_FLASH_IMAGE)" \
	  --app-offset "$(APP_FLASH_OFFSET)" \
	  --flash-size "$(FLASH_SIZE)" \
	  --nav-context-offset "$(GPS_NAV_CONTEXT_FLASH_BASE)" \
	  --ephemeris-offset "$(GPS_EPHEMERIS_FLASH_BASE)" \
	  --combined-hex "$(FLASH_HEX)" \
	  --app-hex "$(APP_HEX)" \
	  --manifest "$(FLASH_MANIFEST)"

program-flash-loader: efinity-program-check
	@if [[ ! -f "$(FLASH_LOADER)" ]]; then
	  echo "Missing Ti375 JTAG-to-SPI loader: $(FLASH_LOADER)" >&2
	  exit 1
	fi
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	ftdi_args=()
	if [[ -n "$(FTDI_URL)" ]]; then
	  ftdi_args+=(--url "$(FTDI_URL)")
	fi
	"$(EFX_PGM)" --mode jtag "$${ftdi_args[@]}" "$(FLASH_LOADER)"

program-flash program-flash-elf program-app: flash-regions
	@$(MAKE) -C "$(FPGA_DIR)" program-flash-loader
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	ftdi_args=()
	if [[ -n "$(FTDI_URL)" ]]; then
	  ftdi_args+=(--url "$(FTDI_URL)")
	fi
	"$(EFX_PGM)" --mode jtag_bridge --jtag_bridge_mode all \
	  --verify_method "$(FLASH_VERIFY)" --address "$(APP_FLASH_OFFSET)" \
	  "$${ftdi_args[@]}" "$(APP_HEX)"

program-flash-bitstream program-bitstream:
	@if [[ ! -f "$(CONFIG_HEX)" ]]; then
	  echo "Missing Efinity configuration HEX: $(CONFIG_HEX)" >&2
	  echo "Run: make -C $(FPGA_DIR) bitstream" >&2
	  exit 1
	fi
	$(MAKE) -C "$(FPGA_DIR)" program-flash-loader
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	ftdi_args=()
	if [[ -n "$(FTDI_URL)" ]]; then
	  ftdi_args+=(--url "$(FTDI_URL)")
	fi
	"$(EFX_PGM)" --mode jtag_bridge --jtag_bridge_mode all \
	  --verify_method "$(FLASH_VERIFY)" --address 0 \
	  "$${ftdi_args[@]}" "$(CONFIG_HEX)"

program-flash-regions program-flash-all program-flash-combined: flash-image
	@$(MAKE) -C "$(FPGA_DIR)" program-flash-loader
	set +u
	if ! source "$(EFINITY_SETUP)" >/dev/null; then
	  echo "Failed to initialize Efinity from $(EFINITY_SETUP)" >&2
	  exit 1
	fi
	set -u
	ftdi_args=()
	if [[ -n "$(FTDI_URL)" ]]; then
	  ftdi_args+=(--url "$(FTDI_URL)")
	fi
	"$(EFX_PGM)" --mode jtag_bridge --jtag_bridge_mode all \
	  --verify_method "$(FLASH_VERIFY)" --address 0 \
	  "$${ftdi_args[@]}" "$(FLASH_HEX)"

clean-sw:
	@$(MAKE) -C "$(SOC_SW_DIR)" clean

clean-fpga:
	rm -rf "$(OUT_DIR)" "$(FIRRTL_DIR)" "$(VERILOG_DIR)"
	rm -f "$(INTERFACE_SCRIPT)" \
	  "$(FLOW_DIR)/ti375c529_rocket.peri.xml" \
	  "$(FLOW_DIR)/ti375c529_rocket.pt.sdc" \
	  "$(FLOW_DIR)/ti375c529_rocket.pt.rpt"

clean: clean-fpga

FORCE:
