Gem5 是針對電腦系統架構所設計的模擬器,目前主要用於系統層面相關的研究,可以模擬出執行所需的 cycle 數、記憶體 access 數、cache miss 等。其中 Gem5 將各組件模擬模組化,並透過 C++ 後端和 python 前端設置的方式來實作,除了可以快速地置換和配置想要的組件之外,也方便研究人員自行新增想要的新設計。這篇文章主要介紹 Gem5 模擬系統運行的機制和相關 configuration 初始化原理。
The gem5 simulator is a modular platform for computer-system architecture research, encompassing system-level architecture as well as processor microarchitecture.
Simulate Mechanism
Gem5 是一個 event-driven 模擬器,所以最主要是透過一個全域 (global) 的 mainEventQueue 和 main loop 來實現系統模擬,這也意味著所有的行為 (callback function) 都必須依照正確的順序加入到 event queue 中,最後的模擬結果才會如預期。
1/* file: src/sim/eventq.cc
2 global main queue. */
3uint32_t numMainEventQueues = 0;
4
5/* In most cases, we only use mainEventQueue[0] for system simulation */
6std::vector<EventQueue *> mainEventQueue;
7__thread EventQueue *_curEventQueue = NULL;
8bool inParallelMode = false;
這邊以 CPU 的 fetch instruction 為例子, 一開始 gem5 初始化系統配置時,CPU 的 fetch event 會被加入到 event queue 中,因此在系統執行模擬時,會先執行 CPU 的 fetch event;而 CPU 會從 Icache 中取出當前要執行的 instruction,因此在執行 CPU fetch event handler 時,會在 event queue 中加入 Icache event,這樣 Icache event 就能夠在下一個流程執行。同理,如果此時 cache miss,則會需要從 memory 中讀取 instruction,因此 Icache event handler 會再把 memory event 加到 event queue 中,然後 memory event 會接續處理相關要求。
上面機制可以處理電腦運作時的同步行為,不過我們常常會有一些非同步的事件,像是 I/O 和 interrupt 等,而 gem5 在每次 loop 中去偵測是否有非同步事件發生,如果有,則會從 poll queue 中取出這些非同步事件並處理,這個設計方式很像 javascript engine 的 event loop 和 callback queue 的概念。
1Event *
2doSimLoop(EventQueue *eventq)
3{
4 // more code..
5 if (async_event && testAndClearAsyncEvent()) {
6 // Take the event queue lock in case any of the service
7 // routines want to schedule new events.
8 std::lock_guard<EventQueue> lock(*eventq);
9
10 if (async_io) {
11 async_io = false;
12 pollQueue.service();
13 }
14
15 if (async_exit) {
16 async_exit = false;
17 exitSimLoop("user interrupt received");
18 }
19 }
20}
Event Queue Implementation
為了確保 event 能依據先後時間點和優先權順序來執行,Event queue 本身是以雙層 linked list的資料結構實作。其中第一層的 linked list 會根據各 event 的 timestamp + priority 數值排序,而當遇到多個 event 的 timestamp + priority 數值為相同時,則會使用第二層的 linked list 將 event 堆疊起來。這邊要注意的是,第一層的 event linked list 會從 head event 開始依序取出,但是第二層的 event linked list 則是以 last in first out 的順序取出。
System Configuration & Initialization
簡單介紹完 gem5 模擬運作機制後,我們從更細節來看 gem5 的機制實現方式,首先來看 gem5 如何根據 configuration 來建立起我們所需模擬的系統架構。
Configuration
Gem5 的系統設置是透過 python script 結合 C++ class 的方式來實作。一個簡單的範例如下:
1# configs/learning_gem5/part1/simple.py
2
3system = System()
4
5# Create a simple CPU
6system.cpu = TimingSimpleCPU()
7
8# Create a memory bus, a system crossbar, in this case
9system.membus = SystemXBar()
10
11# Hook the CPU ports up to the membus
12system.cpu.icache_port = system.membus.cpu_side_ports
13system.cpu.dcache_port = system.membus.cpu_side_ports
14
15# Create a process for a simple "Hello World" application
16process = Process()
17# Set the command
18# cmd is a list which begins with the executable (like argv)
19process.cmd = [binary]
20# Set the cpu to use the process as its workload and create thread contexts
21system.cpu.workload = process
22system.cpu.createThreads()
23
24# set up the root SimObject and start the simulation
25root = Root(full_system = False, system = system)
26# instantiate all of the objects we've created above
27m5.instantiate()
28
29exit_event = m5.simulate()
上述 configuration 所產生的架構為:
其中 configuration 中的 root、system、membus、cpu,以及 mem_ctrl 都是 SimObject。SimObject 是 gem5 架構中最基本的 simulation objects,任何跟硬體相關的組件都會是由 SimObject class 擴充而成。SimObject 提供了相關 lifecycle (init、startup) 與 control 的 API,每個硬體元件 class 都會實作這些 API 來模擬硬體初始化和設定的行為,而 gem5 執行時就是透過呼叫這些 API 來建立硬體並組織出系統架構。
SimObject Initialization APIs
- SimObject::init();
- SimObject::regStats(); // regStats is typically used for complex stats. - SimObject::initState(); - SimObject::loadState();
- SimObject::resetStats();
- SimObject::startup();
- Drainable::drainResume();
EventManager
此外,SimObject 結合 EventManager
的 API,讓研究人員在開發 gem5 的硬體組件時,可以透過呼叫 schedule、deschedule 等 API 將特定 event callback 加入到 event queue 中。
1/* EventManager implements APIs for event queue manipulation */
2class SimObject : public EventManager, public Serializable, public Drainable,
3 public statistics::Group, public Named
4{
5}
舉例來說,AtomicSimpleCPU class 是從 SimObject 擴充而成,因此我們可以在想要的流程中呼叫 schedule 加入合適的 event。同時,這些 event queue 相關 API 也能成為我們觀察硬體執行順序的重要對象。
1void
2AtomicSimpleCPU::drainResume()
3{
4 if (!tickEvent.scheduled()) {
5 schedule(tickEvent, nextCycle());
6 }
7}
Recap
- Gem5 系統模擬流程是由一個 main event queue 來實現,而我們在實作 gem5 所需的硬體組件時,需要合適地方將 event 加入到 event queue 中,以確保模擬順序如預期。
- SimObject 是最基本的硬體組件 class,gem5 在將 configuration 中的系統架構初始化時,會呼叫 SimObject 相關 APIs 來實現各硬體組件的初始化過程。因此研究人員在客製化硬體組件時,需要實作這些 API 來達成目的。