Merge remote-tracking branch 'origin/master' into timingapi

This commit is contained in:
Eddie Hung 2018-11-13 12:12:11 -08:00
commit 2d39cde17b
18 changed files with 1038 additions and 860 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)
@ -152,6 +154,30 @@ void BaseCtx::removeConstraint(IdString constrName)
constraints.erase(constrName);
}
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

@ -288,7 +288,7 @@ struct ClockConstraint;
struct NetInfo : ArchNetInfo
{
IdString name;
int32_t udata;
int32_t udata = 0;
PortRef driver;
std::vector<PortRef> users;
@ -579,13 +579,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;
@ -657,6 +667,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);

View File

@ -28,19 +28,19 @@ NEXTPNR_NAMESPACE_BEGIN
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
{
wirelen_t wirelength = 0;
Loc driver_loc;
bool driver_gb;
CellInfo *driver_cell = net->driver.cell;
if (!driver_cell)
return 0;
if (driver_cell->bel == BelId())
return 0;
driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
driver_loc = ctx->getBelLocation(driver_cell->bel);
bool driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
if (driver_gb)
return 0;
IdString clock_port;
bool timing_driven = ctx->timing_driven && type == MetricType::COST && ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port) != TMG_IGNORE;
delay_t negative_slack = 0;
delay_t worst_slack = std::numeric_limits<delay_t>::max();
Loc driver_loc = ctx->getBelLocation(driver_cell->bel);
int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y;
for (auto load : net->users) {
if (load.cell == nullptr)
@ -48,7 +48,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
CellInfo *load_cell = load.cell;
if (load_cell->bel == BelId())
continue;
if (ctx->timing_driven && type == MetricType::COST) {
if (timing_driven) {
delay_t net_delay = ctx->predictDelay(net, load);
auto slack = load.budget - net_delay;
if (slack < 0)
@ -65,7 +65,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
xmax = std::max(xmax, load_loc.x);
ymax = std::max(ymax, load_loc.y);
}
if (ctx->timing_driven && type == MetricType::COST) {
if (timing_driven) {
wirelength = wirelen_t(
(((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-ctx->getDelayNS(worst_slack) / 5)))));
} else {

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

@ -40,7 +40,7 @@ class ECP5CommandHandler : public CommandHandler
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions();
po::options_description getArchOptions() override;
};
ECP5CommandHandler::ECP5CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}

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

@ -37,7 +37,7 @@ class GenericCommandHandler : public CommandHandler
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions();
po::options_description getArchOptions() override;
};
GenericCommandHandler::GenericCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}

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

View File

@ -43,7 +43,7 @@ class Ice40CommandHandler : public CommandHandler
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions();
po::options_description getArchOptions() override;
};
Ice40CommandHandler::Ice40CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}