本文请读者【直接关闭】,我后面的实践结果似乎和本文的实践结果不一样,真是见鬼了!我不知道发生了什么,还没有来得及进一步校验!
在其他文件不变的前提下,如果即将生成的mk文件和已有的mk文件不一样,就更新全部的源文件
这个事情说起来就有点诡异了,我们解释一下
- 我们只使用
--exe
,完成到生成全部的源文件,以及获取生成可执行文件的makefile文件这一步 - 编译的命令保持不变,例如
verilator --cc --exe Top.v main.cpp
- 命令中提及的文件全都保持不变,例如
Top.v main.cpp
,这里的保持不变,是指其时间戳保持不变 - 对于生成的
VTop.mk
文件,是本次测试唯一的变量,我们接下来看一看
测试一:不改变mk文件
假设,我们已经运行了指令verilator --cc --exe Top.v main.cpp
,生成了相关的文件。
接下来:
首先,我们运行
ls obj_dir/ --full-time > 1.txt
把测试前的文件时间戳存储起来
total 128
-rw-rw-r-- 1 jht jht 19595 2022-04-17 11:44:49.941869470 +0800 VTop___024root.cpp
-rw-rw-r-- 1 jht jht 1846 2022-04-17 11:44:49.941869470 +0800 VTop___024root.h
-rw-rw-r-- 1 jht jht 11606 2022-04-17 11:44:49.941869470 +0800 VTop___024root__Slow.cpp
...
这里展示一小部分,太多了没必要展示。
接下来,我们再次运行verilator --cc --exe Top.v main.cpp
,然后运行ls obj_dir/ --full-time > 2.txt
,把新的时间戳存储起来。
紧接着,使用colordiff 1.txt 2.txt
进行对比,没有任何输出,说明前后文件时间戳完全一样,这意味着,第二次运行的指令,没有导致生成结果的更新!
要知道,生成的结果
- 是从外面其他文件(c/cpp)拷贝过来的
- 是编译Verilog文件生成的
而第二次没有更新,没有重新编译和拷贝,证明verilator本身是有检测机制的,不会每次无脑编译更新。
测试2:修改mk文件
接下来,做一个新的测试。
假设,我们已经运行了指令verilator --cc --exe Top.v main.cpp
,生成了相关的文件。
- 运行
ls obj_dir/ --full-time > 1.txt
- 修改
VTop.mk
文件,比如给其任意位置,加一个注释,运行sed -i "1 i # test" obj_dir/VTop.mk
或者手动修改该文件 - 再次运行
verilator --cc --exe Top.v main.cpp
- 运行
ls obj_dir/ --full-time > 2.txt
- 使用
colordiff 1.txt 2.txt
进行对比
神奇的事情发生了!,所有的源文件,都被修改了时间戳,也就是说这些源文件都是新生成或者新拷贝的!
2,16c2,16
< -rw-rw-r-- 1 jht jht 19595 2022-04-17 11:52:02.256126245 +0800 VTop___024root.cpp
< -rw-rw-r-- 1 jht jht 1846 2022-04-17 11:52:02.252126217 +0800 VTop___024root.h
< -rw-rw-r-- 1 jht jht 11606 2022-04-17 11:52:02.256126245 +0800 VTop___024root__Slow.cpp
...
---
> -rw-rw-r-- 1 jht jht 19595 2022-04-17 11:52:16.824228142 +0800 VTop___024root.cpp
> -rw-rw-r-- 1 jht jht 1846 2022-04-17 11:52:16.824228142 +0800 VTop___024root.h
> -rw-rw-r-- 1 jht jht 11606 2022-04-17 11:52:16.824228142 +0800 VTop___024root__Slow.cpp
...
这里只展示一部分,事实上,是所有的源文件都更新了。
另外经过测试,给mk文件加个空行也可以达到该效果。
这就意味着什么呢……至少
在生成文件更新的检测机制上,verilator会检测,当前命令生成的mk文件和已有的mk文件是否有不同,如果不同,就重新生成和更新全部源文件。
实验3:仅修改Verilog文件
这里,同样是命令verilator --cc --exe Top.v main.cpp
,我们单独修改Top.v
,再查看前后对比结果。
发现也是全部更新源文件,和上面一样。
实验4:仅修改cpp文件
仅仅修改main.cpp
NOTE:不会更新!
因为cpp文件没有被拷贝进去,而是直接编译的外面的源文件。
实验5:修改obj_dir
中任意一个文件(生成的源文件)
随便修改一个文件,前后对比,发现仍然是更新全部源文件。
结论
经过这么多的测试,你就应该明白了,对于verilator的--cc --exe
阶段,也就是生成可执行文件之前的阶段,无论你是修改命令中给出的源文件(指Verilog源文件,c/cpp不算),还是修改生成之后的任意一个文件,都会导致下一次重新运行该命令的时候,全部重新编译和更新。
注意,修改生成的文件,是指的运行VTop.mk
之前的那些文件,运行make之后生成内些文件不会跟着更新,它们的更新依靠的是make规则。
应用
好了,这个在文档没有找到,估计只能在源码中找了,先不管,反正测试结果就是这样!
我们看一下,这个特性能够让我们有什么应用呢?
- VTop.mk文件,生成VTop的规则,是依赖于
obj_dir
文件夹中源文件的,外部文件它是不管的! - 如果你引入了外部生成的
.o
文件,这个文件不会被放在obj_dir
文件夹中,只是在VTop.mk
中提供外部.o
文件的路径,以供链接使用 - 也就是说,如果你更新了外部的
.o
文件,按照生成规则来说,verilator的不知道这件事的,它会默认你的.o
文件是一直不变的,这就意味着,你需要额外的规则来改变这件事 - 我们的放在工程,会在外部直接编译
.c
文件到.o
文件,但是verilator不知道这件事,我们有两种方法- 强制每次都重新编译Verilog和拷贝cpp源文件,这种方式就用了我们上面提到的那个特点,你只需要编译完成之后,再给
VTop.mk
随便编辑一下,下一次编译一定会全部更新的。但是这样似乎不太好……
2.通过makefile来执行编译指令,并且识别.o
文件是否更新,如果更新,则可以- 强制编译和更新源文件,但是没有必要这么做,毕竟其他文件又没变
- 删除掉生成的
VTop
可执行文件,重新进行链接即可。这种方式是可取的,如果其他的都没变的话,只是外部的.o
文件改变,只需要重新链接就可以
- 强制每次都重新编译Verilog和拷贝cpp源文件,这种方式就用了我们上面提到的那个特点,你只需要编译完成之后,再给
加粗部分,是提倡采取的方案。
下面是提供的复杂工程的示例
simulate
├── csrc # c/cpp 源文件和可重定位目标文件
│ ├── build # 由 .c 文件生成,.o文件提供给verilator
│ │ ├── memory
│ │ │ └── mem.o
│ │ └── monitor
│ │ ├── monitor.o
│ │ └── sdb
│ │ └── sdb.o
│ ├── include # 头文件
│ │ ├── common.h
│ │ ├── main.h
│ │ └── mem.h
│ ├── main.cpp # 提供给verilator的cpp文件
│ ├── Makefile # 生成 build 的规则
│ ├── memory # c 源文件
│ │ └── mem.c
│ ├── monitor
│ │ ├── monitor.c
│ │ └── sdb
│ │ └── sdb.c
│ └── utils
├── Makefile # verilator编译和生成规则
└── obj_dir # verilator生成目录
# 备注:Verilog文件在其他路径,可由verilator来指定位置和文件