Merge pull request #107 from YosysHQ/router_improve

Major rewrite of "router1"
This commit is contained in:
Eddie Hung 2018-11-13 11:39:51 -08:00 committed by GitHub
commit 3b2b15dc4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1029 additions and 851 deletions

View File

@ -18,6 +18,7 @@
*/
#include "nextpnr.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@ -25,6 +26,7 @@ assertion_failure::assertion_failure(std::string msg, std::string expr_str, std:
: runtime_error("Assertion failure: " + msg + " (" + filename + ":" + std::to_string(line) + ")"), msg(msg),
expr_str(expr_str), filename(filename), line(line)
{
log_flush();
}
void IdString::set(const BaseCtx *ctx, const std::string &s)
@ -51,6 +53,30 @@ void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
}
const char *BaseCtx::nameOfBel(BelId bel) const
{
const Context *ctx = getCtx();
return ctx->getBelName(bel).c_str(ctx);
}
const char *BaseCtx::nameOfWire(WireId wire) const
{
const Context *ctx = getCtx();
return ctx->getWireName(wire).c_str(ctx);
}
const char *BaseCtx::nameOfPip(PipId pip) const
{
const Context *ctx = getCtx();
return ctx->getPipName(pip).c_str(ctx);
}
const char *BaseCtx::nameOfGroup(GroupId group) const
{
const Context *ctx = getCtx();
return ctx->getGroupName(group).c_str(ctx);
}
WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
{
if (net_info->driver.cell == nullptr)

View File

@ -269,7 +269,7 @@ struct PipMap
struct NetInfo : ArchNetInfo
{
IdString name;
int32_t udata;
int32_t udata = 0;
PortRef driver;
std::vector<PortRef> users;
@ -487,13 +487,23 @@ struct BaseCtx
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
template <typename T> const char *nameOf(const T *obj)
const char *nameOf(IdString name) const
{
return name.c_str(this);
}
template <typename T> const char *nameOf(const T *obj) const
{
if (obj == nullptr)
return "";
return obj->name.c_str(getCtx());
return obj->name.c_str(this);
}
const char *nameOfBel(BelId bel) const;
const char *nameOfWire(WireId wire) const;
const char *nameOfPip(PipId pip) const;
const char *nameOfGroup(GroupId group) const;
// --------------------------------------------------------------
bool allUiReload = true;
@ -541,6 +551,7 @@ struct Context : Arch, DeterministicRNG
delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const;
// provided by router1.cc
bool checkRoutedDesign() const;
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
std::unordered_map<WireId, PipId> *route = nullptr, bool useEstimate = true);

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,10 @@ struct Router1Cfg : Settings
bool cleanupReroute;
bool fullCleanupReroute;
bool useEstimate;
delay_t wireRipupPenalty;
delay_t netRipupPenalty;
delay_t reuseBonus;
delay_t estimatePrecision;
};
extern bool router1(Context *ctx, const Router1Cfg &cfg);

View File

@ -30,15 +30,15 @@ delay_t maxDelay() const { return delay; }
### BelId
A type representing a bel name. `BelId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<BelId>`.
A type representing a bel name. `BelId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<BelId>`.
### WireId
A type representing a wire name. `WireId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<WireId>`.
A type representing a wire name. `WireId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<WireId>`.
### PipId
A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<PipId>`.
A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<PipId>`.
### GroupId
@ -215,14 +215,15 @@ Return true if the wire is available, i.e. can be bound to a net.
Return the net a wire is bound to.
### NetInfo \*getConflictingWireNet(WireId wire) const
### WireId getConflictingWireWire(WireId wire) const
If this returns a non-nullptr, then unbinding that net
If this returns a non-WireId(), then unbinding that wire
will make the given wire available.
This returns nullptr if the wire is already available,
or if there is no single net that can be unbound to make this
wire available.
### NetInfo \*getConflictingWireNet(WireId wire) const
If this returns a non-nullptr, then unbinding that entire net
will make the given wire available.
### DelayInfo getWireDelay(WireId wire) const
@ -282,18 +283,23 @@ This method must also update `NetInfo::wires`.
Returns true if the given pip is available to be bound to a net.
Users must also check if the pip destination wire is available
with `checkWireAvail(getPipDstWire(pip))` before binding the
pip to a net.
### NetInfo \*getBoundPipNet(PipId pip) const
Return the net this pip is bound to.
### WireId getConflictingPipWire(PipId pip) const
If this returns a non-WireId(), then unbinding that wire
will make the given pip available.
### NetInfo \*getConflictingPipNet(PipId pip) const
Return the net that needs to be unbound in order to make this
pip available.
This does not need to (but may) return the conflicting wire if the conflict is
limited to the conflicting wire being bound to the destination wire for this
pip.
If this returns a non-nullptr, then unbinding that entire net
will make the given pip available.
### const\_range\<PipId\> getPips() const

View File

@ -38,7 +38,94 @@ For nextpnr we are using the following terminology.
Adding new architectures to nextpnr
-----------------------------------
TBD
### Implementing new architectures
Each nextpnr architecture must implement the *nextpnr architecture API*.
See [archapi.md](archapi.md) for a complete reference of the architecture API.
### Delay Estimates
Each architecture must implement a `estimateDelay()` method that estimates the expected delay for a path from given `src` to `dst` wires.
*It is very important that this method slightly overestimates the expected delay.* Furthermore, it should overestimate the expected delay
by a slightly larger margin for longer paths than for shorter paths. Otherwise there will be performance issues with the router.
The delays estimates returned by that method should also be as fine-grain as possible. It definitely pays off to spend some time improving the `estimateDelay()`
for your architecture once implementing small designs work.
### Ripup Information
The `getConflictingWireWire()`, `getConflictingWireNet()`, `getConflictingPipWire()`, and `getConflictingPipNet()` methods are used by the router
to determine which resources to rip up in order to make a given routing resource (wire or pip) available.
The architecture must guanrantee that the following invariants hold.
**Invariant 1:**
```
if (!ctx->checkWireAvail(wire)) {
WireId w = getConflictingWireWire(wire);
if (w != WireId()) {
ctx->unbindWire(w);
assert(ctx->checkWireAvail(wire));
}
}
```
**Invariant 2:**
```
if (!ctx->checkWireAvail(wire)) {
NetInfo *n = getConflictingWireNet(wire);
if (n != nullptr) {
for (auto &it : n->wires)
ctx->unbindWire(it.first);
assert(ctx->checkWireAvail(wire));
}
}
```
**Invariant 3:**
```
if (!ctx->checkPipAvail(pip)) {
WireId w = getConflictingPipWire(pip);
if (w != WireId()) {
ctx->unbindWire(w);
assert(ctx->checkPipAvail(pip));
}
}
```
**Invariant 4:**
```
if (!ctx->checkPipAvail(pip)) {
NetInfo *n = getConflictingPipNet(pip);
if (n != nullptr) {
for (auto &it : n->wires)
ctx->unbindWire(it.first);
assert(ctx->checkPipAvail(pip));
}
}
```
**Invariant 5:**
```
if (ctx->checkWireAvail(wire)) {
// bind is guaranteed to succeed
ctx->bindWire(wire, net, strength);
}
```
**Invariant 6:**
```
if (ctx->checkPipAvail(pip) && ctx->checkWireAvail(ctx->getPipDstWire(pip))) {
// bind is guaranteed to succeed
ctx->bindPip(pip, net, strength);
}
```
Nextpnr and other tools
-----------------------

View File

@ -400,7 +400,7 @@ BelId Arch::getBelByLocation(Loc loc) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
return 100 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
return 170 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@ -409,7 +409,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
return 100 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
return 170 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
}
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }

View File

@ -619,6 +619,8 @@ struct Arch : BaseCtx
return wire_to_net.at(wire);
}
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
@ -724,6 +726,8 @@ struct Arch : BaseCtx
return pip_to_net.at(pip);
}
WireId getConflictingPipWire(PipId pip) const { return WireId(); }
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());

View File

@ -75,6 +75,7 @@ struct Location
bool operator==(const Location &other) const { return x == other.x && y == other.y; }
bool operator!=(const Location &other) const { return x != other.x || y != other.y; }
bool operator<(const Location &other) const { return y == other.y ? x < other.x : y < other.y; }
};
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); }
@ -86,6 +87,7 @@ struct BelId
bool operator==(const BelId &other) const { return index == other.index && location == other.location; }
bool operator!=(const BelId &other) const { return index != other.index || location != other.location; }
bool operator<(const BelId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct WireId
@ -95,6 +97,7 @@ struct WireId
bool operator==(const WireId &other) const { return index == other.index && location == other.location; }
bool operator!=(const WireId &other) const { return index != other.index || location != other.location; }
bool operator<(const WireId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct PipId
@ -104,6 +107,7 @@ struct PipId
bool operator==(const PipId &other) const { return index == other.index && location == other.location; }
bool operator!=(const PipId &other) const { return index != other.index || location != other.location; }
bool operator<(const PipId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct GroupId

View File

@ -373,6 +373,8 @@ NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net;
NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; }
WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); }
const std::vector<PipId> &Arch::getPips() const { return pip_ids; }
Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; }

View File

@ -172,6 +172,7 @@ struct Arch : BaseCtx
void unbindWire(WireId wire);
bool checkWireAvail(WireId wire) const;
NetInfo *getBoundWireNet(WireId wire) const;
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const;
DelayInfo getWireDelay(WireId wire) const { return DelayInfo(); }
const std::vector<WireId> &getWires() const;
@ -186,6 +187,7 @@ struct Arch : BaseCtx
void unbindPip(PipId pip);
bool checkPipAvail(PipId pip) const;
NetInfo *getBoundPipNet(PipId pip) const;
WireId getConflictingPipWire(PipId pip) const;
NetInfo *getConflictingPipNet(PipId pip) const;
const std::vector<PipId> &getPips() const;
Loc getPipLocation(PipId pip) const;

View File

@ -638,6 +638,8 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundWireNet(wire)), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Wire",
ctx->getWireName(ctx->getConflictingWireWire(wire)).c_str(ctx), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingWireNet(wire)),
ElementType::NET);
@ -698,6 +700,8 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Wire",
ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
ElementType::NET);
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),

View File

@ -404,7 +404,7 @@ struct Arch : BaseCtx
std::vector<CellInfo *> bel_to_cell;
std::vector<NetInfo *> wire_to_net;
std::vector<NetInfo *> pip_to_net;
std::vector<NetInfo *> switches_locked;
std::vector<WireId> switches_locked;
ArchArgs args;
Arch(ArchArgs args);
@ -546,7 +546,7 @@ struct Arch : BaseCtx
auto pip = it->second.pip;
if (pip != PipId()) {
pip_to_net[pip.index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = WireId();
}
net_wires.erase(it);
@ -566,6 +566,8 @@ struct Arch : BaseCtx
return wire_to_net[wire.index];
}
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
@ -608,14 +610,15 @@ struct Arch : BaseCtx
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);
pip_to_net[pip.index] = net;
switches_locked[chip_info->pip_data[pip.index].switch_index] = net;
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == WireId());
WireId dst;
dst.index = chip_info->pip_data[pip.index].dst;
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
pip_to_net[pip.index] = net;
switches_locked[chip_info->pip_data[pip.index].switch_index] = dst;
wire_to_net[dst.index] = net;
net->wires[dst].pip = pip;
net->wires[dst].strength = strength;
@ -627,7 +630,7 @@ struct Arch : BaseCtx
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != WireId());
WireId dst;
dst.index = chip_info->pip_data[pip.index].dst;
@ -636,33 +639,39 @@ struct Arch : BaseCtx
pip_to_net[pip.index]->wires.erase(dst);
pip_to_net[pip.index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = WireId();
refreshUiPip(pip);
refreshUiWire(dst);
}
bool checkPipAvail(PipId pip) const
bool ice40_pip_hard_unavail(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
auto &pi = chip_info->pip_data[pip.index];
auto &si = chip_info->bits_info->switches[pi.switch_index];
if (switches_locked[pi.switch_index] != nullptr)
return false;
if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
NPNR_ASSERT(si.bel >= 0);
if (bel_to_cell[si.bel] != nullptr)
return false;
return true;
}
if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
NPNR_ASSERT(si.bel >= 0);
if (bel_carry[si.bel])
return false;
return true;
}
return true;
return false;
}
bool checkPipAvail(PipId pip) const
{
if (ice40_pip_hard_unavail(pip))
return false;
auto &pi = chip_info->pip_data[pip.index];
return switches_locked[pi.switch_index] == WireId();
}
NetInfo *getBoundPipNet(PipId pip) const
@ -671,10 +680,21 @@ struct Arch : BaseCtx
return pip_to_net[pip.index];
}
WireId getConflictingPipWire(PipId pip) const
{
if (ice40_pip_hard_unavail(pip))
return WireId();
return switches_locked[chip_info->pip_data[pip.index].switch_index];
}
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return switches_locked[chip_info->pip_data[pip.index].switch_index];
if (ice40_pip_hard_unavail(pip))
return nullptr;
WireId wire = switches_locked[chip_info->pip_data[pip.index].switch_index];
return wire == WireId() ? nullptr : wire_to_net[wire.index];
}
AllPipRange getPips() const

View File

@ -66,6 +66,7 @@ struct BelId
bool operator==(const BelId &other) const { return index == other.index; }
bool operator!=(const BelId &other) const { return index != other.index; }
bool operator<(const BelId &other) const { return index < other.index; }
};
struct WireId
@ -74,6 +75,7 @@ struct WireId
bool operator==(const WireId &other) const { return index == other.index; }
bool operator!=(const WireId &other) const { return index != other.index; }
bool operator<(const WireId &other) const { return index < other.index; }
};
struct PipId
@ -82,6 +84,7 @@ struct PipId
bool operator==(const PipId &other) const { return index == other.index; }
bool operator!=(const PipId &other) const { return index != other.index; }
bool operator<(const PipId &other) const { return index < other.index; }
};
struct GroupId