# ============================================================================ # 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 还原成 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 要一起改。"