diff --git a/.gitignore b/.gitignore index 8330fd0..f1a1650 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ pipelined-cpu/* +# 例外:一键重建工程的分享脚本要入库(其余工程生成物不入库) +!pipelined-cpu/create_project.tcl # 本地临时目录(仿真脚本、日志、中间文件) _tmp/ diff --git a/cpu21_riscv_redirect_int_bpb说明.md b/cpu21_riscv_redirect_int_bpb说明.md index c858560..5dccf26 100644 --- a/cpu21_riscv_redirect_int_bpb说明.md +++ b/cpu21_riscv_redirect_int_bpb说明.md @@ -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 +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 仿真的 + 工作目录(`.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 +``` diff --git a/pipelined-cpu/create_project.tcl b/pipelined-cpu/create_project.tcl new file mode 100644 index 0000000..2fa48d1 --- /dev/null +++ b/pipelined-cpu/create_project.tcl @@ -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 要一起改。" diff --git a/testbench/tb_cpu21_riscv_redirect_int_bpb.v b/testbench/tb_cpu21_riscv_redirect_int_bpb.v index 98f290c..0657aec 100644 --- a/testbench/tb_cpu21_riscv_redirect_int_bpb.v +++ b/testbench/tb_cpu21_riscv_redirect_int_bpb.v @@ -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 仿真的工作 + // 目录是 .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).", diff --git a/testbench/tb_no_intr.v b/testbench/tb_no_intr.v index e7f4229..660193f 100644 --- a/testbench/tb_no_intr.v +++ b/testbench/tb_no_intr.v @@ -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 仿真的工作 + // 目录是 .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).",