硬件自发现解析流程
更新时间:2025/10/15
在Gitcode上查看源码

硬件自发现解析流程

硬件自发现主要分为两大流程:加载和解析。加载流程详见《硬件自发现CSR选择策略》文档,本文主要介绍自发现加载到CSR数据后如何解析数据,以及数据在每一个流程中会发生什么样的变化。

前言

在介绍整体的解析流程前,先简单介绍解析流程中会用到的数据以及它们在代码中的命名。

整个解析流程主要需要两个对象数据:解析后的Connector的数据和下级CSR数据。解析后的Connector的数据将会作为下一级CSR解析的上下文,辅助下级CSR解析。

自发现解析总体流程

自发现解析流程总体分为五步:match(匹配连接器与总线)→ rename(对象重命名)→ append(天池标准组件生成EEPROM对象及引用关系)→ arrange(对象排列)→ analyse(对象解析)。下面将按顺序逐步介绍解析的细节。

注意:解析platform.sr不包含match和append步骤。

1. match 匹配连接器与总线

简要介绍:

这一步主要是将Connector里的Buses和Connector对应的CSR的Buses建立映射关系,不会修改CSR数据。

主要步骤有校验Anchor数据和建立buses映射表。

1.1 校验Anchor数据

在建立映射关系前,会先校验CSR内是否配置了ManagementTopology:{Anchor:{Buses:{xxx}}},如果没有配置,则终止解析并抛出错误(可以配空,但不能不配)。

然后校验上级Connector里的Buses数量和CSR里Buses数量是否一致,不一致则会打印WARNING级别日志:position: %s, unmatched anchor buses, parent number: %d, current number: %d,并继续解析。

注意:如果是解析root.sr则不存在上级Connector,Buses映射表实际是自己映射到自己。

1.2 建立buses映射表

CSR和上级Connector的Buses可以不一致,因此分3种情况介绍映射表建立。

假设CSR配置为:

json
"ManagementTopology": {
    "Anchor": {
        "Buses": [
            "I2c_1",
            "I2c_2",
            "I2c_3"
        ]
    }
}

1.2.1 CSR和上级Connector的Buses数量一致

假设上级Connector配置为:

json
"Connector_EXU_1": {
    "Buses": [
        "I2c_A",
        "I2c_B",
        "I2c_C"
    ]
}

数量一致,按顺序将Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。

lua
buses_map = {
    I2c_1 = 'I2c_A',
    I2c_2 = 'I2c_B',
    I2c_3 = 'I2c_C',
}

1.2.2 上级Connector的Buses数量大

假设上级Connector配置为:

json
"Connector_EXU_1": {
    "Buses": [
        "I2c_A",
        "I2c_B",
        "I2c_C",
        "I2c_D"
    ]
}

数量不一致且上级Connector的Buses数量大,按顺序将上级Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。上级Connector内多余的总线不记录于映射表中。这种情况建议修改上级Connector,保持和下级CSR一致。

lua
buses_map = {
    I2c_1 = 'I2c_A',
    I2c_2 = 'I2c_B',
    I2c_3 = 'I2c_C',
}

1.2.3 CSR的Buses数量大

假设上级Connector配置为:

json
"Connector_EXU_1": {
    "Buses": [
        "I2c_A"
    ]
}

数量不一致且CSR的Buses数量大,按顺序将上级Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。CSR内多余的总线记录于extra_buses中,CSR内配置于多余总线下的Chip和Connector对象在后续解析时被丢弃,进而导致相关的所有对象都会不解析,其他组件也将拿不到对应的对象。这种情况建议修改CSR,保证上级Connector和下级CSR总线一致。

lua
buses_map = {
    I2c_1 = 'I2c_A'
}
lua
extra_buses = {'I2C_2', 'I2C_3', 'I2C_4', 'I2C_5', 'I2C_6'}

2. rename 对象重命名

简要介绍:

从这一步开始会逐步修改CSR数据。

这一步主要是将CSR内Object里的对象、对象属性同步、引用的其他对象以及需要重命名的下级属性加上Position后缀,并在bus_map中将CSR内ManagementTopology里Chip底下的Buses添加进去,即把通过9545之类分路出来的新bus对象添加进映射表里。

主要步骤有重命名topology拓扑结构、重命名objects对象结构和重命名对象属性。

2.1 重命名topology拓扑结构

这一步主要是重命名ManagementTopology下的Chip、Connector对象,添加Position后缀,将9545之类分路出来的新bus对象添加进映射表里,且通过上级Connector传递下来的bus对象不添加Position后缀;分路出的新对象添加Position后缀;以及根据buses_map替换Connector里的Buses(假设Connector的Position为01)。

假设CSR的ManagementTopology和Connector为:

json
    "ManagementTopology": {
        "Anchor": {
            "Buses": [
                "I2c_2"
            ]
        },
        "I2c_2": {
            "Chips": [
                "Pca9545_i2c2_chip"
            ],
            "Connectors": [
                "Connector_Exu_1"
            ]
        },
        "Pca9545_i2c2_chip": {
            "Buses": [
                "I2cMux_pca9545_chan1",
                "I2cMux_pca9545_chan2"
            ]
        }
    },
    "Connector_Exu_1": {
        "Buses": [
            "I2cMux_pca9545_chan1",
            "I2c_2"
        ]
    }

match传递下来的buses_map为:

lua
buses_map = {
    I2c_2 = 'I2c_A'
}

那么,经过重命名后,ManagementTopology、Connector和buses_map分别为:

json
    "ManagementTopology": {
        "Anchor": {
            "Buses": [
                "I2c_A"
            ]
        },
        "I2c_A": {
            "Chips": [
                "Pca9545_i2c2_chip_01"
            ],
            "Connectors": [
                "Connector_Exu_1_01"
            ]
        },
        "Pca9545_i2c2_chip_01": {
            "Buses": [
                "I2cMux_pca9545_chan1_01",
                "I2cMux_pca9545_chan2_01"
            ]
        }
    },
    "Connector_Exu_1": {
        "Buses": [
            "I2cMux_pca9545_chan1_01",
            "I2c_A"
        ]
    }
lua
buses_map = {
    I2c_2 = 'I2c_A',
    I2cMux_pca9545_chan1 = 'I2cMux_pca9545_chan1_01',
    I2cMux_pca9545_chan2 = 'I2cMux_pca9545_chan2_01'
}

2.2 重命名objects对象结构

这一步主要是重命名Object下对象以及对象属性引用、同步的对象,添加Position后缀,并将上级Connector对象的属性值作为参数,替换下级加载组件的CSR中对象的变量。

目前支持替换的动态参数有:${Slot}${SystemId}${ManagerId}${Container}${GroupId}${ChassisId}${GroupPosition}${SilkText}${DataVersion}${FormatVersion}

假设CSR里Object对象为:

json
"Connector_Exu_1": {
    "Bom": "14100513",
    "Slot": "\${GroupId}",
    "Position": 1,
    "Presence": 0,
    "Id": "#/NewClass_1.NewProperty1",
    "AuxId": "<=/NewClass_1.NewProperty2",
    "Buses": [
        "I2cMux_pca9545_chan1_01",
        "I2c_2"
    ],
    "SystemId": "<=/::Connector_Exu_2.SystemId",
    "ManagerId": "<=/Pca9545_i2c2_chip.HealthStatus",
    "SilkText": "#/Connector_Exu_1.Bom",
    "IdentifyMode": 2,
    "@Default": &#123;
        "ManagerId": "0"
    &#125;,
    "NewProperty1": "#/NewClass_1.NewProperty1",
    "NewProperty2": "<=/NewClass_1.NewProperty2",
    "NewProperty3": "#/Connector_Exu_1.Bom",
    "NewProperty4": "<=/Connector_Exu_1.Bom"
}

经过重命名后,对象数据变为:

json
"Connector_Exu_1_01": {
    "Bom": "14100513",
    "Slot": 2,
    "Position": 1,
    "Presence": 0,
    "Id": "#/NewClass_1_01.NewProperty1",
    "AuxId": "<=/NewClass_1_01.NewProperty2",
    "Buses": [
        "I2cMux_pca9545_chan1_01",
        "I2c_2"
    ],
    "SystemId": "<=/::Connector_Exu_2_01.SystemId",
    "ManagerId": "<=/Pca9545_i2c2_chip_01.HealthStatus",
    "SilkText": "#/Connector_Exu_2_01.Bom",
    "IdentifyMode": 2,
    "@Default": &#123;
        "ManagerId": "0"
    &#125;,
    "NewProperty1": "#/NewClass_1_01.NewProperty1",
    "NewProperty2": "<=/NewClass_1_01.NewProperty2",
    "NewProperty3": "#/Connector_Exu_2_01.Bom",
    "NewProperty4": "<=/Connector_Exu_2_01.Bom"
}

2.3 重命名对象属性

这一步主要是重命名的下级属性,目前仅支持重命名SmcDfxInfo对象下Mapping里的对象名。

假设SmcDfxInfo对象配置为:

json
"SmcDfxInfo_exu": {
    "Chip": "#/Smc_ExpBoardSMC",
    "Offset": 7424,
    "Size": 2,
    "Period": 400,
    "SmcVersion": 1,
    "Config": {
        "1": &#123;"cpld_ver": 255&#125;,
        "2": &#123;"test_for_dxf": 1&#125;
    },
    "Mapping": {
        "Scanner_Dftenabled": &#123;"Value": "expr($test_for_dxf)"&#125;
    }
}

重命名后,对象数据变为:

json
"SmcDfxInfo_exu_01": {
    "Chip": "#/Smc_ExpBoardSMC_01",
    "Offset": 7424,
    "Size": 2,
    "Period": 400,
    "SmcVersion": 1,
    "Config": {
        "1": &#123;"cpld_ver": 255&#125;,
        "2": &#123;"test_for_dxf": 1&#125;
    },
    "Mapping": {
        "Scanner_Dftenabled_01": &#123;"Value": "expr($test_for_dxf)"&#125;
    }
}

3. append 天池标准组件生成EEPROM对象及引用关系

简要介绍:

这一步主要是为天池标准组件的Connector生成对应的EEPROM对象,用于加载下级CSR时可以根据这个EEPROM对象读取对应的EEPROM,从而读取对应的CSR信息。

根据Connector配置于哪路总线下,对应的EEPROM也会生成在哪路总线下,并为Connector添加引用关系。并对Connector对象数据预处理,添加GroupId和GroupPosition(唯一标识)。

假设CSR配置为:

json
"ManagementTopology": {
    "Anchor": &#123;
        "Buses": [
            "I2c_2"
        ]
    &#125;,
    "I2c_2": &#123;
        "Connectors": [
            "Connector_Eep_1"
        ]
    &#125;
}
"Connector_Eep_1": &#123;
    "Bom": "14100513",
    "Slot": 1,
    "Position": 2,
    "Presence": 0,
    "Id": "",
    "AuxId": "",
    "Buses": [
        "I2c_2"
    ],
    "SystemId": 1,
    "IdentifyMode": 3
&#125;

添加对象及引用关系后,对象数据变为:

json
"ManagementTopology": {
    "Anchor": &#123;
        "Buses": [
            "I2c_2"
        ]
    &#125;,
    "I2c_2": &#123;
        "Connectors": [
            "Connector_Eep_1"
        ],
        "Chips": [
            "Eeprom_3_2_01"
        ]
    &#125;
},
"Connector_Eep_1": &#123;
    "Bom": "14100513",
    "Slot": 1,
    "Position": 2,
    "Presence": 0,
    "Id": "",
    "AuxId": "",
    "Buses": [
        "I2c_2"
    ],
    "SystemId": 1,
    "IdentifyMode": 3,
    "CSRVersion": "#/Eeprom_3_2_01.CSRVersion",
    "Chip": "#/Eeprom_3_2_01",
    "GroupId": 2,
    "GroupPosition": "0102"
&#125;,
"Eeprom_3_2_01": &#123;
    "OffsetWidth": 2,
    "Address": 174,
    "AddrWidth": 1,
    "RwBlockSize": 32,
    "WriteInterval": 20,
    "ReadTmout": 100,
    "WriteTmout": 100
&#125;

4. arrange 对象排列

简要介绍:

这一步主要检查CSR对象中是否存在不合理的配置,检查内容包括对象存在多个父层关系,对象属性同步/引用自身,对象属性同步/引用、对象引用成环,这些不合理的配置都会导致CSR解析失败。下面将举例说明这些不合理的配置。

4.1 对象存在多个父层关系

CSR配置举例,一个对象配置两个不同的@Parent,这种配置是不合理的。

json
"Objects": {
    "ParentTest_1": {},
    "ParentTest_2": {},
    "ChildTest_2": &#123;
        "@Parent": "ParentTest_1",
        "@Parent": "ParentTest_2",
        "Test": 1
    &#125;
}

4.2 对象属性引用自身

CSR配置举例,对象属性同步/引用自己,这个配置是不合理的。

json
"Objects": {
    "ObjectA": &#123;
        "Relation": "#/ObjectA.Relation"
    &#125;
}

4.3 对象同步/引用关系存在环

CSR配置举例,对象属性同步/引用、对象引用成环,这些配置是不合理的。

json
"Objects": {
    "ObjectA": &#123;
        "Relation": "#/ObjectB"
    &#125;,
    "ObjectB": &#123;
        "Relation": "#/ObjectC"
    &#125;,
    "ObjectC": &#123;
        "Relation": "#/ObjectA"
    &#125;,
    "SMC_2": &#123;
        "ChipModel": "SMC",
        "Address": 96
    &#125;,
    "Scanner_SMC_2_0": &#123;
        "Reading": "<=/Scanner_SMC_2_1.Value | <=/Scanner_SMC_2_2.Value"
    &#125;,
    "Scanner_SMC_2_1": &#123;
        "Chip": "#/SMC_2",
        "Size": 2,
        "Value": "<=/Scanner_SMC_2_3.Value"
    &#125;,
    "Scanner_SMC_2_2": &#123;
        "Chip": "#/SMC_2",
        "Size": 2,
        "Value": "<=/Scanner_SMC_2_1.Value"
    &#125;,
    "Scanner_SMC_2_3": &#123;
        "Chip": "#/SMC_2",
        "Size": 2,
        "Value": "<=/Scanner_SMC_2_0.Reading"
    &#125;
}

5. analyse 对象解析

简要介绍:

这一步主要是将CSR对象解析成代码可理解的形式,并为每个对象打上所属组件标记等。

5.1 删除非法总线下的Chip对象

根据总线匹配时匹配到的非法总线(extra_buses),将非法总线下的Chip和Connector对象剔除出解析队列。

5.2 预解析对象

这里主要是生成object_info和object_child两个表,object_info记录每个对象的app、object_name和path(私有对象没有path)。

比如Connector_Eep_1_01,对应的object_info如下:

lua
"object_info" = {
    "Connector_Eep_1_01" = {
        "app" = "hwdiscovery",
        "object_name" = "Connector_Eep_1_01",
        "path" = "/bmc/kepler/Connector/Connector_Eep_1_01"
    }
}

object_child记录对象的层级关系,这个是配置了@Parent属性才会用到。

如果对象所属的app不存在,或者具有层级的对象的上层对象不存在,则会忽略该对象,将对象剔除出解析队列。

5.3 解析对象

这一步主要处理属性的同步、引用和表达式语法和封装对象,将对象的各种语法解析为软件可以理解的语言,在后续框架处理时可以根据解析的结果处理属性,并将对象进一步封装,方便使用。

5.3.1 同步属性

假设CSR配置为:

json
"Object_A": &#123;
    "Presence": "<=/Scanner_A.Value"
&#125;

经过解析后:

json
"Object_A_01": {
    "Presence": "$sync:{\"properties\":[&#123;\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Scanner/Scanner_A_01\", \"Interface\": \"bmc.kepler.Scanner\", \"Property\": \"Value\"&#125;]}"
}

$sync表示属性值同步其他属性,同步对象为hwproxy组件下Scanner_A_01对象的bmc.kepler.Scanner接口下的Value属性,当被同步的属性发生变化后,框架会更新同步属性的值。

5.3.2 引用属性

假设CSR配置为:

json
"Object_A": &#123;
    "CSRVersion": "#/Eeprom_A.CSRVersion"
&#125;

经过解析后:

json
"Object_A_01": {
    "CSRVersion": "$ref:{\"properties\":[&#123;\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Chip/Eeprom/Eeprom_A_01\", \"Interface\": \"bmc.kepler.EepromData\", \"Property\": \"CSRVersion\"&#125;]}"
}

$ref表示属性值引用其他属性,引用对象为hwproxy组件下Eeprom_A_01对象的bmc.kepler.EepromData接口下的CSRVersion属性,引用属性只有主动访问属性才会更新属性。

5.3.3 引用对象

假设CSR配置为:

json
"Object_A": &#123;
    "Chip": "#/Eeprom_A"
&#125;

经过解析后:

json
"Object_A_01": {
    "Chip": "$ref:&#123;\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Chip/Eeprom/Eeprom_A_01\"&#125;"
}

$ref表示属性值引用其他对象,引用对象为hwproxy组件下Eeprom_A_01对象。

5.3.4 表达式语法

假设CSR配置为:

json
"Object_A": &#123;
    "Reading": "<=/CPU_1.MaxMemoryTemperatureCelsius |> expr($1 == 32768 ? 1 : 0)",
    "Slot": "100 |> expr($1 * 2 + 2) |> expr(($1 + 1) * 2) |> expr(($1 + 1) * 2)",
    "Presence": "#/Accessor_Test.Size |> expr(($1 + 1) * 2) |> expr(($1 + 1) * 2)"
&#125;

经过解析后:

json
"Object_A_01": {
    "Reading": "$sync:{\"properties\":[&#123;\"Service\": \"bmc.kepler.compute\", \"Path\": \"/bmc/kepler/Systems/1/Processors/CPU/CPU_1_01\", \"Interface\": \"bmc.kepler.Systems.Processor.CPU\", \"Property\": \"MaxMemoryTemperatureCelsius\"&#125;],\"expressions\":[\"expr($1 == 32768 ? 1 : 0)\"]}",
    "Slot": "$const:&#123;\"properties\":[100],\"expressions\":[\"expr($1 * 2 + 2)\", \"expr(($1 + 1) * 2)\", \"expr(($1 + 1) * 2)\"]&#125;",
    "Presence": "$ref:{\"properties\":[&#123;\"Path\": \"/bmc/kepler/Accessor/Accessor_Test\", \"Interface\": \"bmc.kepler.Accessor\", \"Property\": \"Size\"&#125;],\"expressions\":[\"expr(($1 + 1) * 2)\", \"expr(($1 + 1) * 2)\"]}"
}

表达式语法的解析主要是将多个表达式收集起来,存于expressions字段中,方便后续框架计算表达式。解析时如果遇到同步、引用语法就根据上面介绍的方式解析。

5.3.5 对象封装

将上面预解析和解析的结果归一,封装到对象数据,存在表里。

假设CSR配置为:

json
"Connector_Exu_1": &#123;
        "Bom": "14100513",
        "Slot": 1,
        "Position": 1,
        "Presence": 0,
        "AuxId": "",
        "SystemId": 1,
        "ManagerId": "2",
        "SilkText": "EXU",
        "IdentifyMode": 3
    &#125;

经过解析后的Lua数据:

lua
{
    app_name = 'hwdiscovery',
    class_name = 'Connector',
    object_name = 'Connector_Exu_1_01',
    object_props = {
        AuxId = '',
        Bom = '14100513',
        Chip = '$ref:&#123;"Service": "bmc.kepler.hwproxy", "Path": "/bmc/kepler/Eeprom/Eeprom_3_1_01"&#125;',
        Id = '',
        Position = 1,
        Presence = 0,
        SilkText = 'EXU',
        Slot = 1,
        SystemId = 1,
        ManagerId = '2',
        IdentifyMode = 3,
        GroupId = 5,
        GroupPosition = '0101'
    },
    path = '/bmc/kepler/Connector/Connector_Exu_1_01',
    extra_props = {
        Path = '/bmc/kepler/Connector/Connector_Exu_1_01',
    },
    position = '01'
}

5.4 对象解析后处理

这里主要是将上面解析完的一个个对象整合到一个表内,作为对象组数据。若是框架对象则会在extra_props添加"Framework = true"标记。

假设CSR配置为:

json
"Objects": {
    "Connector_Exu_1": &#123;
        "Bom": "14100513",
        "Slot": 1,
        "Position": 1,
        "Presence": 0,
        "AuxId": "",
        "SystemId": 1,
        "ManagerId": "2",
        "SilkText": "EXU",
        "IdentifyMode": 3
    &#125;,
    "Eeprom_3_100_1": &#123;
        "AddrWidth": 1,
        "Address": 174,
        "OffsetWidth": 2,
        "ReadTmout": 100,
        "WriteTmout": 100,
        "RwBlockSize": 32,
        "WriteInterval": 20
    &#125;
}

经过解析后的Lua数据:

lua
{
    [1] = {
        app_name = 'hwproxy',
        class_name = 'Eeprom',
        object_name = 'Eeprom_3_1_01',
        object_props = {
            AddrWidth = 1,
            Address = 174,
            OffsetWidth = 2,
            ReadTmout = 100,
            WriteTmout = 100,
            RwBlockSize = 32,
            WriteInterval = 20
        },
        path = '/bmc/kepler/Eeprom/Eeprom_3_1_01',
        extra_props = {
            Path = '/bmc/kepler/Eeprom/Eeprom_3_1_01',
            Framework = true
        },
        position = '01'
    },
    [2] = {
        app_name = 'hwdiscovery',
        class_name = 'Connector',
        object_name = 'Connector_Exu_1_01',
        object_props = {
            AuxId = '',
            Bom = '14100513',
            Chip = '$ref:&#123;"Service": "bmc.kepler.hwproxy", "Path": "/bmc/kepler/Eeprom/Eeprom_3_1_01"&#125;',
            Id = '',
            Position = 1,
            Presence = 0,
            SilkText = 'EXU',
            Slot = 1,
            SystemId = 1,
            ManagerId = '2',
            IdentifyMode = 3,
            GroupId = 5,
            GroupPosition = '0101'
        },
        path = '/bmc/kepler/Connector/Connector_Exu_1_01',
        extra_props = {
            Path = '/bmc/kepler/Connector/Connector_Exu_1_01',
            Framework = true
        },
        position = '0005'
    }
}

至此,CSR数据从原始配置到最终提供给组件的数据,中间经历的解析流程全部梳理完毕,组件收到对象后可以根据业务使用对象。