同步了 fpga_flash, vivado-prj-create 两个skill
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
---
|
||||
name: fpga_flash
|
||||
description: |
|
||||
烧写 Artix-7 系列 FPGA 的外部 Flash(SPI NOR Flash)。适用于 EMV7.1 等 Artix-7 板卡,支持 Micron mt25ql128 和 Winbond w25q128bv 两种 Flash 型号。
|
||||
当用户提到"烧写 flash"、"flash 编程"、"固化 bit 文件"、"下载到 flash"、"program flash"、"write flash"、"burn flash"、"Vivado flash"、"SPI flash 烧写"时,必须触发此 skill。
|
||||
---
|
||||
|
||||
# FPGA Flash 烧写流程(Artix-7 / EMV7.1)
|
||||
|
||||
本 skill 由 Claude **全自动执行**完整的 Flash 烧写流程。除了向用户确认必要信息外,所有操作(文件检查、CSV 修改、Vivado 命令执行、硬件编程)均由 Claude 直接完成,不要求用户手动执行任何命令。
|
||||
|
||||
---
|
||||
|
||||
## 执行规范
|
||||
|
||||
**自动执行原则:**
|
||||
- 每个需要调用 Vivado 的步骤,Claude 通过 Bash 工具调用 skill `scripts/` 目录下的 Python 脚本
|
||||
- **禁止**在用户工程目录或系统临时目录(`%TEMP%`、`/tmp`)下创建任何脚本文件
|
||||
- 所有可执行脚本已预置在 skill 目录的 `scripts/` 子目录中,Claude 只需传入正确的参数调用
|
||||
|
||||
**定位 skill 脚本目录:**
|
||||
|
||||
Claude 在执行前需先确认脚本的完整路径。使用 Glob 工具查找:
|
||||
```
|
||||
C:/Users/<当前用户>/.claude/skills/fpga_flash/scripts/gen_mcs.py
|
||||
```
|
||||
或直接使用默认路径:`C:/Users/bowei/.claude/skills/fpga_flash/scripts/`
|
||||
|
||||
**scripts/ 目录内容:**
|
||||
```
|
||||
scripts/
|
||||
check_flash_csv.py # 第一步:检查/更新 Vivado Flash 器件表
|
||||
gen_mcs.py # 第三步:生成 .mcs 文件
|
||||
program_flash.py # 第四、五步:连接目标板并烧写 Flash
|
||||
step3_write_mcs.tcl # gen_mcs.py 使用的 Tcl 模板(勿手动修改)
|
||||
step4_5_program.tcl # program_flash.py 使用的 Tcl 模板(勿手动修改)
|
||||
```
|
||||
|
||||
**调用方式(Bash 工具):**
|
||||
```bash
|
||||
python "C:/Users/bowei/.claude/skills/fpga_flash/scripts/<script>.py" [参数...]
|
||||
```
|
||||
|
||||
各脚本通过命令行参数接受变量(Vivado 安装目录、工程名、bit 文件路径、mcs 文件路径等),内部调用 Vivado `-mode batch -source <tcl模板> -tclargs <参数>` 完成执行,全程不产生临时文件。
|
||||
|
||||
---
|
||||
|
||||
## 第一步:检查并修复 Vivado Flash 器件表
|
||||
|
||||
**Claude 自动执行:**
|
||||
|
||||
调用 `check_flash_csv.py`,检查 `xicom_cfgmem_part_table.csv` 中是否已包含 `w25q128bv-spi-x1_x2_x4`:
|
||||
|
||||
```bash
|
||||
python "C:/Users/bowei/.claude/skills/fpga_flash/scripts/check_flash_csv.py" \
|
||||
--vivado-dir "D:/Xilinx/Vivado/2018.1"
|
||||
```
|
||||
|
||||
**返回值含义:**
|
||||
- 退出码 `0`:器件已存在,直接进入第二步
|
||||
- 退出码 `2`:已成功添加器件行,需提示用户**重启 Vivado** 后才能识别
|
||||
- 退出码 `1`:CSV 未找到,询问用户提供正确的 Vivado 安装目录后重试
|
||||
|
||||
若 Vivado 不在默认路径 `D:/Xilinx/Vivado/2018.1`,询问用户后替换 `--vivado-dir` 的值。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:选择 Flash 型号
|
||||
|
||||
询问用户板卡上焊接的 Flash 芯片型号,仅支持以下两种:
|
||||
|
||||
| 选项 | 厂商 | `--flash-part` 值 |
|
||||
|------|------|-----------------|
|
||||
| 1 | Micron | `mt25ql128-spi-x1_x2_x4` |
|
||||
| 2 | Winbond | `w25q128bv-spi-x1_x2_x4` |
|
||||
|
||||
记录用户选择,后续步骤中作为 `--flash-part` 参数传入。如果用户不确定,提示查看板卡丝印或原理图中 Flash 芯片的位号。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:确认文件路径并自动生成 .mcs
|
||||
|
||||
**需向用户确认:**
|
||||
1. **.bit 文件**的完整路径(通常在工程 `runs/impl_1/` 目录下)
|
||||
2. **输出 .mcs 文件**的保存路径和文件名(建议与 .bit 文件同目录)
|
||||
|
||||
用户确认后,**Claude 自动调用**:
|
||||
|
||||
```bash
|
||||
python "C:/Users/bowei/.claude/skills/fpga_flash/scripts/gen_mcs.py" \
|
||||
--vivado-dir "D:/Xilinx/Vivado/2018.1" \
|
||||
--bit-file "E:/Xilinx_design/smod/SMOD_DTMB_CG/RU_golden/RU_golden.runs/impl_1/RU_golden_top.bit" \
|
||||
--mcs-file "E:/Xilinx_design/smod/SMOD_DTMB_CG/RU_golden/RU_golden.runs/impl_1/RU_golden_smod.mcs"
|
||||
```
|
||||
|
||||
将示例路径替换为用户实际的 Vivado 安装路径、.bit 文件路径、.mcs 输出路径。
|
||||
|
||||
**验证成功的标志:** 输出中包含 `write_cfgmem completed successfully` 且显示 `0 Errors`。
|
||||
|
||||
---
|
||||
|
||||
## 第四步 + 第五步:连接目标板、配置存储器并烧写
|
||||
|
||||
确认板卡已上电并连接 JTAG 后,**Claude 自动调用**:
|
||||
|
||||
```bash
|
||||
python "C:/Users/bowei/.claude/skills/fpga_flash/scripts/program_flash.py" \
|
||||
--vivado-dir "D:/Xilinx/Vivado/2018.1" \
|
||||
--flash-part "w25q128bv-spi-x1_x2_x4" \
|
||||
--bit-file "E:/Xilinx_design/smod/SMOD_DTMB_CG/RU_golden/RU_golden.runs/impl_1/RU_golden_top.bit" \
|
||||
--mcs-file "E:/Xilinx_design/smod/SMOD_DTMB_CG/RU_golden/RU_golden.runs/impl_1/RU_golden_smod.mcs" \
|
||||
--ltx-file "E:/Xilinx_design/smod/SMOD_DTMB_CG/RU_golden/RU_golden.runs/impl_1/RU_golden_top.ltx"
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `--flash-part`:第二步用户选择的型号
|
||||
- `--bit-file`:第三步确认的 .bit 路径
|
||||
- `--mcs-file`:第三步生成的 .mcs 路径
|
||||
- `--ltx-file`:若设计无 ILA,省略此参数(脚本自动跳过 PROBES.FILE 配置)
|
||||
|
||||
此脚本在同一个 Vivado batch 会话中完成:硬件连接 → cfgmem 器件创建 → 擦除/编程/验证。
|
||||
|
||||
**验证成功的标志:** 输出包含 `End of startup status: HIGH` 且无 `ERROR` 行。
|
||||
|
||||
ILA core 的 `Dropping logic core` WARNING 是正常现象(golden 镜像不含 ILA core),不影响烧写结果。
|
||||
|
||||
---
|
||||
|
||||
## 第六步:上电验证
|
||||
|
||||
烧写完成后,提示用户:
|
||||
1. **断开 JTAG**,给板卡重新上电(或按复位键)
|
||||
2. 观察 FPGA 是否从 Flash 正常加载(DONE 灯亮起)
|
||||
3. 如果 DONE 灯不亮,检查:
|
||||
- BOOT_MODE 跳线是否选择 SPI Flash 启动模式
|
||||
- MCS 生成时起始地址是否为 `0x0`
|
||||
- 板卡电源是否满足 Flash 的 VCC 要求(3.3V / 1.8V)
|
||||
|
||||
---
|
||||
|
||||
## 常见错误排查
|
||||
|
||||
| 现象 | 可能原因 | 解决方法 |
|
||||
|------|----------|----------|
|
||||
| `check_flash_csv.py` 退出码 1 | Vivado 不在默认路径 | 询问用户,更换 `--vivado-dir` 参数 |
|
||||
| Vivado 中找不到 w25q128bv | CSV 已修改但 Vivado 未重启 | 提示用户重启 Vivado 后重新执行 |
|
||||
| `No hardware target` 错误 | JTAG 未连接或板卡未上电 | 检查连接后重新执行第四步 |
|
||||
| DONE 灯不亮 | 启动模式跳线错误 | 检查 MODE 引脚电平,确认为 SPI 启动模式 |
|
||||
| 烧写后板卡跑旧程序 | Flash 未完全擦除 | 确认 `PROGRAM.ERASE 1` 已设置后重新执行 |
|
||||
| `PermissionError` 写 CSV | 权限不足 | 提示用户以管理员身份运行 Claude Code |
|
||||
@@ -0,0 +1,73 @@
|
||||
# OpenClaw 平台 FPGA Flash 烧写 Skill 改造方案
|
||||
|
||||
## 1. 背景与改造难点
|
||||
|
||||
目前 `fpga_flash` 这个 skill 是为原版 Claude Code(运行在 Windows 主机上)设计的,脚本中直接调用了 `python` 并使用 Windows 风格的路径(如 `D:/Xilinx/...`、`C:/Users/...`)。
|
||||
|
||||
由于 **OpenClaw 是安装在 WSL (Linux) 环境下**的,直接将现有 `SKILL.md` 迁移过去会面临以下跨系统环境问题:
|
||||
1. **Python 解释器环境错位**:在 WSL 的 Bash 中执行 `python` 会调用 Linux 版的 Python,而不是 Windows 环境下的 Python,这可能导致调用 Windows 版 Vivado 时遇到各种环境变量和子进程调用的兼容性问题。
|
||||
2. **文件路径格式不兼容**:WSL 中的文件路径是 `/mnt/c/...` 或 `/mnt/e/...` 的形式,而原 Python 脚本(如 `check_flash_csv.py`、`program_flash.py`)预期的是 Windows 路径(`C:/...`、`E:/...`)。直接传入 Linux 路径会导致脚本中的 `os.path.isfile()` 等检查失败。
|
||||
3. **硬件连接限制**:FPGA 的 JTAG 烧写器通常连接在 Windows 主机的 USB 端口。虽然 WSL 支持 USB 透传,但最稳定可靠的方式是直接在 Windows 端发起 `vivado -mode batch` 调用,以直接使用 Windows 的 Hardware Server。
|
||||
|
||||
## 2. 核心改造方案
|
||||
|
||||
利用 WSL 优秀的**互操作性(Interop)**,我们不需要对 `scripts/` 目录下的 Python 脚本和 Tcl 脚本做任何修改,只需修改 `SKILL.md` 中的 Prompt,指导 OpenClaw agent 跨环境调用。
|
||||
|
||||
**核心机制:**
|
||||
- **通过 `python.exe` 越界调用**:在 WSL 中可以直接执行 Windows 命令。我们将原 `SKILL.md` 中的 `python` 替换为 `python.exe`。这样执行时会自动启动 Windows Python 解释器,完全在 Windows 上下文中运行脚本。
|
||||
- **强制要求路径转换**:在 `SKILL.md` 中增加明确的提示词(Prompt),要求 OpenClaw 在提取文件路径(可能由于它在 WSL 中浏览文件,拿到的是 `/mnt/e/...`)后,**必须先将其转换为 Windows 盘符格式**再作为参数传递。
|
||||
|
||||
## 3. 具体实施步骤
|
||||
|
||||
### 步骤 1:修改 `SKILL.md` 提示词
|
||||
|
||||
需要在 `SKILL.md` 中的“执行规范”或相关步骤的命令示例中进行如下调整(在后续实施时可直接修改):
|
||||
|
||||
1. **更改执行命令**:将所有示例中的 `python` 更改为 `python.exe`。
|
||||
*原版:* `python "C:/Users/bowei/.claude/skills/fpga_flash/scripts/gen_mcs.py" ...`
|
||||
*修改后:* `python.exe "C:/Users/bowei/.claude/skills/fpga_flash/scripts/gen_mcs.py" ...`
|
||||
|
||||
2. **增加跨环境路径转换指令**:在 `SKILL.md` 头部或执行规范中增加一条针对 WSL 环境的强调说明:
|
||||
> **【WSL 跨环境执行重点约束】**
|
||||
> 由于你当前运行在 WSL 环境中,而脚本、Vivado 及硬件都在 Windows 主机上,因此在执行调用前:
|
||||
> 1. 必须使用 `python.exe`(而非 `python`)来调用脚本。
|
||||
> 2. 所有传递给 `--vivado-dir`、`--bit-file`、`--mcs-file` 和 `--ltx-file` 的参数,**必须转换为 Windows 路径格式**(例如:将 `/mnt/e/Xilinx_design/...` 替换为 `E:/Xilinx_design/...`)。
|
||||
|
||||
### 步骤 2:在 OpenClaw 中安装 Skill
|
||||
|
||||
OpenClaw 对 `SKILL.md` 的支持与 Claude Code 兼容。可以通过以下方式将其挂载到 OpenClaw 中:
|
||||
|
||||
在 WSL 中执行 OpenClaw 的 skill 安装命令,直接指向包含 `SKILL.md` 的目录。例如:
|
||||
```bash
|
||||
openclaw skills install /mnt/c/Users/bowei/.claude/skills/fpga_flash
|
||||
```
|
||||
*(注:根据 OpenClaw `skills --help` 的提示,`install` 指令支持本地路径安装。)*
|
||||
|
||||
### 步骤 3:验证与测试
|
||||
|
||||
在 OpenClaw 中对话输入:“请帮我把 E:/Xilinx_design/... 下的 bit 固化到 flash 中”。
|
||||
观察 OpenClaw 的行动轨迹,确认它是否:
|
||||
1. 成功识别出此时处于 WSL 环境,并正确生成了 `python.exe` 指令。
|
||||
2. 成功将 `/mnt/e/...` 等路径翻译成了 `E:/...` 格式。
|
||||
3. 成功连接到 Windows 侧的 Vivado Hardware Manager 并走通流程。
|
||||
|
||||
## 4. 可选的鲁棒性优化(Wrapper Script)
|
||||
|
||||
如果发现 OpenClaw 在路径转换上经常犯错(比如忘记转换),可以在 `scripts/` 目录下新增一个 WSL 专用的启动脚本 `run_in_win.sh`,其内容如下:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 自动将传入的 WSL 路径通过 wslpath 转换为 Windows 路径,并调用 Windows python
|
||||
SCRIPT_PATH=$(wslpath -w "$1")
|
||||
shift
|
||||
ARGS=()
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == /mnt/* ]]; then
|
||||
ARGS+=("$(wslpath -w "$arg")")
|
||||
else
|
||||
ARGS+=("$arg")
|
||||
fi
|
||||
done
|
||||
python.exe "$SCRIPT_PATH" "${ARGS[@]}"
|
||||
```
|
||||
此时 `SKILL.md` 中直接指导 OpenClaw 调用 `./run_in_win.sh scripts/gen_mcs.py --bit-file /mnt/e/...` 即可,把环境切换的负担交给 Bash 脚本处理。这是一种更为稳妥的防呆设计。
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Step 1: Check and update Vivado Flash device CSV to include w25q128bv.
|
||||
|
||||
Checks whether 'w25q128bv-spi-x1_x2_x4' is present in Vivado's
|
||||
xicom_cfgmem_part_table.csv. If not, appends the correct row.
|
||||
|
||||
Usage:
|
||||
python check_flash_csv.py --vivado-dir "D:/Xilinx/Vivado/2018.1"
|
||||
|
||||
Exit codes:
|
||||
0 = device already present (no changes made)
|
||||
2 = device was successfully added (Vivado restart required)
|
||||
1 = error (CSV not found or write failure)
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
DEVICE_NAME = "w25q128bv-spi-x1_x2_x4"
|
||||
# Fields: NAME, MEM_DEVICE, MEM_TYPE, MEM_DENSITY, DATA_WIDTH,
|
||||
# MEM_MANUFACTURER, NUM_CFG_FILES, MEM_DEVICE_ALIAS, MEM_FAMILY
|
||||
NEW_ROW = "w25q128bv-spi-x1_x2_x4,w25q128bv,spi,128,x1_x2_x4,Winbond,1,w25q128b,w25q"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Check/update Vivado Flash CSV for w25q128bv"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--vivado-dir",
|
||||
required=True,
|
||||
help="Vivado install root, e.g. D:/Xilinx/Vivado/2018.1",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
csv_path = os.path.join(
|
||||
args.vivado_dir, "data", "xicom", "xicom_cfgmem_part_table.csv"
|
||||
)
|
||||
csv_path = csv_path.replace("\\", "/")
|
||||
|
||||
if not os.path.isfile(csv_path):
|
||||
print(f"ERROR: CSV not found at: {csv_path}", file=sys.stderr)
|
||||
print(
|
||||
" Please verify --vivado-dir points to a valid Vivado installation.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
if DEVICE_NAME in content:
|
||||
print(f"[check_flash_csv] '{DEVICE_NAME}' already present. No changes needed.")
|
||||
sys.exit(0)
|
||||
|
||||
# Append the new row (ensure trailing newline before adding)
|
||||
if not content.endswith("\n"):
|
||||
content += "\n"
|
||||
content += NEW_ROW + "\n"
|
||||
|
||||
try:
|
||||
with open(csv_path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
except PermissionError:
|
||||
print(
|
||||
f"ERROR: Cannot write to {csv_path}. Try running as Administrator.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[check_flash_csv] Added '{DEVICE_NAME}' to:")
|
||||
print(f" {csv_path}")
|
||||
print()
|
||||
print("NOTE: You must restart Vivado for the new Flash device to be recognized.")
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Step 3: Generate .mcs configuration memory file using Vivado batch mode.
|
||||
|
||||
Calls Vivado with step3_write_mcs.tcl (located in the same scripts/ directory)
|
||||
via -tclargs so no temporary script files are created.
|
||||
|
||||
Usage:
|
||||
python gen_mcs.py \
|
||||
--vivado-dir "D:/Xilinx/Vivado/2018.1" \
|
||||
--bit-file "E:/project/RU_golden/RU_golden.runs/impl_1/RU_golden_top.bit" \
|
||||
--mcs-file "E:/project/RU_golden/RU_golden.runs/impl_1/RU_golden_smod.mcs"
|
||||
|
||||
Exit codes:
|
||||
0 = success ("write_cfgmem completed successfully" 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="Generate .mcs from .bit via Vivado write_cfgmem"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--vivado-dir",
|
||||
required=True,
|
||||
help="Vivado install root, e.g. D:/Xilinx/Vivado/2018.1",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bit-file",
|
||||
required=True,
|
||||
help="Full path to the .bit file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mcs-file",
|
||||
required=True,
|
||||
help="Full path for the output .mcs file",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
vivado_dir = norm(args.vivado_dir)
|
||||
bit_file = norm(args.bit_file)
|
||||
mcs_file = norm(args.mcs_file)
|
||||
|
||||
# Locate the TCL template alongside this script
|
||||
scripts_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
tcl_template = os.path.join(scripts_dir, "step3_write_mcs.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)
|
||||
|
||||
if not os.path.isfile(bit_file):
|
||||
print(f"ERROR: .bit file not found: {bit_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", bit_file, mcs_file,
|
||||
]
|
||||
print(f"[gen_mcs] Vivado: {vivado_exe}")
|
||||
print(f"[gen_mcs] bit : {bit_file}")
|
||||
print(f"[gen_mcs] mcs : {mcs_file}")
|
||||
print()
|
||||
|
||||
result = subprocess.run(cmd)
|
||||
sys.exit(result.returncode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,118 @@
|
||||
#!/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()
|
||||
@@ -0,0 +1,16 @@
|
||||
# Template: Step 3 - Generate .mcs configuration memory file
|
||||
# Called by gen_mcs.py via: vivado -mode batch -source step3_write_mcs.tcl -tclargs <bit_file> <mcs_file>
|
||||
#
|
||||
# Arguments (passed via -tclargs):
|
||||
# argv[0] = BIT_FILE_PATH - Full path to the .bit file (forward slashes)
|
||||
# argv[1] = MCS_FILE_PATH - Full path for the output .mcs file (forward slashes)
|
||||
#
|
||||
# Success indicator: output contains "write_cfgmem completed successfully" and "0 Errors"
|
||||
|
||||
set bit_file [lindex $argv 0]
|
||||
set mcs_file [lindex $argv 1]
|
||||
|
||||
write_cfgmem -format mcs -size 16 -interface SPIx1 \
|
||||
-loadbit [list up 0x00000000 $bit_file] \
|
||||
-checksum -force \
|
||||
-file $mcs_file
|
||||
@@ -0,0 +1,47 @@
|
||||
# Template: Steps 4+5 - Connect hardware, configure cfgmem, and program Flash
|
||||
# Called by program_flash.py via: vivado -mode batch -source step4_5_program.tcl -tclargs <flash_part> <bit_file> <mcs_file> <ltx_file>
|
||||
#
|
||||
# Arguments (passed via -tclargs):
|
||||
# argv[0] = FLASH_PART - Flash part name, e.g. w25q128bv-spi-x1_x2_x4
|
||||
# argv[1] = BIT_FILE_PATH - Full path to the .bit file
|
||||
# argv[2] = MCS_FILE_PATH - Full path to the .mcs file
|
||||
# argv[3] = LTX_FILE_PATH - Full path to .ltx file, or empty string "" if no ILA
|
||||
#
|
||||
# Success indicator: output contains "End of startup status: HIGH" and no ERROR lines
|
||||
|
||||
set flash_part [lindex $argv 0]
|
||||
set bit_file [lindex $argv 1]
|
||||
set mcs_file [lindex $argv 2]
|
||||
set ltx_file [lindex $argv 3]
|
||||
set has_ltx [expr {$ltx_file ne ""}]
|
||||
|
||||
open_hw
|
||||
connect_hw_server
|
||||
open_hw_target
|
||||
|
||||
set_property PROGRAM.FILE $bit_file [get_hw_devices xc7a200t_0]
|
||||
if {$has_ltx} {
|
||||
set_property PROBES.FILE $ltx_file [get_hw_devices xc7a200t_0]
|
||||
set_property FULL_PROBES.FILE $ltx_file [get_hw_devices xc7a200t_0]
|
||||
}
|
||||
current_hw_device [get_hw_devices xc7a200t_0]
|
||||
refresh_hw_device [lindex [get_hw_devices xc7a200t_0] 0]
|
||||
|
||||
create_hw_cfgmem -hw_device [lindex [get_hw_devices xc7a200t_0] 0] \
|
||||
[lindex [get_cfgmem_parts $flash_part] 0]
|
||||
set_property PROGRAM.BLANK_CHECK 0 [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
set_property PROGRAM.ERASE 1 [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
set_property PROGRAM.CFG_PROGRAM 1 [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
set_property PROGRAM.VERIFY 1 [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
set_property PROGRAM.CHECKSUM 0 [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
set_property PROGRAM.FILES [list $mcs_file] \
|
||||
[get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices xc7a200t_0] 0]]
|
||||
refresh_hw_device [lindex [get_hw_devices xc7a200t_0] 0]
|
||||
|
||||
if {$has_ltx} {
|
||||
set_property PROBES.FILE $ltx_file [get_hw_devices xc7a200t_0]
|
||||
set_property FULL_PROBES.FILE $ltx_file [get_hw_devices xc7a200t_0]
|
||||
}
|
||||
set_property PROGRAM.FILE $bit_file [get_hw_devices xc7a200t_0]
|
||||
program_hw_devices [get_hw_devices xc7a200t_0]
|
||||
refresh_hw_device [lindex [get_hw_devices xc7a200t_0] 0]
|
||||
Reference in New Issue
Block a user