系列文章 : [gem5] 從零開始的 gem5 學習筆記
這裡我們可以開始嘗試 gem5 的核心之一 : 模擬時間。
當 gem5 想要模擬時間的時候,就是把某個事件安排 ( schedule ) 到某個時間點,當時間到了之後,就執行相對應的程式碼。
這篇文章絕大部份來自 learning gem5 / Event-driven programming.
可以依照下面的指令,去將 gem5 編譯,並運行起範例。
git clone -b example-object https://github.com/TommyWu-fdgkhdkgh/gem5.git
cd gem5
scons build/RISCV/gem5.debug -j$(nproc)
# 開始進行模擬
./build/RISCV/gem5.debug --debug-flags=TimeExampleObject ./configs/learning_gem5/tommy/time_example.py --latency 10ns --times-left 6
最後該會看到下面的 log。
0: example: Hello World! From a SimObject!
Beginning simulation!
100: example: Processing the event! 5 left
10100: example: Processing the event! 4 left
20100: example: Processing the event! 3 left
30100: example: Processing the event! 2 left
40100: example: Processing the event! 1 left
50100: example: Processing the event! 0 left
50100: example: Last event !
以這個 log 來說,最左邊的 40100 代表的就是這個事件發生的時間點。
40100 的單位是 Tick,也就是在第 40100 個 Tick 的時候,觸發了這個 log 。
40100: example: Processing the event! 1 left
Tick 是 gem5 自己定義的時間單位,那一個 Tick 代表了多長的時間呢 ?
一個 Tick 到底代表了多少秒呢 ?
一個 Tick 的長度其實是可變的!
就如同開啟 gem5 時會顯示的 log 一樣
Global frequency set at 1000000000000 ticks per second
Tick 預設的長度是 1/1000000000000 秒,也就是 1 Picosecond ( 皮秒 )。
希望改變 Tick 長度的話,可以在 python configuration script 裡面加上
m5.ticks.setGlobalFrequency("1ns")
這樣就可以將 1 個 Tick 的長度從 1/1000000000000 ( 1 Picosecond ) 秒,改成 1/1000000000 ( 1 nanosecond )
在 gem5 裡面,每一個 event 都會有一個 callback function,當 event 被觸發的時候,就會呼叫這一個 callback function。
在我們的範例裡面 ( TimeExampleObject ),我們需要宣告 ( declare ) 一個新的 function ( processEvent ),每當 Event 被 trigger 的時候,就需要被執行。
EventFunctionWrapper 則是 event 本身,他會記錄 callback function ( processEvent ) 的資訊,以便知道自己被觸發的時候,要呼叫誰。
{ gem5/src/learning_gem5/tommy/time_example_object.hh }
class TimeExampleObject : public SimObject
{
private:
void processEvent();
EventFunctionWrapper event;
const Tick latency;
int timesLeft;
public:
TimeExampleObject(const TimeExampleObjectParams &p);
void startup() override;
};
以下是 source file 的部分。
{ gem5/src/learning_gem5/tommy/time_example_object.cc }
TimeExampleObject::TimeExampleObject(const TimeExampleObjectParams ¶ms)
: SimObject(params),
event([this] { processEvent(); }, name()),
latency(params.latency),
timesLeft(params.times_lelft)
{ DPRINTF(TimeExampleObject, "Hello World! From a SimObject!\n"); }
在這裡,我們會初始化這個 event。
第一個參數是該 event 被觸發的時候,要呼叫什麼 function。
第二個參數是帶有這個 event 的 SimObject 的名稱。
event([this] { processEvent(); }, name()),
startup 去 schedule 第一個 Eventstartup function 是一個特別的 function,他是繼承自 class SimObject。startup function 會在即將要開始進行模擬的時候呼叫。
{ gem5/src/python/m5/simulate.py }
def simulate(*args, **kwargs):
global need_startup
global _instantiated
if not _instantiated:
fatal("m5.instantiate() must be called before m5.simulate().")
if need_startup:
root = Root.getInstance()
for obj in root.descendants():
obj.startup()
need_startup = False
在這裡,我們嘗試在 startup 去 schedule 第一個 Event
schedule 的第一個參數是我們想要 schedule 的 event。
第二個參數是設定這個 event 要在多久後被 trigger,單位是 Tick。
因為我沒有做任何其他的設定,所以每一個 Tick 是 1 picosecond,於是這個 event 會在 100 picosecond 後被觸發。
void
TimeExampleObject::startup()
{ schedule(event, 100); }
Event 裡面去 schedule 其他 Event當 Event 被觸發後,會去執行 processEvent 這個我們一開始就設定好的 callback function。
這邊可以發現,在 event 的 callback function 裡面,可以再去 schedule 其他的 event。在這裡,我們每個 Event 會間隔 latency,總共 schedule timesLeft 個 events
void
TimeExampleObject::processEvent()
{
timesLeft--;
DPRINTF(TimeExampleObject, "Processing the event! %d left\n", timesLeft);
if (timesLeft <= 0) {
DPRINTF(TimeExampleObject, "Last event !\n");
} else {
schedule(event, curTick() + latency);
}
}
相關的檔案
其實模擬時間的原理很簡單,在 gem5 內的實作是一個 events 串在一起的 linked list,並且會以時間做排序,時間越小的會排在越前面。另外也會用 priority 做排序,但這邊先介紹簡化版的。
所以這個 linked-list 的 head 節點永遠都會有最小的時間。
在模擬的時候,會做以下的操作
這種把對於連續時間的模擬,變成像這樣離散的一個個事件,就叫做 discrete event simulation ( DES )。