Skip to content
216 changes: 115 additions & 101 deletions src/Compiler/CompilerOpenFPGA_ql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6235,120 +6235,127 @@ bool CompilerOpenFPGA_ql::GenerateIOFloorPlanConstraints(bool forceOverwrite) {

m_blifParser.load(netlist_path);
//m_blifParser.printHierachy(); // debug

auto [pinTableFile, error] = findCurrentDevicePinTableCsv();
if (pinTableFile.empty())
Message(std::string(__func__) + ": pin table csv not found, cannot pass it to the generate_floorplanning.");

std::filesystem::path floor_planning_constraint_filepath = QLSettingsManager::getInstance()->getQDCFilePath();
if (!fs::exists(floor_planning_constraint_filepath)){
Message("qdc Constraint File Does Not Exist. Skipping IO Floor Plan Constraint Generation.\n");
return true;
if (!fs::exists(floor_planning_constraint_filepath) && !fs::exists(pinTableFile)){
ErrorMessage("qdc Constraint File and Pin Table File Does Not Exist. Exiting the Flow.\n");
return false;
}
std::string region_groups_str = "";
if (fs::exists(floor_planning_constraint_filepath)) {
std::unordered_set<std::string> leftSet, rightSet, topSet, bottomSet;
std::unordered_map<std::string, std::unordered_set<std::string>*> sideMap = {
{"left", &leftSet},
{"right", &rightSet},
{"top", &topSet},
{"bottom", &bottomSet}
};

std::unordered_map<std::string, std::unordered_set<std::string>> partitionMap;

std::string qdcContent = FileUtils::GetFileContent(floor_planning_constraint_filepath);
StringUtils::replaceAllInPlace(qdcContent, fp::QdcSerializer::lineDelimiter(), ""); // remove syntax sugar added by better human readability
std::vector<std::string_view> lines = StringUtils::splitLines(qdcContent);
for (std::string_view lineView: lines) {
std::string line{lineView};
line = StringUtils::trim(line);

// drop comment part
if (auto pos = line.find("#"); pos != std::string::npos) {
line = line.substr(0, pos); // drop commented part of line
}

std::unordered_set<std::string> leftSet, rightSet, topSet, bottomSet;
std::unordered_map<std::string, std::unordered_set<std::string>*> sideMap = {
{"left", &leftSet},
{"right", &rightSet},
{"top", &topSet},
{"bottom", &bottomSet}
};

std::unordered_map<std::string, std::unordered_set<std::string>> partitionMap;

std::string qdcContent = FileUtils::GetFileContent(floor_planning_constraint_filepath);
StringUtils::replaceAllInPlace(qdcContent, fp::QdcSerializer::lineDelimiter(), ""); // remove syntax sugar added by better human readability
std::vector<std::string_view> lines = StringUtils::splitLines(qdcContent);
for (std::string_view lineView: lines) {
std::string line{lineView};
line = StringUtils::trim(line);

// drop comment part
if (auto pos = line.find("#"); pos != std::string::npos) {
line = line.substr(0, pos); // drop commented part of line
}

if (line.empty()){
continue; // Skip empty line
}
if (line.empty()){
continue; // Skip empty line
}

std::istringstream iss(line);
std::string token, signalName;
iss >> token;

static std::unordered_set<std::string> supportedCommands = {"set_io_side", "set_region"};
if (supportedCommands.find(token) == supportedCommands.end()){
ErrorMessage("Invalid QDC command '" + token + "'. Available commands are [" + StringUtils::toString(supportedCommands)+ "].");
return false;
}
std::istringstream iss(line);
std::string token, signalName;
iss >> token;
static std::unordered_set<std::string> supportedCommands = {"set_io_side", "set_region"};
if (supportedCommands.find(token) == supportedCommands.end()){
ErrorMessage("Invalid QDC command '" + token + "'. Available commands are [" + StringUtils::toString(supportedCommands)+ "].");
return false;
}

if (token == "set_io_side") {
iss >> signalName;
std::string side;
while (iss >> side) {
StringUtils::toLower(side);
auto it = sideMap.find(side);
if (it != sideMap.end()) {
it->second->insert(signalName); // insert avoids duplicates
if (token == "set_io_side") {
iss >> signalName;
std::string side;
while (iss >> side) {
StringUtils::toLower(side);
auto it = sideMap.find(side);
if (it != sideMap.end()) {
it->second->insert(signalName); // insert avoids duplicates
}
}
}
signalName.clear();
} else if (token == "set_region") {
iss >> signalName;
std::vector<std::string> elements;
std::vector<std::string> patterns = StringUtils::tokenize(signalName, ",");
for (const std::string& pattern: patterns) {
std::vector<std::string> patternElements = m_blifParser.findMatchingNames(pattern);
if (patternElements.empty()) {
ErrorMessage("QDC file contains invalid hierarchy pattern '" + pattern + "' in line: " + line + "\n");
return false;
} else {
elements.push_back(pattern);
signalName.clear();
} else if (token == "set_region") {
iss >> signalName;
std::vector<std::string> elements;
std::vector<std::string> patterns = StringUtils::tokenize(signalName, ",");
for (const std::string& pattern: patterns) {
std::vector<std::string> patternElements = m_blifParser.findMatchingNames(pattern);
if (patternElements.empty()) {
ErrorMessage("QDC file contains invalid hierarchy pattern '" + pattern + "' in line: " + line + "\n");
return false;
} else {
elements.push_back(pattern);
}
}
}

std::string partition;
iss >> partition;
bool hasPartition = !iss.fail();
std::string partition;
iss >> partition;
bool hasPartition = !iss.fail();

std::string partitionName; // optional partitionName as last argument, to keep compatibility with old qdc format
iss >> partitionName;
bool hasPartitionName = !iss.fail();
std::string partitionName; // optional partitionName as last argument, to keep compatibility with old qdc format
iss >> partitionName;
bool hasPartitionName = !iss.fail();

if (hasPartition) {
StringUtils::toLower(partition);
std::string partitionKey = hasPartitionName ? partitionName + "|" + partition : partition;
if (partitionMap.find(partitionKey) == partitionMap.end()) {
partitionMap[partitionKey] = {};
}
for (const std::string& element: elements) {
partitionMap[partitionKey].insert(element);
if (hasPartition) {
StringUtils::toLower(partition);
std::string partitionKey = hasPartitionName ? partitionName + "|" + partition : partition;
if (partitionMap.find(partitionKey) == partitionMap.end()) {
partitionMap[partitionKey] = {};
}
for (const std::string& element: elements) {
partitionMap[partitionKey].insert(element);
}
}
}

signalName.clear();
signalName.clear();
}
}
}

std::string leftStr = StringUtils::toString(leftSet);
std::string rightStr = StringUtils::toString(rightSet);
std::string topStr = StringUtils::toString(topSet);
std::string bottomStr = StringUtils::toString(bottomSet);
std::string leftStr = StringUtils::toString(leftSet);
std::string rightStr = StringUtils::toString(rightSet);
std::string topStr = StringUtils::toString(topSet);
std::string bottomStr = StringUtils::toString(bottomSet);

std::string partitionStr;
for (const auto& [partition, patternsSet]: partitionMap) {
partitionStr += "partition:" + partition + "|" + StringUtils::toString(patternsSet) + ";";
}
std::string partitionStr;
for (const auto& [partition, patternsSet]: partitionMap) {
partitionStr += "partition:" + partition + "|" + StringUtils::toString(patternsSet) + ";";
}

// Output results
if (!leftStr.empty())
leftStr = std::string("left:" + leftStr + ";");
if (!rightStr.empty())
rightStr = std::string("right:" + rightStr + ";");
if (!topStr.empty())
topStr = std::string("top:" + topStr + ";");
if (!bottomStr.empty())
bottomStr = std::string("bottom:" + bottomStr + ";");
// Output results
if (!leftStr.empty())
leftStr = std::string("left:" + leftStr + ";");
if (!rightStr.empty())
rightStr = std::string("right:" + rightStr + ";");
if (!topStr.empty())
topStr = std::string("top:" + topStr + ";");
if (!bottomStr.empty())
bottomStr = std::string("bottom:" + bottomStr + ";");

if (leftStr.empty() && rightStr.empty() && topStr.empty() && bottomStr.empty() && partitionStr.empty()) {
ErrorMessage("QDC file either does not contain a valid side/region or the side/region is empty\n");
return false;
if (leftStr.empty() && rightStr.empty() && topStr.empty() && bottomStr.empty() && partitionStr.empty()) {
ErrorMessage("QDC file either does not contain a valid side/region or the side/region is empty\n");
return false;
}
region_groups_str = leftStr + rightStr + topStr + bottomStr + partitionStr;
}

std::filesystem::path generate_floorplanning_script_path =
Expand Down Expand Up @@ -6378,7 +6385,7 @@ bool CompilerOpenFPGA_ql::GenerateIOFloorPlanConstraints(bool forceOverwrite) {
#endif // USE_IPGENERATOR_PYTHON_FOR_FLOORPLANNING
}

std::string command = python_exec.string();
const std::string command = python_exec.string();
std::vector<std::string> args;
args.push_back(generate_floorplanning_script_path.string());
args.push_back("--blif_file");
Expand All @@ -6387,13 +6394,20 @@ bool CompilerOpenFPGA_ql::GenerateIOFloorPlanConstraints(bool forceOverwrite) {
args.push_back(m_architectureFile.string());
args.push_back("--fpga_layout");
args.push_back(QLSettingsManager::getStringValue("general", "device", "layout"));
args.push_back("--groups");
args.push_back(leftStr + rightStr + topStr + bottomStr + partitionStr);
args.push_back("--output_path");
args.push_back(output_path.string());

if(fs::exists(pinTableFile)) {
args.push_back("--pin_table_file");
args.push_back(pinTableFile.string());
}
if(region_groups_str != "") {
args.push_back("--region_groups");
args.push_back(region_groups_str);
}

std::filesystem::path pin_constraint_filepath = QLSettingsManager::getInstance()->getPCFFilePath();
if (fs::exists(floor_planning_constraint_filepath)) {
if (fs::exists(pin_constraint_filepath)) {
args.push_back("--pcf_file");
args.push_back(pin_constraint_filepath.string());
}
Expand Down Expand Up @@ -9408,4 +9422,4 @@ void VprArchitectureFileProfider::clean()
if (m_isFileTemporary && std::filesystem::exists(m_architectureFile)) {
std::filesystem::remove(m_architectureFile);
}
}
}
Loading