diff options
author | gatecat <gatecat@ds0.me> | 2021-07-12 20:04:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-12 20:04:48 +0100 |
commit | 034467ff615494b2a74cee08081cb17edc9ea1a4 (patch) | |
tree | 44fdb97d65f7b2e94295153e2d93ae2807c813e5 | |
parent | 24b7084feb6beecacbe447a15f9b2de04b14d685 (diff) | |
parent | 7abfeb11c3ff153b5d353404b7c7c6767f3823a9 (diff) | |
download | nextpnr-034467ff615494b2a74cee08081cb17edc9ea1a4.tar.gz nextpnr-034467ff615494b2a74cee08081cb17edc9ea1a4.tar.bz2 nextpnr-034467ff615494b2a74cee08081cb17edc9ea1a4.zip |
Merge pull request #761 from acomodi/interchange-constrs
interchange: add user placement constraints handling
-rw-r--r-- | common/placer_heap.cc | 1 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 1 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 3 | ||||
-rw-r--r-- | fpga_interchange/arch_place_constr.cc | 106 | ||||
-rw-r--r-- | fpga_interchange/xdc.cc | 76 |
5 files changed, 174 insertions, 13 deletions
diff --git a/common/placer_heap.cc b/common/placer_heap.cc index dbb8d36c..f8385cef 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -401,6 +401,7 @@ class HeAPPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); + auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 901725d4..ccd2dfb2 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -787,6 +787,7 @@ bool Arch::place() getCtx()->check(); #endif + place_constraints(); place_globals(); std::string placer = str_or_default(settings, id("placer"), defaultPlacer); diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 630c4480..8afb71e1 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -720,6 +720,9 @@ struct Arch : ArchAPI<ArchRanges> void prepare_cluster(const ClusterPOD *cluster, uint32_t index); dict<ClusterId, Cluster> clusters; + // User constraints + void place_constraints(); + void decode_lut_cells(); const GlobalCellPOD *global_cell_info(IdString cell_type) const; diff --git a/fpga_interchange/arch_place_constr.cc b/fpga_interchange/arch_place_constr.cc new file mode 100644 index 00000000..3af48656 --- /dev/null +++ b/fpga_interchange/arch_place_constr.cc @@ -0,0 +1,106 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 Symbiflow Authors + * + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +void Arch::place_constraints() +{ + std::vector<std::pair<IdString, BelId>> constrained_cells; + for (auto &cell_pair : cells) { + CellInfo *cell = cell_pair.second.get(); + auto loc_constr = cell->attrs.find(id("LOC")); + auto bel_constr = cell->attrs.find(id("BEL")); + + if (loc_constr == cell->attrs.end() || bel_constr == cell->attrs.end()) + continue; + + IdString loc_name = id(loc_constr->second.as_string()); + IdString bel_name = id(bel_constr->second.as_string()); + + BelId bel; + for (size_t i = 0; i < chip_info->tiles.size(); ++i) { + const auto &tile = chip_info->tiles[i]; + bool site_found = false; + for (size_t j = 0; j < tile.sites.size(); ++j) { + auto &site_data = chip_info->sites[tile.sites[j]]; + if (loc_name == id(site_data.site_name.get())) { + site_found = true; + break; + } + } + + if (!site_found) + continue; + + const auto &tile_type = chip_info->tile_types[tile.type]; + bool bel_found = false; + for (size_t j = 0; j < tile_type.bel_data.size(); ++j) { + const BelInfoPOD &bel_data = tile_type.bel_data[j]; + if (bel_name == IdString(bel_data.name)) { + bel.tile = i; + bel.index = j; + bel_found = true; + break; + } + } + + if (bel_found) + break; + else + log_error("No bel found for user constraint \'%s/%s\' for cell \'%s\'\n", loc_name.c_str(getCtx()), + bel_name.c_str(getCtx()), cell->name.c_str(getCtx())); + } + + if (!isValidBelForCellType(cell->type, bel)) + log_error("Bel \'%s\' is invalid for cell \'%s\' (%s)\n", nameOfBel(bel), cell->name.c_str(getCtx()), + cell->type.c_str(getCtx())); + + auto bound_cell = getBoundBelCell(bel); + if (bound_cell) + log_error("Cell \'%s\' cannot be bound to bel \'%s\' " + "since it is already bound to cell \'%s\'\n", + cell->name.c_str(getCtx()), nameOfBel(bel), bound_cell->name.c_str(getCtx())); + + bindBel(bel, cell, STRENGTH_USER); + + cell->attrs.erase(id("BEL")); + constrained_cells.emplace_back(cell->name, bel); + } + + if (constrained_cells.empty()) + return; + + log_info("Cell placed via user constraints:\n"); + for (auto cell_bel : constrained_cells) { + IdString cell_name = cell_bel.first; + BelId bel = cell_bel.second; + + if (!isBelLocationValid(bel)) + log_error(" - Bel \'%s\' is not valid for cell \'%s\'\n", nameOfBel(bel), cell_name.c_str(getCtx())); + + log_info(" - %s placed at %s\n", cell_name.c_str(getCtx()), nameOfBel(cell_bel.second)); + } +} + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index ca1340b5..9085615b 100644 --- a/fpga_interchange/xdc.cc +++ b/fpga_interchange/xdc.cc @@ -31,7 +31,7 @@ NEXTPNR_NAMESPACE_BEGIN -static int port_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } +static int obj_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s) { @@ -55,12 +55,21 @@ static void port_update_string(Tcl_Obj *objPtr) set_tcl_obj_string(objPtr, port_name); } -static void port_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) +static void cell_update_string(Tcl_Obj *objPtr) +{ + const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1); + CellInfo *cell_info = static_cast<CellInfo *>(objPtr->internalRep.twoPtrValue.ptr2); + + std::string cell_name = cell_info->name.str(ctx); + set_tcl_obj_string(objPtr, cell_name); +} + +static void obj_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) { dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue; } -static void port_free(Tcl_Obj *objPtr) {} +static void obj_free(Tcl_Obj *objPtr) {} static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { @@ -71,7 +80,11 @@ static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) } static Tcl_ObjType port_object = { - "port", port_free, port_dup, port_update_string, port_set_from_any, + "port", obj_free, obj_dup, port_update_string, obj_set_from_any, +}; + +static Tcl_ObjType cell_object = { + "cell", obj_free, obj_dup, cell_update_string, obj_set_from_any, }; static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) @@ -106,6 +119,38 @@ static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CON } } +static int get_cells(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const Context *ctx = static_cast<const Context *>(data); + if (objc == 1) { + // Return list of all ports. + Tcl_SetStringResult(interp, "Unimplemented"); + return TCL_ERROR; + } else if (objc == 2) { + const char *arg0 = Tcl_GetString(objv[1]); + IdString cell_name = ctx->id(arg0); + + auto iter = ctx->cells.find(cell_name); + if (iter == ctx->cells.end()) { + Tcl_SetStringResult(interp, "Could not find cell " + cell_name.str(ctx)); + return TCL_ERROR; + } + + Tcl_Obj *result = Tcl_NewObj(); + result->typePtr = &cell_object; + result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); + result->internalRep.twoPtrValue.ptr2 = (void *)(iter->second.get()); + + result->bytes = nullptr; + cell_update_string(result); + + Tcl_SetObjResult(interp, result); + return TCL_OK; + } else { + return TCL_ERROR; + } +} + static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // set_property <property> <value> <object> @@ -118,17 +163,21 @@ static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const char *value = Tcl_GetString(objv[2]); const Tcl_Obj *object = objv[3]; - if (object->typePtr != &port_object) { - Tcl_SetStringResult(interp, "Only port objects are handled right now!"); - return TCL_ERROR; - } + if (object->typePtr == &port_object) { + const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); + PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2); + NPNR_ASSERT(port_info->net != nullptr); + CellInfo *cell = ctx->port_cells.at(port_info->name); - const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); - PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2); - NPNR_ASSERT(port_info->net != nullptr); - CellInfo *cell = ctx->port_cells.at(port_info->name); + cell->attrs[ctx->id(property)] = Property(value); + } else if (object->typePtr == &cell_object) { + const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); + CellInfo *cell = static_cast<CellInfo *>(object->internalRep.twoPtrValue.ptr2); - cell->attrs[ctx->id(property)] = Property(value); + cell->attrs[ctx->id(property)] = Property(value); + } else { + return TCL_ERROR; + } return TCL_OK; } @@ -150,6 +199,7 @@ TclInterp::TclInterp(Context *ctx) " }\n" "}") == TCL_OK); Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr); + Tcl_CreateObjCommand(interp, "get_cells", get_cells, ctx, nullptr); Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr); } |