diff options
Diffstat (limited to 'fpga_interchange')
-rw-r--r-- | fpga_interchange/arch.cc | 27 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 5 | ||||
-rw-r--r-- | fpga_interchange/arch_pack_clusters.cc | 7 | ||||
-rw-r--r-- | fpga_interchange/arch_pack_io.cc | 9 | ||||
-rw-r--r-- | fpga_interchange/arch_place_constr.cc | 106 | ||||
-rw-r--r-- | fpga_interchange/chipdb.h | 3 | ||||
-rw-r--r-- | fpga_interchange/dedicated_interconnect.cc | 74 | ||||
-rw-r--r-- | fpga_interchange/site_router.cc | 11 | ||||
-rw-r--r-- | fpga_interchange/xdc.cc | 81 |
9 files changed, 291 insertions, 32 deletions
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 3b0572fa..a39f49e6 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); @@ -1751,8 +1752,12 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const } } - // If this pip is a route-though, make sure all of the route-though - // wires are unbound. + // If this pip is a route-though, make sure all of the route-though are valid. + // A route-through is valid if: + // - none of the pseudo-pip wires are bound to a net + // - the pseudo pip wires are bound to the same net and there are no + // lut elements in the tile type: this to prevent the router from choosing + // a pseudo-pip on the same LUT and on a different input pin for the same net. const TileTypeInfoPOD &tile_type = loc_info(chip_info, pip); const PipInfoPOD &pip_data = tile_type.pip_data[pip.index]; WireId wire; @@ -1763,14 +1768,20 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const NPNR_ASSERT(dst != wire); NetInfo *other_net = getConflictingWireNet(wire); - if (other_net != nullptr && other_net != net) { + bool is_null_net = other_net == nullptr; + if (!is_null_net) { + bool is_same_net = other_net == net; + bool tile_has_luts = tile_type.lut_elements.size() > 0; + if (!is_same_net || tile_has_luts) { #ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Pip %s is not available because wire %s is tied to net %s\n", getCtx()->nameOfPip(pip), - getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx())); - } + if (getCtx()->verbose) + log_info("Pip %s is not available because wire %s is tied to a different net " + "(other net: %s - orig net: %s) or the pseudo pip may traverses LUTs\n", + getCtx()->nameOfPip(pip), getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx()), + net == nullptr ? "NULL net" : net->name.c_str(getCtx())); #endif - return false; + return false; + } } } diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 36f6a7dc..0fb4f462 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -722,6 +722,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; @@ -857,7 +860,7 @@ struct Arch : ArchAPI<ArchRanges> const CellInfo *cell = tile_status.boundcells[bel.index]; if (cell != nullptr) { - if (cell->cluster == ClusterId() && !dedicated_interconnect.isBelLocationValid(bel, cell)) + if (!dedicated_interconnect.isBelLocationValid(bel, cell)) return false; if (io_port_types.count(cell->type)) { diff --git a/fpga_interchange/arch_pack_clusters.cc b/fpga_interchange/arch_pack_clusters.cc index 18162aa4..f4e50233 100644 --- a/fpga_interchange/arch_pack_clusters.cc +++ b/fpga_interchange/arch_pack_clusters.cc @@ -193,6 +193,8 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, const Context *ctx = getCtx(); const Cluster &packed_cluster = clusters.at(cluster); + auto &cluster_data = cluster_info(chip_info, packed_cluster.index); + CellInfo *root_cell = getClusterRootCell(cluster); if (!ctx->isValidBelForCellType(root_cell->type, root_bel)) return false; @@ -205,8 +207,6 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, next_bel = root_bel; } else { // Find next chained cluster node - auto &cluster_data = cluster_info(chip_info, packed_cluster.index); - IdString next_bel_pin(cluster_data.chainable_ports[0].bel_source); WireId next_bel_pin_wire = ctx->getBelPinWire(next_bel, next_bel_pin); next_bel = BelId(); @@ -256,7 +256,8 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, WireId bel_pin_wire = ctx->getBelPinWire(next_bel, bel_pin); ExpansionDirection direction = port_type == PORT_IN ? CLUSTER_UPHILL_DIR : CLUSTER_DOWNHILL_DIR; - pool<BelId> cluster_bels = find_cluster_bels(ctx, bel_pin_wire, direction); + pool<BelId> cluster_bels = + find_cluster_bels(ctx, bel_pin_wire, direction, (bool)cluster_data.out_of_site_clusters); if (cluster_bels.size() == 0) continue; diff --git a/fpga_interchange/arch_pack_io.cc b/fpga_interchange/arch_pack_io.cc index 19d8cece..38b619a3 100644 --- a/fpga_interchange/arch_pack_io.cc +++ b/fpga_interchange/arch_pack_io.cc @@ -55,8 +55,6 @@ bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, WireId dst = downhill ? arch->getPipDstWire(pip) : arch->getPipSrcWire(pip); if (already_visited.count(dst)) return; - if (!arch->is_site_wire(dst) && arch->get_wire_category(dst) == WIRE_CAT_GENERAL) - return; // this pass only considers dedicated routing visit_queue.push(dst); already_visited.insert(dst); }; @@ -83,6 +81,7 @@ void Arch::place_iobufs(WireId pad_wire, NetInfo *net, if (ctx->verbose) log_info("Placed IO cell %s:%s at %s.\n", ctx->nameOf(cell_port.first), ctx->nameOf(cell_port.first->type), ctx->nameOfBel(cell_port.first->bel)); + placed_cells->insert(cell_port.first); } } @@ -246,7 +245,10 @@ void Arch::pack_ports() } if (possible_site_types.empty()) { - log_error("Port '%s' has no possible site types!\n", port_name.c_str(getCtx())); + if (getCtx()->verbose) + log_info("Port '%s' has no possible site types, falling back to all types!\n", + port_name.c_str(getCtx())); + possible_site_types = package_pin_site_types; } if (getCtx()->verbose) { @@ -315,6 +317,7 @@ void Arch::pack_ports() for (CellInfo *cell : placed_cells) { NPNR_ASSERT(cell->bel != BelId()); if (!isBelLocationValid(cell->bel)) { + explain_bel_status(cell->bel); log_error("Tightly bound BEL %s was not valid!\n", nameOfBel(cell->bel)); } } 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/chipdb.h b/fpga_interchange/chipdb.h index fde35e7f..9ebbc1f3 100644 --- a/fpga_interchange/chipdb.h +++ b/fpga_interchange/chipdb.h @@ -34,7 +34,7 @@ NEXTPNR_NAMESPACE_BEGIN * kExpectedChipInfoVersion */ -static constexpr int32_t kExpectedChipInfoVersion = 11; +static constexpr int32_t kExpectedChipInfoVersion = 13; // Flattened site indexing. // @@ -421,6 +421,7 @@ NPNR_PACKED_STRUCT(struct ClusterPOD { RelSlice<uint32_t> root_cell_types; RelSlice<ChainablePortPOD> chainable_ports; RelSlice<ClusterCellPortPOD> cluster_cells_map; + uint32_t out_of_site_clusters; }); NPNR_PACKED_STRUCT(struct ChipInfoPOD { diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc index 1254b367..56b6b706 100644 --- a/fpga_interchange/dedicated_interconnect.cc +++ b/fpga_interchange/dedicated_interconnect.cc @@ -39,6 +39,12 @@ enum WireNodeState IN_SOURCE_SITE = 2 }; +enum ExpansionDirection +{ + EXPAND_DOWNHILL = 0, + EXPAND_UPHILL = 1 +}; + struct WireNode { WireId wire; @@ -52,6 +58,50 @@ struct WireNode // interconnect. constexpr int kMaxDepth = 6; +static uint32_t get_num_pips(const Context *ctx, WireId wire, ExpansionDirection direction) +{ + uint32_t num_pips = 0; + + if (direction == EXPAND_DOWNHILL) { + for (PipId pip : ctx->getPipsDownhill(wire)) { + auto &pip_data = pip_info(ctx->chip_info, pip); + if (pip_data.pseudo_cell_wires.size() > 0) + continue; + + if (ctx->getPipDstWire(pip) == WireId()) + continue; + + if (ctx->is_pip_synthetic(pip)) + continue; + + if (ctx->is_site_port(pip)) + continue; + + num_pips++; + } + } else { + NPNR_ASSERT(direction == EXPAND_UPHILL); + for (PipId pip : ctx->getPipsUphill(wire)) { + auto &pip_data = pip_info(ctx->chip_info, pip); + if (pip_data.pseudo_cell_wires.size() > 0) + continue; + + if (ctx->getPipSrcWire(pip) == WireId()) + continue; + + if (ctx->is_pip_synthetic(pip)) + continue; + + if (ctx->is_site_port(pip)) + continue; + + num_pips++; + } + } + + return num_pips; +} + void DedicatedInterconnect::init(const Context *ctx) { this->ctx = ctx; @@ -99,6 +149,16 @@ bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, B WireNode node_to_expand = nodes_to_expand.back(); nodes_to_expand.pop_back(); + auto num_pips = get_num_pips(ctx, node_to_expand.wire, EXPAND_DOWNHILL); + + // Usually, dedicated interconnects do not have more than one PIPs in the out-of-site + if (node_to_expand.depth > 1 && node_to_expand.state == IN_ROUTING && num_pips > 1) { + if (ctx->verbose) + log_info("Wire %s is on a non-dedicated path (number of pips %d)\n", + ctx->nameOfWire(node_to_expand.wire), num_pips); + continue; + } + for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { if (ctx->is_pip_synthetic(pip)) { continue; @@ -147,7 +207,7 @@ bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, B #ifdef DEBUG_EXPANSION log_info(" - Not dedicated site routing because loop!"); #endif - return false; + continue; } next_node.state = IN_SINK_SITE; break; @@ -365,6 +425,10 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) continue; } + if (ctx->io_port_types.count(net->driver.cell->type)) { + continue; + } + // Only check sink BELs. if (net->driver.cell == cell && net->driver.port == port_name) { if (!is_driver_on_net_valid(bel, cell, port_name, net)) { @@ -394,15 +458,19 @@ void DedicatedInterconnect::explain_bel_status(BelId bel, const CellInfo *cell) // This net doesn't have a driver, probably not valid? NPNR_ASSERT(net->driver.cell != nullptr); + if (ctx->io_port_types.count(net->driver.cell->type)) { + continue; + } + // Only check sink BELs. if (net->driver.cell == cell && net->driver.port == port_name) { if (!is_driver_on_net_valid(bel, cell, port_name, net)) { - log_info("Driver %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx), + log_info("Driver %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), net->name.c_str(ctx)); } } else { if (!is_sink_on_net_valid(bel, cell, port_name, net)) { - log_info("Sink %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx), + log_info("Sink %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), net->name.c_str(ctx)); } } diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 820b8d68..947081f4 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -862,6 +862,8 @@ static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetI SiteWire wire = user; PipId inverting_pip; + PhysicalNetlist::PhysNetlist::NetType pref_const = PhysicalNetlist::PhysNetlist::NetType::SIGNAL; + while (wire != site_net->driver) { SitePip pip = site_net->wires.at(wire).pip; NPNR_ASSERT(site_arch.getPipDstWire(pip) == wire); @@ -884,9 +886,16 @@ static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetI } wire = site_arch.getPipSrcWire(pip); + pref_const = site_arch.prefered_constant_net_type(pip); } - if (!is_path_inverting && !path_can_invert) { + bool is_pref_const = true; + if (pref_const == PhysicalNetlist::PhysNetlist::NetType::VCC && net == gnd_net) + is_pref_const = false; + else if (pref_const == PhysicalNetlist::PhysNetlist::NetType::GND && net == vcc_net) + is_pref_const = false; + + if (!is_path_inverting && (!path_can_invert || is_pref_const)) { // This routing is boring, use base logic. apply_simple_routing(ctx, site_arch, net, site_net, user); continue; diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index ca1340b5..5f43ed3c 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,18 +163,28 @@ 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!"); + 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); + + 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); + } else { return TCL_ERROR; } - 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); + return TCL_OK; +} +static int create_clock(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + // FIXME: add implementation return TCL_OK; } @@ -150,7 +205,9 @@ 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); + Tcl_CreateObjCommand(interp, "create_clock", create_clock, ctx, nullptr); } TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); } |