Files
risc-v-cpu/pipelined-cpu/create_project_saolei.tcl
Alpha 132c9202f6 feat(saolei): 命令行交互式玩扫雷(无需 VGA 显示器)
- srcs/tb/tb_saolei_play.v:受 cmd.txt 驱动的交互式 TB,每 100us 轮询一个按键字符,
  每 1ms 把显存 dump 到 now.txt
- tools/play_saolei.tcl:xsim 里 k c / k u / k f 一步一按键,自动把 now.txt 画到终端
- saolei/tools/fb2png.py:新增 --term,用 ANSI 24 位色在终端画 40x30 棋盘,光标格反色
- .gitignore:放行 create_project_saolei.tcl
实测:k c 进关卡、k uuuu 光标精确移动 4 格、k c 首击展开出数字 1/2
2026-09-19 19:54:56 +08:00

171 lines
7.8 KiB
Tcl
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ============================================================================
# create_project_saolei.tcl —— 一键重建【扫雷专用】Vivado 工程
# FPGA:Nexys4DDR / xc7a100tcsg324-1(Vivado 2020.2)
#
# 与 create_project.tcl 的区别(那个是"通用/可切换程序"的脚本):
# * 只装配扫雷需要的东西:rtl/*.v + board/cpu21_board_top.v + 扫雷 ROM 头文件
# * sources_1 与 sim_1 都固定定义宏 **SAOLEI_ROM**(不用再改任何开关)
# * 工程名是独立的 pipelined-cpu-saolei,跟通用工程 pipelined-cpu 互不影响,
# 两个工程可以并存;本脚本只清理自己的生成物。
# * sim_1 顶层直接就是 tb_saolei(GUI 里 Run Simulation 就能出画面 dump)
#
# 用法(在 Vivado Tcl Shell 里):
# source create_project_saolei.tcl
# 或命令行:
# vivado -mode batch -source create_project_saolei.tcl -log create_saolei.log
#
# 前置条件:扫雷的 ROM 镜像必须先由 C 程序生成
# cd saolei
# powershell -ExecutionPolicy Bypass -File tools\build.ps1 `
# -Prefix 'C:\Users\29653\tools\riscv\xpack-riscv-none-elf-gcc-15.2.0-1\bin\riscv-none-elf-'
# → 产出 saolei/build/saolei.hex 与 pipelined-cpu/srcs/board/cpu21_saolei_rom.vh
# (改了 saolei/sw/*.c 之后要重跑这条命令,再综合/再跑仿真)
#
# 不想自动清理旧工程:把下面的 clean_first 改成 0。
# 想建完就顺手综合:把 run_synth 改成 1(2020.2 上约 1~2 分钟)。
#
# 附:没有 VGA 显示器时怎么"看/玩"扫雷
# 看:本工程的 sim_1 跑 tb_saolei,会把显存 dump 成文本;再用
# py saolei\tools\fb2png.py <dump.txt> <out.png> 还原成 640x480 画面。
# 玩:见 pipelined-cpu/tools/play_saolei.tcl(命令行交互式仿真,一步一截图)。
# ============================================================================
set origin_dir [file dirname [file normalize [info script]]]
set proj_name "pipelined-cpu-saolei"
set part_name "xc7a100tcsg324-1"
# ---------------------------------------------------------------- 自检
set saolei_rom "$origin_dir/srcs/board/cpu21_saolei_rom.vh"
set required [list \
"$origin_dir/srcs/board/cpu21_board_top.v" \
"$origin_dir/srcs/constrs/cpu21_board_top.xdc" \
"$origin_dir/srcs/tb/tb_saolei.v"]
if {[llength [glob -nocomplain "$origin_dir/srcs/rtl/*.v"]] == 0} {
lappend required "$origin_dir/srcs/rtl/*.v"
}
set missing {}
foreach f $required { if {![file exists $f]} { lappend missing $f } }
if {[llength $missing] > 0} {
puts "ERROR: 缺少下列文件,请确认 srcs/ 目录完整:"
foreach f $missing { puts " $f" }
return 1
}
if {![file exists $saolei_rom]} {
puts "ERROR: 还没有扫雷 ROM 镜像:$saolei_rom"
puts " 先在 saolei/ 目录跑 tools\\build.ps1 生成它(见本脚本头部注释)。"
return 1
}
# ---------------------------------------------------------------- 清理旧工程
# 和 create_project.tcl 同一套策略:只删生成物,srcs/ 等源文件一律不动。
set clean_first 1
set run_synth 0
set synth_jobs 4
if {$clean_first} {
set proj_items [list \
"$origin_dir/$proj_name.xpr" \
"$origin_dir/$proj_name.srcs" \
"$origin_dir/$proj_name.runs" \
"$origin_dir/$proj_name.sim" \
"$origin_dir/$proj_name.cache" \
"$origin_dir/$proj_name.hw" \
"$origin_dir/$proj_name.gen" \
"$origin_dir/$proj_name.ip_user_files" \
"$origin_dir/$proj_name.lpr" \
"$origin_dir/.Xil"]
set junk_items [list \
"$origin_dir/xsim.dir" \
"$origin_dir/webtalk.jou" \
"$origin_dir/webtalk.log" \
"$origin_dir/usage_statistics_webtalk.html" \
"$origin_dir/usage_statistics_webtalk.xml"]
foreach pat {vivado*.jou vivado*.log vivado*.str} {
foreach f [glob -nocomplain "$origin_dir/$pat"] { lappend junk_items $f }
}
set blocked {}
set removed 0
foreach {group items} [list proj $proj_items junk $junk_items] {
foreach d $items {
if {![file exists $d]} { continue }
if {[catch {file delete -force $d} err]} {
puts " WARNING: 删不掉 [file tail $d]($err)"
if {$group eq "proj"} { lappend blocked [file tail $d] }
} else {
puts " 已删除:[file tail $d]"
incr removed
}
}
}
puts "旧工程清理完成(删除 $removed 项,只动 $proj_name.* 与自己产生的临时文件)"
if {[llength $blocked] > 0} {
puts "ERROR: 这些工程目录删不掉:$blocked"
puts " 它们正被另一个 Vivado(GUI / Hardware Manager)占用,关掉后重跑。"
return 1
}
} else {
if {[file exists "$origin_dir/$proj_name.xpr"] || [file exists "$origin_dir/$proj_name.runs"]} {
puts "ERROR: 工程已存在且 clean_first = 0:$origin_dir/$proj_name.xpr"
puts " 请先删掉它,或把 clean_first 改成 1。"
return 1
}
}
# ---------------------------------------------------------------- 建工程
# 只用 -part,不依赖 Digilent 板卡文件(引脚全在 srcs/constrs/*.xdc 里)。
create_project $proj_name $origin_dir -part $part_name -force
# 设计源文件:处理器 RTL + 板级顶层 + 扫雷 ROM
add_files -norecurse -fileset sources_1 [glob "$origin_dir/srcs/rtl/*.v"]
add_files -norecurse -fileset sources_1 [list \
"$origin_dir/srcs/board/cpu21_board_top.v" \
$saolei_rom]
set_property file_type {Verilog Header} [get_files $saolei_rom]
# 关键:两个 fileset 都定义 SAOLEI_ROM,cpu21_board_top.v 的 `ifdef 链才会选中扫雷 ROM。
# (sources_1 管综合/上板,sim_1 管 GUI 仿真;sim_1 不定义的话会跑成走马灯。)
set_property verilog_define {SAOLEI_ROM} [get_filesets sources_1]
set_property verilog_define {SAOLEI_ROM} [get_filesets sim_1]
# 约束(引脚 + 时钟分频)
add_files -norecurse -fileset constrs_1 [glob "$origin_dir/srcs/constrs/*.xdc"]
# 仿真:扫雷调试台。POINTER_SAMPLE_DIV 由 tb 自己 defparam 压小,上板仍是 50000。
add_files -norecurse -fileset sim_1 [list "$origin_dir/srcs/tb/tb_saolei.v"]
# ---------------------------------------------------------------- 顶层与顺序
set_property top cpu21_board_top [get_filesets sources_1]
set_property top tb_saolei [get_filesets sim_1]
update_compile_order -fileset sources_1
update_compile_order -fileset sim_1
# GUI 仿真默认只跑 1000 ns,这里改成跑完(否则看起来像卡住)
catch { set_property -name {xsim.simulate.runtime} -value {all} -objects [get_filesets sim_1] }
# ---------------------------------------------------------------- 可选:顺手综合
if {$run_synth} {
puts "开始综合 synth_1(run_synth = 1)..."
reset_run synth_1
launch_runs synth_1 -jobs $synth_jobs
wait_on_run synth_1
if {[get_property PROGRESS [get_runs synth_1]] ne "100%"} {
puts "ERROR: 综合未完成,状态 = [get_property STATUS [get_runs synth_1]]"
return 1
}
puts "综合完成:0 error 即可 Run Implementation → Generate Bitstream。"
}
# ---------------------------------------------------------------- 结果汇总
puts ""
puts "工程已创建:$origin_dir/$proj_name.xpr"
puts " sources_1 顶层 = cpu21_board_top,程序 = 扫雷(已定义 SAOLEI_ROM)"
puts " sources_1 ROM = [file tail $saolei_rom]"
puts " sim_1 顶层 = tb_saolei(跑仿真会打印显存 dump,用 fb2png.py 还原成 PNG)"
puts " 约束 = [file tail [lindex [glob "$origin_dir/srcs/constrs/*.xdc"] 0]]"
puts " 下一步:Run Synthesis → Run Implementation → Generate Bitstream"
puts " (本工程已固定扫雷,不用再改 create_project.tcl 里的三个开关)"
puts " 上板操作:BTNU/D/L/R 移动光标,BTNC = 左键,SW3 = 插旗模式"
puts " 注意:CPU 时钟 = 100 MHz / CLK_DIV(默认 2) = 50 MHz;改分频比时"
puts " cpu21_board_top.v 的 CLK_DIV 与 cpu21_board_top.xdc 的 -divide_by 要一起改。"