Add initial handling of local site inverters and constant signals.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
5e96740451
commit
77bc2f9130
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
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)));
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER);
|
||||
wire = site_arch.getPipSrcWire(pip);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user