Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2022-04-01 23:54:04

0001 #include "L1Trigger/DemonstratorTools/interface/utilities.h"
0002 
0003 #include <algorithm>
0004 #include <fstream>
0005 #include <regex>
0006 #include <unordered_map>
0007 
0008 #ifdef CMSSW_GIT_HASH
0009 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0010 #endif
0011 #include "L1Trigger/DemonstratorTools/interface/BoardData.h"
0012 
0013 namespace {
0014 
0015   l1t::demo::BoardData createBoardDataFromRows(const std::string& id,
0016                                                const std::vector<size_t>& channels,
0017                                                const std::vector<std::vector<l1t::demo::Frame>>& dataRows) {
0018     l1t::demo::BoardData boardData(id);
0019 
0020     for (size_t i = 0; i < channels.size(); i++) {
0021       std::vector<l1t::demo::Frame> channelData(dataRows.size());
0022       for (size_t j = 0; j < dataRows.size(); j++)
0023         channelData.at(j) = dataRows.at(j).at(i);
0024       boardData.add(channels.at(i), channelData);
0025     }
0026 
0027     return boardData;
0028   }
0029 
0030   std::vector<std::string> searchAndTokenize(std::istream& file, const std::string& linePrefix) {
0031     std::string line;
0032 
0033     while (getline(file, line)) {
0034       // Strip leading spaces
0035       size_t startIndex = line.find_first_not_of(" \t");
0036       if (startIndex != std::string::npos)
0037         line = line.substr(startIndex);
0038 
0039       if (line.empty())
0040         continue;
0041       if (line[0] == '#')
0042         continue;
0043 
0044       if (line.rfind(linePrefix, 0) != std::string::npos) {
0045         std::vector<std::string> tokens;
0046 
0047         // Split the line into tokens
0048         const std::regex delimiterRegex("\\s+");
0049         std::sregex_token_iterator it(line.begin() + linePrefix.size(), line.end(), delimiterRegex, -1);
0050 
0051         for (; it != std::sregex_token_iterator(); it++) {
0052           const std::string token(it->str());
0053           if (token.empty())
0054             continue;
0055           tokens.push_back(token);
0056         }
0057 
0058         return tokens;
0059       } else
0060         throw std::logic_error("Found unexpected line found when searching for \"" + linePrefix + "\": \"" + line +
0061                                "\"");
0062     }
0063     throw std::logic_error("Couldn't find any line starting with \"" + linePrefix + "\"");
0064   }
0065 
0066 }  // namespace
0067 
0068 namespace l1t::demo {
0069 
0070   FileFormat parseFileFormat(const std::string& s) {
0071     static const std::unordered_map<std::string, FileFormat> kFormatStringMap({{"EMP", FileFormat::EMP},
0072                                                                                {"emp", FileFormat::EMP},
0073                                                                                {"APx", FileFormat::APx},
0074                                                                                {"apx", FileFormat::APx},
0075                                                                                {"X20", FileFormat::X20},
0076                                                                                {"x20", FileFormat::X20}});
0077 
0078     const auto it = kFormatStringMap.find(s);
0079     if (it == kFormatStringMap.end())
0080       throw std::runtime_error("Could not convert '" + s + "' to FileFormat enum value");
0081 
0082     return it->second;
0083   }
0084 
0085   BoardData readAPxFile(std::istream&, const FileFormat);
0086 
0087   BoardData readEMPFile(std::istream&, const FileFormat);
0088 
0089   BoardData readX20File(std::istream&, const FileFormat);
0090 
0091   BoardData read(const std::string& filePath, const FileFormat format) {
0092     std::ifstream file(filePath);
0093 
0094     if (not file.is_open())
0095       throw std::runtime_error("Could not open file '" + filePath + "'");
0096 
0097     return read(file, format);
0098   }
0099 
0100   BoardData read(std::istream& file, const FileFormat format) {
0101     if (format == FileFormat::APx)
0102       return readAPxFile(file, format);
0103     else if (format == FileFormat::EMP)
0104       return readEMPFile(file, format);
0105     else
0106       return readX20File(file, format);
0107   }
0108 
0109   BoardData readAPxFile(std::istream& file, const FileFormat format) {
0110     std::string line;
0111 
0112     // Complain if file is empty
0113     if (not std::getline(file, line))
0114       throw std::runtime_error("Specified file is empty!");
0115 
0116     // If first line is sideband, skip it and move onto 2nd line
0117     if (line.find("#Sideband") == 0) {
0118       if (not std::getline(file, line))
0119         throw std::runtime_error("APx file has incorrect format: Link labels and data missing!");
0120     }
0121 
0122     // Parse link labels
0123     if (line.find("#LinkLabel") != 0)
0124       throw std::runtime_error(
0125           "APx file has incorrect format: Link header does not start with '#LinkLabel' (line is '" + line + "')");
0126 
0127     std::vector<size_t> indices;
0128     const std::regex delimiterRegex("\\s+");
0129     std::sregex_token_iterator it(line.begin() + 10, line.end(), delimiterRegex, -1);
0130     for (; it != std::sregex_token_iterator(); it++) {
0131       const std::string token(it->str());
0132       if (token.empty())
0133         continue;
0134 
0135       if (token.find("LINK_") != 0)
0136         throw std::runtime_error("Link column name '" + token + "' (does not start with 'LINK_')");
0137       if (token.size() == 5)
0138         throw std::runtime_error("Link column name '" + token + "' is too short");
0139       if (not std::all_of(token.begin() + 5, token.end(), ::isdigit))
0140         throw std::runtime_error("Link column name '" + token + "' does not end with a number");
0141 
0142       indices.push_back(std::stoul(token.substr(5)));
0143     }
0144 
0145     // Check for '#BeginData' line
0146     if (not std::getline(file, line))
0147       throw std::runtime_error("APx file has incorrect format: Data missing!");
0148     if (line != "#BeginData")
0149       throw std::runtime_error("APx file has incorrect format: '#BeginData' line missing (found '" + line + "')");
0150 
0151     // Parse link data
0152     std::vector<std::vector<l1t::demo::Frame>> dataRows;
0153     while (std::getline(file, line)) {
0154       it = std::sregex_token_iterator(line.begin(), line.end(), delimiterRegex, -1);
0155       size_t count = 0;
0156       for (; it != std::sregex_token_iterator(); it++, count++) {
0157         const std::string token(it->str());
0158 
0159         if ((token.find("0x") != 0) or (not std::all_of(token.begin() + 2, token.end(), ::isxdigit)))
0160           throw std::runtime_error("APx file has incorrect format: Data token '" + token +
0161                                    "' is not hexadecimal number");
0162 
0163         if (count == 0) {
0164           size_t rowIndex = std::stoul(token, nullptr, 16);
0165           if (rowIndex != dataRows.size())
0166             throw std::runtime_error("APx file has incorrect format: Expected data row " +
0167                                      std::to_string(dataRows.size()) + ", but found row " + std::to_string(rowIndex));
0168           dataRows.push_back(std::vector<l1t::demo::Frame>(indices.size()));
0169         }
0170         // Sideband info
0171         else if ((count % 2) == 1) {
0172           uint16_t sbValue = std::stoul(token, nullptr, 16);
0173           dataRows.back().at((count - 1) / 2).valid = (sbValue & 0x1);
0174           dataRows.back().at((count - 1) / 2).start = ((sbValue >> 1) & 0x1);
0175           dataRows.back().at((count - 1) / 2).end = ((sbValue >> 3) & 0x1);
0176         }
0177         // Data word
0178         else
0179           dataRows.back().at((count - 1) / 2).data = ap_uint<64>(std::stoull(token, nullptr, 16));
0180       }
0181 
0182       if (count != (2 * indices.size() + 1))
0183         throw std::runtime_error("APx file has incorrect format: Line has incorrect number of tokens (expected " +
0184                                  std::to_string(2 * indices.size() + 1) + ", found " + std::to_string(count) + "!");
0185     }
0186 
0187     return createBoardDataFromRows("", indices, dataRows);
0188   }
0189 
0190   BoardData readEMPFile(std::istream& file, const FileFormat format) {
0191     // 1) Search for ID string
0192     std::string id, line;
0193     while (getline(file, line)) {
0194       if (line.empty())
0195         continue;
0196       if (line[0] == '#')
0197         continue;
0198 
0199       if (line.rfind("Board ", 0) != std::string::npos) {
0200         id = line.substr(6);
0201         break;
0202       } else
0203         throw std::logic_error("Found unexpected line found when searching for board ID: \"" + line + "\"");
0204     }
0205 
0206     // 2) Search for column labels (i.e. list of channels/links)
0207     searchAndTokenize(file, "Quad/Chan :");
0208     const auto tokens = searchAndTokenize(file, "Link :");
0209     std::vector<size_t> channels;
0210     std::transform(tokens.begin(), tokens.end(), std::back_inserter(channels), [](const std::string& s) {
0211       return std::stoull(s);
0212     });
0213 
0214     // 3) Read the main data rows
0215     const std::regex delimiterRegex("\\s+");
0216     static const std::regex frameRegex("([01]s)?([01]v)([0-9a-fA-F]{16})");
0217     std::vector<std::vector<Frame>> dataRows;
0218     while (file.good() and getline(file, line)) {
0219       if (line.empty() or line[0] == '#')
0220         continue;
0221 
0222       std::ostringstream prefixStream;
0223       prefixStream << "Frame ";
0224       prefixStream << std::setw(4) << std::setfill('0') << dataRows.size();
0225       prefixStream << " :";
0226 
0227       const std::string prefix(prefixStream.str());
0228       if (line.rfind(prefix, 0) == std::string::npos)
0229         throw std::logic_error("Found unexpected line found when searching for \"" + prefix + "\": \"" + line + "\"");
0230 
0231       std::vector<l1t::demo::Frame> row;
0232       std::sregex_token_iterator it(line.begin() + prefix.size(), line.end(), delimiterRegex, -1);
0233       for (; it != std::sregex_token_iterator(); it++) {
0234         const std::string token(it->str());
0235         if (token.empty())
0236           continue;
0237 
0238         std::smatch what;
0239         if (not std::regex_match(token, what, frameRegex))
0240           throw std::logic_error("Token '" + token + "' doesn't match the valid format");
0241 
0242         l1t::demo::Frame value;
0243         // Import strobe if the strobe group is matched
0244         if (what[1].matched) {
0245           value.strobe = (what[1] == "1s");
0246         }
0247 
0248         value.valid = (what[2] == "1v");
0249         value.data = ap_uint<64>(std::stoull(what[3].str(), nullptr, 16));
0250 
0251         row.push_back(value);
0252       }
0253 
0254       dataRows.push_back(row);
0255     }
0256 
0257     return createBoardDataFromRows(id, channels, dataRows);
0258   }
0259 
0260   BoardData readX20File(std::istream& file, const FileFormat format) {
0261     throw std::runtime_error("Reading X20 file format not yet implemented. Will be done ASAP.");
0262   }
0263 
0264   void writeAPxFile(const BoardData&, std::ostream&, const FileFormat);
0265 
0266   void writeEMPFile(const BoardData&, std::ostream&, const FileFormat);
0267 
0268   void writeX20File(const BoardData&, std::ostream&, const FileFormat);
0269 
0270   void write(const BoardData& data, const std::string& filePath, const FileFormat format) {
0271     // Open file
0272 #ifdef CMSSW_GIT_HASH
0273     edm::LogInfo("L1TDemonstratorTools")
0274 #else
0275     std::cout
0276 #endif
0277         << "Writing board data (" << std::distance(data.begin(), data.end()) << " channels, "
0278         << data.begin()->second.size() << " frames) to file '" << filePath << "' (format: " << format << ")"
0279         << std::endl;
0280     std::ofstream file(filePath);
0281 
0282     if (not file.is_open())
0283       throw std::runtime_error("Could not open file '" + filePath + "'");
0284 
0285     write(data, file, format);
0286   }
0287 
0288   void write(const BoardData& data, std::ostream& file, const FileFormat format) {
0289     // Check that number of frames is same for every channel
0290     const auto firstChannel = data.begin();
0291 
0292     for (const auto& channel : data) {
0293       const auto i = channel.first;
0294       const auto channelData = channel.second;
0295       if (channelData.size() != firstChannel->second.size())
0296         throw std::runtime_error("Cannot write board data to file - channels do not all have the same length (" +
0297                                  std::to_string(channelData.size()) + " words on channel " + std::to_string(i) +
0298                                  ", but " + std::to_string(firstChannel->second.size()) + " words on channel " +
0299                                  std::to_string(firstChannel->first) + ")");
0300     }
0301 
0302     // Call relevant write function
0303     switch (format) {
0304       case FileFormat::APx:
0305         writeAPxFile(data, file, format);
0306         return;
0307       case FileFormat::EMP:
0308         writeEMPFile(data, file, format);
0309         return;
0310       case FileFormat::X20:
0311         writeX20File(data, file, format);
0312         return;
0313     }
0314   }
0315 
0316   void writeAPxFile(const BoardData& data, std::ostream& file, const FileFormat format) {
0317     // Note: APx sideband encoding
0318     //   Short-term, simulation only:
0319     //     0 -> Valid
0320     //     1 -> EOF
0321     //   Planned (from ~ May 2021)
0322     //     0 -> Valid
0323     //     1 -> SOF (Start Of Frame)
0324     //     2 -> FFO (First Frame of Orbit)
0325     //     3 -> EOF (End Of Frame)
0326     //     4 -> FERR (Frame Error)
0327     //     5 -> RSV1
0328     //     6 -> RSV2
0329     //     7 -> RSV3
0330 
0331     file << std::setfill('0');
0332     file << "#Sideband ON" << std::endl;
0333 
0334     // Channel header
0335     file << "#LinkLabel";
0336     for (const auto& channel : data) {
0337       const auto i = channel.first;
0338       file << "                LINK_" << std::setw(2) << i << "    ";
0339     }
0340     file << std::endl;
0341 
0342     file << "#BeginData" << std::endl;
0343 
0344     // Frames
0345     file << std::hex;
0346     const auto firstChannel = data.begin();
0347     for (size_t i = 0; i < firstChannel->second.size(); i++) {
0348       file << "0x" << std::setw(4) << i;
0349       for (const auto& channel : data) {
0350         //const auto j = channel.first;
0351         const auto channelData = channel.second;
0352         uint16_t sideband = channelData.at(i).valid;
0353         sideband |= channelData.at(i).start << 1;
0354         sideband |= channelData.at(i).end << 3;
0355         file << "    0x" << std::setw(2) << sideband;
0356         file << " 0x" << std::setw(16) << uint64_t(channelData.at(i).data);
0357       }
0358       file << std::endl;
0359     }
0360   }
0361 
0362   void writeEMPFile(const BoardData& data, std::ostream& file, const FileFormat format) {
0363     file << std::setfill('0');
0364 
0365     // Board name/id
0366     file << "Board CMSSW" << std::endl;
0367 
0368     // Quad/chan header
0369     file << " Quad/Chan :";
0370     for (const auto& channel : data) {
0371       const auto i = channel.first;
0372       file << "         q" << std::setw(2) << i / 4 << 'c' << std::setw(1) << i % 4 << "       ";
0373     }
0374     file << std::endl;
0375 
0376     // Link header
0377     file << "      Link :";
0378     for (const auto& channel : data) {
0379       const auto i = channel.first;
0380       file << "          " << std::setw(3) << i << "        ";
0381     }
0382     file << std::endl;
0383 
0384     // Frames
0385     const auto firstChannel = data.begin();
0386     for (size_t i = 0; i < firstChannel->second.size(); i++) {
0387       file << "Frame " << std::setw(4) << i << " :";
0388       for (const auto& channel : data) {
0389         //const auto j = channel.first;
0390         const auto channelData = channel.second;
0391         file << " ";
0392         //TODO: Add strobe if zero anywhere on channel
0393         file << "  ";
0394         file << std::setw(1) << channelData.at(i).valid << "v" << std::setw(16) << std::hex
0395              << uint64_t(channelData.at(i).data);
0396       }
0397       file << std::endl << std::dec;
0398     }
0399   }
0400 
0401   void writeX20File(const BoardData& data, std::ostream& file, const FileFormat format) {
0402     throw std::runtime_error("Writing X20 file format not yet implemented. Will be done ASAP.");
0403   }
0404 
0405 }  // namespace l1t::demo