| #include "include/power_rail/power_rail_utility.hpp" |
| |
| #include "include/common_utility.hpp" |
| |
| #include <utility> |
| |
| namespace powerfault |
| { |
| |
| uint8_t readCPLDFlag(const uint8_t bus, const uint8_t addr, const uint8_t reg) |
| { |
| std::vector<uint8_t> writeData = {0x00, reg}; |
| std::vector<uint8_t> readData = util::i2cWriteRead(bus, addr, writeData, |
| writeData.size(), 1); |
| |
| if (readData.empty()) |
| { |
| lg2::error("Failed to read CPLD flag"); |
| return 0; |
| } |
| |
| return readData[0]; |
| } |
| |
| void executeAction(uint8_t bus, uint8_t addr, uint8_t action) |
| { |
| uint8_t commandCode = 0x00; |
| uint8_t actionReg = 0x30; |
| uint8_t statusReg = 0x2D; |
| uint8_t clearFlag = 0x00; |
| uint8_t cpldFlag = 0x00; |
| // clear action flag |
| std::vector<uint8_t> writeData = {commandCode, actionReg, clearFlag}; |
| util::i2cWriteRead(bus, addr, writeData, writeData.size(), 0); |
| |
| switch (action) |
| { |
| case 0x02: // read 0x30 bit1 |
| case 0x04: // erase 0x30 bit2 |
| writeData = {commandCode, actionReg, action}; |
| util::i2cWriteRead(bus, addr, writeData, writeData.size(), 0); |
| do |
| { |
| cpldFlag = readCPLDFlag(bus, addr, statusReg); |
| if ((cpldFlag & kCpldUfmComponentFaultFlag) != 0) |
| { |
| lg2::error("UFM component fault"); |
| break; |
| } |
| } while ((cpldFlag & kCpldBusyFlag) != 0); |
| break; |
| case 0x08: // disable 0x30 bit3 |
| writeData = {commandCode, actionReg, action}; |
| util::i2cWriteRead(bus, addr, writeData, writeData.size(), 0); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| std::optional<RailInfo> processRail(const nlohmann::json& rail) |
| { |
| auto requiredFields = {kName, kType, kSource, kBus, kAddress, kBit}; |
| |
| for (const auto& field : requiredFields) |
| { |
| if (!rail.contains(field) && |
| !(rail[kType] == kSequencer && field == kBit)) |
| { |
| lg2::error("Rail object: {RAIL}", "RAIL", rail.dump()); |
| lg2::error("Rail object is missing the {FIELD}", "FIELD", field); |
| return std::nullopt; |
| } |
| } |
| |
| try |
| { |
| RailInfo railInfo( |
| rail[kName].get<std::string>(), rail[kType].get<std::string>(), |
| rail[kSource].get<std::string>(), util::parseDec(rail[kBus]), |
| util::parseHexByte(rail[kAddress])); |
| |
| if (railInfo.type == kCPLD) |
| { |
| if (rail.contains(kBit)) |
| { |
| railInfo.bit = util::parseDec(rail[kBit]); |
| } |
| if (rail.contains(kReg)) |
| { |
| railInfo.reg = util::parseHexByte(rail[kReg]); |
| } |
| if (rail.contains(kReverseFlag)) |
| { |
| railInfo.reverse_flag = util::parseDec(rail[kReverseFlag]); |
| } |
| } |
| else if (railInfo.type == kSequencer && rail.contains(kPage)) |
| { |
| railInfo.page = util::parseDec(rail[kPage]); |
| } |
| else |
| { |
| lg2::error("Unknown rail type: {TYPE}", "TYPE", railInfo.type); |
| return std::nullopt; |
| } |
| |
| return railInfo; |
| } |
| catch (const std::exception& e) |
| { |
| lg2::error("Exception occurred while processing rail: {ERROR}", "ERROR", |
| e.what()); |
| return std::nullopt; |
| } |
| } |
| |
| std::pair<std::vector<std::string>, std::vector<std::string>> |
| readPriorityFiles(const fs::path& priorityDirPath) |
| { |
| fs::path zigguratPriorityPath = priorityDirPath / "ziggurat.json"; |
| fs::path peripheralPriorityPath = priorityDirPath / "peripheral.json"; |
| |
| std::vector<std::string> zigguratPriority; |
| std::vector<std::string> peripheralPriority; |
| |
| if (fs::exists(zigguratPriorityPath)) |
| { |
| std::ifstream ifs(zigguratPriorityPath); |
| json zigguratPriorityJson; |
| ifs >> zigguratPriorityJson; |
| zigguratPriority = zigguratPriorityJson.get<std::vector<std::string>>(); |
| } |
| |
| if (fs::exists(peripheralPriorityPath)) |
| { |
| std::ifstream ifs(peripheralPriorityPath); |
| json peripheralPriorityJson; |
| ifs >> peripheralPriorityJson; |
| peripheralPriority = |
| peripheralPriorityJson.get<std::vector<std::string>>(); |
| } |
| |
| lg2::info("Ziggurat Priority JSON content: {CONTENT}", "CONTENT", |
| json(zigguratPriority).dump()); |
| lg2::info("Peripheral Priority JSON content: {CONTENT}", "CONTENT", |
| json(peripheralPriority).dump()); |
| |
| return {zigguratPriority, peripheralPriority}; |
| } |
| |
| std::pair<json, json> readRegisterMapFiles(const fs::path& registerMapDirPath) |
| { |
| fs::path cpldRegisterMapPath = registerMapDirPath / "CPLD.json"; |
| fs::path sequencerRegisterMapPath = registerMapDirPath / "Sequencer.json"; |
| |
| json cpldRegisterMap; |
| json sequencerRegisterMap; |
| |
| if (fs::exists(cpldRegisterMapPath)) |
| { |
| std::ifstream ifs(cpldRegisterMapPath); |
| ifs >> cpldRegisterMap; |
| } |
| |
| if (fs::exists(sequencerRegisterMapPath)) |
| { |
| std::ifstream ifs(sequencerRegisterMapPath); |
| ifs >> sequencerRegisterMap; |
| } |
| |
| return {cpldRegisterMap, sequencerRegisterMap}; |
| } |
| |
| std::map<std::string, json> collectPowerRails(const json& registerMap, |
| std::string_view registerType) |
| { |
| std::map<std::string, json> railToConfigMap; |
| |
| for (const auto& config : registerMap) |
| { |
| if (registerType == kCPLD && config.contains(kCPLDRegisters)) |
| { |
| for (const auto& cpldRegister : config[kCPLDRegisters]) |
| { |
| if (cpldRegister.contains(kBitToPowerRailMap)) |
| { |
| std::vector<std::string> reverseFlags = {}; |
| if (cpldRegister.contains(kReverseFlag)) |
| { |
| reverseFlags = cpldRegister[kReverseFlag]; |
| } |
| |
| for (const auto& [bit, powerRail] : |
| cpldRegister[kBitToPowerRailMap].items()) |
| { |
| const std::string reverseFlag = |
| std::find(reverseFlags.begin(), reverseFlags.end(), |
| bit) != reverseFlags.end() |
| ? "1" |
| : "0"; |
| railToConfigMap[powerRail] = { |
| {kName, powerRail}, |
| {kType, kCPLD}, |
| {kSource, config[kSource]}, |
| {kBus, config[kBus]}, |
| {kAddress, config[kAddress]}, |
| {kReg, cpldRegister[kRegister]}, |
| {kBit, bit}, |
| {kReverseFlag, reverseFlag}}; |
| } |
| } |
| } |
| } |
| else if (registerType == kSequencer && |
| config.contains(kSequencerRegisters)) |
| { |
| for (const auto& sequencerRegister : config[kSequencerRegisters]) |
| { |
| if (sequencerRegister.contains(kPageToPowerRailMap)) |
| { |
| for (const auto& [page, powerRail] : |
| sequencerRegister[kPageToPowerRailMap].items()) |
| { |
| railToConfigMap[powerRail] = { |
| {kName, powerRail}, |
| {kType, kSequencer}, |
| {kSource, config[kSource]}, |
| {kBus, config[kBus]}, |
| {kAddress, config[kAddress]}, |
| {kReg, sequencerRegister[kRegister]}, |
| {kPage, page}}; |
| } |
| } |
| } |
| } |
| } |
| |
| return railToConfigMap; |
| } |
| |
| ProcessResult |
| processPriorityList(const std::vector<std::string>& priorityList, |
| const std::map<std::string, RailInfo>& railInfoMap, |
| const fs::path& logDirPath, |
| const std::map<uint8_t, uint8_t>& cpldRegValues) |
| { |
| ProcessResult result; |
| result.success = true; |
| result.hasErrorInZiggurat = false; |
| result.runtime = false; |
| |
| for (const auto& railName : priorityList) |
| { |
| auto it = railInfoMap.find(railName); |
| if (it != railInfoMap.end()) |
| { |
| const RailInfo& railInfo = it->second; |
| |
| fs::path filePath = logDirPath / (railInfo.name + ".log"); |
| std::ofstream ofs(filePath); |
| |
| if (railInfo.type == kCPLD) |
| { |
| ProcessResult cpldResult = handleCPLDRail(railInfo, |
| cpldRegValues, ofs); |
| if (!cpldResult.success) |
| { |
| result.success = false; |
| return result; |
| } |
| result.runtime = cpldResult.runtime; |
| result.hasErrorInZiggurat = cpldResult.hasErrorInZiggurat || |
| result.hasErrorInZiggurat; |
| result.powerFaultRails.insert( |
| result.powerFaultRails.end(), |
| cpldResult.powerFaultRails.begin(), |
| cpldResult.powerFaultRails.end()); |
| } |
| else if (railInfo.type == kSequencer) |
| { |
| ProcessResult sequencerResult = handleSequencerRail(railInfo, |
| ofs); |
| if (!sequencerResult.success) |
| { |
| result.success = false; |
| return result; |
| } |
| result.hasErrorInZiggurat = |
| sequencerResult.hasErrorInZiggurat || |
| result.hasErrorInZiggurat; |
| result.powerFaultRails.insert( |
| result.powerFaultRails.end(), |
| sequencerResult.powerFaultRails.begin(), |
| sequencerResult.powerFaultRails.end()); |
| } |
| |
| ofs.close(); |
| |
| if (result.hasErrorInZiggurat) |
| { |
| showPowerFault(result.runtime, result.powerFaultRails, |
| logDirPath); |
| return result; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| ProcessResult handleCPLDRail(const RailInfo& railInfo, |
| std::map<uint8_t, uint8_t> cpldRegValues, |
| std::ofstream& logFile) |
| { |
| ProcessResult result; |
| result.success = true; |
| result.hasErrorInZiggurat = false; |
| result.runtime = false; |
| result.cpldRegValues = std::move(cpldRegValues); |
| |
| auto it = result.cpldRegValues.find(railInfo.reg); |
| if (it == result.cpldRegValues.end()) |
| { |
| lg2::error("Rail {RAIL} not found in CPLD register values", "RAIL", |
| railInfo.name); |
| result.success = false; |
| return result; |
| } |
| |
| uint8_t regValue = it->second; |
| std::string regValueHex = util::byteArrayToHexString(®Value, 1); |
| boost::algorithm::trim(regValueHex); |
| logFile << util::getCurrentTimestamp() << " " << railInfo.name + ": " |
| << regValueHex << '\n'; |
| |
| if (railInfo.name == kCPLDRuntimeFlag) |
| { |
| result.runtime = (regValue & 0x1) == 1; |
| } |
| else if (((regValue & (1 << railInfo.bit)) ^ railInfo.reverse_flag) != 0) |
| { |
| result.powerFaultRails.emplace_back(railInfo.name); |
| if (railInfo.source == kZiggurat) |
| { |
| result.hasErrorInZiggurat = true; |
| } |
| } |
| |
| return result; |
| } |
| |
| ProcessResult handleSequencerRail(const RailInfo& railInfo, |
| std::ofstream& logFile) |
| { |
| ProcessResult result; |
| result.success = true; |
| result.hasErrorInZiggurat = false; |
| |
| std::string railResult = fromMax34451Mb(railInfo.page); |
| logFile << railInfo.name + ": " << railResult << '\n'; |
| |
| if (railResult == "Fault") |
| { |
| result.powerFaultRails.emplace_back(railInfo.name); |
| if (railInfo.source == kZiggurat) |
| { |
| result.hasErrorInZiggurat = true; |
| } |
| } |
| |
| return result; |
| } |
| |
| std::pair<uint8_t, uint8_t> |
| getCPLDRegisterRange(const std::map<std::string, RailInfo>& railInfoMap, |
| std::string_view cpldType) |
| { |
| uint8_t min = 0xFF; |
| uint8_t max = 0x00; |
| for (const auto& [railName, railObject] : railInfoMap) |
| { |
| if (railObject.source == cpldType) |
| { |
| min = std::min(min, railObject.reg); |
| max = std::max(max, railObject.reg); |
| } |
| } |
| if (min > max) |
| { |
| lg2::error("Failed to get min and max register values"); |
| return std::make_pair(0, 0); |
| } |
| |
| return std::make_pair(min, max); |
| } |
| |
| std::pair<uint8_t, uint8_t> |
| getCPLDBusAndAddress(const std::map<std::string, RailInfo>& railInfoMap, |
| std::string_view source) |
| { |
| for (const auto& [railName, railObject] : railInfoMap) |
| { |
| if (railObject.source == source) |
| { |
| return std::make_pair(railObject.bus, railObject.address); |
| } |
| } |
| return std::make_pair(0, 0); |
| } |
| |
| std::map<uint8_t, uint8_t> readCPLDRegisters(const uint8_t bus, |
| const uint8_t address, |
| const uint8_t minReg, |
| const size_t regRange, |
| const bool& setCPLDFlag) |
| { |
| std::map<uint8_t, uint8_t> registerValues; |
| std::vector<uint8_t> writeData = {0x00, minReg}; |
| std::vector<uint8_t> readData(regRange, 0); |
| |
| if (setCPLDFlag) |
| { |
| executeAction(bus, address, kCpldDisableFlag); |
| executeAction(bus, address, kCpldReadFlag); |
| usleep(16); |
| } |
| |
| try |
| { |
| readData = util::i2cWriteRead(bus, address, writeData, writeData.size(), |
| regRange); |
| |
| if (readData.empty() || readData.size() != regRange) |
| { |
| for (size_t i = 0; i < regRange; i++) |
| { |
| registerValues[minReg + i] = 0; |
| } |
| return registerValues; |
| } |
| |
| for (size_t i = 0; i < regRange; i++) |
| { |
| registerValues[minReg + i] = readData[i]; |
| } |
| } |
| catch (const std::exception& e) |
| { |
| lg2::error("{ERROR}", "ERROR", e.what()); |
| for (size_t i = 0; i < regRange; i++) |
| { |
| registerValues[minReg + i] = 0; |
| } |
| } |
| |
| if (setCPLDFlag) |
| { |
| // Make CPLD execute erase action |
| executeAction(bus, address, kCpldEraseFlag); |
| // delay 1.6 seconds to make sure CPLD erase UFM successfully |
| usleep(1600000); |
| // clear action flag |
| executeAction(bus, address, kCpldClearFlag); |
| } |
| return registerValues; |
| } |
| |
| std::pair<bool, std::map<std::string, RailInfo>> |
| getRailInfoMap(const fs::path& registerMapDirPath) |
| { |
| bool setCPLDFlag = false; |
| auto [cpldRegisterMap, |
| sequencerRegisterMap] = readRegisterMapFiles(registerMapDirPath); |
| |
| for (const auto& config : cpldRegisterMap) |
| { |
| if (config.contains(kSetCPLDFlag)) |
| { |
| setCPLDFlag = config[kSetCPLDFlag].get<bool>(); |
| break; |
| } |
| } |
| |
| std::map<std::string, json> railToConfigMapCPLD = |
| collectPowerRails(cpldRegisterMap, kCPLD); |
| std::map<std::string, json> railToConfigMapSequencer = |
| collectPowerRails(sequencerRegisterMap, kSequencer); |
| |
| std::map<std::string, json> railToConfigMap; |
| railToConfigMap.insert(railToConfigMapCPLD.begin(), |
| railToConfigMapCPLD.end()); |
| railToConfigMap.insert(railToConfigMapSequencer.begin(), |
| railToConfigMapSequencer.end()); |
| |
| std::map<std::string, RailInfo> railInfoMap; |
| for (const auto& [railName, railObject] : railToConfigMap) |
| { |
| auto railInfo = processRail(railObject); |
| if (railInfo.has_value()) |
| { |
| railInfoMap.emplace(railName, railInfo.value()); |
| } |
| else |
| { |
| lg2::error("Failed to process rail: {RAIL}", "RAIL", railName); |
| } |
| } |
| |
| return {setCPLDFlag, railInfoMap}; |
| } |
| |
| std::map<uint8_t, uint8_t> |
| getCPLDRegValues(const std::map<std::string, RailInfo>& railInfoMap, |
| const bool& setCPLDFlag) |
| { |
| auto [bus, addr] = getCPLDBusAndAddress(railInfoMap, kCPLDM); |
| auto [minReg, maxReg] = getCPLDRegisterRange(railInfoMap, kCPLDM); |
| size_t regRange = maxReg - minReg + 1; |
| |
| lg2::info("Min register value: {MIN}", "MIN", minReg); |
| lg2::info("Max register value: {MAX}", "MAX", maxReg); |
| lg2::info("Register range: {RANGE}", "RANGE", regRange); |
| lg2::info("CPLD bus: {BUS}", "BUS", static_cast<int>(bus)); |
| lg2::info("CPLD address: {ADDR}", "ADDR", static_cast<int>(addr)); |
| |
| std::map<uint8_t, uint8_t> cpldRegValues = |
| readCPLDRegisters(bus, addr, minReg, regRange, setCPLDFlag); |
| |
| return cpldRegValues; |
| } |
| |
| bool logRailState(const fs::path& priorityDirPath, |
| const fs::path& registerMapDirPath, |
| const fs::path& logDirPath, |
| std::string_view specifiedRailName) |
| { |
| auto [setCPLDFlag, railInfoMap] = getRailInfoMap(registerMapDirPath); |
| |
| std::map<uint8_t, uint8_t> cpldRegValues = getCPLDRegValues(railInfoMap, |
| setCPLDFlag); |
| |
| if (!specifiedRailName.empty()) |
| { |
| if (railInfoMap.find(std::string(specifiedRailName)) == |
| railInfoMap.end()) |
| { |
| lg2::error("Specified rail {RAIL} does not exist", "RAIL", |
| std::string(specifiedRailName)); |
| return false; |
| } |
| |
| std::vector<std::string> priorityList; |
| priorityList.emplace_back(specifiedRailName); |
| ProcessResult result = processPriorityList(priorityList, railInfoMap, |
| logDirPath, cpldRegValues); |
| |
| if (!result.success) |
| { |
| lg2::error("Failed to process specified rail"); |
| return false; |
| } |
| if (result.hasErrorInZiggurat) |
| { |
| showPowerFault(result.runtime, result.powerFaultRails, logDirPath); |
| return true; |
| } |
| |
| return true; |
| } |
| |
| auto [zigguratPriority, |
| peripheralPriority] = readPriorityFiles(priorityDirPath); |
| |
| ProcessResult result = processPriorityList(zigguratPriority, railInfoMap, |
| logDirPath, cpldRegValues); |
| |
| if (!result.success) |
| { |
| lg2::error("Failed to process ziggurat priority list"); |
| return false; |
| } |
| |
| if (!result.hasErrorInZiggurat) |
| { |
| result = processPriorityList(peripheralPriority, railInfoMap, |
| logDirPath, cpldRegValues); |
| if (!result.success) |
| { |
| lg2::error("Failed to process peripheral priority list"); |
| return false; |
| } |
| } |
| |
| showPowerFault(result.runtime, result.powerFaultRails, logDirPath); |
| return true; |
| } |
| |
| std::string fromMax34451Mb(const uint8_t page) |
| { |
| const uint8_t kMax34451Mbbus = 44; |
| const uint8_t kMax34451MbAddress = 0x59; |
| |
| try |
| { |
| util::i2cWriteByte(kMax34451Mbbus, kMax34451MbAddress, 0x00, page); |
| } |
| catch (const std::runtime_error& e) |
| { |
| return "set page failed"; |
| } |
| |
| uint8_t regVal = 0; |
| try |
| { |
| regVal = util::i2cReadByte(kMax34451Mbbus, kMax34451MbAddress, 0x7a); |
| } |
| catch (const std::runtime_error& e) |
| { |
| return "read register failed"; |
| } |
| |
| if (max34451::checkPowerRailFault(regVal, false)) |
| { |
| return "Fault"; |
| } |
| return "Normal"; |
| } |
| |
| void showPowerFault(const bool runtime, const PowerFaultRail& powerFaultRails, |
| const fs::path& logDirPath) |
| { |
| std::string message; |
| std::string rails; |
| for (const auto& powerFaultRail : powerFaultRails) |
| { |
| rails += powerFaultRail + "\n"; |
| } |
| |
| if (!powerFaultRails.empty()) |
| { |
| if (runtime) |
| { |
| message = "The following rail occur power fault during runtime"; |
| } |
| else |
| { |
| message = "The following rail occur power fault during power up"; |
| } |
| message += "\n" + rails; |
| lg2::error("{MSG}", "MSG", message); |
| util::summarizeLog(getDbusUtil(), message, logDirPath, |
| util::kSumLogName); |
| } |
| } |
| |
| } // namespace powerfault |