1. 整體模擬架構(CPU / Cache / Memory 模組互動流程) 2. 記憶體控制器內部架構(排程器、仲裁器、位址解碼、刷新單元) 3. 對應原始碼模組與檔案位置,方便後續開發與修改。 完成後我會立即通知您。 # Ramulator 2.0 模擬器開發者導覽 ## 整體模擬架構 *Ramulator 2.0 整體軟體架構示意圖:圖中展示了一個 DDR5 記憶體系統(含 RowHammer 緩解機制)的主要介面(深色方框)及其典型實作(淺色方框),以及各元件之間的交互流程*。Ramulator 2.0 採用**前端(CPU/Trace)**與**記憶體系統**分離的架構:前端產生記憶體請求,透過`send(mem_req)`介面送入記憶體系統;記憶體系統接收請求後,先經由\*\*位址對應模組(Address Mapper)**將記憶體位址解析為通道、排(Rank)、銀行(Bank)、列(Row)、欄(Column)等 DRAM 座標。完成位址轉換後,請求會放入**記憶體控制器(Memory Controller)\*\*的請求佇列中等待處理。記憶體控制器負責管理記憶體請求的排程、命令發送以及與 DRAM 裝置的互動。記憶體控制器每個時脈週期會執行以下步驟: 1. **刷新管理(Refresh Manager)**計時:控制器觸發刷新管理單元`tick()`,如到達刷新時間點則由刷新單元產生高優先權的維護請求(如`refresh`命令),並通過控制器的優先佇列插入等待執行。在典型 DDRx 系統中,刷新單元預設實作為**「全銀行刷新」(All-Bank Refresh)**,即發出一條命令刷新該 Rank 所有記憶體銀行。Ramulator 2.0 也支援其他刷新策略(例如逐銀行刷新),開發者可透過更換 RefreshManager 模組實作來模擬不同刷新機制。 2. **請求排程(Request Scheduling)**:控制器調用**排程器(Scheduler)**的`schedule_req()`方法,從請求佇列中選取下一個要服務的請求。Ramulator 2.0 預設的排程策略是 **FR-FCFS(First Ready, First Come First Serve)**,即優先選擇「已具備發出所需DRAM命令條件」且最早抵達的請求。排程器在決定服務哪一請求時,會考慮記憶體**列緩衝區命中**(Row hit)情況,以提高列緩衝重複使用率(FR-FCFS 正是透過優先已開啟列的請求來減少延遲)。如果需要實現其他排程政策(例如引入重新排序緩衝的演算法),可以透過新增不同的 Scheduler 實作類別來擴充(詳見後述)。 3. **DRAM 命令解碼與發行**:排程器選定請求後,會查詢\*\*DRAM 裝置模型(DRAM Device Model)\*\*以解碼對應的 DRAM 記憶體命令。例如,對於一個讀取請求,裝置模型可能回傳需要發出的命令(如`ACT`啟動列或`READ`讀取命令)。排程器取得建議命令後,控制器會檢查該命令是否符合 DRAM 定時約束(透過裝置模型的`check_ready(cmd)`介面檢查命令是否已滿足時序條件)。一旦確認可以發行,控制器即透過`issue_cmd(cmd)`將命令發送到 DRAM 裝置模型。**DRAM 裝置模型**會隨即更新其內部狀態和定時資訊(例如,記錄最近一次`ACT`/`READ`的時間,用於後續時序限制判斷)。Ramulator 2.0 的 DRAM 模型以階層式狀態機實現各種 DRAM 標準,並透過簡潔的人類可讀定義來描述不同命令的時序限制和作用。例如,DDR4/DDR5 等標準的規格參數已內建為預設值,使用者可在設定檔中以`timing: preset: DDR4_2400R`等方式載入。 4. **完成通知**:當某個記憶體請求被服務完畢(例如讀取資料已返回或寫入完成),記憶體控制器會執行該請求所註冊的完成回呼函式(`mem_req.callback()`)。這會通知前端處理器該記憶體操作已完成,從而讓處理器或 trace 模型繼續後續操作。若 Ramulator 2.0 以 **獨立模式** 執行,前端通常是一個簡單處理器模型或記憶體存取 trace parser;如果以 **庫模式** 與系統模擬器(如 gem5)整合,前端則由外部模擬器提供(例如 gem5 的 CPU + 快取階層),Ramulator 2.0 僅接收經快取未命中後送出的記憶體請求並模擬記憶體子系統。 整體而言,Ramulator 2.0 將 CPU 前端與記憶體系統解耦,並明確區分了各組件的介面與實作,使各模組間僅透過介面互動。上述流程中,紅色箭頭表示**記憶體請求路徑**,藍色箭頭表示**DRAM命令路徑**,綠色箭頭則表示**維護請求(如刷新)路徑**。透過模組化設計,Ramulator 2.0 的使用者可以方便地替換其中任一組件的實現,例如更換排程演算法或記憶體裝置類型,而無需修改其他模組的程式碼。 ## 記憶體控制器架構 Ramulator 2.0 的**記憶體控制器**是內含多個子模組的複雜組件,負責協調記憶體請求從佇列到轉換為DRAM命令直至完成回報的全過程。其架構包含以下主要單元: * **請求佇列(Request Queue)**:控制器內部維護一組請求佇列來暫存尚未服務的記憶體請求。典型實作會區分讀取與寫入請求的隊列,並可能設定各自的容量與門檻。例如,在預設 GenericController 中,讀寫請求可能分開緩衝,當寫入佇列累積過多時控制器可優先處理寫入以防止飽和。佇列實作與管理邏輯定義在控制器模組的原始碼中(如 `src/controller/controller.h` 中) 。 * **排程器(Scheduler)**:排程器決定從請求佇列中選出哪一個請求來發送對應的DRAM命令。Ramulator 2.0 將排程策略封裝為獨立介面 **IRequestScheduler**,並提供了多種具體實作。例如,`FRFCFS` 排程器實作位於 `src/scheduler/simple_scheduler.h`(假設檔名,內含 FR-FCFS 演算法);若需要更換為其他排程策略,只需在設定檔中將 Scheduler 的 `impl` 屬性改為相應實作類別名稱。例如,可以定義並註冊一個 `ReorderScheduler` 來模擬具\*\*重新排序緩衝(Reorder Buffer)\*\*的排程策略,實現更積極的請求重新排序能力。排程器透過控制器提供的介面存取請求佇列,並可查詢 DRAM 裝置狀態以判斷命令是否可以發出。 * **仲裁器(Arbiter)**:仲裁器在某些情況下用於決定不同來源或類型請求的服務順序。例如,如果系統包含多個主體(如多核CPU)產生請求,共用同一記憶體控制器,仲裁器負責在不同來源之間公平或依優先級地分配記憶體頻寬。又或者,在控制器內部存在讀寫兩種佇列,仲裁器需要決策何時切換讀寫操作以維持效能和防止飢餓。Ramulator 2.0 中,如果使用簡單前端(單一來源)則不需特別仲裁;但在整合外部系統(如 gem5 多處理器)時,這部分策略通常由外部系統決定或可在 Ramulator 2.0 控制器中擴充實作。 * **位址解碼與映射(Address Mapping)**:位址映射模組在請求進入控制器前就已將物理位址轉譯為 DRAM 座標。然而,控制器需要知道所選請求的列是否已開啟、所在銀行是否閒置等資訊,這些都來自位址解析結果。因此位址映射的策略間接影響控制器行為。Ramulator 2.0 將映射策略封裝為 **IAddrMapper** 介面,典型預設為 `RoBaRaCoCh`(Row -> Bank -> Rank -> Column -> Channel 的位元排列)。如果要模擬不同的映射方式,如「Bank-Row-Col」順序,只需在設定檔中將 `AddrMapper: impl` 設為對應的類別(例如假設存在 `BaRoCoCh` 實作)。開發者也可新增自訂的地址映射類,在原始碼中新增檔案(例如 `src/memory_system/custom_mapper.h`)並繼承定義好的介面即可。 * **記憶體刷新單元(Refresh Manager)**:如前節所述,刷新單元負責按規定的刷新週期產生刷新命令。Ramulator 2.0 中刷新管理被抽象為 **IRefreshManager** 介面,預設實作為 `AllBankRefresh`(對應 JEDEC 標準的全銀行自刷新)。其原始碼定義可在 `src/controller/refresh_manager.h`(或類似路徑)中找到。若需要模擬其他刷新策略(例如 Fine-Grained Refresh 或 Per-Bank Refresh),可新增對應的實作類別,並在設定中將 RefreshManager 的 `impl` 指定為新類別名稱。 * **列開啟/關閉策略(Row Policy)**:DRAM 控制器在每次存取後可以選擇是否關閉(Precharge)開啟的列。Ramulator 2.0 提供 **IRowPolicy** 介面來抽象這一策略,預設實作為 **ClosedRowPolicy**,即每次存取完成後立即關閉該列(適合隨機存取降低行命中延遲)。另一常見策略是 **OpenRowPolicy**(開啟行策略),在預期同一列可能有後續存取時保持 row buffer 開啟,以提升行命中率。Ramulator 2.0 中可以透過更換 RowPolicy 模組來模擬不同策略,相關程式碼位於例如 `src/controller/row_policy.h`。在設定檔中,可將 `RowPolicy: impl` 設為 `OpenRowPolicy` 或其他自訂策略類別。 上述各模組透過控制器內部定義的介面進行協作。例如,排程器在每個週期被控制器呼叫以選擇請求,並可能透過裝置模型查詢當前命令可否發出;若選擇的是刷新請求則優先處理等。這種模組化設計讓記憶體控制器的功能更易於擴充和維護。開發者可以針對不同研究需求替換控制器的各部分組件,例如替換不同的 Scheduler、RefreshManager 或 RowPolicy,而不需要改動其他部分的代碼。 ## 多種控制器與位址映射的配置與擴充 Ramulator 2.0 通過\*\*組態檔(YAML)\*\*來配置記憶體系統的各項參數與模組實現。使用者可以在組態中指定不同的控制器策略和位址映射方案,而無需修改程式碼。例如,以下是一段(假想的)Ramulator 2.0 設定片段,展示如何配置控制器的不同組件: ```yaml MemorySystem: impl: GenericDRAM # 記憶體系統實作類型 DRAM: impl: DDR4 # DRAM 規格(DDR4) org: preset: DDR4_8Gb_x8 # DRAM 組織預設參數 timing: preset: DDR4_2400R # DRAM 時序參數 AddrMapper: impl: RoBaRaCoCh # 位址映射:Row->Bank->Rank->Col->Channel Controller: impl: GenericController # 記憶體控制器實作類型 Scheduler: impl: FRFCFS # 請求排程策略:FR-FCFS cap: 4 # (例如)FR-FCFS 重新排序上限參數 RefreshManager: impl: AllBank # 刷新策略:全銀行刷新 RowPolicy: impl: ClosedRowPolicy # 列緩衝策略:每次存取後關閉 # ...(如有需要可在此處列出其他插件或參數)... ``` 上例展示了**FR-FCFS**與**Closed-row**策略的基線控制器配置。如果想切換為具\*\*重新排序緩衝(Reorder Buffer)\*\*的控制器實作,可採取以下兩種方式: * **更換 Scheduler 策略**:如果重新排序僅影響排程演算法本身(例如允許更大範圍的 out-of-order 執行),可以透過提供新的 Scheduler 類別來實現。例如,可實作一個名為 `OutOfOrderScheduler` 的類別(繼承自 Scheduler 介面),在其中維護一個類似處理器 reorder buffer 的結構來允許請求更自由地重新排序。實作完成並註冊後,在設定檔中將 `Scheduler: impl` 改為 `OutOfOrderScheduler` 即可。控制器其餘部分(如控制流程、刷新機制)可繼續使用原有 GenericController 不變,但因 Scheduler 不同,行為上相當於一種新的控制器策略。 * **更換整個 Controller 實作**:若重新排序緩衝的引入涉及不只排程器,而是需要改變控制器內部對請求的管理方式(例如增加額外佇列或複雜的仲裁邏輯),則可以透過定義新的 **IController** 實作類別達成。例如創建 `ReorderBufferController` 類別(可參考 `src/controller/controller.h` 介面繼承實作),在其中自訂請求佇列結構和排程決策。同樣地,新類別定義好並在系統中註冊後,只需在 YAML 組態中將 Controller 的 `impl` 設為 `ReorderBufferController`。Ramulator 2.0 的工廠機制會自動載入此控制器類別,替換原本的 GenericController。 位址映射方面,Ramulator 2.0 預設提供如上所示的 **RoBaRaCoCh**(列→銀行→排→欄→通道)映射方式。若研究者想評估不同位址位元對應方案,例如 **Bank-Row-Col**(銀行位元在較高位,列位元次之)或更複雜的 XOR 映射,步驟與上述類似: 1. 在原始碼中新增一個繼承自 IAddrMapper 介面的實作類別,例如 `BankRowColMapper`,實作其位址轉換函式(將輸入地址位元排列為 BANK-ROW-COL 順序)。這個檔案可位於 `src/memory_system/bank_row_col_mapper.h`,並在實作中透過巨集或工廠註冊機制註冊類名「BankRowColMapper」。 2. 編譯更新後的 Ramulator 2.0,然後在 YAML 組態中將 `AddrMapper: impl` 改為 `BankRowColMapper`。重跑模擬即可使用新的位址映射。Ramulator 2.0 會自動辨識新介面實作並據此產生對應的記憶體系統配置。 值得注意的是,Ramulator 2.0 採用了**自我註冊的工廠模式**來管理這些介面與實作類別。也就是說,每當我們在程式中新增一個繼承介面的實作類,透過特定登錄機制(通常是在類的 cpp 檔案中呼叫工廠註冊函式或使用靜態初始化)註冊後,程式會將該類別與對應的介面關聯存入登錄表。之後,只要在組態檔的相應位置填入實作類的名稱,Ramulator 2.0 在啟動時就能自動地從工廠登錄表建立該實作物件並取代預設實作。這種做法免去了手動修改程式碼串接新模組的繁瑣步驟,極大地方便了模擬器的擴充。 總而言之,**FR-FCFS** 與 **Reorder Buffer** 代表了兩種不同的控制器請求排程理念:前者屬於**行緩衝優先**的漁翁式排程策略,而後者則允許更大範圍的請求重新排序以優化吞吐。Ramulator 2.0 透過高度模組化的架構,允許開發者以最小的代價在模擬中切換或實現這些策略。「**ROW-BANK-COL**」與「**BANK-ROW-COL**」則是地址位元分配順序的差異,會影響記憶體系統的空間區域性和並行度表現。使用 Ramulator 2.0,研究者可以輕鬆地在設定中調整這些參數並觀察其影響,而如果內建選項不敷所需,也能依上述步驟客製化新的控制器行為或位址映射方案。 ## 主要邏輯模組與原始碼檔案對應 為方便開發者深入 Ramulator 2.0 的實作細節,下表列出本導覽中提及的主要邏輯模組及其對應的原始碼檔案位置: * **Memory Controller(記憶體控制器)**:核心介面與基礎實作定義於 **`src/controller/controller.h`**。GenericController 及其衍生類(如 ReorderBufferController)也位於同一路徑下的 `.cpp` 實作檔案中。這些檔案包含控制器對請求佇列管理、與前端/裝置模型互動等邏輯。 * **Request Scheduler(請求排程器)**:位於 **`src/scheduler/`** 資料夾下,各種排程策略各有獨立檔案。例如,FR-FCFS 策略的實作在 *`simple_scheduler.h`*(名稱依 Ramulator 原始碼而定)中定義;如果有實作其他排程(如 `atlas_scheduler.h` 針對 ATLAS 演算法等),也會在此目錄下。對應的 `.cpp` 檔實現了 `schedule()` 等核心函式。 * **Address Mapper(位址映射模組)**:定義在 **`src/memory_system/address_mapper.h`** 以及對應的實作檔案中。此處包含 IAddrMapper 介面,以及 `RoBaRaCoCh` 等內建映射類別的定義。如果新增自訂映射類別(如 BankRowColMapper),則會新增檔案於此目錄下。 * **Refresh Manager(刷新管理單元)**:相關介面與實作位於 **`src/controller/refresh_manager.h`**(或可能的 `src/memory_system/refresh.h`)。AllBankRefresh 等策略的邏輯在對應 `.cpp` 實作中。Ramulator 1.0 將刷新策略實作在 Refresh.h 中;在 Ramulator 2.0,則經由介面類別細分,可在上述路徑中找到各策略類別。 * **Row Policy(列緩衝策略)**:其介面與實作可在 **`src/controller/row_policy.h`** 中查閱,包含 ClosedRowPolicy、OpenRowPolicy 等類別。這些類別的函式實作則通常在同名 `.cpp` 中實現。 * **Memory Device Models(記憶體裝置模型)**:各 DRAM 標準的模型定義散見於 **`src/dram/`** 或 **`src/memory_system/`** 目錄下。比如 DDR4、DDR5、HBM 等標準可能各有對應檔案(如 `ddr4.h/.cpp`)。這些檔案運用 Ramulator 2.0 提供的描述語法定義了 DRAM 的組織結構(banks, rows 等)以及時序參數和命令集合。 * **FrontEnd(前端處理器與核心)**:Ramulator 2.0 內建的簡易亂序核心和 trace 前端實作位於 **`src/frontend/`** 目錄內,例如 `trace_parser.h`、`ooocore.h` 等檔案。這部分模組產生並發出記憶體請求,透過 IFrontEnd 介面與記憶體系統溝通。在大多數記憶體系統研究中,此部分可視需要替換為更複雜的處理器模型或由外部模擬器提供。 以上檔案路徑及名稱對應於 Ramulator 2.0 專案的原始碼結構。在實際開發時,建議先閱讀 `README.md` 及相關說明文件以了解專案目錄佈局,再根據上述指引定位具體的檔案。Ramulator 2.0 的模組化設計使得各部分程式碼職責單一且集中。例如,想修改記憶體排程策略,只需專注於 `src/scheduler/` 下相關檔案而無須瀏覽整個專案。透過閱讀這些原始碼並結合本文前述的架構講解,開發者應能迅速上手在 Ramulator 2.0 上進行二次開發與實驗。各模組的實作細節和介面函式說明也可在程式碼註解和文獻中找到,鼓勵讀者深入參考以充分瞭解 Ramulator 2.0 的強大擴充能力與正確使用方法。 **參考來源:** 本文內容參考並整理自 Ramulator 2.0 論文以及 Ramulator 2.0 官方文件與原始碼。上述架構圖與技術細節摘錄自 Onur Mutlu 團隊發表的 Ramulator 2.0 論文及 GitHub 專案說明,以確保內容的正確性與權威性。希望本繁體中文開發者導覽能幫助您快速瞭解 Ramulator 2.0 模擬器的架構設計與模組實作,順利展開相關研究與開發工作!