工程整理:源文件复制进 pipelined-cpu/,提供一键重建脚本 create_project.tcl

- pipelined-cpu/ 变成自包含的工程目录,所有设计文件都在里面,可以整个拷给别人:
  srcs/rtl(12 个处理器 RTL)、srcs/board(板级顶层 + 片内 ROM 初始化,含 __FAST 版)、
  srcs/constrs(引脚约束 + 板级母版)、srcs/tb(三个测试平台)、srcs/rom(ROM 镜像),
  另外附上 tools/(迷你汇编器)与 programs/(测试程序源码与期望值)。
  Vivado 工程里的引用全部改到这些副本上(sources_1 ×15、constrs_1 ×2、sim_1 ×12、sim_2 ×2),
  并用副本重新综合通过。
- 新增 pipelined-cpu/create_project.tcl:手写的一键重建脚本。
  * 用 [info script] 定位自身目录,整个文件夹拷到任意路径都能用;
  * 就地创建工程(源文件留在 srcs/ 下不动),带文件自检与“工程已存在”提示;
  * 配好三个 fileset 的顶层、cpu21_board_rom(.vh) 的 Verilog Header 类型、
    xsim.simulate.runtime=all。
  实测:把工程文件夹拷到别处、从另一个工作目录运行脚本,工程重建成功且
  synth_design Complete!(分享包只有 create_project.tcl + srcs/ + tools/ + programs/ +
  说明文档,约 0.3 MB)。
- testbench 从 sources_1 移到仿真 fileset(sim_1 / sim_2):不再需要
  USED_IN_SYNTHESIS=0 这类设置,也彻底避免“testbench 被当成顶层”的
  [Place 30-494] The design is empty 问题。
- 两个 CPU 测试平台增加 srcs/rom/ 探测路径(含 GUI 仿真目录向上 5 层的相对路径),
  别人拿到工程后不用额外配置就能直接 Run Simulation。
- .gitignore:放行 pipelined-cpu/create_project.tcl(其余工程生成物仍不入库,
  这样重建脚本不会因为工程目录被忽略而丢失)。
- 说明文档新增“工程分享(Tcl 一键重建)”一节:目录结构、脚本用法、分享前应删除的
  生成目录、仓库源码与工程副本的同步命令,以及用 write_project_tcl 自动生成版本时
  需要改的两行(origin_dir / create_project 目标)。
- 验证:muldiv_test 13/13、isa_test 24/24、lui_test 4/4、mmio_test 5/5 以及两个
  流水基线全部通过(与改动前一致)。
This commit is contained in:
2026-09-13 18:59:25 +08:00
parent 4a81685066
commit fed01f5910
5 changed files with 187 additions and 3 deletions
+2
View File
@@ -1,4 +1,6 @@
pipelined-cpu/*
# 例外:一键重建工程的分享脚本要入库(其余工程生成物不入库)
!pipelined-cpu/create_project.tcl
# 本地临时目录(仿真脚本、日志、中间文件)
_tmp/
+69 -3
View File
@@ -253,9 +253,10 @@ Vivado 工程 `pipelined-cpu/pipelined-cpu.xpr` 被 `.gitignore` 排除,需要
2. 加入 `board/cpu21_board_top.v`、`board/cpu21_board_top.xdc`,
`board/cpu21_board_rom.vh` 的文件类型设成 `Verilog Header`(不能当普通源码,
否则会被当成顶层候选);
3. 三个 testbench(`testbench/tb_cpu21_riscv_redirect_int_bpb.v`、`tb_no_intr.v`、
`tb_board_top.v`)的 `USED_IN_SYNTHESIS` / `USED_IN_IMPLEMENTATION` 设为 `0`,
否则综合可能把 testbench 当顶层,报 `[Place 30-494] The design is empty`。
3. 三个 testbench 放在**仿真 fileset**(`sim_1` 顶层 `tb_cpu21_riscv_redirect_int_bpb`、
`sim_2` 顶层 `tb_no_intr`),不参与综合,因此不会再出现“testbench 被当成顶层”
导致的 `[Place 30-494] The design is empty`。仓库里的 `testbench/` 与工程里的
`srcs/tb/` 是同一批文件。
`xc7a100tcsg324-1` 上的实现结果(乘除法改为多周期单元、`CLK_DIV=2` = 50 MHz):
@@ -272,3 +273,68 @@ CPU 时钟 6.25 MHz → 50 MHz(快 8 倍),并且乘除法不再位于关
上板操作:用 Vivado 打开工程 → `Open Hardware Manager` → `Program Device`,选上述
`.bit`。复位后跑马灯立即从 `0001` 开始逐位左移(演示程序不执行 `ecall`,所以不用
按 `go`;`go` 只对执行了 `ecall` 停机、等待继续运行的程序有意义),LED16_B 每次写入时点亮。
## 工程分享(Tcl 一键重建)
`pipelined-cpu/` 已整理成“自包含 + 可直接分享”的工程目录,设计文件都在里面,
别人拿到整个文件夹后**不需要改任何路径**:
```text
pipelined-cpu/
├─ create_project.tcl 一键重建脚本(用 [info script] 定位自身目录)
├─ srcs/rtl/*.v 处理器 RTL(12 个文件)
├─ 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
└─ pipelined-cpu.xpr(+ *.runs/…) 本机工程,分享时可删
```
用法(在 Vivado Tcl Shell 里,或命令行 `vivado -mode batch -source create_project.tcl`):
```tcl
cd <pipelined-cpu 所在目录>
source create_project.tcl
```
脚本会就地创建 `pipelined-cpu.xpr`,并配好:
- `sources_1` 顶层 `cpu21_board_top`(处理器 RTL + 板级顶层 + 片内 ROM 初始化,
`cpu21_board_rom.vh` 标记为 `Verilog Header`);
- `constrs_1`:`cpu21_board_top.xdc`(引脚 + `CLK_DIV` 分频时钟)与板级母版;
- `sim_1`(顶层 `tb_cpu21_riscv_redirect_int_bpb`)与 `sim_2`(顶层 `tb_no_intr`);
测试平台会依次探测 ROM 镜像,其中 `../../../../../srcs/rom/` 对应 GUI 仿真的
工作目录(`<proj>.sim/sim_1/behav/xsim`),所以创建完就能直接 `Run Simulation`;
- 两个仿真 fileset 的 `xsim.simulate.runtime` 设为 `all`,避免 GUI 仿真只跑 1000 ns
就停下(看起来像卡死)。
分享前建议删掉生成目录:`*.xpr`、`*.runs`、`*.sim`、`*.cache`、`*.hw`、`*.gen`、
`*.ip_user_files`、`.Xil`。实测只带 `create_project.tcl` + `srcs/` + `tools/` +
`programs/` + 说明文档约 **0.3 MB**,在任意路径重建工程并综合成功。
> 仓库根目录的 `rtl/`、`board/`、`testbench/` 是源文件,`pipelined-cpu/srcs/` 里的是
> 分享用副本。在仓库里改了 RTL/测试平台后,同步一下即可:
>
> ```powershell
> Copy-Item rtl\*.v pipelined-cpu\srcs\rtl\ -Force
> Copy-Item board\cpu21_board_top.v,board\cpu21_board_rom*.vh pipelined-cpu\srcs\board\ -Force
> Copy-Item board\cpu21_board_top.xdc pipelined-cpu\srcs\constrs\ -Force
> Copy-Item testbench\tb_*.v pipelined-cpu\srcs\tb\ -Force
> Copy-Item testbench\rom\*.hex pipelined-cpu\srcs\rom\ -Force
> ```
也可以用 Vivado 自动生成:
```tcl
write_project_tcl -force -paths_relative_to <工程目录> <工程目录>/create_project.tcl
```
注意自动生成的版本会把源路径写成**生成时的绝对路径**、并且要求 `origin_dir` 指向
工程目录,所以要手动改两行才能给别人用:
```tcl
set origin_dir [file dirname [file normalize [info script]]]
create_project ${_xil_proj_name_} $origin_dir -part xc7a100tcsg324-1
```
+106
View File
@@ -0,0 +1,106 @@
# ============================================================================
# cpu21-riscv 流水线 CPU —— 一键重建 Vivado 工程
# FPGA:Nexys4DDR / xc7a100tcsg324-1(Vivado 2025.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) 建议先删掉本目录下的 pipelined-cpu.xpr,以及 *.runs / *.sim / *.cache /
# *.hw / *.gen / *.ip_user_files 等生成目录(不删会在下面报错提示);
# 2) 在 Vivado Tcl Shell 里执行: source create_project.tcl
# 或命令行: vivado -mode batch -source create_project.tcl -log create.log
# 3) 工程就地创建:pipelined-cpu.xpr(源文件仍在 srcs/ 下,不会被搬动)。
#
# 脚本用 [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
}
if {[file exists "$origin_dir/$proj_name.xpr"]} {
puts "ERROR: 工程已存在:$origin_dir/$proj_name.xpr"
puts " 请先删除 .xpr(以及 *.runs / *.sim 等生成目录)后重跑本脚本。"
return 1
}
# ---------------------------------------------------------------- 建工程
create_project $proj_name $origin_dir -part $part_name
# 设计源文件:处理器 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"]
# 约束(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 + 引脚)"
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 要一起改。"
@@ -235,6 +235,11 @@ module tb_cpu21_riscv_redirect_int_bpb #(
if (!rom_loaded) load_rom_image({"../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/rom/", ROM_FILE}, rom_loaded);
// 分享出去的 Vivado 工程里的镜像目录(工程目录/srcs/rom/),GUI 仿真的工作
// 目录是 <proj>.sim/sim_1/behav/xsim,所以需要向上 5 层。
if (!rom_loaded) load_rom_image({"srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded)
$display(
"WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).",
+5
View File
@@ -238,6 +238,11 @@ module tb_no_intr #(
if (!rom_loaded) load_rom_image({"../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/rom/", ROM_FILE}, rom_loaded);
// 分享出去的 Vivado 工程里的镜像目录(工程目录/srcs/rom/),GUI 仿真的工作
// 目录是 <proj>.sim/sim_1/behav/xsim,所以需要向上 5 层。
if (!rom_loaded) load_rom_image({"srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../srcs/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded)
$display(
"WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).",