blob: 0bc788209f3191652d9d6cc0eedd5c2a958d8393 [file]
#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(&regValue, 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