- 新增 clean_first 开关(默认 1):先删掉旧工程与所有生成目录再新建, 只删生成物(*.xpr/*.srcs/*.runs/*.sim/*.cache/*.hw/*.gen/*.ip_user_files/ .Xil/xsim.dir/*.lpr/vivado*.jou|log|str/webtalk.*),srcs/、tools/、programs/ 不动 - 被别的 Vivado 占用的工程目录删不掉时,给出明确提示并中止(不再抛 Vivado 原始异常) - create_project 加 -force,残留目录也能覆盖 - 实测:空目录建工程成功;连续第二次运行自动清理 7 项后重建成功,综合通过
234 lines
12 KiB
Tcl
234 lines
12 KiB
Tcl
# ============================================================================
|
||
# cpu21-riscv 流水线 CPU —— 一键重建 Vivado 工程
|
||
# FPGA:Nexys4DDR / xc7a100tcsg324-1(Vivado 2020.2 实测:建工程 + 综合均通过)
|
||
#
|
||
# 目录结构(本脚本所在目录就是工程目录):
|
||
# create_project.tcl 本脚本
|
||
# srcs/rtl/*.v 处理器 RTL(顶层 cpu21_riscv_redirect_int_bpb)
|
||
# srcs/board/*.v|*.vh 板级顶层、片内 ROM 初始化(含 __FAST 仿真版本)
|
||
# srcs/constrs/*.xdc 引脚约束与板级母版
|
||
# srcs/tb/*.v 测试平台(中断/分支预测、无中断、板级)
|
||
# srcs/rom/*.hex 测试平台加载的 ROM 镜像
|
||
# tools/、programs/ 迷你汇编器与测试程序(改程序、重生成 ROM 用)
|
||
# 说明文档:cpu21_riscv_redirect_int_bpb说明.md
|
||
#
|
||
# 用法:
|
||
# 1) 在 Vivado Tcl Shell 里执行: source create_project.tcl
|
||
# 或命令行: vivado -mode batch -source create_project.tcl -log create.log
|
||
# 2) 脚本会**先自动清理旧工程**再新建:只删生成物(*.xpr / *.srcs / *.runs /
|
||
# *.sim / *.cache / *.hw / *.gen / *.ip_user_files / .Xil / xsim.dir /
|
||
# vivado*.jou|log|str / webtalk.*),srcs/、tools/、programs/ 一律不动。
|
||
# 不想自动清理(比如想在旧工程上追加设置)就把下面的 clean_first 改成 0。
|
||
# 3) 工程就地创建:pipelined-cpu.xpr(源文件仍在 srcs/ 下,不会被搬动)。
|
||
# 4) 若提示 .xpr 删不掉,说明有 Vivado(含 Hardware Manager)正开着那个工程,
|
||
# 关掉它再跑一遍即可。
|
||
#
|
||
# 脚本用 [info script] 定位自身目录,所以整个文件夹拷到任何路径都能直接用。
|
||
# 若想用 Vivado 自动生成的版本,见说明文档“工程分享”一节。
|
||
# ============================================================================
|
||
|
||
set origin_dir [file dirname [file normalize [info script]]]
|
||
set proj_name "pipelined-cpu"
|
||
set part_name "xc7a100tcsg324-1"
|
||
|
||
# ---------------------------------------------------------------- 自检
|
||
set required [list \
|
||
"$origin_dir/srcs/board/cpu21_board_top.v" \
|
||
"$origin_dir/srcs/board/cpu21_board_rom.vh" \
|
||
"$origin_dir/srcs/board/cpu21_board_rom_fast.vh" \
|
||
"$origin_dir/srcs/constrs/cpu21_board_top.xdc" \
|
||
"$origin_dir/srcs/tb/tb_cpu21_riscv_redirect_int_bpb.v" \
|
||
"$origin_dir/srcs/tb/tb_no_intr.v" \
|
||
"$origin_dir/srcs/tb/tb_board_top.v"]
|
||
set missing {}
|
||
foreach f $required { if {![file exists $f]} { lappend missing $f } }
|
||
if {[llength [glob -nocomplain "$origin_dir/srcs/rtl/*.v"]] == 0} {
|
||
lappend missing "$origin_dir/srcs/rtl/*.v"
|
||
}
|
||
if {[llength $missing] > 0} {
|
||
puts "ERROR: 缺少下列文件,请确认 srcs/ 目录完整:"
|
||
foreach f $missing { puts " $f" }
|
||
return 1
|
||
}
|
||
|
||
# ---------------------------------------------------------------- 清理旧工程
|
||
# 一键重建:先删掉旧工程和所有生成目录。工程完全由 srcs/ + 本脚本决定,
|
||
# 所以删掉不会丢任何源文件;不想自动清理就把 clean_first 改成 0。
|
||
set clean_first 1
|
||
|
||
if {$clean_first} {
|
||
# 必须删掉的工程目录:任何一个删不掉都不能继续(否则 Vivado 会拒绝建工程)
|
||
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"]
|
||
# 顺带清掉的杂项:删不掉也无所谓(本进程自己会占着 vivado.jou / .log / .str)
|
||
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 项)"
|
||
if {[llength $blocked] > 0} {
|
||
puts "ERROR: 这些工程目录删不掉:$blocked"
|
||
puts " 它们正被另一个 Vivado(GUI / Hardware Manager)占用。"
|
||
puts " 请关闭 Vivado 后重跑本脚本。"
|
||
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 建工程,**不依赖"板卡文件"**:Vivado 默认不带 Nexys4DDR 的 board 文件,
|
||
# 所以 GUI 新建向导的 Boards 页里找不到它;board 只影响"选板/板级预设",
|
||
# 不影响综合与实现(引脚全在 srcs/constrs/*.xdc 里显式约束)。
|
||
# 若以后装了 Digilent 板卡文件(data/boards/board_files/nexys4-ddr),想按板卡建:
|
||
# create_project $proj_name $origin_dir -part $part_name \
|
||
# -board_part digilentinc.com:nexys4ddr:part0:1.1
|
||
# (那样工程会记上板卡,别人没装板卡文件就打不开了,所以默认不这么写。)
|
||
# -force:万一还有残留目录(例如被占用的 .runs)也能覆盖,不至于卡死。
|
||
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" \
|
||
"$origin_dir/srcs/board/cpu21_board_rom.vh"]
|
||
set_property file_type {Verilog Header} \
|
||
[get_files "$origin_dir/srcs/board/cpu21_board_rom.vh"]
|
||
|
||
# 默认跑哪个内置程序(同一时间只应启用一个):
|
||
# use_intrupt_sllsb = 1 -> 课程新版「多级中断测试 + 附加 SLL/SB/BLTU」
|
||
# (programs/risc-v-intrupt-sllsb.asm / .hex)
|
||
# use_intrupt_ccab = 1 -> 上一版中断测试(programs/risc-v-intrupt-ccab.*)
|
||
# use_saolei = 1 -> 扫雷(saolei/tools/build.ps1 生成)
|
||
# 三个都为 0 -> 走马灯 demo
|
||
set use_intrupt_sllsb 1
|
||
set use_intrupt_ccab 0
|
||
set use_saolei 0
|
||
|
||
# 中断测试程序的 ROM 初始化。
|
||
set intrupt_ccab_rom "$origin_dir/srcs/board/risc-v-intrupt-ccab.vh"
|
||
if {[file exists $intrupt_ccab_rom]} {
|
||
add_files -norecurse -fileset sources_1 [list $intrupt_ccab_rom]
|
||
set_property file_type {Verilog Header} [get_files $intrupt_ccab_rom]
|
||
} else {
|
||
puts "NOTE: 还没有 $intrupt_ccab_rom"
|
||
}
|
||
|
||
# 课程新版中断测试(附加 SLL/SB/BLTU)的 ROM 初始化。
|
||
set intrupt_sllsb_rom "$origin_dir/srcs/board/risc-v-intrupt-sllsb.vh"
|
||
if {[file exists $intrupt_sllsb_rom]} {
|
||
add_files -norecurse -fileset sources_1 [list $intrupt_sllsb_rom]
|
||
set_property file_type {Verilog Header} [get_files $intrupt_sllsb_rom]
|
||
} else {
|
||
puts "NOTE: 还没有 $intrupt_sllsb_rom(用 tools/hex2vh.py 生成)"
|
||
}
|
||
|
||
# 扫雷程序的 ROM 初始化(由 saolei/tools/build.ps1 生成,可能还不存在)。
|
||
# 综合/仿真时定义宏 SAOLEI_ROM 就会用它替换上面的跑马灯 ROM。
|
||
set saolei_rom "$origin_dir/srcs/board/cpu21_saolei_rom.vh"
|
||
if {[file exists $saolei_rom]} {
|
||
add_files -norecurse -fileset sources_1 [list $saolei_rom]
|
||
set_property file_type {Verilog Header} [get_files $saolei_rom]
|
||
} else {
|
||
puts "NOTE: 还没有 $saolei_rom —— 需要先跑 saolei/tools/build.ps1 生成"
|
||
}
|
||
|
||
# 关键:sources_1 里定义对应宏,cpu21_board_top.v 的 `ifdef 链才会选中那个 ROM
|
||
# (宏只影响综合用的 sources_1;仿真 fileset 仍各自用 tb 自带的 ROM)
|
||
if {$use_intrupt_sllsb && [file exists $intrupt_sllsb_rom]} {
|
||
set_property verilog_define {RISC_V_INTRUPT_SLLSB_ROM} [get_filesets sources_1]
|
||
puts "NOTE: sources_1 已定义 RISC_V_INTRUPT_SLLSB_ROM —— 上板跑「中断测试 + SLL/SB/BLTU」"
|
||
} elseif {$use_intrupt_ccab && [file exists $intrupt_ccab_rom]} {
|
||
set_property verilog_define {RISC_V_INTRUPT_CCAB_ROM} [get_filesets sources_1]
|
||
puts "NOTE: sources_1 已定义 RISC_V_INTRUPT_CCAB_ROM —— 上板跑 risc-v-intrupt-ccab"
|
||
} elseif {$use_saolei && [file exists $saolei_rom]} {
|
||
set_property verilog_define {SAOLEI_ROM} [get_filesets sources_1]
|
||
puts "NOTE: sources_1 已定义 SAOLEI_ROM —— 上板跑扫雷;想跑走马灯把本脚本里 use_saolei 改成 0"
|
||
} else {
|
||
puts "NOTE: 未定义 SAOLEI_ROM —— 上板跑走马灯 demo"
|
||
}
|
||
|
||
# 约束(cpu21_board_top.xdc 是引脚与分频时钟约束)
|
||
add_files -norecurse -fileset constrs_1 [glob "$origin_dir/srcs/constrs/*.xdc"]
|
||
|
||
# 仿真文件:测试平台 + ROM 镜像 + 快速版 ROM 头文件
|
||
add_files -norecurse -fileset sim_1 [list \
|
||
"$origin_dir/srcs/tb/tb_cpu21_riscv_redirect_int_bpb.v" \
|
||
"$origin_dir/srcs/tb/tb_no_intr.v" \
|
||
"$origin_dir/srcs/tb/tb_board_top.v" \
|
||
"$origin_dir/srcs/board/cpu21_board_rom_fast.vh"]
|
||
add_files -norecurse -fileset sim_1 [glob "$origin_dir/srcs/rom/*.hex"]
|
||
set_property file_type {Verilog Header} \
|
||
[get_files "$origin_dir/srcs/board/cpu21_board_rom_fast.vh"]
|
||
|
||
# 第二个仿真 fileset:无中断测试平台(tb_no_intr)
|
||
create_fileset -simset sim_2
|
||
add_files -norecurse -fileset sim_2 [list \
|
||
"$origin_dir/srcs/tb/tb_no_intr.v" \
|
||
"$origin_dir/srcs/tb/tb_cpu21_riscv_redirect_int_bpb.v"]
|
||
|
||
# ---------------------------------------------------------------- 顶层与顺序
|
||
set_property top cpu21_board_top [get_filesets sources_1]
|
||
set_property top tb_cpu21_riscv_redirect_int_bpb [get_filesets sim_1]
|
||
set_property top tb_no_intr [get_filesets sim_2]
|
||
update_compile_order -fileset sources_1
|
||
update_compile_order -fileset sim_1
|
||
update_compile_order -fileset sim_2
|
||
|
||
# GUI 仿真默认只跑 1000 ns,这里改成跑完(否则看起来像卡住)
|
||
foreach fs {sim_1 sim_2} {
|
||
catch { set_property -name {xsim.simulate.runtime} -value {all} -objects [get_filesets $fs] }
|
||
}
|
||
|
||
# ---------------------------------------------------------------- 结果汇总
|
||
puts "工程已创建:$origin_dir/$proj_name.xpr"
|
||
puts " sources_1 顶层 = cpu21_board_top(上板:时钟分频 + 片内 ROM/RAM + 引脚)"
|
||
if {$use_intrupt_sllsb && [file exists $intrupt_sllsb_rom]} {
|
||
set selected_program {risc-v-intrupt-sllsb (RISC_V_INTRUPT_SLLSB_ROM) 中断+SLL/SB/BLTU}
|
||
} elseif {$use_intrupt_ccab && [file exists $intrupt_ccab_rom]} {
|
||
set selected_program {risc-v-intrupt-ccab (RISC_V_INTRUPT_CCAB_ROM)}
|
||
} elseif {$use_saolei && [file exists $saolei_rom]} {
|
||
set selected_program {扫雷 (SAOLEI_ROM)}
|
||
} else {
|
||
set selected_program {走马灯 demo}
|
||
}
|
||
puts " sources_1 程序 = $selected_program"
|
||
puts " sim_1 顶层 = tb_cpu21_riscv_redirect_int_bpb(中断/分支预测)"
|
||
puts " sim_2 顶层 = tb_no_intr(指令集测试,ROM 镜像在 srcs/rom/)"
|
||
puts " 下一步:Run Synthesis → Run Implementation → Generate Bitstream,或 Run Simulation"
|
||
puts " 提示:CPU 时钟 = 100 MHz / CLK_DIV(默认 2)= 50 MHz;改分频比时"
|
||
puts " cpu21_board_top.v 的 CLK_DIV 与 cpu21_board_top.xdc 的 -divide_by 要一起改。"
|