mirror of https://github.com/YosysHQ/nextpnr.git
gatemate: floorplanning [sc-168] (#1607)
* gatemate: add CCF floorplanning parser * apply constraints * cleanup * print detected region and error if not found * Add wildcard matching * Validate placebox and use official coordinate system * Fix some messages
This commit is contained in:
parent
0c970d6891
commit
b0d6b97936
|
|
@ -154,6 +154,44 @@ struct GateMateCCFReader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::regex pattern_to_regex(const std::string &pat)
|
||||||
|
{
|
||||||
|
std::string expr;
|
||||||
|
expr.reserve(pat.size() * 2);
|
||||||
|
expr += '^';
|
||||||
|
for (char c : pat) {
|
||||||
|
switch (c) {
|
||||||
|
case '*':
|
||||||
|
expr += ".*";
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
expr += ".";
|
||||||
|
break;
|
||||||
|
// Escape regex metacharacters
|
||||||
|
case '.':
|
||||||
|
case '+':
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
case '^':
|
||||||
|
case '$':
|
||||||
|
case '|':
|
||||||
|
case '\\':
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
expr += '\\';
|
||||||
|
expr += c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
expr += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr += '$';
|
||||||
|
return std::regex(expr);
|
||||||
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
log_info("Parsing CCF file..\n");
|
log_info("Parsing CCF file..\n");
|
||||||
|
|
@ -167,6 +205,7 @@ struct GateMateCCFReader
|
||||||
};
|
};
|
||||||
lineno = 0;
|
lineno = 0;
|
||||||
count = std::vector<int>(uarch->dies, 0);
|
count = std::vector<int>(uarch->dies, 0);
|
||||||
|
bool floorplanning = false;
|
||||||
while (std::getline(in, line)) {
|
while (std::getline(in, line)) {
|
||||||
++lineno;
|
++lineno;
|
||||||
// Both // and # are considered start of comment
|
// Both // and # are considered start of comment
|
||||||
|
|
@ -180,6 +219,125 @@ struct GateMateCCFReader
|
||||||
continue;
|
continue;
|
||||||
linebuf += line;
|
linebuf += line;
|
||||||
|
|
||||||
|
boost::algorithm::to_lower(line);
|
||||||
|
if (line.find("start floorplanning") != std::string::npos) {
|
||||||
|
floorplanning = true;
|
||||||
|
linebuf = "";
|
||||||
|
continue;
|
||||||
|
} else if (line.find("end floorplanning") != std::string::npos) {
|
||||||
|
floorplanning = false;
|
||||||
|
linebuf = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (floorplanning) {
|
||||||
|
// int size = -1;
|
||||||
|
std::string src_location;
|
||||||
|
|
||||||
|
std::string s = linebuf;
|
||||||
|
boost::trim(s);
|
||||||
|
|
||||||
|
// split input into segments by ';'
|
||||||
|
std::vector<std::string> segments;
|
||||||
|
boost::split(segments, s, boost::is_any_of(";"));
|
||||||
|
for (auto &seg : segments)
|
||||||
|
boost::trim(seg);
|
||||||
|
|
||||||
|
if (!segments.empty()) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
boost::split(parts, segments[0], boost::is_any_of(":"));
|
||||||
|
for (auto &p : parts)
|
||||||
|
boost::trim(p);
|
||||||
|
|
||||||
|
// index is numeric token
|
||||||
|
// int index = -1;
|
||||||
|
if (!parts.empty() && !parts[0].empty() &&
|
||||||
|
std::all_of(parts[0].begin(), parts[0].end(), ::isdigit)) {
|
||||||
|
// index = std::stoi(parts[0]);
|
||||||
|
parts.erase(parts.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// find size
|
||||||
|
// size = -1;
|
||||||
|
for (size_t i = 0; i + 1 < parts.size(); ++i) {
|
||||||
|
if (boost::iequals(parts[i], "size")) {
|
||||||
|
if (!parts[i + 1].empty() &&
|
||||||
|
std::all_of(parts[i + 1].begin(), parts[i + 1].end(), ::isdigit)) {
|
||||||
|
// size = std::stoi(parts[i + 1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always the last piece of the main segment
|
||||||
|
if (!parts.empty()) {
|
||||||
|
src_location = parts.back();
|
||||||
|
boost::trim(src_location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segments.size() > 1) {
|
||||||
|
std::string &tmp = segments[1];
|
||||||
|
boost::trim(tmp);
|
||||||
|
|
||||||
|
if (!tmp.empty()) {
|
||||||
|
std::vector<std::string> subparts;
|
||||||
|
boost::split(subparts, tmp, boost::is_any_of(":"));
|
||||||
|
for (auto &p : subparts)
|
||||||
|
boost::trim(p);
|
||||||
|
|
||||||
|
if (!subparts.empty() && boost::iequals(subparts[0], "pb")) {
|
||||||
|
if (subparts.size() == 2) {
|
||||||
|
std::string pb_position = subparts[1];
|
||||||
|
|
||||||
|
std::regex pb_regex(R"(x(-?\d+)y(-?\d+)x(-?\d+)y(-?\d+))");
|
||||||
|
std::smatch match;
|
||||||
|
if (std::regex_match(pb_position, match, pb_regex)) {
|
||||||
|
int x1 = std::stoi(match[1]) + 2;
|
||||||
|
int y1 = std::stoi(match[2]) + 2;
|
||||||
|
int x2 = std::stoi(match[3]) + 2;
|
||||||
|
int y2 = std::stoi(match[4]) + 2;
|
||||||
|
|
||||||
|
if (x1 < 0 || x1 >= ctx->getGridDimX() || x2 < 0 || x2 >= ctx->getGridDimX() ||
|
||||||
|
y1 < 0 || y1 >= ctx->getGridDimY() || y2 < 0 || y2 >= ctx->getGridDimY())
|
||||||
|
log_error("Placebox coordinates out of range '%s' in line %d.\n",
|
||||||
|
pb_position.c_str(), lineno);
|
||||||
|
|
||||||
|
IdString scopename(ctx, src_location.c_str());
|
||||||
|
std::regex expr = pattern_to_regex(src_location);
|
||||||
|
bool matched_any = false;
|
||||||
|
|
||||||
|
for (const IdString &name : uarch->scopenames) {
|
||||||
|
if (std::regex_match(name.str(ctx), expr)) {
|
||||||
|
matched_any = true;
|
||||||
|
|
||||||
|
ctx->createRectangularRegion(name, x1, y1, x2, y2);
|
||||||
|
uarch->scopenames_used.emplace(name);
|
||||||
|
|
||||||
|
log_info(" Constraining region '%s' to '%s'\n", name.c_str(ctx),
|
||||||
|
pb_position.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched_any) {
|
||||||
|
log_error("Unknown scope name or pattern: '%s' in line %d.\n",
|
||||||
|
src_location.c_str(), lineno);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("Placebox format invalid: %s in line %d.\n", pb_position.c_str(), lineno);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("Missing data for pB (in line %d).\n", lineno);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("Unexpected content in last segment (in line %d).\n", lineno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linebuf = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
size_t pos = linebuf.find(';');
|
size_t pos = linebuf.find(';');
|
||||||
// Need to concatenate lines until there is closing ; sign
|
// Need to concatenate lines until there is closing ; sign
|
||||||
while (pos != std::string::npos) {
|
while (pos != std::string::npos) {
|
||||||
|
|
@ -222,6 +380,14 @@ struct GateMateCCFReader
|
||||||
parse_params(params, false, &cell->params);
|
parse_params(params, false, &cell->params);
|
||||||
} else
|
} else
|
||||||
log_warning("Pad with name '%s' not found in netlist.\n", pin_name.c_str());
|
log_warning("Pad with name '%s' not found in netlist.\n", pin_name.c_str());
|
||||||
|
} else if (type == "start" || type == "end") {
|
||||||
|
std::string word2 = words.at(1);
|
||||||
|
boost::algorithm::to_lower(word2);
|
||||||
|
if (word2 == "floorplanning") {
|
||||||
|
log("Found floor planning\n");
|
||||||
|
} else
|
||||||
|
log_error("Unknown command '%s' in line %d.\n", word2.c_str(), lineno);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log_error("Unknown type '%s' in line %d.\n", type.c_str(), lineno);
|
log_error("Unknown type '%s' in line %d.\n", type.c_str(), lineno);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,8 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
dict<int, IdString> index_to_die;
|
dict<int, IdString> index_to_die;
|
||||||
dict<IdString, int> die_to_index;
|
dict<IdString, int> die_to_index;
|
||||||
dict<IdString, dict<IdString, IdString>> pass_backtrace;
|
dict<IdString, dict<IdString, IdString>> pass_backtrace;
|
||||||
|
pool<IdString> scopenames;
|
||||||
|
pool<IdString> scopenames_used;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||||
|
|
|
||||||
|
|
@ -512,11 +512,42 @@ void GateMatePacker::assign_clocks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IdString get_scopename(Context *ctx, CellInfo &ci)
|
||||||
|
{
|
||||||
|
std::string scope = "top";
|
||||||
|
if (ci.attrs.count(ctx->id("scopename"))) {
|
||||||
|
scope = str_or_default(ci.attrs, ctx->id("scopename"), "");
|
||||||
|
scope = "top " + scope;
|
||||||
|
} else if (ci.attrs.count(ctx->id("hdlname"))) {
|
||||||
|
scope = str_or_default(ci.attrs, ctx->id("hdlname"), "");
|
||||||
|
scope = "top " + scope;
|
||||||
|
}
|
||||||
|
return IdString(ctx, scope.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMatePacker::find_regions()
|
||||||
|
{
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo &ci = *cell.second;
|
||||||
|
IdString name = get_scopename(ctx, ci);
|
||||||
|
uarch->scopenames.emplace(name);
|
||||||
|
}
|
||||||
|
if (uarch->scopenames.size() > 1) {
|
||||||
|
log_info("Detected regions..\n");
|
||||||
|
for (auto &scope : uarch->scopenames) {
|
||||||
|
log_info(" %s\n", scope.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GateMatePacker::assign_regions()
|
void GateMatePacker::assign_regions()
|
||||||
{
|
{
|
||||||
log_info("Assign cell region based on attributes..\n");
|
log_info("Assign cell region based on attributes..\n");
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo &ci = *cell.second;
|
CellInfo &ci = *cell.second;
|
||||||
|
IdString name = get_scopename(ctx, ci);
|
||||||
|
if (uarch->scopenames_used.count(name))
|
||||||
|
ctx->constrainCellToRegion(ci.name, name);
|
||||||
if (ci.attrs.count(id_GATEMATE_DIE) != 0) {
|
if (ci.attrs.count(id_GATEMATE_DIE) != 0) {
|
||||||
std::string die_name = str_or_default(ci.attrs, id_GATEMATE_DIE, "");
|
std::string die_name = str_or_default(ci.attrs, id_GATEMATE_DIE, "");
|
||||||
IdString die = ctx->id(die_name);
|
IdString die = ctx->id(die_name);
|
||||||
|
|
@ -566,6 +597,8 @@ void GateMatePacker::fix_regions()
|
||||||
void GateMateImpl::pack()
|
void GateMateImpl::pack()
|
||||||
{
|
{
|
||||||
const ArchArgs &args = ctx->args;
|
const ArchArgs &args = ctx->args;
|
||||||
|
GateMatePacker packer(ctx, this);
|
||||||
|
packer.find_regions();
|
||||||
if (args.options.count("ccf")) {
|
if (args.options.count("ccf")) {
|
||||||
parse_ccf(args.options["ccf"].as<std::string>());
|
parse_ccf(args.options["ccf"].as<std::string>());
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +633,6 @@ void GateMateImpl::pack()
|
||||||
if (strategy == MultiDieStrategy::REUSE_CLK1 || strategy == MultiDieStrategy::FULL_USE)
|
if (strategy == MultiDieStrategy::REUSE_CLK1 || strategy == MultiDieStrategy::FULL_USE)
|
||||||
preferred_die = 0;
|
preferred_die = 0;
|
||||||
|
|
||||||
GateMatePacker packer(ctx, this);
|
|
||||||
if (forced_die == IdString())
|
if (forced_die == IdString())
|
||||||
packer.assign_regions();
|
packer.assign_regions();
|
||||||
packer.pack_constants();
|
packer.pack_constants();
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ struct GateMatePacker
|
||||||
void reassign_clocks();
|
void reassign_clocks();
|
||||||
void copy_clocks();
|
void copy_clocks();
|
||||||
void assign_clocks();
|
void assign_clocks();
|
||||||
|
void find_regions();
|
||||||
void assign_regions();
|
void assign_regions();
|
||||||
void fix_regions();
|
void fix_regions();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue