*openUBMC 支持PDC4000D12_LC多路输出电源详细设计说明书*

<table>
    <tr>
        <td>所属SIG组:</td>
        <td>hardware_sig</td>
    </tr>
    <tr>
        <td>落入版本:</td>
        <td>26.4</td>
    </tr>
    <tr>
        <td>设计人员:</td>
        <td>章鹏</td>
    </tr>
    <tr>
        <td>日期:</td>
        <td>2026.02.03</td>
    </tr>
</table>

**Copyright © 2026 openUBMC Community**

您对&quot;本文档&quot;的复制，使用，修改及分发受木兰宽松许可证, 第2版协议(以下简称&quot;MulanPSL2&quot;)的约束。
为了方便用户理解，您可以通过访问<https://license.coscl.org.cn/MulanPSL2>了解MulanPSL2的概要 (但不是替代)。
MulanPSL2的完整协议内容您可以访问如下网址获取：<https://license.coscl.org.cn/MulanPSL2>。

**改版记录**

<table>
    <tr>
        <th>日期</th>
        <th>修订版本</th>
        <th>修订描述</th>
        <th>作者</th>
        <th>审核</th>
    </tr>
    <tr>
        <td>2026.02.03</td>
        <td>1.0</td>
        <td>初稿</td>
        <td>章鹏</td>
        <td>审核人</td>
    </tr>
</table>

**List of abbreviations**  **缩略语清单** ：

<table>
    <tr>
        <th>Abbreviations 缩略语</th>
        <th>Full spelling 英文全名</th>
        <th>Chinese explanation 中文解释</th>
    </tr>
    <tr>
        <td>BMC</td>
        <td>Baseboard Management Controller</td>
        <td>基板管理控制器</td>
    </tr>
    <tr>
        <td>PSU</td>
        <td>Power Supply Unit</td>
        <td>电源供应单元</td>
    </tr>
    <tr>
        <td>PMBus</td>
        <td>Power Management Bus</td>
        <td>电源管理总线</td>
    </tr>
    <tr>
        <td>PAGE</td>
        <td>PMBus Page Command</td>
        <td>PMBus切换输出页命令字</td>
    </tr>
</table>

[TOC]

# 1.功能分析
## 1.1 功能背景
现有多路输出电源，具有A路（PAGE=0x0）与B路（PAGE=0x1）两路输出通道。现有实现仅读取A路状态信息，无法准确反映B路输出状态。当B路发生输出电压故障、输出电流故障、过温故障等异常时，BMC无法感知，导致告警遗漏，影响系统可靠性。现在要支持检测两路输出健康状态，任何一路异常均要保证电源能够记录到。

## 1.2 功能描述
现在要支持检测两路输出健康状态，任何一路异常均要保证电源能够记录到。

## 1.3 功能场景
1. A路正常，B路异常：合并后产生告警。<br>
2. A路异常，B路正常：合并后产生告警。<br>
3. A/B均异常：合并后告警。<br>
4. A/B均正常：合并后无告警。

## 1.4 功能列表
| 功能编号 | 功能标题 | 功能描述 |
| ------- | ------- | -------- |
| 1 | 多路输出健康状态监控 | A/B路状态轮询与合并，确保任一路异常均上报 |

# 2.功能设计
## 2.1 总体方案分析

### 2.1.1 方案详细设计
#### 2.1.1.1 方案概述
**总体策略：**
- PAGE切换与输出读取在同一协程内串行执行，避免加锁带来的性能开销与数据一致性问题。
- 输出参数读取保持原周期，仅在A路读取；B路仅读取告警。
- 输入告警读取优先级为A路缓存 -> B路缓存 -> 上一轮；A/B缓存均无值时保持上一轮值。
- 输入参数默认读取A路，维持现有采集方式不做A/B合并。

#### 2.1.1.2 开发视图
```mermaid
flowchart LR
    subgraph Layer1[上层-告警管理层]
        Event[event组件<br>配置告警对象<br>接收并上报告警]
    end

    subgraph Layer2[CSR]
        CSR[电源CSR<br>承载OnePower和告警对象]
        OnePower[OnePower对象<br>刷新属性并驱动告警]
        Alarm[告警对象<br>表达式判定告警]
    end

    subgraph Layer3[电源协议层]
        Monitor[Monitor对象<br>双路状态轮询与合并]
        PMBus[PMBus协议对象<br>PAGE切换与寄存器读取]
    end

    subgraph Layer4[硬件层]
        PSU[PSU硬件<br>PDC4000D12_LC]
    end

    Event -.配置表达式.-> Alarm
    Alarm -.触发告警.-> Event
    OnePower -->|属性同步| Alarm
    CSR --- OnePower
    CSR --- Alarm
    OnePower -->|调用| Monitor
    Monitor -->|调用| PMBus
    PMBus -->|I2C通信| PSU
```

#### 2.1.1.3 运行视图
```mermaid
sequenceDiagram
    participant Event as event组件
    box 单电源CSR
        participant Alarm as 告警对象
        participant OnePower as OnePower对象
        participant Monitor as Monitor对象
    end
    box PMBus协议层
        participant PMBus as PMBus对象
    end
    participant PSU as PSU硬件

    Note over Monitor,PSU: 双路轮询(500ms)
    loop 轮询周期

        Monitor->>PMBus: set_page(PAGE_PRIMARY/0x0)
        alt PAGE切换成功
            Monitor->>PMBus: 读取A路输出参数
            PMBus->>PSU: READ_12VOUT/READ_12VIOUT/READ_POUT (A路)
            PSU-->>PMBus: 返回A路输出参数
            PMBus-->>Monitor: 返回A路输出参数
            PMBus->>PSU: 写PAGE=0x0
            PSU-->>PMBus: 返回写入结果
            Monitor->>PMBus: 读取A路告警
            PMBus->>PSU: STATUS_* (A路)
            PSU-->>PMBus: 返回A路告警
            PMBus-->>Monitor: 返回A路告警
        else PAGE切换失败
            Monitor->>Monitor: 跳过A路输出信息读取
        end

        Monitor->>PMBus: set_page(PAGE_SECONDARY/0x1)
        alt PAGE切换成功
            PMBus->>PSU: 写PAGE=0x1
            PSU-->>PMBus: 返回写入结果
            Monitor->>PMBus: 读取B路健康状态
            PMBus->>PSU: STATUS_* (B路)
            PSU-->>PMBus: 返回B路状态
            PMBus-->>Monitor: 返回B路状态
        else PAGE切换失败
            Monitor->>Monitor: 跳过B路输出信息读取
        end

        Monitor->>Monitor: A/B路状态按位或合并
        Monitor->>OnePower: 刷新health/health_event/scan_status
        OnePower->>Alarm: 属性变化通知
    end

    loop 告警表达式计算
        Alarm->>Alarm: 根据同步属性求值
        alt 条件满足
            Alarm->>Event: 触发告警事件
        else
            Note over Alarm: 保持清除
        end
        Event->>Event: 汇总并上报告警
    end
```

### 2.1.2 内部依赖分析
不涉及新增组件依赖，仅涉及power_mgmt组件内部的协议与监控实现。

### 2.1.3 外部依赖分析
不涉及。

### 2.1.4 北向接口分析
| 特性 | SNMP | CLI | WEB | KVM_VMM | IPMI(RMCP/RMCP+) | HMM | Redfish |
| ---- | ---- | ---- | --- | ------ | ---------------- | --- | ------- |
| PDC4000D12_LC多路输出告警监控 | NA | NA | NA | NA | NA | NA | NA |

### 2.1.5 兼容性分析
| 影响点 | 影响描述 | 应对措施 |
| --- | --- | --- |
| 非PDC4000D12_LC电源 | 仅该型号加载专用monitor/protocol实现 | 通过设备树协议映射隔离 |
| 输出状态合并 | 上报值可能包含B路异常 | 预期行为，提升告警完整性 |

### 2.1.6 定制化接口分析
不涉及。

### 2.1.7 导入导出分析
不涉及。

### 2.1.8 传感器分析
不涉及。

### 2.1.9 精准告警事件分析
复用现有输出电压/输出电流/温度/输入异常等告警规则，同时新增2个维护事件（维护事件不会产生告警记录，仅记录维护日志）
| 事件码 | 维护日志  |
| --- | --- |
| 0x03000091  | 电源%1内部故障（%2） |
| 0x03000093   | 电源%1切换输出信息上报通道%2失败 |


### 2.1.10 系统锁定分析
不涉及。

### 2.1.11 用例场景分析
| 用例编号 | 场景  | 预期  |
| --- | --- | --- |
| 1   | A路正常，B路输出欠压 | 产生输出欠压告警 |
| 2   | A路过流，B路正常 | 产生输出过流告警 |
| 3   | A路过温，B路过温 | 产生过温告警 |
| 4   | A路过温，B路正常 | 产生过温告警 |
| 5   | A/B两路均正常 | 无告警产生 |
| 6   | A路other_status的bit7为1，B路正常 | 产生维护事件 |
| 7   | PAGE切换到A路失败 | A路读取跳过，产生维护事件 |
| 7   | PAGE切换到B路失败 | B路读取跳过，产生维护事件 |
| 8   | A路告警读取失败 | 1、连续3轮周期读取失败，scan_status的bit0置零；读取成功一次，scan_status的bit0置1。  <br>2、影响communication_status的更新，A/B两路健康状态采集结果均采集失败且输入功率采集失败才会置为1，此时触发电源通讯异常告警。 |
| 9   | B路告警读取失败 | 1、连续3轮周期读取失败，scan_status的bit4置零；读取成功一次，scan_status的bit4置1。  <br>2、影响communication_status的更新，A/B两路健康状态采集结果均采集失败且输入功率采集失败才会置为1，此时触发电源通讯异常告警。 |
| 10  | A路输出参数读取失败 | 连续3轮周期读取失败，scan_status的bit5置零；读取成功一次，scan_status的bit5置1 |

## 2.2 非功能质量属性设计
### 2.2.1 扩展性分析
1.  读取状态命令封装在协议层get_health_event，轮询任务封装在monitor层，便于扩展新增电源状态命令字。

### 2.2.2 重用性分析
1. PAGE切换能力封装在协议层set_page/get_page，便于其他多路电源复用。
2. Monitor层状态合并逻辑可复制到其他多路输出电源型号。。

### 2.2.3 可测试性分析
1. 通过打桩协议回读的告警值模拟A/B路状态组合，验证合并逻辑。
2. 通过UT构造PAGE切换失败/读失败场景验证scan_status与health更新。

### 2.2.4 资料分析
不涉及。

### 2.2.5 资源使用分析
1. I2C通信增加：每轮询增加PAGE切换与B路状态读取，输出参数周期保持不变。
2. CPU与内存开销：状态合并为位运算，新增少量状态计数与拷贝表，影响可忽略。
3. 协程数量未增加，仍使用现有监控任务。

### 2.2.6 可靠性分析
| 编号  | 场景  | 问题描述 | 可靠性影响 | 问题级别 | 建议解决方案/需求 | 讨论结果 | 跟踪人 | 备注  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1   | A路健康状态读取失败 | 路状态读取失败 | A路异常无法检测 | 一般  | 1、连续3轮周期读取失败，scan_status的bit0置零；读取成功一次，scan_status的bit0置1。  <br>2、影响communication_status的更新，A/B两路健康状态采集结果均采集失败且输入功率采集失败才会置为1，此时触发电源通讯异常告警。 |     |     |     |
| 2   | B路健康状态读取失败 | B路状态读取失败 | B路异常无法检测 | 一般  | 1、连续3轮周期读取失败，scan_status的bit4置零；读取成功一次，scan_status的bit4置1。  <br>2、影响communication_status的更新，A/B两路健康状态采集结果均采集失败且输入功率采集失败才会置为1，此时触发电源通讯异常告警。 |     |     |     |
| 3   | PAGE切到B路失败 | set_page=0x1失败或回读不一致 | B路告警无法检测 | 一般  | 1、单轮周期重试3次均失败，产生维护事件：PSU%s set page to B failed  <br>2、跳过B路告警获取，视为B路告警获取失败。 |     |     |     |
| 4   | PAGE切回A路失败 | set_page=0x0失败或回读不一致 | A路告警和输出参数无法检测 | 一般  | 1、单轮周期重试3次均失败，产生维护事件：PSU%s set page to A failed  <br>2、跳过A路告警获取，视为A路告警获取失败。 |     |     |     |
| 5   | A路输出参数读取失败 | output_voltage/current/power读取失败 | 输出参数不更新 | 低   | 连续3轮周期读取失败，scan_status的bit5置零；读取成功一次，scan_status的bit5置1。 |     |     |     |
| 6   | PAGE切换重试导致延迟 | PAGE切换连续失败重试 | 轮询周期抖动 | 低   | 单轮周期重试次数限制为3，且仅对PAGE切换场景生效 |     |     |     |
| 7   | 状态合并一致性 | A/B读取共享协议内部状态表 | 合并结果被覆盖 | 一般  | 对A路健康状态结果做深拷贝后再读取B路进行与运算，保证合并正确 |     |     |     |

### 2.2.7 安全性分析
| 安全合规项 | 是否涉及 | 现有的防护措施 |
| --- | --- | --- |
| 访问控制（通道、文件权限、用户权限、查询权限、配置权限、接口权限） | 不涉及 |  |
| 敏感数据 | 不涉及 |  |
| 日志（操作日志、维护日志、安全日志、运行日志） | 不涉及 |  |
| 文档 | 不涉及 |  |
| 加密及算法 | 不涉及 |  |
| 新增关键资源（密钥、证书、关键配置）备份、恢复 | 不涉及 |  |
| 新增对外接口入参校验 | 不涉及 |  |
| 新增开源及三方软件引入 | 不涉及 |  |

# 3.功能实现
## 3.1 功能实现设计
### 3.1.1 功能实现设计
本功能基于PMBus标准PAGE命令（0x00）在Monitor协程内完成A/B路状态读取与合并：
1) 切换至A路（PAGE=0x0）读取输出告警寄存器；
2) 切换至B路（PAGE=0x1）读取输出告警寄存器；
3) 将A/B路输出相关状态按位或合并，上报到电源模型属性；
4) 输入状态优先使用A路缓存结果；A路缓存无值时回退使用B路缓存结果；A/B缓存均无值时保持上一轮值；
5) 输出电压/电流/功率参数仅在A路（A路为主路）读取。
### 3.1.2 功能详细设计
### 3.1.2.1 协议层支持与PAGE切换
- 新增/使用PMBus PAGE命令字（0x00）并实现`set_page`/`get_page`。
- `set_page`写入PAGE后回读确认一致，写失败或回读不一致均判定切换失败。
- `get_health_event`在读取STATUS_VOUT/IOUT/INPUT/TEMPERATURE等基础上补充厂商状态字段（STATUS_MFR_SPECIFIC、STATUS_CML、STATUS_OTHER），并按PAGE维护A/B两份`health_event`缓存；寄存器读取失败时不清空对应字段。

**关键命令字：**
| 寄存器 | 地址  | 用途  | health_event属性 | 资源树属性 |
| --- | --- | --- | --- | --- |
| PAGE | 0x00 | 输出页切换（0x0=A路，0x1=B路） | NA  | NA  |
| STATUS_VOUT | 0x7A | 输出电压状态 | output_voltage_fault | OutputVoltageFault |
| STATUS_IOUT | 0x7B | 输出电流状态 | output_current_fault | OutputCurrentFault |
| STATUS_INPUT | 0x7C | 输入状态（输入告警按A缓存->B缓存->上一轮回退） | input_voltage_fault | InputVoltageFault |
| STATUS_TEMPERATURE | 0x7D | 温度状态 | temper_fault | OverTemperature |
| STATUS_CML | 0x7E | 通信/逻辑/内存状态 | cml_fault | CMLStatus |
| STATUS_OTHER | 0x7F | 维护/其他状态 | other_fault | OtherStatus |
| STATUS_MFR_SPECIFIC | 0x80 | 厂商自定义状态 | mfr_specific_status | MfrSpecificStatus |
| READ_12VOUT | 0x8B | 输出电压采样（仅A路） | output_voltage | OutputVoltage |
| READ_12VIOUT | 0x8C | 输出电流采样（仅A路） | output_current_amps | OutputCurrentAmps |
| READ_POUT | 0x96 | 输出功率采样（仅A路） | output_power_watts | OutputPowerWatts |

### 3.1.2.2 Monitor层双路状态轮询
**核心入口：**
- `power_monitor()`以500ms周期执行`update_dual_output()`。

**update_dual_output关键逻辑：**
1. PAGE切换到A路：`set_page_with_retry`最多重试3次；失败则产生维护事件并跳过A路读取。<br>
2. A路输出参数读取：读取`output_voltage/output_current_amps/output_power_watts`，并更新`scan_status bit5`。<br>
3. A路健康状态读取：调用`get_health_event`，失败计数累计3次并更新`scan_status bit0`。<br>
4. PAGE切换到B路：`set_page_with_retry`最多重试3次；失败则产生维护事件并跳过B路读取。<br>
5. B路健康状态读取：仅在PAGE切换成功后读取；失败计数累计3次并更新`scan_status bit4`。<br>
6. A/B两路健康状态合并处理：直接基于协议层A/B缓存做合并（输入A优先、输出按位或）；当A/B缓存同字段都无值时回退上一轮该字段值。<br>
7. 通信状态更新：仅当A/B两路健康状态均采集失败（按3轮防抖）且输入功率采集失败时，`communication_status`置为1。<br>
8. 属性刷新：刷新`scan_status`、`health_event`以及`health`。当A/B任一路读取失败时`health`置为`get_pbit(4)`；否则置为合并后的`health_event.event`。

**关键状态位：**
```lua
-- scan_status 位定义
-- bit 0: A路健康状态采集成功置为1
-- bit 1: 输入功率扫描成功置为1
-- bit 2: 输入参数采集成功置为1
-- bit 3: (保留)
-- bit 4: B路健康状态采集成功置为1
-- bit 5: A路输出参数采集成功置为1
```

**流程图：**
```mermaid
flowchart TD
    Start([开始]) --> Init[初始化<br/>failed_flag_a=false<br/>failed_flag_b=false]
    
    %% ========== A路采集 ==========
    Init --> PageA["PAGE切换到A路<br/>set_page_with_retry<br/>最多3次重试"]
    PageA --> PageAOk{切A路成功?}
    
    PageAOk -->|是| AParams["读取A路输出参数<br/>output_voltage<br/>output_current_amps<br/>output_power_watts"]
    AParams --> ApplyBit5["apply_status_bit<br/>更新scan_status bit5<br/>计数器防抖"]
    ApplyBit5 --> AHealth["调用get_health_event<br/>读取A路健康状态"]
    AHealth --> CopyA["拷贝health_event_a<br/>到self.health_event"]
    CopyA --> ApplyBit0["apply_status_bit<br/>更新scan_status bit0"]
    ApplyBit0 --> BPathStart
    
    PageAOk -->|否| LogA["日志:切A路失败<br/>failed_flag_a=true"]
    LogA --> ApplyFailA5["apply_status_bit<br/>bit5置失败"]
    ApplyFailA5 --> ApplyFailA0["apply_status_bit<br/>bit0置失败"]
    ApplyFailA0 --> BPathStart
    
    %% ========== B路采集 ==========
    BPathStart --> PageB["PAGE切换到B路<br/>set_page_with_retry<br/>最多3次重试"]
    PageB --> PageBOk{切B路成功?}
    
    PageBOk -->|是| BHealth["调用get_health_event<br/>读取B路健康状态<br/>保存到health_event_b"]
    BHealth --> ApplyBit4["apply_status_bit<br/>更新scan_status bit4"]
    ApplyBit4 --> CommUpdate["聚合A/B健康失败结果<br/>更新communication_status"]
    CommUpdate --> MergeStart
    
    PageBOk -->|否| LogB["日志:切B路失败<br/>failed_flag_b=true"]
    LogB --> ApplyFailB4["apply_status_bit<br/>bit4置失败"]
    ApplyFailB4 --> CommUpdate
    
    %% ========== 状态合并 ==========
    MergeStart --> Merge["合并A/B路健康状态<br>按位或OR运算<br>同字段A/B均失败维持上一轮状态"]
    
    %% ========== 属性刷新 ==========
    Merge --> RefreshScan["刷新scan_status"]
    RefreshScan --> CheckFail{failed_flag_a或<br/>failed_flag_b?}
    CheckFail -->|是| HealthBad["health置为0x10<br/>get_pbit(4)"]
    CheckFail -->|否| HealthOk["health置为<br/>合并后的event值"]
    HealthBad --> RefreshHealth["刷新health_event<br/>刷新health"]
    HealthOk --> RefreshHealth
    RefreshHealth --> Done([返回E_OK])
```

### 3.1.2.3 告警状态保持策略
当A/B缓存同字段都无值时，保持该字段上一轮告警状态：

| 字段 | 合并/回退规则 |
| --- | --- |
| `input_voltage_fault` | 优先A路缓存；A路缓存无值时回退B路缓存；A/B缓存均无值时保持上一轮值 |
| `temper_fault`/`output_voltage_fault`/`output_current_fault`/`cml_fault`/`other_fault`/`mfr_specific_status`/`event` | 本轮对A/B缓存按位或合并；A/B缓存同字段均无值时保持上一轮值 |

### 3.1.3 开发者测试
#### 3.1.3.1 单元测试
| 组件名 | 用例类型 | 测试用例描述 | 用例名称 | 预置条件 | 操作步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- |
| power_mgmt | UT  | PAGE切B失败跳过 | test_set_page_b_fail | set_page返回失败，打桩维护事件调用方法 | 调用update_dual_output | 产生维护事件 |
| power_mgmt | UT  | PAGE切A路失败跳过 | test_set_page_a_fail | set_page返回失败，打桩维护事件调用方法 | 调用update_dual_output | 产生维护事件 |
| power_mgmt | UT  | A路告警读取失败 | test_health_a_failed | get_health_event返回failed_flag为0 | 调用update_dual_output | scan_status bit0为0 |
| power_mgmt | UT  | B路告警读取失败 | test_health_b_failed | get_health_event返回failed_flag为0 | 调用update_dual_output | scan_status bit4为0 |
| power_mgmt | UT  | A路输出参数读取失败 | test_output_failed | 打桩输出参数读取返回失败 | 调用update_dual_output | scan_status的bit5为0 |
| power_mgmt | UT  | 正常读取所有输出参数 | test_output_metrics_success | 打桩A/B路输出参数寄存器 | 调用update_dual_output | 三个参数均被读取，scan_status bit5置位 |
| power_mgmt | UT  | A路正常，B路输出欠压 | test_alert_a_normal_b_undervoltage | 打桩A/B路状态寄存器 | 调用update_dual_output | 合并event包含B路告警(0x02)，health正确上报 |
| power_mgmt | UT  | A路过流告警，B路正常 | test_alert_a_overcurrent_b_normal | 打桩A/B路状态寄存器 | 调用update_dual_output| 合并event包含A路告警(0x04)，output_current_fault正确 |
| power_mgmt | UT  | A/B两路均有过温 | test_alert_ab_overtemperature | 打桩A/B路状态寄存器 | 调用update_dual_output | temper_fault按位或合并(0xf0\|0x80) |
| power_mgmt | UT  | 输入欠压（仅A路） | test_alert_input_undervoltage | 打桩A/B路状态寄存器 | 调用update_dual_output | 输入告警正确上报(0x10) |
| power_mgmt | UT  | A/B两路均正常 | test_ab_normal_no_alert | 打桩A/B路状态寄存器 | 调用update_dual_output | 无告警(event=0)，scan_status相关位均置位 | 

#### 3.1.3.2 集成测试
| 用例编号 | 场景  | 预期  |
| --- | --- | --- |
| 1   | A路正常，B路输出欠压 | 产生输出欠压告警 |
| 2   | A路过流，B路正常 | 产生输出过流告警 |
| 3   | 输入欠压（仅A路） | 产生输入欠压告警 |
| 4   | A路过温，B路正常 | 产生过温告警 |
| 5   | A/B两路均正常 | 无告警产生 |
| 6   | A路other_status的bit7为1 | 无告警产生，产生维护事件 |

电源开发协助提供支持打桩pmbus报文回读值的固件，端到端验证以上集成测试用例
