本文請讀者【直接關閉】,我后面的實踐結果似乎和本文的實踐結果不一樣,真是見鬼了!我不知道發生了什么,還沒有來得及進一步校驗!
在其他文件不變的前提下,如果即將生成的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來指定位置和文件