SystemC Hello World:從 sc_main 到模擬時間
- SystemC
- 模擬建模
SystemC 常被說成「一種硬體描述語言」,但這個說法其實不太對,SystemC 的用法不是這樣。
真正的硬體描述語言(HDL) 指的是 Verilog 、 VHDL 這種可以被合成工具(Synopsys Design Compiler、Xilinx Vivado…)轉成 gate level netlist 的語言。
SystemC 要做的事情不是合成硬體電路,他真正的戰場是在 RTL 做完前先做出簡化的版本來做架構探索(architecture exploration)、效能估算(performance estimation)。
它的規格是 IEEE 1666。實作上最常見的是 Accellera 的參考實作。
第一個版本:只是 C++
SystemC 的進入點不是 main,而是 sc_main:
#include <systemc.h>
int sc_main(int argc, char* argv[]) { std::cout << "Hello, SystemC" << std::endl; return 0;}函式庫內部有自己的 main,它負責初始化模擬核心,然後呼叫你的 sc_main。所以連結時不要自己再寫一個 main。
這段能編譯、能跑、會印出字串 —— 但它跟 SystemC 一點關係都沒有。把 #include 拿掉、sc_main 改回 main,行為完全相同。模擬核心從頭到尾沒有啟動過。
真正的 Hello World
要讓它成為 SystemC 程式,需要三樣東西:一個模組(module)、一個行程(process),以及啟動核心。
#include <systemc.h>
SC_MODULE(Hello) { SC_CTOR(Hello) { SC_THREAD(say_hello); }
void say_hello() { std::cout << sc_time_stamp() << " : Hello, SystemC" << std::endl; wait(10, SC_NS); std::cout << sc_time_stamp() << " : 10 ns later" << std::endl; }};
int sc_main(int argc, char* argv[]) { Hello hello("hello"); sc_start(); return 0;}輸出:
0 s : Hello, SystemC10 ns : 10 ns later那個 10 ns 是整段程式的重點。
這裡發生了什麼
SC_MODULE(Hello) 展開後是 struct Hello : sc_module。SC_CTOR 是建構子的巨集,它接的字串引數 "hello" 成為這個實例在模擬階層中的名字 —— 之後錯誤訊息與波形檔都靠這個名字定位。
SC_THREAD(say_hello) 向核心註冊一個行程。SystemC 有兩種常用行程:
| 巨集 | 特性 | 適用 |
|---|---|---|
SC_METHOD |
被觸發時整個跑完,不能 wait() |
組合邏輯、快速反應 |
SC_THREAD |
有自己的執行堆疊,可以 wait() 後續跑 |
有時序行為的流程、testbench |
sc_start() 才是把核心跑起來的那一行。沒有它,行程只是註冊了,永遠不會被排程。
wait(10, SC_NS) 是分界線。它不是 sleep() —— 不會讓程式暫停十億分之一秒,實際上是立刻執行完的。它做的是把行程掛起、告訴核心「十奈秒的模擬時間之後再叫醒我」。核心把時間直接推進到下一個有事件的時間點。
模擬時間與真實時間之間沒有任何關係。模擬一小時的晶片行為可能花三秒,也可能花三天。
編譯
Ubuntu / Debian 直接裝套件最省事:
sudo apt install libsystemc-devg++ -std=c++17 hello.cpp -o hello -lsystemc./hello從 Accellera 原始碼自行安裝的話,要自己指路徑:
g++ -std=c++17 hello.cpp -o hello \ -I"$SYSTEMC_HOME/include" \ -L"$SYSTEMC_HOME/lib-linux64" \ -lsystemc \ -Wl,-rpath,"$SYSTEMC_HOME/lib-linux64"最後那行 -Wl,-rpath 把函式庫路徑寫進執行檔,省掉每次都要設 LD_LIBRARY_PATH。lib 目錄的名字會因版本與建置方式而異,CMake 安裝的通常就是 lib。
最容易卡住的一個錯誤
編譯過了、連結卻失敗,訊息裡出現一個看起來很奇怪、名字裡帶著版本號和 cxx201703L 之類字樣的 undefined reference —— 這幾乎一定是 -std 與函式庫建置時用的不一致。
SystemC 刻意把 C++ 標準版本編進一個 API 版本符號裡,就是為了在連結期擋下這種組合。ABI 不相容如果放到執行期才爆,會變成極難追查的記憶體問題;擋在連結期是刻意的設計。
修法是讓兩邊對齊:查出函式庫是用哪個標準建的,然後用同一個 -std 編譯你的程式。
順帶一提
#include <systemc.h> 會 using namespace sc_core 與 sc_dt,寫起來短,但在大一點的專案裡容易撞名。比較乾淨的做法是:
#include <systemc> // 不污染命名空間,要寫 sc_core::#include <systemc.h> // 方便,但 using namespace教學文件多半用 .h 版本。自己的專案建議一開始就用不帶 .h 的版本,之後不用回頭改。