diff --git a/docs/guide-for-beginner/git-tutorial.md b/docs/guide-for-beginner/git-tutorial.md index 60a36a1..263f25e 100644 --- a/docs/guide-for-beginner/git-tutorial.md +++ b/docs/guide-for-beginner/git-tutorial.md @@ -38,7 +38,7 @@ Git 的管理是基于仓库(Repository)的,仓库可以看成一个储存 - 暂存区(Staging Area):即我们的缓存区,我们可以通过 `git add` 命令将工作区的文件(或文件的变动)添加到暂存区。暂存区存储了我们对文件的修改。 - 仓库(Repository):即我们的版本库,我们可以通过 `git commit` 命令将暂存区的文件提交到仓库。 -值得注意的是,由于我们对文件的编辑都发生在工作目录下,所以我们增删文件或对文件进行修改,都只会影响工作区,而不会影响暂存区和仓库。所以我们可以很容易地撤销在工作区发生的修改,这也是 Git 的优势之一。 我们真正想要在仓库中修改一个文件,实际上需要经历三个步骤: +值得注意的是,由于我们对文件的编辑都发生在工作目录下,所以我们增删文件或对文件进行修改,都只会影响工作区,而不会影响暂存区和仓库。所以我们可以很容易地撤销在工作区发生的修改,这也是 Git 的优势之一。我们真正想要在仓库中修改一个文件,实际上需要经历三个步骤: 1. 在工作目录中修改文件。 2. 使用 `git add` 将修改的文件添加到暂存区。 diff --git a/docs/popular-science/bash-dlc.md b/docs/popular-science/bash-dlc.md index 0163112..e3d9671 100644 --- a/docs/popular-science/bash-dlc.md +++ b/docs/popular-science/bash-dlc.md @@ -91,7 +91,7 @@ $ echo a{d,c,b}e ade ace abe ``` -大括号内还可以写数字/字符+增长,比如 `{x..y[..incr]}` +大括号内还可以写数字/字符 + 增长,比如 `{x..y[..incr]}` 大括号展开是严格 "词法" 的,它不会对任何特殊字符作出 "反应", 包括大括号自己: diff --git a/docs/unofficial-lab-env/comp-organ-lab2.md b/docs/unofficial-lab-env/comp-organ-lab2.md new file mode 100644 index 0000000..ac459db --- /dev/null +++ b/docs/unofficial-lab-env/comp-organ-lab2.md @@ -0,0 +1,449 @@ +# 计算机组成原理实验 2 环境指南(非官方) + +以下指南适合有 Linux/macOS 环境的同学使用。 + +笔者心态:对 Verilog 感到不适,并且想折腾一些花样。 + +## 实验需要的工具 + +- Verilator +- Chisel +- GTKWave + +## 安装 C/C++ 编译环境 + +实验工具安装需要 + +- gcc +- git +- g++ +- make + +通过以下命令可以安装 + +```sh +# Debian/Ubuntu +sudo apt install build-essential git device-tree-compiler +# Fedora +sudo dnf install build-essential git device-tree-compiler +``` + +如果配置环境过程中出现 "command not found." 可能是有依赖的工具没装,此时可以利用搜索引擎。 + +macOS 需要安装 Homebrew 作为包管理器,请查阅 [Tuna Mirrors 的安装指南](https://mirrors.tuna.tsinghua.edu.cn/help/homebrew) 。 + +## 安装 Verilator + +使用包管理器安装: + +```sh +# Debian/Ubuntu +sudo apt install verilator +# Fedora/CentOS +sudo dnf install verilator verilator-devel +# macOS +brew install verilator +``` + +但是有一些 Linux 的发行版,他会将包拆的比较奇怪,导致没法找到 `verilated.mk` 之类的情况发生。 +亦或者是版本比较老,则可以使用编译安装: + +```sh +# 先 cd 到一个文件夹中,推荐 cd 到 $HOME/Downloads +git clone https://github.com/verilator/verilator +cd verilator +autoconf # 生成 ./configure 文件 +./configure --prefix=$HOME/app/verilator # 配置 makefile 和安装路径 +make -j `nproc` # 多核编译 +make install # 这会安装到 $HOME/app/verilator 中 +echo 'export verilator_ROOT=$HOME/app/verilator' >> .bashrc # 追加环境变量 +``` + +如果想要其他支持,请查阅 [Verilator 的安装文档](https://verilator.org/guide/latest/install.html) 。 + +Verilator 在编译过程中,需要用到 GNU 方言,因此推荐使用 Linux。 + +## 使用 SDKMAN 安装 Scala/Java/sbt + +SDKMAN 是一个用于管理 Java 相关开发环境的软件。可以通过下面这个命令安装: + +```sh +curl -s "https://get.sdkman.io" | bash +``` + +安装过程中,注意看提示,会要求在安装完成后配置环境变量。 + +通过 SDKMAN 安装 Scala/Java/sbt: + +```sh +sdk install java +sdk install scala 2.12.13 +sdk install sbt +``` + +## 安装 GTKWave + +```sh +# Debian/Ubuntu +sudo apt install gtkwave +# Fedora +sudo dnf install gtkwave +``` + +macOS 请看 [附录:macOS-安装-GTKWave](#macos-安装-gtkwave) + +## 一个 Chisel 与 Verilator 结合的例子 + +下面这个例子可以在 [这里](https://github.com/KINGFIOX/chisel-verilator-example) 找到。 + +### 创建 Chisel 项目 + +Chisel 是 Scala 的一个库并且目前为止并没有一个真正的 Chisel IDE。我们可以通过官方的 Chisel-template 创建我们的项目。 + +```sh +git clone https://github.com/chipsalliance/chisel-template.git +``` + +### 编写 Chisel 代码 + +chisel-template 项目下天然的提供了一个 GCD 硬件 module。 + +```scala +/// src/main/scala/gcd/GCD.scala +package gcd + +import Chisel3._ +// _root_ disambiguates from package Chisel3.util.circt if user imports Chisel3.util._ +import _root_.circt.stage.ChiselStage + +/** + * Compute GCD using subtraction method. + * Subtracts the smaller from the larger until register y is zero. + * value in register x is then the GCD + */ +class GCD extends Module { + val io = IO(new Bundle { + val value1 = Input(UInt(16.W)) + val value2 = Input(UInt(16.W)) + val loadingValues = Input(Bool()) + val outputGCD = Output(UInt(16.W)) + val outputValid = Output(Bool()) + }) + + val x = Reg(UInt()) + val y = Reg(UInt()) + + when(x > y) { x := x - y } + .otherwise { y := y - x } + + when(io.loadingValues) { + x := io.value1 + y := io.value2 + } + + io.outputGCD := x + io.outputValid := y === 0.U +} + +object GCD extends App { + ChiselStage.emitSystemVerilogFile( + new GCD, + firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info") + ) +} +``` + +上面这个 `class GCD` 继承了 Chisel 的 Module 抽象类。在这个类里面,我们描述了 GCD 模块的行为。等价于 SystemVerilog 中的 `module GCD();`。`object GCD` 表示创建了一个名为 GCD 的单例,这个单例继承自 App,起到程序入口的作用,这里用于发射 SystemVerilog 代码。 + +然后我们通过在命令行中输入 `sbt "runMain gcd.GCD"` 来启动 SystemVerilog 代码的发射 。最后,我们就可以在项目的根目录下找到生成的 `GCD.sv` 文件。 + +```sv +// Generated by CIRCT firtool-1.62.0 +module GCD( + input clock, + reset, + input [15:0] io_value1, + io_value2, + input io_loadingValues, + output [15:0] io_outputGCD, + output io_outputValid +); + + reg [15:0] x; + reg [15:0] y; + always @(posedge clock) begin + if (io_loadingValues) begin + x <= io_value1; + y <= io_value2; + end + else if (x > y) + x <= x - y; + else + y <= y - x; + end // always @(posedge) + assign io_outputGCD = x; + assign io_outputValid = y == 16'h0; +endmodule +``` + +- 这里是 Scala 教程:https://docs.scala-lang.org/zh-cn/ +- 这里是 Chisel 教程:https://www.chisel-lang.org/docs/cookbooks/cookbook + +### 使用 Verilator C++ 调试 Chisel 生成的 SystemVerilog + +我们可以用 C++ 编写一个 golden model 作为对照。 + +```cpp +// 一个正确的 gcd 实现 +int gcd(uint16_t a, uint16_t b) +{ + while (b != 0) { + int remainder = a % b; + a = b; + b = remainder; + } + return a; +} +``` + +下面我们进行了硬件模块的仿真。这里的 `#include "VGCD.h"` 是在使用 Verilator 将 SystemVerilog 代码编译成 C++ 类的过程中自动生成的头文件。这个头文件包含了编译后的 C++ 类,用于在仿真环境中模拟硬件行为。 + +```cpp +#include "VGCD.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +int main(int argc, char* argv[]) +{ + Verilated::commandArgs(argc, argv); + Verilated::traceEverOn(true); // 启用波形跟踪 + + size_t fail_cnt = 0; + size_t success_cnt = 0; + + auto dut = std::make_unique(); + VerilatedVcdC* vcd = new VerilatedVcdC(); + dut->trace(vcd, 99); // 设定跟踪级别 + vcd->open("gcd.vcd"); // 打开 VCD 文件 + + // 重置设备 + dut->reset = 1; + dut->clock = 0; + for (int i = 0; i < 5; i++) { + dut->clock = !dut->clock; + dut->eval(); + vcd->dump(10 * i); // 记录时间点 + } + dut->reset = 0; + + srand(time(NULL)); + uint16_t x = rand(); + uint16_t y = rand(); + + // 主仿真循环 + for (int cycle = 0; cycle < 400; cycle++) { + dut->io_loadingValues = (cycle == 5); + dut->io_value1 = x; + dut->io_value2 = y; + + dut->clock = 1; + dut->eval(); + vcd->dump(10 * cycle + 5); + + dut->clock = 0; + dut->eval(); + vcd->dump(10 * cycle + 10); + + dut->io_loadingValues = 0; + } + + // 收集结果和清理 + uint16_t top_z = dut->io_outputGCD; + + dut->final(); + vcd->close(); // 关闭 VCD 文件 + + if (uint16_t z = gcd(x, y); z == top_z) { + std::cout << "success" << std::endl; + } else { + std::cout << "fail" << std::endl; + std::cout << "x: " << (int)x << std::endl; + std::cout << "y: " << (int)y << std::endl; + std::cout << "gcd(x, y): " << (int)z << std::endl; + std::cout << "dut(x, y): " << (int)top_z << std::endl; + } + + return 0; +} +``` + +### 编译 C++ 和 SystemVerilog + +```sh +verilator -Wall --cc --trace -Iobj_dir -Wno-UNUSEDSIGNAL GCD.sv --exe tb.cxx # 会生成 obj_dir 文件夹,这是将 SystemVerilog 编译成了 C++ 的 class +make -C obj_dir -f VGCD.mk +``` + +这样,我们就使用 Verilator 完成了 C++ 和 SystemVerilog 的编译。我们可以在 `obj_dir` 文件夹中找到 `VGCD` 可执行文件。通过在命令行中执行 `./obj_dir/VGCD`,我们可以运行这个可执行文件。执行完成后,如果看到输出中显示 `success` 字样,这表示仿真成功。同时,在项目的根目录下会生成一个名为 `gcd.vcd` 的文件。我们可以使用 VSCode 的插件 wavetrace(付费) 来打开这个 `.vcd` 文件,以便查看波形数据。 + +详细的用法查阅 [Verilator 的使用教程](https://itsembedded.com/dhd_list) 。 + +### 使用 GTKWave 打开波形仿真文件 + +```sh +gtkwave gcd.vcd +``` + +#### 使用 tcl 脚本 + +每次使用 GTKWave 调试波形图时,我们需要执行一系列重复的步骤:打开程序、选择信号、然后使用 append/insert 将信号添加到视图中。 +这很不优雅。尤其是:当我们需要频繁的调试:打开 GTKWave -> 选中信号 -> a/i -> 看波形 -> 改代码 -> make run -> 打开 GTKWave -> +选中信号 -> a/i -> 看波形 -> ... + +然而,我们可以通过自动化来简化这个流程。具体做法是编写一个 tcl 脚本,这样每次打开 GTKWave 时,脚本会自动将所有必要的信号添加到显示界面中。这样可以提高调试效率。 + +创建一个 `load_all_waves.tcl` 文件,写入以下内容,保存。 + +```tcl +# load_all_waves.tcl +# Add all signals +set nfacs [ gtkwave::getNumFacs ] +set all_facs [list] +for {set i 0} {$i < $nfacs } {incr i} { + set facname [ gtkwave::getFacName $i ] + lappend all_facs "$facname" +} +set num_added [ gtkwave::addSignalsFromList $all_facs ] +puts "num signals added: $num_added" + +# Zoom full +gtkwave::/Time/Zoom/Zoom_Full + +# Print +# set dumpname [ gtkwave::getDumpFileName ] +# gtkwave::/File/Print_To_File PDF {Letter (8.5" x 11")} Minimal $dumpname.pdf +``` + +然后我们可以通过下面这个命令打开`.tcl`和`.vcd`: + +```sh +# 可以通过 gtkwave -h 知道参数的含义: +# -S, --script=FILE. specify Tcl command script file for execution +# -f, --dump=FILE. specify dumpfile name +gtkwave -S load_all_waves.tcl -f gcd.vcd +``` + +# 附录 + +## macOS 安装 GTKWave + +macOS 下安装 GTKWave 是一件非常令人疑惑的事情。macOS 有着地狱一般的向下兼容问题。 + +GTKWave 并没有被 Homebrew 官方收录,但可以使用第三方仓库 randomplum/gtkwave 安装: + +```sh +brew tap randomplum/gtkWave +brew install --HEAD randomplum/gtkwave/gtkwave +``` + +然而,此仓库中维护的 GTKWave 并不包含 TCL 脚本支持,无法使用[上文](#使用-tcl-脚本)提到的便利方法,因此并不推荐使用这种方式安装。 + +Nix 是 Mac 上不同于 Homebrew 的其他包管理器,可以用于安装带 TCL 脚本支持的 GTKWave: + +### Nix + +Nix 是 Linux 发行版 NixOS 的包管理器,具有强大而可复现的环境管理功能; 它也可以脱离 NixOS, 单独作为一些其他 Linux distro 以及 macOS 的包管理器使用。 + +通过这行命令安装 Nix。安装过程中请注意看提示。 + +```sh +sh <(curl -L https://nixos.org/nix/install) +``` + +下面是通过 Nix 安装 GTKWave 的方法: + +```sh +# i 表示 install +# A 表示 attr 这告诉 nix-env 通过它的属性名来选择软件包 +nix-env -iA nixpkgs.gtkwave +``` + +实际上 Nix 不仅仅能起到包管理的作用,它几乎可以完成任何项目的完整环境复现。对 Nix 和 NixOS 感兴趣的同学可以额外阅读 [Nix 中文指南](https://nixoscn.vercel.app/user_guide/) + +## 远程开发的 GTKWave 的问题 + +远端机器上通常没有桌面环境,当我们连接到远端机器开发时,肯定是不能指望直接在远端机器上执行 `gtkwave <.vcd>` 就能在本地弹出一个窗口出来的。 +自然,我们可以为远端机器安装桌面环境和配置 X-Forward。 +但倘若只是想用 GTKWave 查看波形,那么将波形文件以某种方式下载到本地,再使用本地的 GTKWave 打开查看显然是更合理的选择。 +但每次重新运行仿真都要手动通过 VSCode 或 scp 下载波形文件确实令人烦躁,那么应该如何快捷地将远端的某个文件下载或同步到本地呢? +我们可以使用 SSHFS 或者基于 SSHFS 的其他软件(例如 CyberDuck, MountainDuck 等)将远端的目录直接挂载到本地, +每次本地打开挂载点内文件时自动从远端拉取文件内容,从而便利我们在本地使用 GTKWave 查看波形。 + +### 安装 SSHFS + +#### Windows/Linux + +```sh +# Windows +winget install -h -e --id "WinFsp.WinFsp" +# Ubuntu/Debian +sudo apt install sshfs +# Fedora/Centos +sudo dnf install sshfs +``` + +#### macOS 安装 SSHFS + +macOS 安装 SSHFS 相对来说比较复杂,需要开启 macOS 的内核拓展。 + +可以在这里下载 macOS 下 SSHFS 的相关软件 [macFUSE 和 SSHFS](https://osxfuse.github.io) + +使用 macFUSE 需要开启内核拓展,请看这个教程 [Getting Started with macFUSE](https://github.com/macfuse/macfuse/wiki/Getting-Started) + +### 使用 SSHFS + +```sh +sshfs @:<远端目录> <挂载点> +``` + +例如在笔者的电脑上是这样挂载的 + +```sh +sshfs wangfiox@192.168.1.1:/home/wangfiox/Documents/organ ~/Document/workspace/organ +``` + +我们可以通过下面这个命令验证 + +```sh +ll organ +# 输出结果如下 +# total 48 +# -rw-r--r-- 1 wangfiox staff 9B May 25 19:30 README.md +# drwxr-xr-x 1 wangfiox staff 32B May 26 19:04 archive +# drwxr-xr-x 1 wangfiox staff 16B May 26 18:49 hitsz +# drwxr-xr-x 1 wangfiox staff 162B May 25 19:30 lab1 +# drwxr-xr-x 1 wangfiox staff 272B May 29 20:22 lab2 +# drwxr-xr-x 1 wangfiox staff 96B May 25 19:30 rars +``` + +## Chisel(Scala) IDE 的选择 + +Chisel 只是 Scala 中的一个库。因此,只要 IDE 能支持 Scala,那么自然也是支持 Chisel 了。 +但是一些 IDE 如 VSCode/JetBrains IDEA 会对 Chisel 语法有更加好的 highlight 支持。 + +经观察:对于稍微大一些的 Chisel 项目 (小学期级别), VSCode + Metals 会很卡,建议使用 JetBrain IDEA + +### VSCode 对 Chisel(Scala) 的支持 + +下面是笔者使用的 VSCode 插件 + +- Chisel Syntax +- Scala (Metals) +- Scala Snippets +- Scala Syntax (official) + +### JetBrains IDEA 对 Chisel(Scala) 的支持 + +下面是笔者使用的 JetBrain IDEA 插件 + +- Scala diff --git a/mkdocs.yml b/mkdocs.yml index e8a5b28..e40d49c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -87,6 +87,7 @@ nav: - PyTorch on SLURM: hpc-doc/pytorch.md - 非官方实验环境指南: - 计算机组成原理lab1: unofficial-lab-env/comp-organ-lab1.md + - 计算机组成原理lab2: unofficial-lab-env/comp-organ-lab2.md plugins: - search: