#!/usr/bin/env python3 """ Steps 4+5: Connect hardware target, configure cfgmem device, and program Flash. Calls Vivado with step4_5_program.tcl (located in the same scripts/ directory) via -tclargs so no temporary script files are created. Usage: python program_flash.py \ --vivado-dir "D:/Xilinx/Vivado/2018.1" \ --flash-part "w25q128bv-spi-x1_x2_x4" \ --bit-file "E:/project/impl_1/RU_golden_top.bit" \ --mcs-file "E:/project/impl_1/RU_golden_smod.mcs" \ [--ltx-file "E:/project/impl_1/RU_golden_top.ltx"] If the design has no ILA (no .ltx file), omit --ltx-file or pass an empty string; the TCL template will skip the PROBES.FILE settings. Supported flash parts: Micron : mt25ql128-spi-x1_x2_x4 Winbond : w25q128bv-spi-x1_x2_x4 Exit codes: 0 = success ("End of startup status: HIGH" in output) 1 = failure """ import argparse import os import subprocess import sys def norm(path: str) -> str: """Normalize path to forward slashes (Vivado on Windows accepts both).""" return path.replace("\\", "/") def main(): parser = argparse.ArgumentParser( description="Program FPGA SPI Flash via Vivado Hardware Manager" ) parser.add_argument( "--vivado-dir", required=True, help="Vivado install root, e.g. D:/Xilinx/Vivado/2018.1", ) parser.add_argument( "--flash-part", required=True, help="Vivado Flash part name, e.g. w25q128bv-spi-x1_x2_x4", ) parser.add_argument( "--bit-file", required=True, help="Full path to the .bit file", ) parser.add_argument( "--mcs-file", required=True, help="Full path to the .mcs file (from Step 3)", ) parser.add_argument( "--ltx-file", default="", help="Full path to the .ltx probes file (optional; omit if no ILA)", ) args = parser.parse_args() vivado_dir = norm(args.vivado_dir) bit_file = norm(args.bit_file) mcs_file = norm(args.mcs_file) ltx_file = norm(args.ltx_file) if args.ltx_file else "" # Locate the TCL template alongside this script scripts_dir = os.path.dirname(os.path.abspath(__file__)) tcl_template = os.path.join(scripts_dir, "step4_5_program.tcl").replace("\\", "/") # Validate inputs vivado_exe = os.path.join(vivado_dir, "bin", "vivado.bat") if not os.path.isfile(vivado_exe): print(f"ERROR: Vivado not found: {vivado_exe}", file=sys.stderr) sys.exit(1) for label, path in [("bit", bit_file), ("mcs", mcs_file)]: if not os.path.isfile(path): print(f"ERROR: .{label} file not found: {path}", file=sys.stderr) sys.exit(1) if ltx_file and not os.path.isfile(ltx_file): print(f"ERROR: .ltx file not found: {ltx_file}", file=sys.stderr) sys.exit(1) if not os.path.isfile(tcl_template): print(f"ERROR: TCL template not found: {tcl_template}", file=sys.stderr) sys.exit(1) cmd = [ vivado_exe, "-mode", "batch", "-source", tcl_template, "-tclargs", args.flash_part, bit_file, mcs_file, ltx_file, ] print(f"[program_flash] Vivado : {vivado_exe}") print(f"[program_flash] Flash part: {args.flash_part}") print(f"[program_flash] bit : {bit_file}") print(f"[program_flash] mcs : {mcs_file}") if ltx_file: print(f"[program_flash] ltx : {ltx_file}") else: print(f"[program_flash] ltx : (none - no ILA)") print() result = subprocess.run(cmd) sys.exit(result.returncode) if __name__ == "__main__": main()