【一生一芯】Verilator上手
搭建完Verilator的环境之后,就该考虑去上手实操运行一些实例了。这里参考了官方手册、It's embedded以及B站上一些资料。
一、创建一个二级制执行文件——Hello World
我们可以将System Verilog代码编译成二进制文件让Verilator执行。
进入“YSYX”文件夹,新建一个“test_our”文件夹:
cd ~/Desktop/YSYX mkdir test_our
然后新建一个Verilog文件:
cat >our.v <<'EOF' module our; initial begin $display("Hello World"); $finish; end endmodule EOF
使用Verilator来运行我们编写的这个小例子:
verilator --binary -j 0 -Wall our.v
接着运行我们编译出来的文件:
obj_dir/Vou
可以看到终端返回Hello World:
Hello World - our.v:2: Verilog $finish
二、创建一个C++执行文件
接着上一节的内容,在“test_our”路径下新建一个C++文件:
cat >sim_main.cpp <<'EOF' #include "Vour.h" #include "verilated.h" int main(int argc, char** argv) { VerilatedContext* contextp = new VerilatedContext; contextp->commandArgs(argc, argv); Vour* top = new Vour{contextp}; while (!contextp->gotFinish()) { top->eval(); } delete top; delete contextp; return 0; } EOF
然后使用Verilator运行这个例子:
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
Verilator运行完成后,C++文件到了“obj_dir”这个目录下。那么我们运行刚才生存的文件:
obj_dir/Vour
返回以下结果:
Hello World - our.v:2: Verilog $finish
最好使用Makefile来帮助你执行上述步骤,以免在修改源码之后又要重复执行上述步骤,Makefile能帮助你执行所有需要的步骤。
三、编写一个简单的CmakeLists
Verilator可以使用Cmake运行,在“example”目录下有CMake的例子可以参考。接下来写一个简短的Cmakelists.txt文件(提示,使用Cat指令,或者直接用文本编辑器新建一个存放在“test_our”目录下):
cmake_minimum_required(VERSION 3.22) #手册中没有这一行,但是Cmake要求每个Cmakelists开头必须添加这一行,否则报错 project(cmake_example) find_package(verilator HINTS $ENV{VERILATOR_ROOT}) add_executable(Vour sim_main.cpp) verilate(Vour SOURCES our.v)
接着我们新建一个build目录并运行Cmake:
mkdir build cd build cmake .. cmake --build .
提示,如果缺少Cmake要先安装Cmake:
sudo apt install cmake
接着运行生成的文件:
./Vour
返回Hello World:
Hello World - our.v:2: Verilog $finish
四、一个DUT(device under test)实例
首先获取源码,并切换到pt1分支:
cd ~/Desktop/YSYX git clone https://github.com/n-kremeris/verilator_basics cd verilator_basics git checkout verilator_pt1
通过一个DUT实例,可以更加仔细地学习Verilator究竟是怎样工作的,上述操作获取的代码中已经存在一个DUT了。打开alu.sv可以看到以下Verilog代码:
/* * Primitive alu example for Verilator example * * File name: alu.sv * Author: Norbertas Kremeris 2021 * */ typedef enum logic [1:0] { add = 2'h1, sub = 2'h2, nop = 2'h0 } operation_t /*verilator public*/; module alu #( parameter WIDTH = 6 ) ( input clk, input rst, input operation_t op_in, input [WIDTH-1:0] a_in, input [WIDTH-1:0] b_in, input in_valid, output logic [WIDTH-1:0] out, output logic out_valid ); operation_t op_in_r; logic [WIDTH-1:0] a_in_r; logic [WIDTH-1:0] b_in_r; logic in_valid_r; logic [WIDTH-1:0] result; // Register all inputs always_ff @ (posedge clk, posedge rst) begin if (rst) begin op_in_r <= nop; // 这里需要改称nop,否则会报Error a_in_r <= '0; b_in_r <= '0; in_valid_r <= '0; end else begin op_in_r <= op_in; a_in_r <= a_in; b_in_r <= b_in; in_valid_r <= in_valid; end end // Compute the result always_comb begin result = '0; if (in_valid_r) begin case (op_in_r) add: result = a_in_r + b_in_r; sub: result = a_in_r + (~b_in_r+1'b1); default: result = '0; endcase end end // Register outputs always_ff @ (posedge clk, posedge rst) begin if (rst) begin out <= '0; out_valid <= '0; end else begin out <= result; out_valid <= in_valid_r; end end endmodule;
/* verilator lint_off EOFNEWLINE */
// 记得末尾要用回车空一行,否则会报Error
如你所见,alu文件中包含的电路功能十分简单,他只有两个寄存器,没有延迟,只支持两个操作——加或减。以下是我们期待alu电路行为的波形图:
在第一个阶段里,输入量都被存在输入寄存器中。在第二阶段,寄存器的组合结果被存在输出寄存器中。心中对此有了大概之后,就可以开始编写测试文件。
由于alu是Verilog文件,我们需要借助Verilator将其转换为C++文件:
verilator --cc alu.sv
“--CC“意味着告诉Verilator将alu文件转化为C++文件,当然也可以使用”--SC“将其转化为C文件。
接下来我们可以写测试文件了,打开名为“tb_alu.cpp
”的测试文件,可以看到如下代码:
// Verilator Example // Norbertas Kremeris 2021 #include <stdlib.h> #include <iostream> #include <verilated.h> #include <verilated_vcd_c.h> #include "Valu.h" #include "Valu___024unit.h" #define MAX_SIM_TIME 20 vluint64_t sim_time = 0; int main(int argc, char** argv, char** env) { Valu *dut = new Valu; Verilated::traceEverOn(true); VerilatedVcdC *m_trace = new VerilatedVcdC; dut->trace(m_trace, 5); m_trace->open("waveform.vcd"); while (sim_time < MAX_SIM_TIME) { dut->clk ^= 1; dut->eval(); m_trace->dump(sim_time); sim_time++; } m_trace->close(); delete dut; exit(EXIT_SUCCESS); }
接下来建立仿真的可执行文件:
verilator -Wall --trace -cc alu.sv --exe tb_alu.cpp
编译它:
make -C obj_dir -f Valu.mk Valu
运行:
./obj_dir/Valu
查看波形:
gtkwave waveform.vcd
至此,已经完成了整个DUT的仿真过程。也大致接触/理解了Verilator的一些基本用法。
五、双控开关
新建一个文件夹用于存放工程:
cd ~/Desktop/YSYX/ysyx-workbench mkdir On_OFF_Switch cd On_OFF_Switch
新建描述双控开关的Verilog文件:
cat >Switch.v <<'EOF' module top ( input a, input b, output f ); assign f = a ^ b; endmodule EOF