diff options
-rw-r--r-- | common/router1.cc | 5 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 115 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 13 | ||||
-rw-r--r-- | fpga_interchange/archdefs.h | 1 | ||||
-rw-r--r-- | fpga_interchange/fpga_interchange.cpp | 36 | ||||
-rw-r--r-- | fpga_interchange/site_arch.h | 6 | ||||
-rw-r--r-- | fpga_interchange/site_arch.impl.h | 19 | ||||
-rw-r--r-- | fpga_interchange/site_router.cc | 325 |
8 files changed, 460 insertions, 60 deletions
diff --git a/common/router1.cc b/common/router1.cc index 30ebc113..11107a40 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -435,8 +435,9 @@ struct Router1 // TODO: this matches the situation before supporting multiple cell->bel pins, but do we want to keep // this invariant? if (phys_idx == 0) - log_error("No wires found for port %s on destination cell %s.\n", - ctx->nameOf(net_info->users[user_idx].port), ctx->nameOf(net_info->users[user_idx].cell)); + log_warning("No wires found for port %s on destination cell %s.\n", + ctx->nameOf(net_info->users[user_idx].port), + ctx->nameOf(net_info->users[user_idx].cell)); } src_to_net[src_wire] = net_info; diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index a8b62f95..5c252802 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -144,6 +144,7 @@ Arch::Arch(ArchArgs args) : args(args) io_port_types.emplace(this->id("$nextpnr_ibuf")); io_port_types.emplace(this->id("$nextpnr_obuf")); io_port_types.emplace(this->id("$nextpnr_iobuf")); + io_port_types.emplace(this->id("$nextpnr_inv")); if (!this->args.package.empty()) { IdString package = this->id(this->args.package); @@ -711,6 +712,10 @@ bool Arch::pack() bool Arch::place() { + // Before placement, ripup placement specific bindings and unmask all cell + // pins. + remove_site_routing(); + std::string placer = str_or_default(settings, id("placer"), defaultPlacer); // Re-map BEL pins without constant pins @@ -750,6 +755,10 @@ bool Arch::route() { std::string router = str_or_default(settings, id("router"), defaultRouter); + // Reset site routing and remove masked cell pins from previous router run + // (if any). + remove_site_routing(); + // Re-map BEL pins with constant pins for (BelId bel : getBels()) { CellInfo *cell = getBoundBelCell(bel); @@ -758,26 +767,6 @@ bool Arch::route() } } - HashTables::HashSet<WireId> wires_to_unbind; - for (auto &net_pair : nets) { - for (auto &wire_pair : net_pair.second->wires) { - WireId wire = wire_pair.first; - if (wire_pair.second.strength != STRENGTH_PLACER) { - // Only looking for bound placer wires - continue; - } - - const TileWireInfoPOD &wire_data = wire_info(wire); - NPNR_ASSERT(wire_data.site != -1); - - wires_to_unbind.emplace(wire); - } - } - - for (WireId wire : wires_to_unbind) { - unbindWire(wire); - } - for (auto &tile_pair : tileStatus) { for (auto &site_router : tile_pair.second.sites) { if (site_router.cells_in_site.empty()) { @@ -805,6 +794,9 @@ bool Arch::route() getCtx()->attrs[getCtx()->id("step")] = std::string("route"); archInfoToAttributes(); + // Now that routing is complete, unmask BEL pins. + unmask_bel_pins(); + return result; } @@ -1799,6 +1791,89 @@ bool Arch::can_invert(PipId pip) const return bel_data.non_inverting_pin == pip_info.extra_data && bel_data.inverting_pin == pip_info.extra_data; } +void Arch::mask_bel_pins_on_site_wire(NetInfo *net, WireId wire) +{ + std::vector<size_t> bel_pins_to_mask; + for (const PortRef &port_ref : net->users) { + if (port_ref.cell->bel == BelId()) { + continue; + } + + NPNR_ASSERT(port_ref.cell != nullptr); + auto iter = port_ref.cell->cell_bel_pins.find(port_ref.port); + if (iter == port_ref.cell->cell_bel_pins.end()) { + continue; + } + + std::vector<IdString> &cell_bel_pins = iter->second; + bel_pins_to_mask.clear(); + + for (size_t bel_pin_idx = 0; bel_pin_idx < cell_bel_pins.size(); ++bel_pin_idx) { + IdString bel_pin = cell_bel_pins.at(bel_pin_idx); + WireId bel_pin_wire = getBelPinWire(port_ref.cell->bel, bel_pin); + if (bel_pin_wire == wire) { + bel_pins_to_mask.push_back(bel_pin_idx); + } + } + + if (!bel_pins_to_mask.empty()) { + std::vector<IdString> &masked_cell_bel_pins = port_ref.cell->masked_cell_bel_pins[port_ref.port]; + // Remove in reverse order to preserve indicies. + for (auto riter = bel_pins_to_mask.rbegin(); riter != bel_pins_to_mask.rend(); ++riter) { + size_t bel_pin_idx = *riter; + masked_cell_bel_pins.push_back(cell_bel_pins.at(bel_pin_idx)); + cell_bel_pins.erase(cell_bel_pins.begin() + bel_pin_idx); + } + } + } +} + +void Arch::unmask_bel_pins() +{ + for (auto &cell_pair : cells) { + CellInfo *cell = cell_pair.second.get(); + if (cell->masked_cell_bel_pins.empty()) { + continue; + } + + for (auto &mask_pair : cell->masked_cell_bel_pins) { + IdString cell_port = mask_pair.first; + const std::vector<IdString> &bel_pins = mask_pair.second; + std::vector<IdString> &cell_bel_pins = cell->cell_bel_pins[cell_port]; + cell_bel_pins.insert(cell_bel_pins.begin(), bel_pins.begin(), bel_pins.end()); + } + + cell->masked_cell_bel_pins.clear(); + } +} + +void Arch::remove_site_routing() +{ + HashTables::HashSet<WireId> wires_to_unbind; + for (auto &net_pair : nets) { + for (auto &wire_pair : net_pair.second->wires) { + WireId wire = wire_pair.first; + if (wire_pair.second.strength != STRENGTH_PLACER) { + // Only looking for bound placer wires + continue; + } + + const TileWireInfoPOD &wire_data = wire_info(wire); + NPNR_ASSERT(wire_data.site != -1); + + wires_to_unbind.emplace(wire); + } + } + + for (WireId wire : wires_to_unbind) { + unbindWire(wire); + } + + // FIXME: !!!!! Remove $nextpnr_inv cells here !!!!! + + unmask_bel_pins(); +} + // Instance constraint templates. template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 2162ce90..23aed6bc 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -1075,6 +1075,19 @@ struct Arch : ArchAPI<ArchRanges> std::string chipdb_hash; std::string get_chipdb_hash() const; + + // Masking moves BEL pins from cell_bel_pins to masked_cell_bel_pins for + // the purposes routing. The idea is that masked BEL pins are already + // handled during site routing, and they shouldn't be visible to the + // router. + void mask_bel_pins_on_site_wire(NetInfo *net, WireId wire); + + // This removes pips and wires bound by the site router, and unmasks all + // BEL pins masked during site routing. + void remove_site_routing(); + + // This unmasks any BEL pins that were masked when site routing was bound. + void unmask_bel_pins(); }; NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h index 8364520d..5d1233b2 100644 --- a/fpga_interchange/archdefs.h +++ b/fpga_interchange/archdefs.h @@ -113,6 +113,7 @@ struct ArchCellInfo int32_t cell_mapping; HashTables::HashMap<IdString, std::vector<IdString>> cell_bel_pins; + HashTables::HashMap<IdString, std::vector<IdString>> masked_cell_bel_pins; HashTables::HashSet<IdString> const_ports; LutCell lut_cell; }; diff --git a/fpga_interchange/fpga_interchange.cpp b/fpga_interchange/fpga_interchange.cpp index 1be1dfde..52d49fa2 100644 --- a/fpga_interchange/fpga_interchange.cpp +++ b/fpga_interchange/fpga_interchange.cpp @@ -220,11 +220,25 @@ static void init_bel_pin( std::string site_name = site_and_type.substr(0, pos); - auto out_bel_pin = branch.getRouteSegment().initBelPin(); - - out_bel_pin.setSite(strings->get_index(site_name)); - out_bel_pin.setBel(strings->get_index(bel_name[1].str(ctx))); - out_bel_pin.setPin(strings->get_index(pin_name.str(ctx))); + const BelInfoPOD & bel_data = bel_info(ctx->chip_info, bel); + if(bel_data.category == BEL_CATEGORY_LOGIC) { + // This is a boring old logic BEL. + auto out_bel_pin = branch.getRouteSegment().initBelPin(); + + out_bel_pin.setSite(strings->get_index(site_name)); + out_bel_pin.setBel(strings->get_index(bel_name[1].str(ctx))); + out_bel_pin.setPin(strings->get_index(pin_name.str(ctx))); + } else { + // This is a local site inverter. This is represented with a + // $nextpnr_inv, and this BEL pin is the input to that inverter. + NPNR_ASSERT(bel_data.category == BEL_CATEGORY_ROUTING); + auto out_pip = branch.getRouteSegment().initSitePIP(); + + out_pip.setSite(strings->get_index(site_name)); + out_pip.setBel(strings->get_index(bel_name[1].str(ctx))); + out_pip.setPin(strings->get_index(pin_name.str(ctx))); + out_pip.setIsInverting(true); + } } @@ -383,10 +397,16 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str StringEnumerator strings; + IdString nextpnr_inv = ctx->id("$nextpnr_inv"); + size_t number_placements = 0; for(auto & cell_name : placed_cells) { const CellInfo & cell = *ctx->cells.at(cell_name); + if(cell.type == nextpnr_inv) { + continue; + } + if(cell.bel == BelId()) { continue; } @@ -412,6 +432,10 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str for(auto & cell_name : placed_cells) { const CellInfo & cell = *ctx->cells.at(cell_name); + if(cell.type == nextpnr_inv) { + continue; + } + if(cell.bel == BelId()) { continue; } @@ -513,8 +537,6 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str net_out.setName(strings.get_index(net.name.str(ctx))); } - // FIXME: Also vcc/gnd nets needs to get special handling through - // inverters. std::unordered_map<WireId, BelPin> root_wires; std::unordered_map<WireId, std::vector<PipId>> pip_downhill; std::unordered_set<PipId> pips; diff --git a/fpga_interchange/site_arch.h b/fpga_interchange/site_arch.h index 95b6fcba..91330aa0 100644 --- a/fpga_interchange/site_arch.h +++ b/fpga_interchange/site_arch.h @@ -25,6 +25,7 @@ #include <unordered_set> #include <vector> +#include "PhysicalNetlist.capnp.h" #include "arch_iterators.h" #include "chipdb.h" #include "hash_table.h" @@ -295,6 +296,11 @@ struct SiteArch // Can this site pip optional invert its signal? inline bool canInvert(const SitePip &site_pip) const NPNR_ALWAYS_INLINE; + // For a site port, returns the preferred constant net type. + // + // If no preference, then NetType is SIGNAL. + inline PhysicalNetlist::PhysNetlist::NetType prefered_constant_net_type(const SitePip &site_pip) const; + inline SitePipDownhillRange getPipsDownhill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE; inline SitePipUphillRange getPipsUphill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE; SiteWireRange getWires() const; diff --git a/fpga_interchange/site_arch.impl.h b/fpga_interchange/site_arch.impl.h index 0be298c9..a471b690 100644 --- a/fpga_interchange/site_arch.impl.h +++ b/fpga_interchange/site_arch.impl.h @@ -295,6 +295,25 @@ inline bool SiteArch::canInvert(const SitePip &site_pip) const return bel_data.non_inverting_pin == pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data; } +inline PhysicalNetlist::PhysNetlist::NetType SiteArch::prefered_constant_net_type(const SitePip &site_pip) const +{ + // FIXME: Implement site port overrides from chipdb once available. + IdString prefered_constant_net(ctx->chip_info->constants->best_constant_net); + IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); + IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); + + if (prefered_constant_net == IdString()) { + return PhysicalNetlist::PhysNetlist::NetType::SIGNAL; + } else if (prefered_constant_net == gnd_net_name) { + return PhysicalNetlist::PhysNetlist::NetType::GND; + } else if (prefered_constant_net == vcc_net_name) { + return PhysicalNetlist::PhysNetlist::NetType::VCC; + } else { + log_error("prefered_constant_net %s is not the GND (%s) or VCC(%s) net?\n", prefered_constant_net.c_str(ctx), + gnd_net_name.c_str(ctx), vcc_net_name.c_str(ctx)); + } +} + NEXTPNR_NAMESPACE_END #endif /* SITE_ARCH_H */ diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 12707f37..32c09dbe 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -19,12 +19,12 @@ #include "nextpnr.h" +#include "design_utils.h" #include "dynamic_bitarray.h" +#include "hash_table.h" #include "log.h" #include "site_routing_cache.h" -#include "hash_table.h" - #include "site_arch.h" #include "site_arch.impl.h" @@ -368,6 +368,7 @@ struct PossibleSolutions std::vector<SitePip>::const_iterator pips_end; bool inverted = false; bool can_invert = false; + PhysicalNetlist::PhysNetlist::NetType prefered_constant_net_type = PhysicalNetlist::PhysNetlist::NetType::SIGNAL; }; bool test_solution(SiteArch *ctx, SiteNetInfo *net, std::vector<SitePip>::const_iterator pips_begin, @@ -404,8 +405,92 @@ void remove_solution(SiteArch *ctx, std::vector<SitePip>::const_iterator pips_be } } +struct SolutionPreference +{ + const SiteArch *ctx; + const std::vector<PossibleSolutions> &solutions; + + SolutionPreference(const SiteArch *ctx, const std::vector<PossibleSolutions> &solutions) + : ctx(ctx), solutions(solutions) + { + } + + bool non_inverting_preference(const PossibleSolutions &lhs, const PossibleSolutions &rhs) const + { + // If the LHS is non-inverting and the RHS is inverting, then put the + // LHS first. + if (!lhs.inverted && rhs.inverted) { + return true; + } + + // Better to have a path that can invert over a path that has no + // option to invert. + return (!lhs.can_invert) < (!rhs.can_invert); + } + + bool inverting_preference(const PossibleSolutions &lhs, const PossibleSolutions &rhs) const + { + // If the LHS is inverting and the RHS is non-inverting, then put the + // LHS first (because this is the inverting preferred case). + if (lhs.inverted && !rhs.inverted) { + return true; + } + + // Better to have a path that can invert over a path that has no + // option to invert. + return (!lhs.can_invert) < (!rhs.can_invert); + } + + bool operator()(size_t lhs_solution_idx, size_t rhs_solution_idx) const + { + const PossibleSolutions &lhs = solutions.at(lhs_solution_idx); + const PossibleSolutions &rhs = solutions.at(rhs_solution_idx); + + NPNR_ASSERT(lhs.net == rhs.net); + + PhysicalNetlist::PhysNetlist::NetType net_type = ctx->ctx->get_net_type(lhs.net->net); + if (net_type == PhysicalNetlist::PhysNetlist::NetType::SIGNAL) { + return non_inverting_preference(lhs, rhs); + } + + // All GND/VCC nets use out of site sources. Local constant sources + // are still connected via synthetic edges to the global GND/VCC + // network. + NPNR_ASSERT(lhs.net->driver.type == SiteWire::OUT_OF_SITE_SOURCE); + + bool lhs_match_preference = net_type == lhs.prefered_constant_net_type; + bool rhs_match_preference = net_type == rhs.prefered_constant_net_type; + + if (lhs_match_preference && !rhs_match_preference) { + // Prefer solutions where the net type already matches the + // prefered constant type. + return true; + } + + if (!lhs_match_preference && rhs_match_preference) { + // Prefer solutions where the net type already matches the + // prefered constant type. In this case the RHS is better, which + // means that RHS < LHS, hence false here. + return false; + } + + NPNR_ASSERT(lhs_match_preference == rhs_match_preference); + + if (!lhs_match_preference) { + // If the net type does not match the preference, then prefer + // inverted solutions. + return inverting_preference(lhs, rhs); + } else { + // If the net type does match the preference, then prefer + // non-inverted solutions. + return non_inverting_preference(lhs, rhs); + } + } +}; + static bool find_solution_via_backtrack(SiteArch *ctx, std::vector<PossibleSolutions> *solutions, - const std::vector<std::vector<size_t>> &sinks_to_solutions) + std::vector<std::vector<size_t>> sinks_to_solutions, + const std::vector<SiteWire> &sinks) { std::vector<uint8_t> routed_sinks; std::vector<size_t> solution_indicies; @@ -422,6 +507,21 @@ static bool find_solution_via_backtrack(SiteArch *ctx, std::vector<PossibleSolut } } + // Sort sinks_to_solutions so that preferred solutions are tested earlier + // than less preferred solutions. + for (size_t sink_idx = 0; sink_idx < sinks_to_solutions.size(); ++sink_idx) { + std::vector<size_t> &solutions_for_sink = sinks_to_solutions.at(sink_idx); + std::stable_sort(solutions_for_sink.begin(), solutions_for_sink.end(), SolutionPreference(ctx, *solutions)); + + if (verbose_site_router(ctx)) { + log_info("Solutions for sink %s\n", ctx->nameOfWire(sinks.at(sink_idx))); + for (size_t solution_idx : solutions_for_sink) { + const PossibleSolutions &solution = solutions->at(solution_idx); + log_info("%zu: inverted = %d, can_invert = %d\n", solution_idx, solution.inverted, solution.can_invert); + } + } + } + for (size_t sink_idx = 0; sink_idx < sinks_to_solutions.size(); ++sink_idx) { size_t solution_count = 0; for (size_t solution_idx : sinks_to_solutions[sink_idx]) { @@ -554,12 +654,14 @@ bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeSt // First convert remaining solutions into a flat solution set. std::vector<PossibleSolutions> solutions; + std::vector<SiteWire> sinks; HashTables::HashMap<SiteWire, size_t> sink_map; std::vector<std::vector<size_t>> sinks_to_solutions; for (const auto *expansion : expansions) { for (const SiteWire &unrouted_sink : expansion->net_users) { auto result = sink_map.emplace(unrouted_sink, sink_map.size()); NPNR_ASSERT(result.second); + sinks.push_back(unrouted_sink); } } @@ -572,12 +674,6 @@ bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeSt for (const auto *expansion : expansions) { for (size_t idx = 0; idx < expansion->num_solutions(); ++idx) { - if (expansion->solution_inverted(idx)) { - // FIXME: May prefer an inverted solution if constant net - // type. - continue; - } - SiteWire wire = expansion->solution_sink(idx); auto begin = expansion->solution_begin(idx); auto end = expansion->solution_end(idx); @@ -595,15 +691,22 @@ bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeSt solution.can_invert = expansion->solution_can_invert(idx); for (auto iter = begin; iter != end; ++iter) { - NPNR_ASSERT(ctx->getPipDstWire(*iter) == wire); - wire = ctx->getPipSrcWire(*iter); + const SitePip &site_pip = *iter; + NPNR_ASSERT(ctx->getPipDstWire(site_pip) == wire); + wire = ctx->getPipSrcWire(site_pip); + + // If there is a input site port, mark on the solution what the + // prefered constant net type is for this site port. + if (site_pip.type == SitePip::SITE_PORT && wire.type == SiteWire::SITE_PORT_SOURCE) { + solution.prefered_constant_net_type = ctx->prefered_constant_net_type(site_pip); + } } NPNR_ASSERT(expansion->net_driver == wire); } } - return find_solution_via_backtrack(ctx, &solutions, sinks_to_solutions); + return find_solution_via_backtrack(ctx, &solutions, sinks_to_solutions, sinks); } void check_routing(const SiteArch &site_arch) @@ -631,26 +734,192 @@ void check_routing(const SiteArch &site_arch) } } -void apply_routing(Context *ctx, const SiteArch &site_arch) +static void apply_simple_routing(Context *ctx, const SiteArch &site_arch, NetInfo *net, const SiteNetInfo *site_net, + const SiteWire &user) { - for (auto &net_pair : site_arch.nets) { - NetInfo *net = net_pair.first; + SiteWire wire = user; + while (wire != site_net->driver) { + SitePip site_pip = site_net->wires.at(wire).pip; + NPNR_ASSERT(site_arch.getPipDstWire(site_pip) == wire); + + if (site_pip.type == SitePip::SITE_PIP || site_pip.type == SitePip::SITE_PORT) { + NetInfo *bound_net = ctx->getBoundPipNet(site_pip.pip); + if (bound_net == nullptr) { + ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); + } else { + NPNR_ASSERT(bound_net == net); + } + } - // If the driver wire is a site wire, bind it. - if (net_pair.second.driver.type == SiteWire::SITE_WIRE) { - WireId driver_wire = net_pair.second.driver.wire; - if (ctx->getBoundWireNet(driver_wire) != net) { - ctx->bindWire(driver_wire, net, STRENGTH_PLACER); + wire = site_arch.getPipSrcWire(site_pip); + } +} + +static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetInfo *net, const SiteNetInfo *site_net) +{ + IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); + NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); + + IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); + NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); + + // This function is designed to operate only on the gnd or vcc net, and + // assumes that the GND and VCC nets have been unified. + NPNR_ASSERT(net == vcc_net || net == gnd_net); + + for (auto &user : site_net->users) { + // FIXME: Handle case where pip is "can_invert", and that + // inversion helps with accomidating "best constant". + bool is_path_inverting = false; + + SiteWire wire = user; + PipId inverting_pip; + while (wire != site_net->driver) { + SitePip pip = site_net->wires.at(wire).pip; + NPNR_ASSERT(site_arch.getPipDstWire(pip) == wire); + + if (site_arch.isInverting(pip)) { + // FIXME: Should be able to handle the general case of + // multiple inverters, but that is harder (and annoying). Also + // most sites won't allow for a double inversion, so just + // disallow for now. + NPNR_ASSERT(!is_path_inverting); + is_path_inverting = true; + NPNR_ASSERT(pip.type == SitePip::SITE_PIP); + inverting_pip = pip.pip; } + + wire = site_arch.getPipSrcWire(pip); } - for (auto &wire_pair : net_pair.second.wires) { - const SitePip &site_pip = wire_pair.second.pip; - if (site_pip.type != SitePip::SITE_PIP && site_pip.type != SitePip::SITE_PORT) { - continue; + if (!is_path_inverting) { + // This routing is boring, use base logic. + apply_simple_routing(ctx, site_arch, net, site_net, user); + continue; + } + + NPNR_ASSERT(inverting_pip != PipId()); + + // This net is going to become two nets. + // The portion of the net prior to the inverter is going to be bound + // to the opposite net. For example, if the original net was gnd_net, + // the portion prior to the inverter will not be the vcc_net. + // + // A new cell will be generated to sink the connection from the + // opposite net. + NetInfo *net_before_inverter; + if (net == gnd_net) { + net_before_inverter = vcc_net; + } else { + NPNR_ASSERT(net == vcc_net); + net_before_inverter = gnd_net; + } + + // First find a name for the new cell + int count = 0; + CellInfo *new_cell = nullptr; + while (true) { + std::string new_cell_name = stringf("%s_%s.%d", net->name.c_str(ctx), site_arch.nameOfWire(user), count); + IdString new_cell_id = ctx->id(new_cell_name); + if (ctx->cells.count(new_cell_id)) { + count += 1; + } else { + new_cell = ctx->createCell(new_cell_id, ctx->id("$nextpnr_inv")); + break; } + } - ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); + auto &tile_type = loc_info(ctx->chip_info, inverting_pip); + auto &pip_data = tile_type.pip_data[inverting_pip.index]; + NPNR_ASSERT(pip_data.site != -1); + auto &bel_data = tile_type.bel_data[pip_data.bel]; + + BelId inverting_bel; + inverting_bel.tile = inverting_pip.tile; + inverting_bel.index = pip_data.bel; + + IdString in_port(bel_data.ports[pip_data.extra_data]); + NPNR_ASSERT(bel_data.types[pip_data.extra_data] == PORT_IN); + + IdString id_I = ctx->id("I"); + new_cell->addInput(id_I); + new_cell->cell_bel_pins[id_I].push_back(in_port); + + new_cell->bel = inverting_bel; + new_cell->belStrength = STRENGTH_PLACER; + ctx->tileStatus.at(inverting_bel.tile).boundcells[inverting_bel.index] = new_cell; + + connect_port(ctx, net_before_inverter, new_cell, id_I); + + // The original BEL pin is now routed, but only through the inverter. + // Because the cell/net model doesn't allow for multiple source pins + // and the fact that the portion of the net after the inverter is + // currently routed, all BEL pins on this site wire are going to be + // masked from the router. + NPNR_ASSERT(user.type == SiteWire::SITE_WIRE); + ctx->mask_bel_pins_on_site_wire(net, user.wire); + + // Bind wires and pips to the two nets. + bool after_inverter = true; + wire = user; + while (wire != site_net->driver) { + SitePip site_pip = site_net->wires.at(wire).pip; + NPNR_ASSERT(site_arch.getPipDstWire(site_pip) == wire); + + if (site_arch.isInverting(site_pip)) { + NPNR_ASSERT(after_inverter); + after_inverter = false; + + // Because this wire is just after the inverter, bind it to + // the net without the pip, as this is a "source". + NPNR_ASSERT(wire.type == SiteWire::SITE_WIRE); + ctx->bindWire(wire.wire, net, STRENGTH_PLACER); + } else { + if (site_pip.type == SitePip::SITE_PIP || site_pip.type == SitePip::SITE_PORT) { + if (after_inverter) { + ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); + } else { + ctx->bindPip(site_pip.pip, net_before_inverter, STRENGTH_PLACER); + } + } + } + + wire = site_arch.getPipSrcWire(site_pip); + } + } +} + +static void apply_routing(Context *ctx, const SiteArch &site_arch) +{ + IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); + NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); + + IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); + NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); + + for (auto &net_pair : site_arch.nets) { + NetInfo *net = net_pair.first; + const SiteNetInfo *site_net = &net_pair.second; + + if (net == gnd_net || net == vcc_net) { + apply_constant_routing(ctx, site_arch, net, site_net); + } else { + // If the driver wire is a site wire, bind it. + if (site_net->driver.type == SiteWire::SITE_WIRE) { + WireId driver_wire = site_net->driver.wire; + if (ctx->getBoundWireNet(driver_wire) != net) { + ctx->bindWire(driver_wire, net, STRENGTH_PLACER); + } + } + + for (auto &wire_pair : site_net->wires) { + const SitePip &site_pip = wire_pair.second.pip; + if (site_pip.type != SitePip::SITE_PIP && site_pip.type != SitePip::SITE_PORT) { + continue; + } + + ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); + } } } } @@ -699,12 +968,6 @@ bool SiteRouter::checkSiteRouting(const Context *ctx, const TileStatus &tile_sta } } - // FIXME: Populate "consumed_wires" with all VCC/GND tied in the site. - // This will allow route_site to leverage site local constant sources. - // - // FIXME: Handle case where a constant is requested, but use of an - // inverter is possible. This is the place to handle "bestConstant" - // (e.g. route VCC's over GND's, etc). auto tile_type_idx = ctx->chip_info->tiles[tile].type; const std::vector<LutElement> &lut_elements = ctx->lut_elements.at(tile_type_idx); std::vector<LutMapper> lut_mappers; |