Verilator的Testbench(測試平臺)主要使用 C++ 或 SystemC 來編寫。這是由Verilator的工作原理決定的:它將你的Verilog/SystemVerilog設計轉換成一個C++類,因此你需要一個C++環境來實例化和驅動這個類。
下面詳細說明這兩種方式以及如何選擇。
1. 使用 C++ 編寫 Testbench (最常見)
這是最直接、最靈活的方式。你的C++代碼將扮演“主宰者”(Master)的角色,負責生成時鐘、復位信號,驅動設計的輸入端口,并檢查輸出端口的正確性。
為什么用 C++?
- 高性能:直接編譯成本機代碼,運行速度快。
- 極致靈活:你可以利用整個C++生態系統,包括標準庫(STL)、文件I/O、網絡套接字、Boost庫等,來創建極其復雜的測試激勵(Stimulus)和檢查器(Checker)。例如,你可以:
- 從文件中讀取測試向量。
- 將設計輸出與一個軟件參考模型(Reference Model)的輸出進行實時比對。
- 構建復雜的、算法生成的測試場景。
- 易于上手:對于有軟件編程背景的工程師來說,學習成本相對較低。
一個簡單的C++ Testbench示例
假設你有一個名為 my_design.v
的Verilog文件,其頂層模塊是 my_design
。
Verilog代碼 (my_design.v
):
module my_design (input clk,input reset,input [7:0] data_in,output logic [7:0] data_out
);always_ff @(posedge clk) beginif (reset) begindata_out <= 8'b0;end else begindata_out <= data_in + 8'd1; // 簡單的加1操作endendendmodule
C++ Testbench (tb_my_design.cpp
):
#include "Vmy_design.h" // 包含Verilator生成的頭文件
#include "verilated.h" // 包含Verilator的核心頭文件
#include "verilated_vcd_c.h" // 包含波形生成頭文件int main(int argc, char** argv) {// 初始化VerilatorVerilated::commandArgs(argc, argv);// 實例化設計Vmy_design* top = new Vmy_design;// 初始化波形追蹤VerilatedVcdC* tfp = new VerilatedVcdC;top->trace(tfp, 99); // 99是追蹤深度tfp->open("waveform.vcd"); // 打開VCD文件// 仿真主循環int sim_time = 0;while (sim_time < 20) {// 驅動時鐘翻轉top->clk = !top->clk;// 在時鐘上升沿進行操作if (top->clk == 1) {if (sim_time < 4) {top->reset = 1; // 施加復位} else {top->reset = 0;top->data_in = sim_time; // 施加測試激勵// 檢查輸出 (預期的輸出是上一個周期的輸入+1)printf("Time=%d, clk=%d, reset=%d, data_in=%d, data_out=%d\n",sim_time, top->clk, top->reset, top->data_in, top->data_out);// 注意:檢查應該基于上一個周期的輸入}}// 評估電路狀態top->eval();// 將當前狀態寫入波形文件tfp->dump(sim_time);sim_time++;}// 清理tfp->close();delete top;delete tfp;return 0;
}
2. 使用 SystemC 編寫 Testbench
SystemC 是一個基于C++的庫和標準(IEEE 1666),專門用于系統級的建模、仿真和驗證。它提供了硬件設計中常見的概念,如模塊(module)、端口(port)、時鐘(clock)和信號(signal),使得測試平臺的結構更接近于HDL(如Verilog/VHDL)。
為什么用 SystemC?
- 結構化:代碼結構更像硬件描述語言,對習慣了HDL的工程師更友好。
- 抽象層次高:非常適合進行事務級建模(Transaction-Level Modeling, TLM),用于SoC(片上系統)級別的驗證。
- 互操作性:作為一個行業標準,便于與其它遵循SystemC/TLM標準的模型進行集成。
一個簡單的SystemC Testbench示例
使用與上面相同的 my_design.v
。
SystemC Testbench (tb_my_design_sc.cpp
):
#include "Vmy_design.h"
#include <systemc.h>SC_MODULE(Testbench) {sc_in<bool> clk; // SystemC時鐘輸入// Testbench內部的信號sc_signal<bool> reset;sc_signal<uint8_t> data_in;sc_signal<uint8_t> data_out;// 實例化Verilated模塊Vmy_design* dut;// 測試激勵進程void stimulus_process() {// 復位reset = 1;wait(4, SC_NS);reset = 0;wait(1, SC_NS);// 施加激勵for (int i = 0; i < 10; ++i) {data_in = i;printf("@%s, Applied data_in = %d\n", sc_time_stamp().to_string().c_str(), i);wait(1, SC_NS);printf("@%s, Observed data_out = %d\n", sc_time_stamp().to_string().c_str(), (int)data_out.read());}sc_stop(); // 停止仿真}SC_CTOR(Testbench) {// 創建DUT實例dut = new Vmy_design("dut");// 端口綁定dut->clk(clk);dut->reset(reset);dut->data_in(data_in);dut->data_out(data_out);// 注冊進程SC_THREAD(stimulus_process);sensitive << clk.pos(); // 進程對時鐘上升沿敏感}// 析構函數,釋放內存~Testbench() {delete dut;}
};int sc_main(int argc, char* argv[]) {// 時鐘定義sc_clock clk("clk", 1, SC_NS, 0.5); // 周期1ns, 占空比50%// 實例化TestbenchTestbench tb("tb");tb.clk(clk); // 將時鐘連接到Testbench// 開始仿真sc_start();return 0;
}
總結與建議
特性 | 純C++ Testbench | SystemC Testbench |
---|---|---|
控制粒度 | 高。直接手動控制每個信號和時鐘周期。 | 中。通過SystemC調度器和事件驅動。 |
代碼結構 | 過程式(像一個普通的C++程序)。 | 結構化(模塊、端口、進程),更像HDL。 |
學習曲線 | 較低(對于C++程序員)。 | 較高(需要學習SystemC庫和概念)。 |
適用場景 | 單元/模塊級驗證,需要高速和靈活性的場景,與軟件模型集成。 | SoC系統級驗證,事務級建模(TLM),需要與其它SystemC模型互聯的場景。 |
給你的建議:
-
如果你是初學者,或者正在進行模塊級別的驗證:
從純C++開始。它最直接,能讓你更好地理解Verilator的底層工作方式,并且足以滿足絕大多數驗證需求。 -
如果你正在構建一個復雜的SoC,或者需要與現有的TLM模型集成:
考慮使用SystemC。它能提供更規范、更結構化的驗證環境,特別是在多團隊協作的大型項目中,其標準化特性非常有優勢。