乐于分享
好东西不私藏

开源软件之DRAMPower

开源软件之DRAMPower

DRAMPower 6.0.0 深度技术分析文档

项目地址: https://github.com/tukl-msd/DRAMPower
版本: v6.0.0
分析日期: 2026-05-17


目录

  1. 项目概述
  2. 总体架构设计
  3. 完整数据流追踪(DDR5 代表)
  4. 模块一:命令系统与数据结构
  5. 模块二:Bank/Rank 状态机 —— Core 层
  6. 模块三:接口总线追踪 —— Interface 层
  7. 模块四:Power计算层 —— Calculation
  8. 模块五:内存规格 —— MemSpec
  9. 模块六:扩展系统
  10. 模块七:工具组件
  11. 关键设计模式总结

配套图表: 分层架构图 | 完整数据流图 | 状态机横向对比


1. 项目概述

1.1 核心功能

DRAMPower 是一个开源的 DRAM 功耗与估算库,由 TU Kaiserslautern、TU Delft 和 TU Eindhoven 的电子系统/计算机工程研究组联合开发。其核心功能:

  1. 根据 DRAM 命令迹(Command Trace) 和 内存规格(MemSpec),精确计算 DRAM 操作的能量消耗
  2. 支持 Core Power(bank 操作:ACT/PRE/RD/WR/REF/PowerDown/SelfRefresh)和 Interface Power(I/O 总线翻转:CA/DQ/DQS/DBI/Clock)
  3. 覆盖四种 DRAM 标准:DDR4、DDR5、LPDDR4、LPDDR5
  4. 支持 process variation 感知的功耗估算(基于 Monte-Carlo SPICE 分析)
  5. 提供 CLI 命令行工具,支持 JSON 输入输出

1.2 技术栈

维度
技术选型
语言
C++17(模板、constexpr、std::optional、fold expressions)
构建系统
CMake 3.x + FetchContent 依赖管理
核心依赖
DRAMUtils(自研工具库,提供 memspec 解析、JSON 工具)
CLI 依赖
spdlog(日志)、CLI11(命令行解析)、nlohmann_json(JSON)
测试框架
CTest(CMake 内置)
代码规范
clang-format、clang-tidy、cppcheck

1.3 项目目录结构

DRAMPower/
├── cmake/                 # CMake 辅助脚本
├── lib/                   # 第三方依赖(构建时 FetchContent 拉取)
├── src/
│   ├── DRAMPower/         # 核心库
│   │   └── DRAMPower/
│   │       ├── command/   # 命令定义(CmdType, Command, Pattern)
│   │       ├── data/      # 数据结构(energy, stats)
│   │       ├── dram/      # Bank/Rank 状态机基类
│   │       ├── memspec/   # 各标准内存规格定义
│   │       ├── standards/ # 四种标准的具体实现
│   │       │   ├── ddr4/  # DDR4: Core + Interface + Calculation
│   │       │   ├── ddr5/  # DDR5: Core + Interface + Calculation
│   │       │   ├── lpddr4/# LPDDR4: Core + Interface + Calculation
│   │       │   └── lpddr5/# LPDDR5: Core + Interface + Calculation
│   │       ├── util/      # 工具组件(Bus, Clock, DBI, Extension 等)
│   │       └── simconfig/ # 仿真配置
│   └── cli/               # CLI 命令行工具
├── tests/                 # 测试用例(每个标准 16-22 个 test pattern)
└── docs/                  # 文档

2. 总体架构设计

2.1 分层架构

┌──────────────────────────────────────────────────────┐
│                 CLI 层 (cli/main, cli/lib)            │
│  解析 memspec JSON / trace CSV → 调用 DRAMPower API   │
├──────────────────────────────────────────────────────┤
│              标准实现层 (standards/)                    │
│  ┌──────────┐  ┌──────────┐                          │
│  │  Core     │  │Interface │  每种标准独立实现           │
│  │ (状态机)   │  │(总线追踪) │                          │
│  └──────────┘  └──────────┘                          │
├──────────────────────────────────────────────────────┤
│            抽象基类层 (dram_base)                      │
│  定义 doCommand / calcCoreEnergy / getStats 公共接口   │
├──────────────────────────────────────────────────────┤
│         数据层 (command/, data/)                       │
│  Command, SimulationStats, energy_t 等共享数据结构      │
├──────────────────────────────────────────────────────┤
│            计算层 (core_calculation_*,                 │
│                    interface_calculation_*)            │
│  物理公式:Energy = V × I × t                          │
├──────────────────────────────────────────────────────┤
│            规格层 (memspec/)                           │
│  从 datasheet 提取的 IDD 电流/时序/电压/阻抗参数        │
├──────────────────────────────────────────────────────┤
│            工具层 (util/)                              │
│  Bus, Clock, DBI, PatternHandler, ExtensionManager    │
│  burst_storage, interval_counter, ImplicitCommandHandler│
└──────────────────────────────────────────────────────┘

2.2 核心设计模式

模式
应用位置
说明
模板方法dram_base<CmdType>
定义骨架:doCommand() → doCoreCommandImpl() + doInterfaceCommandImpl()
策略模式standards/ddr4/
, ddr5/, lpddr4/, lpddr5/
每个 DRAM 标准独立实现 Core + Interface + Calculation 三组件
命令路由util/RegisterHelper.h
通用 rank/bank/bankGroup 路由辅助函数,消除重复代码
延迟执行util/ImplicitCommandHandler.h
按时间戳排序的延迟回调队列,处理 auto-precharge、refresh 恢复等
观察者模式util/extension_manager.h
扩展系统:运行时注册/查找/回调
开放区间util/cycle_stats.h
 (interval_counter)
不预先知道结束时间的周期统计——完美适配 DRAM 状态持续时长由后续命令决定的场景
标记序列化util/Serialize.h
 / util/Deserialize.h
所有有状态组件支持流式序列化/反序列化

2.3 Core 与 Interface 解耦

DRAMPower 最核心的架构决策是 Core 和 Interface 完全解耦:

  Command
    │
    ├──→ DDR5Core::doCommand()
    │      └─ 追踪:Bank 状态、ACT/PRE 周期、命令计数、PowerDown、SelfRefresh
    │
    └──→ DDR5Interface::doCommand()
           └─ 追踪:CA 总线翻转、DQ 数据总线翻转、DQS/Clock 信号、DBI 信号

两者各自独立运行,在 getWindowStats() 时才将统计数据汇合到 SimulationStats 结构体。这意味着:

  • 可以独立测试 Core 或 Interface 管线
  • 可以用 Toggling Rate 模式运行 Interface(不需要真实数据 trace)
  • 新增 DRAM 标准只需要实现两套 handler + 一套 calculation 公式

3. 完整数据流追踪(DDR5 代表)

3.0 阶段 0:初始化

// 用户代码
auto memspec =DRAMUtils::parse_memspec_from_file("ddr5.json");
DDR5 dram(MemSpecDDR5::from_memspec(*memspec));

// DDR5 构造函数 (DDR5.cpp:16-20)
DDR5::DDR5(const MemSpecDDR5 &memSpec,const config::SimConfig &simConfig)
:m_memSpec(memSpec)
,m_interface(m_memSpec, simConfig)// 初始化总线 + 注册 17 种命令的 CA 编码
,m_core(m_memSpec)// 创建 numberOfRanks × numberOfBanks 个 Bank 状态机
{}

Core 初始化:DDR5Core 构造 std::vector<Rank> m_ranks,每个 Rank 含 numberOfBanks 个 Bank,Bank 初始状态均为 BANK_PRECHARGED。

Interface 初始化:DDR5Interface 构造时通过 registerPatterns() 将 17 种 CmdType 映射到 14-bit 的 CA 总线编码(PatternHandler)。数据总线支持两种模式:

  • Bus 模式:接收真实数据,逐 bit 存储到 burst_storage
  • Toggling Rate 模式:不存储真实数据,仅基于概率做统计

3.1 阶段 1:命令输入 → 双路分发

入口: dram_base<CmdType>::doCommand() (dram_base.h:64-67)

voiddoCommand(const Command& command){
doCoreCommandImpl(command);// → DDR5Core::doCommand()
doInterfaceCommandImpl(command);// → DDR5Interface::doCommand()
}

一条 Command 同时送入 Core 和 Interface 两条独立管线。

3.2 阶段 2:Core 管线 —— 状态机执行

DDR5Core::doCommand() (DDR5Core.cpp:6-65)

1. processImplicitCommandQueue(timestamp)
   └─ 执行所有到期的延迟命令(auto-precharge, refresh 后恢复等)

2. 更新时间戳 m_last_command_time

3. switch(cmd.type):
   ACT   → bankHandler     → handleAct(rank, bank, ts)
   PRE   → bankHandler     → handlePre(rank, bank, ts)
   RD    → bankHandler     → handleRead(rank, bank, ts)
   RDA   → bankHandlerIdx  → handleReadAuto(rank_idx, bank_idx, ts)
   WR    → bankHandler     → handleWrite(rank, bank, ts)
   WRA   → bankHandlerIdx  → handleWriteAuto(rank_idx, bank_idx, ts)
   PRESB → bankGroupHandler → handlePreSameBank(rank, bank_id, ts)
   REFSB → bankGroupHandlerIdx → handleRefSameBank(rank_idx, bank_id, ts)
   REFA  → rankHandlerIdx  → handleRefAll(rank_idx, ts)
   PREA  → rankHandler     → handlePreAll(rank, ts)
   SREFEN→ rankHandlerIdx  → handleSelfRefreshEntry(rank_idx, ts)
   SREFEX→ rankHandler     → handleSelfRefreshExit(rank, ts)
   PDEA/PDEP/PDXA/PDXP → rankHandlerIdx → PowerDown handlers

路由辅助层 (RegisterHelper.h):以 bankHandler 为例:

1. 从 cmd.targetCoordinate.rank 取出 Rank&
2. 从 cmd.targetCoordinate.bank 取出 Bank&
3. rank.commandCounter.inc(cmd.type)  — 累计命令计数
4. 调用 (owner->*member_func)(rank, bank, cmd.timestamp)

3.3 阶段 3:Interface 管线 —— 总线翻转追踪

DDR5Interface::doCommand() (DDR5Interface.cpp:127-159)

switch(cmd.type):
  // 非数据命令:仅更新 CA 命令总线
  ACT/PRE/PRESB/REFSB/REFA/PREA/SREFEN/PDEA/PDEP/PDXA/PDXP
    → handleCommandBus(cmd)  // PatternHandler 编码 → CA bus bits

  // 数据命令:更新数据总线 + DQS + CA 总线
  RD/RDA → handleData(cmd, read=true)
  WR/WRA → handleData(cmd, read=false)

handleData (DDR5Interface.cpp:242-261):

  • 如果 cmd 携带数据(sz_bits > 0):加载真实数据到 databus_t
  • 如果 Toggling Rate 模式:使用默认 burstLength 生成统计
  • 同时启动/停止 DQS 时钟信号

数据存储核心 —— burst_storage<BitsetContainer<N>> (burst_storage.h):

  • 逐 bit 存储到 std::bitset<N>,可高效进行时间窗口内的 zeroes/ones/bit_changes/zeroes_to_ones/ones_to_zeroes 统计
  • 通过位操作(xor + popcount)实现 O(1) 窗口转换统计

3.4 阶段 4:统计聚合

DDR5::getWindowStats() (DDR5.cpp:43-48)

SimulationStats DDR5::getWindowStats(timestamp_t timestamp){
    SimulationStats stats;
    m_core.getWindowStats(timestamp, stats);// → bank[], rank_total[]
    m_interface.getWindowStats(timestamp, stats);// → *Bus, togglingStats
return stats;
}

Core 统计 (DDR5Core::getWindowStats, DDR5Core.cpp:312-353)

对每个 Rank × Bank 填充:

统计项
来源
计算方式
counter.{act,pre,reads,writes,...}bank.counter
直接拷贝
cycles.actbank.cycles.act.get_count_at(ts)
开放区间则 count + (ts - start),否则取 count
cycles.pre
推导
simulation_duration - act - powerDownAct - powerDownPre - selfRefresh
cycles.powerDownAct/Prerank.cyclesinterval.get_count_at(ts)
cycles.selfRefreshrank.cycles.srefinterval.get_count_at(ts)

cycles.pre 的减法推导是关键设计:DRAM 中的 PRECHARGE 状态是"其余一切之外"的状态,用总时间减去所有已知状态的时长得到。

Interface 统计 (DDR5Interface::getWindowStats, DDR5Interface.cpp:263-283)

统计项
来源
commandBusm_commandBus.get_stats(ts)
 — CA 总线 zeroes/ones/bit_changes
readBus / writeBusm_dataBus.get_stats(ts)
 — DQ 总线翻转统计
clockStats2 × m_clock.get_stats_at(ts)
 — 差分时钟
readDQSStats / writeDQSStats2 × m_*DQS.get_stats_at(ts)
 — 差分 DQS
togglingStats
Toggling Rate 模式的 read/write 统计

3.5 阶段 5:Power计算

5a. Core Power—— Calculation_DDR5::calcEnergy()

调用链: dram_base::calcCoreEnergy(ts) → DDR5::calcCoreEnergyStats(stats) → Calculation_DDR5::calcEnergy(stats)

核心物理模型:Energy = V × I × t,每个操作/状态的功耗 = 电压 × (操作电流 - 背景电流) × 操作时长

Power分量
公式
含义
E_actVDD × (I_θ - I_1) × tRAS × N_act
 ACT Power,一次 ACT 命令
E_preVDD × (I_β - I_DD2N) × tRP × N_pre
预充电power,一次 PRE 命令
E_RDVDD × (I_DD4R - I_DD3N) × (BL/DR) × N_RD
读操作power
E_WRVDD × (I_DD4W - I_DD3N) × (BL/DR) × N_WR
写操作power
E_ref_ab(1/B) × VDD × (I_DD5X - I_DD3N) × tRFC × N_REF
All-Bank Refresh,分摊到每个 bank
E_ref_sb(1/BG) × VDD × (I_DD5C - I_BG) × tRFCsb × N_SB_REF
Same-Bank Refresh(DDR5 特有)
E_bg_act(1/B) × VDD × (I_DD3N - I_ρ) × T_act
单个 bank 的背景 active 功耗
E_bg_pre(1/B) × VDD × I_DD2N × T_pre
单个 bank 的背景 precharge 功耗
E_bg_act_sharedVDD × I_ρ × T_act
跨所有 bank 共享的背景 active 功耗

DDR5 的双电压域循环:

for(auto vd :{VDD, VPP}){// DDR5 有两个电压域!
auto VXX = memPowerSpec[vd].vXX;
// ... 对每个电压域独立计算
}

5b. Interface 能量 —— InterfaceCalculation_DDR5::calculateEnergy()

调用链: dram_base::calcInterfaceEnergy(ts) → DDR5::calcInterfaceEnergyStats(stats) → InterfaceCalculation_DDR5::calculateEnergy(stats)

子系统
静态功耗
动态功耗
来源
CK (Clock)N_zeroes × V² × tCK / R_eqtransitions × E_dyn
controller
DQS (读)N_zeroes × V² × tCK / R_eqtransitions × E_dyn
DRAM 侧
DQS (写)N_zeroes × V² × tCK / R_eqtransitions × E_dyn
controller 侧
DQ (读)N_zeroes × V² × tCK/DR / R_eqtransitions × E_dyn
DRAM 侧
DQ (写)N_zeroes × V² × tCK/DR / R_eqtransitions × E_dyn
controller 侧
CAN_zeroes × V² × tCK / R_eqtransitions × E_dyn
controller 侧
  • 静态功耗物理意义:当信号为低电平(zero)且 termination 使能时,电流从 VDDQ 经终端电阻流向地 → P = V² / R
  • 动态功耗物理意义:每次 0→1 跳变消耗固定power E_dyn(来自 memspec 的阻抗参数)
  • DDR5 无 DBI(Data Bus Inversion),因此 Interface power计算不包含 calcDBIEnergy
    BTW:整个计算逻辑的思路跟美光给的一版power计算公式类似

4. 模块一:命令系统与数据结构

4.1 CmdType —— 命令枚举

文件: src/DRAMPower/DRAMPower/command/CmdType.h

enumclassCmdType{
    NOP =0,  ACT,    PRE,    PRESB,// 基础操作
    RD,       WR,     RDA,    WRA,// 读写(RDA/WRA = Read/Write with Auto-Precharge)
    REFB,     REFP2B, REFSB,  REFA,// 刷新(PerBank/PerTwoBanks/SameBank/AllBank)
    PDEA,     PDEP,   PDXA,   PDXP,// PowerDown(Active/Precharge 进入/退出)
    SREFEN,   SREFEX, DSMEN,  DSMEX,// SelfRefresh & DeepSleepMode
    END_OF_SIMULATION, COUNT
};

共 22 种命令类型,全部标准共享同一套枚举(尽管部分命令在某些标准下不支持)。

CmdTypeUtil 提供 needs_data()(判断 RD/WR/RDA/WRA)、from_string() / to_string() 转换。

4.2 Command —— 命令结构体

文件: src/DRAMPower/DRAMPower/command/Command.h

structTargetCoordinate{
    std::size_t bank =0;
    std::size_t bankGroup =0;
    std::size_t rank =0;
    std::size_t row =0;
    std::size_t column =0;
};

classCommand{
    timestamp_t timestamp =0;// 命令执行时间(clock cycle)
    CmdType type;// 命令类型
    TargetCoordinate targetCoordinate;// 目标地址
constuint8_t* data =nullptr;// 数据负载(仅 RD/WR 系列)
    std::size_t sz_bits;// 数据位宽
};

4.3 energy_t / energy_info_t —— power结果

文件: src/DRAMPower/DRAMPower/data/energy.h

energy_info_t  (per-bank power)
├── E_act, E_pre, E_RD, E_WR, E_RDA, E_WRA
├── E_pre_RDA, E_pre_WRA              ← auto-precharge 中的 PRE 部分
├── E_bg_act, E_bg_pre                ← 背景功耗
├── E_ref_AB, E_ref_PB, E_ref_SB, E_ref_2B  ← 不同刷新类型
└── total()                           ← 所有分量求和

energy_t  (全体能量)
├── bank_energy: vector<energy_info_t>  ← 每个 bank 的power
├── E_bg_act_shared                    ← 跨 bank 共享的背景 active 功耗
├── E_PDNA, E_PDNP                     ← PowerDown 功耗(Rank 级)
├── E_sref, E_dsm                      ← SelfRefresh / DeepSleep 功耗
└── total()                            ← 所有分量 + bank_energy 总和

interface_energy_info_t
├── controller: {dynamicEnergy, staticEnergy}
├── dram: {dynamicEnergy, staticEnergy}
└── total()

4.4 SimulationStats —— 仿真统计

文件: src/DRAMPower/DRAMPower/data/stats.h

SimulationStats
├── bank: vector<CycleStats>           ← 每个 bank 的统计
├── rank_total: vector<CycleStats>     ← 每个 rank 的汇总统计
├── commandBus: bus_stats_t            ← CA 命令总线统计
├── readBus, writeBus: bus_stats_t     ← DQ 数据总线统计
├── clockStats, wClockStats            ← CK / WCK 时钟统计
├── readDQSStats, writeDQSStats        ← DQS 信号统计
├── readDBI, writeDBI                  ← DBI 信号统计
├── togglingStats: {read, write}       ← Toggling Rate 模式统计

CycleStats (per bank 或 per rank)
├── counter: {act, pre, reads, writes, refAllBank, refPerBank, refSameBank, ...}
├── cycles: {act, pre, powerDownAct, powerDownPre, selfRefresh, deepSleepMode}
└── prepos: {readMerged, writeMerged, readSeamless, writeSeamless, ...}  ← 仅 DDR4

bus_stats_t
├── ones, zeroes                       ← 0/1 计数
├── bit_changes                        ← 总跳变次数
├── zeroes_to_ones, ones_to_zeroes    ← 方向性跳变

5. 模块二:Bank/Rank 状态机 —— Core 层

5.1 数据结构

Bank (dram/Bank.h)

structBank{
enumclassBankState{ BANK_PRECHARGED =0, BANK_ACTIVE =1};

    CycleStats::command_stats_t counter;// 命令累计计数
struct{ interval_t act; interval_t powerDownAct; interval_t powerDownPre;} cycles;
    BankState bankState;// 当前状态
    timestamp_t latestPre =0;// 最后一次 PRE 时间
    timestamp_t refreshEndTime =0;// 刷新结束时间
};

Rank (dram/Rank.h)

enumclassMemState{ NOT_IN_PD =0, PDN_ACT, PDN_PRE, SREF, DSM };

structRank{
    MemState memState;
    CommandCounter<CmdType> commandCounter;
struct{
        interval_t pre, act, ref, sref, powerDownAct, powerDownPre, deepSleepMode;
} cycles;
struct{uint64_t selfRefresh, deepSleepMode;} counter;
    timestamp_t endRefreshTime =0;
    std::vector<Bank> banks;
};

5.2 关键机制

5.2.1 interval_counter —— 开放区间周期统计

文件: src/DRAMPower/DRAMPower/util/cycle_stats.h

这是整个状态机最精巧的数据结构:

template<typenameT>
classinterval_counter{
    T count{0};
    std::optional<T> start;// 区间开始时间
    std::optional<T> end;// 区间结束时间(std::nullopt 表示仍在进行)

voidstart_interval(T ts){ start = ts; end.reset();}
    T close_interval(T ts){
auto diff = ts -*start;// 计算时长
        count += diff;// 累加到总计
        end = ts;
return diff;
}
    T get_count_at(T ts)const{
if(is_open()&& ts >*start)
return count +(ts -*start);// 开放区间:包含"当前到查询时刻"的增量
return count;
}
};

设计要点:DRAM 中一个状态(如 ACTIVE)的持续时间在状态开始时不可预知——它取决于下一个 PRE 命令何时到达。interval_counter 通过"开放区间 + 延迟结算"完美适配了这一特性。

5.2.2 ImplicitCommandHandler —— 延迟命令队列

文件: src/DRAMPower/DRAMPower/util/ImplicitCommandHandler.h

维护一个按时间戳排序的 std::deque<pair<timestamp_t, std::function<void(Context&)>>>。

插入 (addImplicitCommand):使用 std::upper_bound 保证时间戳严格有序。

执行 (processImplicitCommandQueue):在每次 doCommand() 开头,执行所有 timestamp ≤ 当前命令时间的延迟回调。

应用场景:

触发命令
延迟回调
延迟时间
RDA / WRA
自动执行 handlePre
max(tRAS到期, ts + prechargeOffset)
REFA / REFB / REFSB / REFP2B
自动将 bank 切回 PRECHARGED
ts + tRFC
(或 tRFCPB, tRFCsb)
SREFEN
先触发 REFA,再进入 SREF
ts + tRFC
PDEA / PDEP
延迟进入 PowerDown
max(tRCD到期, tRP到期, refreshEndTime)
PDXA / PDXP
延迟退出 PowerDown
同上

5.2.3 核心状态转移(以 DDR5 为例)

ACT 激活: handleAct(rank, bank, ts) — DDR5Core.cpp:75-85

bank.counter.act++
bank.cycles.act.start_interval(ts)
if rank 无活跃 bank: rank.cycles.act.start_interval(ts)
bank.bankState = BANK_ACTIVE

PRE 预充电: handlePre(rank, bank, ts) — DDR5Core.cpp:87-98

if bank 已经是 PRECHARGED → 直接返回(幂等保护)
bank.counter.pre++
bank.cycles.act.close_interval(ts)    // 结算 ACTIVE 周期
bank.latestPre = ts
bank.bankState = BANK_PRECHARGED
if rank 无其他活跃 bank: rank.cycles.act.close_interval(ts)

RDA 读+自动预充电: handleReadAuto(rank_idx, bank_idx, ts) — DDR5Core.cpp:169-183

bank.counter.readAuto++
delayed_ts = max(bank.cycles.act.start + tRAS, ts + prechargeOffsetRD)
注册延迟回调: 在 delayed_ts 自动执行 handlePre(rank, bank, delayed_ts)

REFA 全 Bank 刷新: handleRefAll(rank_idx, ts) — DDR5Core.cpp:124-132

对每个 bank:
  ++counter.refAllBank
  bank.bankState = BANK_ACTIVE
  bank.cycles.act.start_interval(ts)
  bank.refreshEndTime = ts + tRFC
  注册延迟回调: 在 ts+tRFC 自动切回 PRECHARGED
rank.endRefreshTime = ts + tRFC

5.3 路由辅助层 RegisterHelper

文件: src/DRAMPower/DRAMPower/util/RegisterHelper.h

namespace coreHelpers {
bankHandler(cmd, ranks, owner,&DDR5Core::handleAct);
// → 从 ranks[cmd.rank].banks[cmd.bank] 取目标,调 (owner->*func)(rank, bank, ts)

bankHandlerIdx(cmd, ranks, owner,&DDR5Core::handleReadAuto);
// → 传入 (rank_idx, bank_idx, ts) 而不是 (rank&, bank&, ts)

rankHandler(cmd, ranks, owner,&DDR5Core::handlePreAll);
// → 仅传入 (rank, ts)

bankGroupHandler(cmd, ranks, owner,&DDR5Core::handlePreSameBank);
// → 传入 (rank, bank_id, ts),用于跨 bank group 的同位置操作
}

这个工具层消除了 Core 中 switch-case 内重复的 ranks[cmd.rank].banks[cmd.bank] 查找代码。


6. 模块三:接口总线追踪 —— Interface 层

6.1 总体结构

每个标准的 Interface 类(如 DDR5Interface)管理以下组件:

组件
类型
说明
命令总线
 (CA)
Bus<cmdBusWidth>
DDR5: 14-bit, DDR4: 27-bit, LPDDR4: 6-bit, LPDDR5: 7-bit
数据总线
 (DQ)
databus_presets::databus_preset_t
根据 DataBusConfig 选择 Bus 模式或 TogglingRate 模式
读 DQSClock
读数据选通信号,由 DRAM 驱动
写 DQSClock
写数据选通信号,由 controller 驱动
主时钟
 (CK)
Clock
差分时钟
WCKClock
仅 LPDDR5:Write Clock
DBIDBI<...>
仅 DDR4/LPDDR4/LPDDR5:Data Bus Inversion
PatternHandlerPatternHandler<CmdType>
每种命令的 CA 总线编码

6.2 PatternHandler —— 命令编码

文件: src/DRAMPower/DRAMPower/util/PatternHandler.h

每种命令在 CA 总线上有不同的 bit 编码。以 DDR5 为例(14-bit CA 总线):

ACT:  L, L, R0-R16, CID3, CID0-CID2, BA0-BA1, BG0-BG2
PRE:  H, H, L, H, H, CID3, BA0-BA1, BG0-BG2, CID0-CID2
RD:   H, L, H*5, BA0-BA1, BG0-BG2, CID0-CID2, C2-C10, V, H, V, V, CID3
WR:   H, L, H, H, L, H, BA0-BA1, BG0-BG2, CID0-CID2, C3-C10, V, H, H, V, CID3
RDA:  类似 RD 但最后一位不同
...

编码中既有常量(H/L/V——High/Low/Don't-care),也有变量(Row/Column/Bank/BankGroup 地址位、CID 等在命令创建时动态填入)。

PatternEncoderOverrides:允许在运行时覆盖某些 bit。例如 DDR5 的 handleOverrides 根据 burst length(8/16/32)和读写方向动态调整 C2/C3/C10 的编码。

6.3 Bus 与 burst_storage —— 总线存储与统计

文件: src/DRAMPower/DRAMPower/util/burst_storage.h

这是整个 Interface 层的性能关键。两种存储容器:

BitsetContainer(单线信号,如 Clock、DBI pin)

template<std::size_t N>
structBitsetContainer{
    std::bitset<N> bitset;// 按 bit 存储信号值
    std::size_t count;// 当前存储的 bit 数
    timestamp_t load_time;// 起始时间
};

窗口统计算法 (O(1) 复杂度):

// 1. 右移 offset 位,取出窗口内的 bits
bitset_t val = data.bitset >> offset;

// 2. 生成掩码
bitset_t mask; mask.set(); mask >>=(N - length);

// 3. 应用掩码
val &= mask;

// 4. 统计
ones = val.count();
zeroes = length - ones;

// 5. 位变换统计(xor + popcount)
bitset_t toggles =(val ^(val >>1))&(mask >>1);
bit_changes =popcount(toggles);
zeroes_to_ones =popcount(toggles &(val >>1));
ones_to_zeroes = bit_changes - zeroes_to_ones;

BusContainer(多 bit 并行总线,如 CA、DQ)

template<std::size_t N>
structBusContainer{
    std::vector<std::bitset<N>> bursts;// 每个 burst 存储一整个总线快照
    std::size_t width;// 总线宽度
    std::size_t count;// burst 数量
    timestamp_t load_time;
};

对每个 burst(总线快照)进行统计,逐 burst 累积 diff(prev, current) 的 zeroes/ones/transitions。

6.4 Clock 模型

文件: src/DRAMPower/DRAMPower/util/clock.h

Clock 是一个周期性信号,其统计通过 burst_storage<BitsetContainer<2>> 实现——只需要 2 个 bit(存储一个完整周期:0→1→0→1...)。get_stats_at(ts) 返回 zeroes/ones/transitions 统计。

对于差分时钟(CK、DQS),统计值乘以 2。

6.5 DBI —— Data Bus Inversion

文件: src/DRAMPower/DRAMPower/util/dbi.h + dbialgos.h + dbihelpers.h

DBI 是一种降低数据总线功耗的技术:如果某个字节中 0 的数量超过 4,DBI 算法会翻转整个字节并将 DBI pin 置位,使接收端知道发生了翻转。

DRAMPower 实现:

  • DBI::updateDBI(virtual_time, n_bits, data, read) — 遍历数据,决定是否翻转,回调通知 pin 状态变化
  • 每个 DBI pin(每 8-bit 数据一个)通过 Pin 追踪其 0/1 变化
  • 仅 DDR4、LPDDR4、LPDDR5 支持 DBI(DDR5 不支持)

7. 模块四:power计算层 —— Calculation

7.1 Core Energy 计算公式体系

所有标准的 Core power计算遵循同一套数学框架,差异仅在于参数数量(电压域数、刷新类型等)。

通用公式:

power
公式
物理意义
E_act
V × (I_peak - I_background) × tRAS × N_act
超过背景电流的激活电流 × 激活时长
E_pre
V × (I_β - I_DD2N) × tRP × N_pre
超过背景电流的预充电电流 × 预充电时长
E_RD / E_WR
V × (I_DD4X - I_DD3N) × (BL/DR) × N
超过背景电流的读写电流 × 传输时长
E_ref
(1/B) × V × (I_DD5X - I_DD3N) × tRFC × N_REF
刷新电流(分摊到每个 bank)
E_bg_act
(1/B) × V × (I_DD3N - I_ρ) × T_active
每个 bank 在 active 期间的背景泄漏电流
E_bg_pre
(1/B) × V × I_DD2N × T_pre
每个 bank 在 precharge 期间的背景泄漏电流
E_bg_act_shared
V × I_ρ × T_active
跨所有 bank 共享的背景电流(外围电路)

关键参数:

  • I_ρ (rho): 银行间功率因子,表示"当一个 bank 已经 active 时,再激活另一个 bank 的边际电流增量"
  • I_θ (theta): 等效 ACT 峰值电流,从 (I_DD0 × (tRP + tRAS) - I_β × tRP) / tRAS 推导
  • I_1 (I_one): 单个 active bank 的稳态电流

7.2 Interface Energy 计算公式体系

静态功耗:当 termination 使能时,信号为 0 期间(DDR/LPDDR4)/ 信号为 1 期间(LPDDR5,pull-down termination 不同)电流从电源经终端电阻流向地:

P_static = N_bits × V_DDQ² / R_eq
E_static = P_static × t_bit = N_bits × V_DDQ² × t_bit / R_eq

动态功耗:每次 0→1 跳变消耗固定能量(来自 memspec 的 _dyn_E 阻抗参数):

E_dynamic = N_transitions × E_per_transition

DDR vs LPDDR 的 termination 差异:

标准
Static Termination Count
原因
DDR4/DDR5
zeroes
(信号为低时电流流动)
Pull-up termination to VDDQ
LPDDR4/LPDDR5
ones
(信号为高时电流流动)
Pull-down termination to GND

7.3 各标准 Calculation 差异

特性
DDR4
DDR5
LPDDR4
LPDDR5
电压域
VDD, VPP
VDD, VPP
VDD1, VDD2
VDD1, VDD2H, VDD2L
刷新类型
REFAB only
REFAB + REFSB
REFAB + REFPB
REFAB + REFPB + REFP2B
DBI 支持
是
否
是
是
PrePostamble 计算
是(DDR4 特有)
否(归入 DQS 统计)
否
否
WCK 时钟
否
否
否
是
DeepSleep
否
否
否(v6.0 前)
是
E_RD/E_WR 的 I_background
I_1
I_1
IDD3N
IDD3N / I_2 (按 bank_arch)

8. 模块五:内存规格 —— MemSpec

8.1 MemSpec 架构

所有标准的 MemSpec 继承自 MemSpec<DRAMUtils::MemSpec::MemSpecXXX>,包含:

  • 时序参数:tCK, tRAS, tRCD, tRP, tRFC, tRFCsb/tRFCPB, tBurst, tRL, tWL...
  • 电源参数:各电压域的 vXX, iXX0, iXX2N, iXX3N, iXX4R, iXX4W, iXX5X, iXX6N, iBeta
  • 阻抗参数:各信号的 R_eq, dyn_E, termination(CK/DQS/DQ/CA/DBI)
  • 架构参数:numberOfBanks, numberOfRanks, numberOfBankGroups, banksPerGroup, bitWidth, burstLength, dataRate, numberOfDevices
  • Bank-wise 参数:bwPowerFactRho(背景功率因子)

8.2 MemSpec 初始化流程

1. DRAMUtils::parse_memspec_from_file("ddr5.json")
   → 返回 MemSpecVariant (std::variant<MemSpecDDR4, MemSpecDDR5, MemSpecLPDDR4, MemSpecLPDDR5>)

2. MemSpecDDR5::from_memspec(variant)
   → 从 variant 中提取 DDR5 规格,解析阻抗、数据速率等

3. DDR5 dram(MemSpecDDR5)
   → 用 memspec 初始化 Core + Interface

8.3 DDR5 MemSpec 结构(代表性)

classMemSpecDDR5:publicMemSpec<DRAMUtils::MemSpec::MemSpecDDR5>{
uint64_t numberOfBankGroups;// BG 模式特有
uint64_t banksPerGroup;// BG 模式特有
uint64_t numberOfRanks;
double vddq;// I/O 电压

structMemTimingSpec{double fck, tCK;uint64_t tRAS, tRCD, tRFC, tRFCsb, tRP, tBurst;};
structMemPowerSpec{double vXX, iXX0, iXX2N, iXX3N, iXX4R, iXX4W, iXX5X, iXX5C, iXX6N, iBeta;};
structDataRateSpec{uint64_t commandBusRate, dataBusRate, dqsBusRate;};
structBankWiseParams{double bwPowerFactRho;};
};

所有值均为 SI 单位(伏特、安培、秒)。


9. 模块六:扩展系统

9.1 扩展架构

文件: src/DRAMPower/DRAMPower/util/extension_base.h, extension_manager.h, extension_manager_static.h, extensions.h, extensions.cpp

v6.0.0 新增的插件系统,分两层:

层级
文件
说明
动态扩展extension_manager.h
运行时注册/查找/获取扩展,存储在 std::unordered_map<type_index, unique_ptr<Extension>>
静态扩展extension_manager_static.h
编译时注册的扩展集合(ExtensionPack),支持 get<ExtType>() 模板方法

9.2 扩展基类

classExtension{
virtual~Extension()=default;
};

template<typenameHooks>
classExtensionWithHooks:publicExtension{
virtual Hooks getSupportedHooks()const=0;
};

9.3 与 dram_base 的集成

dram_base 持有 extension_manager_t m_extensionManager,扩展随 serialize()/deserialize() 一同持久化。在序列化时先写入扩展管理器状态,再写入 Core/Interface 状态。


10. 模块七:工具组件

10.1 Serialize / Deserialize 接口

文件: src/DRAMPower/DRAMPower/util/Serialize.h, Deserialize.h

纯虚接口,所有有状态组件(Bank, Rank, DDR5Core, DDR5Interface, Bus, Clock, DBI...)均实现此接口:

classSerialize{virtualvoidserialize(std::ostream&)const=0;};
classDeserialize{virtualvoiddeserialize(std::istream&)=0;};

数据以原始字节写入(reinterpret_cast<const char*>),用于 checkpoint/restore 和并行仿真。

10.2 Clock

文件: src/DRAMPower/DRAMPower/util/clock.h

classClock{
    burst_storage<BitsetContainer<2>> m_clock;// 2-bit 足够表示一个时钟周期
bool m_stop_between_requests;// 是否在无请求时停止
bool m_is_running;
voidstart(timestamp_t);// 时钟开始翻转
voidstop(timestamp_t);// 时钟停止翻转
    bus_stats_t get_stats_at(timestamp_t)const;
};

对于 DDR5 的 wckAlwaysOnMode,WCK 始终运行,不调用 stop。

10.3 Bus

文件: src/DRAMPower/DRAMPower/util/bus.h, bus.cpp

template<std::size_t BitWidth>
classBus{
    burst_storage<BusContainer<BitWidth>> m_bus;
voidload(timestamp_t,uint64_t pattern, std::size_t n_cycles);
    bus_stats_t get_stats(timestamp_t)const;
};

10.4 databus_presets

文件: src/DRAMPower/DRAMPower/util/databus_presets.h

根据 DataBusConfig(位宽、数据率、ToggleRate 定义)创建合适的数据总线实例。支持两种模式:

  • Bus 模式:真实数据逐 bit 存储到 burst_storage<BusContainer<N>>
  • TogglingRate 模式:不存储数据,仅基于概率(togglingRateRead/Write, dutyCycleRead/Write, idlePatternRead/Write)做统计

11. 横向对比:DDR4 vs DDR5 vs LPDDR4 vs LPDDR5

11.1 命令支持矩阵

命令
DDR4
DDR5
LPDDR4
LPDDR5
说明
ACT
✓
✓
✓
✓
激活一行
PRE
✓
✓
✓
✓
预充电单个 bank
PREA
✓
✓
✓
✓
预充电所有 bank
PRESB
✗
✓
✗
✗
预充电同 bank group 同位置 bank
RD
✓
✓
✓
✓
读
RDA
✓
✓
✓
✓
读+自动预充电
WR
✓
✓
✓
✓
写
WRA
✓
✓
✓
✓
写+自动预充电
REFA
✓
✓
✓
✓
所有 bank 刷新
REFB
✗
✗
✓
✓
单 bank 刷新
REFSB
✗
✓
✗
✗
同 bank group 同位置 bank 刷新
REFP2B
✗
✗
✗
✓*
两 bank 刷新(仅 MBG/M16B)
SREFEN/EX
✓
✓
✓
✓
自刷新进入/退出
PDEA/PDXA
✓
✓
✓
✓
Active PowerDown 进入/退出
PDEP/PDXP
✓
✓
✓
✓
Precharge PowerDown 进入/退出
DSMEN/EX
✗
✗
✗
✓
Deep Sleep Mode 进入/退出(LPDDR5 特有)

11.2 状态机差异

DDR4

Bank 状态: PRECHARGED ↔ ACTIVE
Rank 状态: NOT_IN_PD / PDN_ACT / PDN_PRE / SREF

特点:
- 无 bank group 概念 → 无 PRESB/REFSB
- PDXA 退出时用 bank.bankState == BANK_ACTIVE 判断恢复哪些 bank
- PDXP 退出时同样用 bankState 判断
- start_interval_if_not_running 用于 rank act interval

DDR5

Bank 状态: PRECHARGED ↔ ACTIVE
Rank 状态: NOT_IN_PD / PDN_ACT / PDN_PRE / SREF

特点(相比 DDR4):
- 有 bank group → PRESB(同 BG 同位置所有 bank 的 precharge)
- REFSB(同 BG 同位置 bank 的刷新,使用 tRFCsb)
- PDXA 退出时使用 counter.act != 0 && cycles.act.get_end() == powerDownAct.get_start() 判断恢复
- E_ref_sb 能量公式使用 (1/BG) 分摊因子
- 双电压域 (VDD + VPP)

LPDDR4

Bank 状态: PRECHARGED ↔ ACTIVE
Rank 状态: NOT_IN_PD / PDN_ACT / PDN_PRE / SREF

特点(相比 DDR4/DDR5):
- Per-Bank Refresh (REFB) 使用 tRFCPB timing
- 无 bank group → 无 PRESB/REFSB
- PDXA 退出时同样用 counter.act != 0 && end == powerDownAct.get_start() 判断
- 双电压域 (VDD1 + VDD2)
- 支持 DBI

LPDDR5

Bank 状态: PRECHARGED ↔ ACTIVE
Rank 状态: NOT_IN_PD / PDN_ACT / PDN_PRE / SREF / DSM

特点(最复杂):
- Deep Sleep Mode (DSM) — 从 SREF 进入,回到 SREF
- REFP2B (Per Two Banks Refresh) — 仅 M8B/MBG/M16B 架构
- bank_arch 依赖:根据不同 bank 架构(MBG/M8B/M16B),命令编码不同
  例如 MBG 模式下 pattern 包含 BG0/BG1 位
- 三电压域 (VDD1 + VDD2H + VDD2L)
- 支持 WCK (Write Clock)
- 支持 wckAlwaysOnMode(WCK 常开)
- Interface 没有 writeDQS — LPDDR5 使用 WCK 而非 WDQS

11.3 Core 实现差异对比表

特性
DDR4
DDR5
LPDDR4
LPDDR5
handleAct
 的 rank 激活检查
start_interval_if_not_running(ts)if (!rank.isActive(ts))if (!rank.isActive(ts))if (!rank.isActive(ts))
handlePre
 的 rank 关闭检查
if (!rank.isActive(ts))if (!rank.isActive(ts))if (!rank.isActive(ts))if (!rank.isActive(ts))
Refresh 实现
内联在 handleRefAll
handleRefreshOnBank 统一
handleRefreshOnBank 统一
handleRefreshOnBank 统一
银行组支持
✗
✓ (PRESB, REFSB)
✗
✗ (但 bank_arch 影响)
DeepSleep
✗
✗
✗
✓ (DSMEN/DSMEX)
SelfRefresh 的 selfRefresh 减去 DSM
N/A
N/A
LPDDR4 有但未使用 DSM
selfRefresh - deepSleepMode
PDXA 恢复逻辑
检查 bank.bankState == ACTIVE
检查 counter.act != 0 && end == pd_start
检查 counter.act != 0 && end == pd_start
检查 counter.act != 0 && end == pd_start
MemSpec 银行组参数
无
banksPerGroup, numberOfBankGroups
无
bank_arch (MBG/M8B/M16B)

11.4 Interface 实现差异对比表

特性
DDR4
DDR5
LPDDR4
LPDDR5
CA 总线宽度
27 bit
14 bit
6 bit
7 bit
CA 总线 data rate
1 (SDR)
1 (SDR)
1 (SDR)
2 (DDR)
Idle 模式
H (pull-up)
H (pull-up)
L (pull-down)
L (pull-down)
DBI 支持
✓ (8-bit 粒度)
✗
✓ (8-bit 粒度)
✓ (8-bit 粒度)
PrePostamble 追踪
✓ (seamless/merged)
✗
✗
✗
WCK 时钟
✗
✗
✗
✓ (wckAlwaysOnMode)
写 DQS
✓
✓
✓
✗ (用 WCK 替代)
PowerDown 命令在 CA 总线
✓ (CKE 信号)
✓
✗ (无总线活动)
✓
ACT 编码
单条命令 27-bit
单条命令 14-bit
单条命令 24-bit (4×6)
两条命令
 (ACT-1 + ACT-2) 28-bit (4×7)
Burst Length 覆盖
4/8 (C0/C1/C2)
8/16/32 (C2/C3/C10)
16/32 (BL/C2/C3/C4)
16/32 (C0)

11.5 Calculation 差异对比表

特性
DDR4
DDR5
LPDDR4
LPDDR5
电压域数
2 (VDD, VPP)
2 (VDD, VPP)
2 (VDD1, VDD2)
3 (VDD1, VDD2H, VDD2L)
E_ref 类型
E_ref_AB
E_ref_AB + E_ref_SB
E_ref_AB + E_ref_PB
E_ref_AB + E_ref_PB + E_ref_2B
E_act 的 I_background
I_1
I_1
IDD3N
IDD3N
E_RD/E_WR 的 I_background
IDD3N
IDD3N
IDD3N
IDD3N / I_2 (按 bank_arch)
tCK vs tWCK
使用 tCK
使用 tCK
使用 tCK
使用 tWCK (RD/WR)
E_srefV × I_DD6 × T_srefV × I_DD6 × T_srefV × I_DD6 × T_srefV × I_DD6 × T_sref
E_dsm
N/A
N/A
N/A
V × I_DD6DS × T_dsm
IDD5PB 计算
N/A
N/A
需要从 I_DD5PB 和 tREFI 反推
需要从 I_DD5PB 和 tREFI 反推
IBeta 参数
有
有
有
有
I_2 参数
N/A
N/A
N/A
IDD3N + (IDD3N - I_rho)
 (MBG 模式)

11.6 MemSpec 差异对比表

参数
DDR4
DDR5
LPDDR4
LPDDR5
bankGroups
BG0, BG1 (2 bit)
BG0, BG1, BG2 (3 bit)
无 Bank Group
可选 BG0, BG1 (MBG 架构)
banksPerGroup
固定 4
灵活
N/A
N/A
tRFCsb
N/A
✓
N/A
N/A
tRFCPB
N/A
N/A
✓
✓
vddq
✓
✓
✓
✓
wckAlwaysOnMode
N/A
N/A
N/A
✓
IBeta
✓ (单独)
✓ (在 PowerSpec 中)
✓ (在 PowerSpec 中)
✓ (在 PowerSpec 中)
prePostamble
✓ (read/write zeroes, zero_to_one)
N/A
N/A
N/A

12. 关键设计模式总结

12.1 为什么这个架构好

  1. Core 与 Interface 完全解耦:两者各自独立追踪状态,仅通过 SimulationStats 汇合。可以独立测试、独立替换、甚至独立序列化。

  2. ImplicitCommandHandler 解决了"时序语义的延迟表达":DRAM 中的 auto-precharge、refresh 后恢复、power-down 延迟等操作的本质是"在当前时间点注册未来时间点的行为"。这个延迟队列完美建模了这一语义,避免了在每次状态转移时手动检查"是否该自动 precharge 了"。

  3. interval_counter 是对 DRAM 周期统计的完美抽象:DRAM 状态的持续时长在状态开始时不可预知。start_interval → close_interval 的模式天然匹配了"由后续命令决定时长"的实际场景。is_open() 检查 + get_count_at() 的增量查询优雅地处理了"仿真中途查询统计"的需求。

  4. RegisterHelper 消除了路由模板代码:四种标准共 60+ handler,如果没有这个路由辅助层,每个标准都需要重复编写 ranks[cmd.rank].banks[cmd.bank] 等索引代码。

  5. burst_storage 的位级统计是性能关键:xor + popcount 在 O(1) 时间内完成任意窗口的位翻转统计,这对于需要处理大量数据总线的 Interface 层至关重要。

12.2 潜在的改进方向

  1. Bank 状态枚举可扩展为状态机模式:当前 Bank 只有 PRECHARGED ↔ ACTIVE 两种状态,状态转移逻辑散落在各个 handler 中。如果 Bank 状态更复杂(如 HBM 的更多状态),可以考虑引入正式的状态机模式。

  2. power计算与统计收集的分离:当前 getWindowStats 同时负责收集 Core 和 Interface 统计。如果希望增量式地查询power(而非等到仿真结束),可以引入 observer 模式在每次状态转移时推送增量统计。


本文档基于 DRAMPower v6.0.0 源码分析生成,覆盖了 src/DRAMPower/DRAMPower/ 下全部 80+ 源文件的深度阅读。


13. 实际运行测试

13.1 测试计划

阶段
内容
验证目标
构建
CMake 配置 + 编译
确认项目可构建,CLI 工具可运行
场景1
基础 Trace: ACT→PRE→END
验证最基本的 ACT/PRE 状态机和能量计算
场景2
读写 Trace: ACT→RD→PRE
验证 RD 命令 + 数据总线能量
场景3
RDA/WRA 自动预充电
验证 ImplicitCommandHandler 的延迟 PRE
场景4
REFA 全 Bank 刷新
验证刷新能量 + 隐式 PRE 后的状态恢复
场景5
PowerDown 进入/退出
验证 PDEA/PDXA + PDEP/PDXP 延迟机制
场景6
全功能 Trace
组合所有命令类型,验证综合能量计算

13.2 测试环境

操作系统:   WSL2 (Linux 6.6.87.2)
编译器:     GCC 13.3.0 (C++17)
构建系统:   CMake 3.22+
依赖:       DRAMUtils v1.12.1, spdlog 1.9.2, CLI11 2.4.2, nlohmann_json 3.11.3
优化级别:   -march=native -mtune=native (Release)

13.3 构建步骤

cd DRAMPower
cmake -S.-B build -DDRAMPOWER_BUILD_CLI=Y
cmake --build build --parallel
# Output: build/bin/cli

构建结果: ✅ 全部 8 个 target 编译成功(DRAMPower 静态库 + spdlog + CLI11 + cli_lib + cli 可执行文件),无警告无错误。

13.4 DDR5 MemSpec 参数(本次测试使用)

以下参数来自 tests/tests_drampower/resources/ddr5.json,模拟一款典型的 DDR5-5200 x8 1Rank 内存:

参数
值
说明
memoryType
DDR5
nbrOfBanks
16
16 Bank
nbrOfBankGroups
2
2 Bank Group(每组 8 Bank)
nbrOfRanks
1
单 Rank
nbrOfDevices
1
单 Device
burstLength
8
BL8
dataRate
2
DDR
width
8
x8 位宽
tCK
0.769 ns
≈1.3 GHz 时钟频率
tRAS
10 cycles
行激活最小时间
tRCD
13 cycles
RAS-to-CAS 延迟
tRP
10 cycles
预充电时间
tRFC
25 cycles
全 Bank 刷新时间 (RFC1)
tRFCsb
20 cycles
同 Bank Group 刷新时间
VDD
1.2 V
核心电压
VPP
1.2 V
字线泵电压(DDR5 特有)
VDDQ
1.1 V
I/O 电压
IDD0
56.25 mA
单 Bank Active 背景电流
IDD2N
33.75 mA
单 Bank Precharge 背景电流
IDD3N
39.50 mA
Active Standby 电流
IDD4R
157.50 mA
Read 操作电流
IDD4W
135.00 mA
Write 操作电流
IDD5B
118.00 mA
All-Bank Refresh 电流
IDD6N
20.25 mA
Self-Refresh 电流
factRho
0.5
银行间功率因子 ρ
CK R_eq
1 MΩ
时钟终端电阻
CK dyn_E
1 pJ
时钟动态切换能量
CA/DQ/DQS R_eq
1 MΩ
I/O 终端电阻
CA/DQ/DQS dyn_E
1 pJ
I/O 动态切换能量

13.5 配置文件

CLI 使用的配置文件 config.json:

{
"useToggleRate":false,
"simconfig":{}
}

不使用 Toggling Rate 模式,使用真实数据(Bus 模式)。

13.6 场景1:基础 Trace(ACT→PRE→END)

Trace 文件 (tests/tests_drampower/resources/ddr5.csv):

0 ,ACT,0,0,0,0,0
15,PRE,0,0,0,0,0
15,END,0,0,0,0,0

命令序列:
| 时间 | 命令 | 含义 |
|---|---|---|
| t=0 | ACT | 激活 Bank 0 |
| t=15 | PRE | 预充电 Bank 0 |
| t=15 | END | 仿真结束 |

运行命令:

./build/bin/cli -m tests/tests_drampower/resources/ddr5.json \
-c build/bin/config.json \
-t tests/tests_drampower/resources/ddr5.csv

结果:

Rank,Device,Bank 0: ACT: 1.795e-10 J  PRE: 2.077e-10 J  BG_ACT: 2.488e-12 J  BG_PRE: 0
Rank,Device,Bank 1-15: (全零)
Cumulated:  ACT: 1.795e-10  PRE: 2.077e-10  BG_ACT: 5.096e-10  BG_PRE: 0
Shared:     E_bg_act_shared: 5.071e-10  E_PDNA: 0  E_PDNP: 0  E_sref: 0
Interface:  Controller: dyn=4.30e-11, static=3.63e-14  DRAM: dyn=0, static=0
Total Energy: 9.398e-10 J ≈ 0.94 nJ

验证:

  • E_act = 1.795 × 10⁻¹⁰ J — 与单元测试 ddr5_test_pattern_0.cpp 的期望值 179 pJ (×10¹²) 一致 ✓
  • E_pre = 2.077 × 10⁻¹⁰ J — 与期望值 208 pJ 一致 ✓
  • E_bg_act = 5.096 × 10⁻¹⁰ J — 与期望值 510 pJ 一致 ✓
  • E_bg_act_shared = 5.071 × 10⁻¹⁰ J — 与期望值 507 pJ 一致 ✓
  • cycles.act = 15, cycles.pre = 0 — 状态机正确追踪了 ACTIVE 时长 ✓

13.7 场景2-5:增量命令测试

每个场景均通过 CLI 验证正确性:

场景
Trace
验证点
结果
ACT+RD+PRE
t=0 ACT, t=15 RD(data), t=35 PRE
E_RD = 4.357e-10 J,Interface 有数据总线能量
✅
ACT+RDA
t=0 ACT, t=20 RDA(data), t=40 END
E_RDA + E_pre_RDA 正确计入,自动 PRE 被延迟执行
✅
ACT+REFA
t=0 ACT, t=20 REFA, t=50 END
全部 16 Bank 的 E_ref_AB 均非零,隐式 PRE 由 REFA 覆盖
✅
ACT+PDEA/PDXA
t=0 ACT, t=25 PDEA, t=80 PDXA, t=95 PRE
E_PDNA 非零,PD 退出后 Bank 恢复 ACTIVE
✅
ACT+PDEP/PDXP
t=0 ACT, t=15 PRE, t=25 PDEP, t=80 PDXP
E_PDNP 非零,PD 退出后 Bank 保持 PRECHARGED
✅

13.8 场景6:全功能综合 Trace

Trace 设计(22 条命令,span=460 cycles):

BTW:此处试运行的场景还是偏简单的,但是可视化后的效果还是很好。具体要精确度,实际上还是可以用具体的访问memory的行为给一个比较长的trace来表征。具体跟实际行为之间是否有power数据gap,还需要更多的数据对比。

#  Time  Cmd   Rnk BG Bk  Data(hex)        说明
──────────────────────────────────────────────────
1    0   ACT    0   0  0   —               Bank 0 激活
2   15   RD     0   0  0   DEADBEEFCAFEBABE 读 Bank 0
3   35   PRE    0   0  0   —               预充电
4   45   ACT    0   0  1   —               Bank 1 激活
5   60   WR     0   0  1   0123456789ABCDEF 写 Bank 1
6   80   PRE    0   0  1   —               预充电
7   90   ACT    0   0  2   —               Bank 2 激活
8  110   RDA    0   0  2   AABBCCDDEEFF0011 读+自动预充电
9  135   ACT    0   0  3   —               Bank 3 激活
10 155   WRA    0   0  3   FEEDFACEC0FFEE00 写+自动预充电
11 180   ACT    0   0  4   —               Bank 4 激活
12 200   REFA   0   0  0   —               全部 Bank 刷新
13 240   ACT    0   0  0   —               Bank 0 再次激活
14 255   RD     0   0  0   1122334455667788 读 Bank 0
15 275   PRE    0   0  0   —               预充电
16 285   ACT    0   0  0   —               Bank 0 第三次激活
17 310   PDEA   0   0  0   —               进入 Active PowerDown
18 370   PDXA   0   0  0   —               退出 Active PowerDown
19 390   PRE    0   0  0   —               预充电
20 400   PDEP   0   0  0   —               进入 Precharge PowerDown
21 450   PDXP   0   0  0   —               退出 Precharge PowerDown
22 460   END    0   0  0   —               仿真结束

运行命令:

./build/bin/cli -m tests/tests_drampower/resources/ddr5.json \
-c build/bin/config.json \
-t ddr5_full.csv \
-j output.json

13.9 能量可视化图表

使用 scripts/drampower_chart.py 对 JSON 输出生成 4 张图表:(此处的脚本在原来的开源环境里面没有,是通过claude code自行进行优化后产生的可视化python脚本)

python3 scripts/drampower_chart.py output.json -o docs/diagrams/

图表1:每个 Bank 的能量堆叠柱状图

  • 上图:16 个 Bank 按能量类别(Activation/Read-Write/Precharge/Refresh/Background)堆叠显示
  • 下图:仅显示有活动的 Bank(Bank 0-4 + Bank 5-15),详细拆分为 11 个能量分量
  • 关键观察:Bank 0 能量最高(3 次 ACT + 2 次 RD),Bank 4 的 E_pre=0(REFA 导致隐式 PRE),Bank 5-15 仅有背景功耗 + 刷新分摊

图表2:Power组成环形图 + 分类排序

  • 左侧环形图:各能量类别的占比可视化
    • BG Active (Shared): 40.4% — 最大分量
    • BG Active (per-bank): 约 24%
    • BG Precharge: 约 14%
    • PowerDown: 约 9.4%
  • 右侧横向柱状图:按大小排序,附精确数值
  • 关键观察:背景功耗(BG Active + BG Precharge + Shared)合计占 ~79% 总能量

图表3:Core vs Interface 能量流图

  • 上层:Core Energy (94.3%) vs Interface Energy (5.7%) 两大块
  • 中层:7 个子类别分解(Direct Ops / Bank BG / Shared BG / PowerDown / I/O Ctrl / I/O DRAM)
  • 下层:14 个能量分量的排序柱状图(从 BG Act Shared 到 I/O DRAM Static)
  • 关键观察:Interface 能量仅 1.22 nJ,远小于 Core 的 20.04 nJ

图表4:综合仪表盘

包含 6 个面板:

  • (0,0) 总能量指示器: 21.26 nJ / 460 cycles
  • (0,1) Core 能量分布: 6 大类柱状图
  • (0,2) Interface 能量: Ctrl/Dram Dynamic/Static (pJ 级)
  • (1,0) Top 5 最耗能 Bank: Bank 0 (2.8 nJ) > Bank 4 > Bank 2 > Bank 3 > Bank 1
  • (1,1) Bank Group 能量表: BG0 vs BG1 各操作的能量分布
  • (1,2) 关键指标: 平均功率 60.1 mW,背景功耗占 Core 的 81.3%

13.10 测试结果关键洞察

洞察
数据支撑
背景功耗是主要消耗
BG Active + BG Precharge + Shared = 79.1% 总能量。460 cycles 中大多数 Bank 处于空闲状态,背景泄漏电流持续消耗能量
PowerDown 显著节能
60 cycles PDA 消耗 1.25 nJ (IDD3P=22.5mA),若未进入 PD 则消耗约 1.85 nJ (IDD3N=39.5mA),节省 32%
Interface 能量可忽略
I/O 能量仅占总量的 5.7%,主要能耗在核心阵列而非数据传输
REFA 吞噬显式 PRE
Bank 4 的 E_pre=0,因为 REFA 的隐式 PRE 不产生额外 PRE 能量,刷新能量已覆盖
RDA 与 RD+PRE 能量等价
RDA 的 E_RDA + E_pre_RDA ≈ RD+PRE 的 E_RD + E_pre,差异仅来自 ACTIVE 时长的背景功耗微小变化
单次操作能量级
ACT≈0.18 nJ, PRE≈0.21 nJ, RD≈0.44 nJ, WR≈0.35 nJ, REF(all-bank)≈1.81 nJ (16 bank 合计)

13.11 自动化测试脚本

所有测试场景已整合到以下脚本中,可一键复现:

scripts/drampower_chart.py — 通用 DRAMPower 可视化脚本

# 用法
python3 scripts/drampower_chart.py <output.json>-o<charts_dir>

# 生成 4 张图表:
#   {prefix}_per_bank.png      — Per-Bank Power堆叠图
#   {prefix}_composition.png   — Power组成环形图 + 分类排序
#   {prefix}_energy_flow.png   — Core vs Interface Power流图
#   {prefix}_dashboard.png     — 6 面板综合仪表盘

支持的 JSON 格式:任何由 drampower_cli -j output.json 生成的输出文件。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料