IPMI / BMC 系列約 8 分鐘

電源供應器 FRU EEPROM 解析:MultiRecord、Power Supply Information 與 DC Output

CRPS / RPS 電源的 IPMI FRU EEPROM 怎麼看?逐位元組解析 MultiRecord Area 的記錄標頭、Power Supply Information(00h)與 DC Output(01h)記錄:額定功率、輸入電壓範圍、Hold-up、輸出電壓與電流,附單位換算陷阱、65 A 電流上限問題與 Python 解析程式。

最後更新:

📘 參考規格書IPMI FRU · IPMI Platform Management FRU Information Storage Definition v1.0 · Rev 1.3原廠文件 ↗

還不熟悉 FRU 基本結構(Common Header、×8 偏移、Type/Length、Checksum)的話,建議先看 IPMI FRU 資料格式完整解析。

電源 FRU EEPROM 的位置與內容

CRPS 這類冗餘電源,常見的設計是 PMBus 控制器和 FRU EEPROM 掛在同一條 SMBus 上。BMC 透過同一條匯流排,既能用 PMBus 讀即時量測,也能讀 FRU 取得型號與規格。

FRU EEPROM 的位址,規格書上通常寫成 A0h、A2h、A4h、A6h,依電源插槽位置決定。這是 8 位元寫入位址的寫法,在 Linux 等使用 7 位元位址的工具中,要換成 0x50、0x51、0x52、0x53:

規格書(8 位元)Linux / i2c-tools(7 位元)
A0h0x50
A2h0x51
A4h0x52
A6h0x53

7 位元與 8 位元位址的差別,詳見 PMBus 常見問題除錯。實際位址、EEPROM 容量與寫入保護方式,請以電源的產品規格書為準。

區域電源 FRU 中的用途
Board Info Area電源模組的製造商、型號、序號、料號、製造日期
Product Info Area產品名稱、版本、序號
MultiRecord Area電源規格:額定功率、輸入範圍、每一路輸出的電壓與電流

範例資料

以下是一顆 800 W 電源的 FRU 資料(169 Bytes),重點放在 68h 開始的 MultiRecord Area:

0000: 01 00 00 01 07 0D 00 EA 01 06 00 F0 71 F7 C4 41   ← Common Header + Board Area
  …   (Board / Product Info Area 省略)
0060: 30 31 C0 C0 C1 00 00 06 00 02 18 D1 15 20 03 E8
0070: 03 28 0A 28 23 20 67 00 00 00 00 2F 3F 0A 0E 84
0080: 13 00 00 00 00 01 02 0D 62 8E 01 C4 04 3C 00 3C
0090: 00 78 00 00 00 E8 FD 01 82 0D 0D 63 82 B0 04 3C
00A0: 00 3C 00 78 00 00 00 C4 09

Common Header 的第 5 個 Byte 是 0Dh,所以 MultiRecord Area 從 0Dh × 8 = 68h 開始。

MultiRecord Area 的結構

MultiRecord Area 是一串記錄,每筆記錄 = 5 Bytes 標頭 + 資料,一筆接一筆,直到標頭中的 End of List 位元為 1。

範例中的三筆記錄:

位置標頭TypeByte 1長度意義
68h00 02 18 D1 1500h02h24Power Supply Information,還有下一筆
85h01 02 0D 62 8E01h02h13DC Output,還有下一筆
97h01 82 0D 0D 6301h82h13DC Output,最後一筆(b7 = 1)

下一筆記錄的位置 = 目前位置 + 5 + 資料長度:68h + 5 + 24 = 85h、85h + 5 + 13 = 97h。

常見的 Record Type:

Type名稱電源 FRU 是否常用
00hPower Supply Information✅ 幾乎必備
01hDC Output✅ 每一路輸出一筆
02hDC Load用電端(例如主機板)描述需求時使用
03hManagement Access管理介面資訊
04h / 05hBase / Extended Compatibility相容性資訊
C0h ~ FFhOEM廠商自訂

Power Supply Information(Record Type 00h)

固定 24 Bytes,所有多位元組欄位都是低位元組在前。

006D: 20 03  E8 03  28  0A  28 23  20 67  00 00  00 00  2F  3F  0A  0E  84 13  00  00 00  00
Offset長度欄位範例解讀
02Overall Capacity20 030320h = 800 W(b[11:0])
22Peak VAE8 031000 VA(FFFFh = 未指定)
41Inrush Current2840 A(FFh = 未指定)
51Inrush Interval0A10 ms
62Input Voltage Range 1 Low28 232328h = 9000 → 90.00 V
82Input Voltage Range 1 High20 676720h = 26400 → 264.00 V
102Input Voltage Range 2 Low00 00只有一個輸入範圍時填 0
122Input Voltage Range 2 High00 00同上
141Input Frequency Low2F47 Hz
151Input Frequency High3F63 Hz
161A/C Dropout Tolerance0A10 ms:AC 中斷多久內輸出仍能維持
171Binary Flags0E見下表
182Peak Wattage84 131384h:b[15:12] = 1 秒、b[11:0] = 900 W
201Combined Wattage 電壓00b[7:4] 電壓 1、b[3:0] 電壓 2
212Combined Wattage00 000 = 未指定
231Predictive Fail Tach Threshold00風扇轉速下限(RPS)

Binary Flags(Offset 17)

Bit意義範例 0Eh
0Predictive Fail 訊號支援0
1Power Factor Correction(PFC)1
2Autoswitch(自動切換輸入電壓範圍)1
3Hot Swap 支援1
4搭配 Predictive Fail 使用的訊號形式設定0
7:5保留0

Peak Wattage 與 Hold-up

Offset 18 的 2 Bytes 塞了兩個值:

1384h=0001⏟Hold-up 1 秒 0011 1000 0100⏟900 W\texttt{1384h} = \underbrace{0001}_{\text{Hold-up 1 秒}}\ \underbrace{0011\,1000\,0100}_{\text{900 W}}

代表電源可以輸出 900 W 的峰值功率,維持 1 秒。

Combined Wattage

有些電源的多路輸出共用功率預算(例如 12 V 和 5 V 合計不能超過某個值),就用這 3 Bytes 描述。電壓代碼:0 = +12 V、1 = −12 V、2 = +5 V、3 = +3.3 V。CRPS 這類以 12 V 為主的電源,通常填 0(未指定)。

DC Output(Record Type 01h)

每一路輸出一筆,固定 13 Bytes。範例有兩筆:主輸出 12 V 和待機輸出 12 VSB。

008A: 01  C4 04  3C 00  3C 00  78 00  00 00  E8 FD     ← 第 1 路:主輸出
009C: 82  B0 04  3C 00  3C 00  78 00  00 00  C4 09     ← 第 2 路:待機輸出
Offset長度欄位單位主輸出待機輸出
01Output Info—01h:第 1 路82h:b7 = 1 待機、第 2 路
12Nominal Voltage10 mV(有號數)04C4h = 12.20 V04B0h = 12.00 V
32Max Negative Deviation10 mV0.60 V0.60 V
52Max Positive Deviation10 mV0.60 V0.60 V
72Ripple & Noise(pk-pk)mV120 mV120 mV
92Minimum Current DrawmA0 A0 A
112Maximum Current DrawmAFDE8h = 65.000 A09C4h = 2.500 A

Output Info 的 b7 是待機輸出旗標,b[3:0] 是輸出編號。

大電流電源的限制:16 位元只能到 65 A

DC Output 的電流欄位是 16 位元、單位 mA,最大只能表示 65.535 A。一顆 1600 W 的 12 V 電源,主輸出電流超過 130 A,根本塞不進這個欄位。

額定功率(12 V 輸出)最大電流DC Output(01h)能否表示
500 W約 42 A✅
800 W約 66 A⚠️ 剛好在上限附近
1600 W約 133 A❌
3000 W約 250 A❌

實務上,大多數電源的 FRU 只使用 DC Output(01h)。規格中另外有兩種做法可以處理大電流,遇到時知道它們是什麼即可:

做法說明
Extended DC Output(09h)FRU 規格 Rev 1.3 新增的記錄類型,結構和 DC Output 類似,目的就是支援更大的電流。ipmitool 等工具不一定都能解析
OEM 記錄(C0h ~ FFh)Record Type C0h 以上保留給廠商自訂,內容格式由廠商或客戶的規格定義,只有對方的工具看得懂

所以主輸出電流超過 65.535 A 時,該怎麼填,要以客戶或平台的 FRU 規格為準。拿到別家電源的 FRU 時,如果 DC Output 的電流看起來不合理,或看到 09h、C0h 以上的記錄,就是上面這兩種情況。

用 ipmitool 查看

在 BMC 上執行 ipmitool fru print,MultiRecord 會被解析成類似以下的內容(實際格式依 ipmitool 版本略有不同):

  Capacity                   : 800 W
  Peak VA                    : 1000 VA
  Inrush Current             : 40 A
  Inrush Interval            : 10 ms
  Input Voltage Range 1      : 90-264 V
  Input Voltage Range 2      : 0-0 V
  Input Frequency Range      : 47-63 Hz
  A/C Dropout Tolerance      : 10 ms
  Flags                      : 'Power factor correction' 'Autoswitch voltage' 'Hot swap'
  Peak capacity              : 900 W
  Peak capacity holdup       : 1 s
  Combined capacity          : not specified
  Output Number              : 1
  Standby power              : No
  Nominal voltage            : 12.20 V
  Max negative deviation     : 0.60 V
  Max positive deviation     : 0.60 V
  Ripple and noise pk-pk     : 120 mV
  Minimum current draw       : 0.000 A
  Maximum current draw       : 65.000 A

用 Python 解析 MultiRecord

以下程式解析 MultiRecord Area,驗證每筆記錄的兩個 Checksum,並解碼 Power Supply Information 與 DC Output:

#!/usr/bin/env python3
"""psu_fru.py:解析電源 FRU 的 MultiRecord Area(Power Supply Information、DC Output)"""
import struct
import sys

RECORD_NAMES = {
    0x00: "Power Supply Information", 0x01: "DC Output", 0x02: "DC Load",
    0x03: "Management Access", 0x04: "Base Compatibility", 0x05: "Extended Compatibility",
}
FLAG_NAMES = ["Predictive Fail", "Power Factor Correction", "Autoswitch", "Hot Swap"]
COMBINED_V = {0: "+12V", 1: "-12V", 2: "+5V", 3: "+3.3V"}


def zero_sum(data):
    return sum(data) & 0xFF == 0


def parse_multirecords(data):
    """從 Common Header 的 MultiRecord 位置開始,讀到 End of List 為止"""
    offset = data[5] * 8
    records = []
    while offset:
        header = data[offset:offset + 5]
        if not zero_sum(header):
            raise ValueError(f"記錄標頭 Checksum 錯誤(位置 {offset:#06x})")
        rtype, fmt, length, record_cs, _ = header
        body = data[offset + 5:offset + 5 + length]
        if (sum(body) + record_cs) & 0xFF:
            raise ValueError(f"記錄資料 Checksum 錯誤(位置 {offset:#06x})")
        records.append((rtype, body))
        offset = 0 if fmt & 0x80 else offset + 5 + length
    return records


def decode_power_supply(b):
    (cap, peak_va, inrush, inrush_ms, lo1, hi1, lo2, hi2, f_lo, f_hi,
     dropout, flags, peak, comb_v, comb_w, rps) = struct.unpack("<HHBBHHHHBBBBHBHB", b[:24])
    return {
        "Overall Capacity": f"{cap & 0x0FFF} W",
        "Peak VA": "未指定" if peak_va == 0xFFFF else f"{peak_va} VA",
        "Inrush Current": "未指定" if inrush == 0xFF else f"{inrush} A",
        "Inrush Interval": f"{inrush_ms} ms",
        "Input Voltage Range 1": f"{lo1 / 100:.2f} ~ {hi1 / 100:.2f} V",
        "Input Voltage Range 2": "無" if lo2 == hi2 == 0 else f"{lo2 / 100:.2f} ~ {hi2 / 100:.2f} V",
        "Input Frequency": f"{f_lo} ~ {f_hi} Hz",
        "A/C Dropout Tolerance": f"{dropout} ms",
        "Flags": ", ".join(n for bit, n in enumerate(FLAG_NAMES) if flags >> bit & 1) or "無",
        "Peak Wattage": f"{peak & 0x0FFF} W(維持 {peak >> 12} 秒)",
        "Combined Wattage": "未指定" if comb_w == 0 else
            f"{comb_w} W({COMBINED_V.get(comb_v >> 4, '?')} + {COMBINED_V.get(comb_v & 0x0F, '?')})",
    }


def decode_dc_output(b):
    info, nominal, neg, pos, ripple, i_min, i_max = struct.unpack("<BhhhHHH", b[:13])
    return {
        "Output Number": info & 0x0F,
        "Standby": "Yes" if info & 0x80 else "No",
        "Nominal Voltage": f"{nominal / 100:.2f} V",
        "Max Negative Deviation": f"{neg / 100:.2f} V",
        "Max Positive Deviation": f"{pos / 100:.2f} V",
        "Ripple & Noise (pk-pk)": f"{ripple} mV",
        "Current Draw": f"{i_min / 1000:.3f} ~ {i_max / 1000:.3f} A",
    }


DECODERS = {0x00: decode_power_supply, 0x01: decode_dc_output}

if __name__ == "__main__":
    with open(sys.argv[1], "rb") as f:
        data = f.read()
    for rtype, body in parse_multirecords(data):
        print(f"[{RECORD_NAMES.get(rtype, 'OEM / 其他')} ({rtype:02X}h)]")
        decoder = DECODERS.get(rtype)
        if decoder is None:
            print("  " + body.hex(" ").upper())
            continue
        for k, v in decoder(body).items():
            print(f"  {k:<24}: {v}")

執行結果:

$ python3 psu_fru.py psu_fru.bin
[Power Supply Information (00h)]
  Overall Capacity        : 800 W
  Peak VA                 : 1000 VA
  Inrush Current          : 40 A
  Inrush Interval         : 10 ms
  Input Voltage Range 1   : 90.00 ~ 264.00 V
  Input Voltage Range 2   : 無
  Input Frequency         : 47 ~ 63 Hz
  A/C Dropout Tolerance   : 10 ms
  Flags                   : Power Factor Correction, Autoswitch, Hot Swap
  Peak Wattage            : 900 W(維持 1 秒)
  Combined Wattage        : 未指定
[DC Output (01h)]
  Output Number           : 1
  Standby                 : No
  Nominal Voltage         : 12.20 V
  Max Negative Deviation  : 0.60 V
  Max Positive Deviation  : 0.60 V
  Ripple & Noise (pk-pk)  : 120 mV
  Current Draw            : 0.000 ~ 65.000 A
[DC Output (01h)]
  Output Number           : 2
  Standby                 : Yes
  Nominal Voltage         : 12.00 V
  Max Negative Deviation  : 0.60 V
  Max Positive Deviation  : 0.60 V
  Ripple & Noise (pk-pk)  : 120 mV
  Current Draw            : 0.000 ~ 2.500 A

Board / Product Info Area 可以用上一篇的 fru_decode.py 解析,兩支程式讀同一個檔案即可。

新手常見錯誤

錯誤正確觀念
把 Record Length 乘 8MultiRecord 的長度是實際 Byte 數,和 Info Area 不同
只驗證一個 Checksum每筆記錄有標頭和資料兩個 Checksum
讀到 End of List 後繼續往下讀Byte 1 的 b7 = 1 就是最後一筆
把 2328h 讀成 9000 V輸入電壓與輸出電壓的單位是 10 mV,要除以 100
把電流單位當成 A 或 10 mADC Output 的電流單位是 mA,最大 65.535 A
把 Peak Wattage 整個當成功率高 4 位元是 Hold-up 秒數,低 12 位元才是瓦數
用大端序讀多位元組欄位FRU 的多位元組欄位一律低位元組在前
修改 FRU 後沒有更新兩個 Checksumipmitool 會回報錯誤或略過該筆記錄

結論

  • 電源 FRU 的規格資料放在 MultiRecord Area,位置記在 Common Header 的第 5 Byte(×8)。
  • 每筆記錄 = 5 Bytes 標頭 + 資料;標頭 Byte 1 的 b7 標示最後一筆,兩個 Checksum 都要驗證。
  • Power Supply Information(00h):24 Bytes,記錄額定功率、輸入範圍(10 mV)、Hold-up 等。
  • DC Output(01h):每一路一筆,13 Bytes,電壓 10 mV、電流 mA(上限 65.535 A)。
  • 多數電源只用 DC Output;主輸出超過 65.535 A 時,依客戶或平台的 FRU 規格處理(Extended DC Output 09h 或 OEM 記錄)。

延伸閱讀: