Merge branch 'xc7-router_improve' into xc7

This commit is contained in:
Eddie Hung 2018-11-13 17:21:46 -08:00
commit e19ad23ef4
27 changed files with 1226 additions and 934 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

@ -405,7 +405,26 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path)
log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay),
ctx->getDelayNS(total), net->name.c_str(ctx), ctx->getDelayNS(sink->budget), driver_loc.x,
driver_loc.y, sink_loc.x, sink_loc.y);
log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
if (ctx->verbose) {
auto driver_wire = ctx->getNetinfoSourceWire(net);
auto sink_wire = ctx->getNetinfoSinkWire(net, *sink);
log_info(" prediction: %f ns estimate: %f ns\n",
ctx->getDelayNS(ctx->predictDelay(net, *sink)), ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire)));
auto cursor = sink_wire;
delay_t cursor_delay;
while (driver_wire != cursor) {
cursor_delay = ctx->getWireDelay(cursor).maxDelay();
log_info(" %1.3f %30s\n", ctx->getDelayNS(cursor_delay), ctx->getWireName(cursor).c_str(ctx));
auto it = net->wires.find(cursor);
assert(it != net->wires.end());
auto pip = it->second.pip;
NPNR_ASSERT(pip != PipId());
cursor = ctx->getPipSrcWire(pip);
}
cursor_delay = ctx->getWireDelay(cursor).maxDelay();
log_info(" %1.3f %30s\n", ctx->getDelayNS(cursor_delay), ctx->getWireName(cursor).c_str(ctx));
}
last_port = sink->port;
}
log_break();

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

@ -453,6 +453,8 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti
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);
@ -513,6 +515,8 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti
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

@ -41,7 +41,7 @@ TorcInfo::TorcInfo(Arch *ctx, const std::string &inDeviceName, const std::string
site_index_to_type(construct_site_index_to_type(ctx, sites)),
bel_to_loc(construct_bel_to_loc(sites, tiles, num_bels, site_index_to_type)),
wire_to_tilewire(construct_wire_to_tilewire(segments, tiles, segment_to_wire, trivial_to_wire)),
num_wires(wire_to_tilewire.size()), wire_to_delay(construct_wire_to_delay(wire_to_tilewire, *ddb)),
num_wires(wire_to_tilewire.size()), wire_to_delay(construct_wire_to_delay(tiles, wire_to_tilewire, *ddb)),
pip_to_arc(construct_pip_to_arc(wire_to_tilewire, *ddb, wire_to_pips_uphill, wire_to_pips_downhill)),
num_pips(pip_to_arc.size())
{
@ -108,7 +108,7 @@ std::vector<Loc> TorcInfo::construct_bel_to_loc(const Sites &sites, const Tiles
for (SiteIndex i(0); i < site_index_to_type.size(); ++i) {
const auto &site = sites.getSite(i);
const auto &tile_info = tiles.getTileInfo(site.getTileIndex());
const auto x = tile_info.getCol();
const auto x = (tile_info.getCol() + 1) / 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs
const auto y = tile_info.getRow();
if (site_index_to_type[i] == id_SLICE_LUT6) {
@ -163,7 +163,7 @@ TorcInfo::construct_wire_to_tilewire(const Segments &segments, const Tiles &tile
wire_to_tilewire.shrink_to_fit();
return wire_to_tilewire;
}
std::vector<DelayInfo> TorcInfo::construct_wire_to_delay(const std::vector<Tilewire> &wire_to_tilewire, const DDB &ddb)
std::vector<DelayInfo> TorcInfo::construct_wire_to_delay(const Tiles &tiles, const std::vector<Tilewire> &wire_to_tilewire, const DDB &ddb)
{
std::vector<DelayInfo> wire_to_delay;
wire_to_delay.reserve(wire_to_tilewire.size());
@ -174,45 +174,62 @@ std::vector<DelayInfo> TorcInfo::construct_wire_to_delay(const std::vector<Tilew
const boost::regex re_BYP_B = boost::regex("BYP_[BL]\\d");
const boost::regex re_BOUNCE_NS = boost::regex("(BYP|FAN)_BOUNCE_[NS]3_\\d");
const boost::regex re_FAN = boost::regex("FAN(_ALT)?\\d");
const boost::regex re_CLB_I1_6 = boost::regex("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
std::unordered_map</*TileTypeIndex*/unsigned, std::vector<delay_t>> delay_lookup;
boost::cmatch what;
ExtendedWireInfo ewi(ddb);
for (const auto &tw : wire_to_tilewire) {
ewi.set(tw);
DelayInfo d;
if (boost::regex_match(ewi.mWireName, what, re_124)) {
switch (what.str(2)[0]) {
case '1':
d.delay = 150;
break;
case '2':
d.delay = 170;
break;
case '4':
d.delay = 210;
break;
case '6':
d.delay = 210;
break;
default:
throw;
const TileInfo& tileInfo = tiles.getTileInfo(tw.getTileIndex());
auto tile_type_index = tileInfo.getTypeIndex();
auto it = delay_lookup.find(tile_type_index);
if (it == delay_lookup.end()) {
auto wireCount = tiles.getWireCount(tile_type_index);
std::vector<delay_t> tile_delays(wireCount);
for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
const WireInfo& wireInfo = tiles.getWireInfo(tile_type_index, wireIndex);
auto wire_name = wireInfo.getName();
if (boost::regex_match(wire_name, what, re_124)) {
switch (what.str(2)[0]) {
case '1': tile_delays[wireIndex] = 150; break;
case '2': tile_delays[wireIndex] = 170; break;
case '4': tile_delays[wireIndex] = 210; break;
case '6': tile_delays[wireIndex] = 210; break;
default: throw;
}
} else if (boost::regex_match(wire_name, what, re_L)) {
std::string l(what[2]);
if (l == "H")
tile_delays[wireIndex] = 360;
else if (l == "VB")
tile_delays[wireIndex] = 300;
else if (l == "V")
tile_delays[wireIndex] = 350;
else
throw;
} else if (boost::regex_match(wire_name, what, re_BYP)) {
tile_delays[wireIndex] = 190;
} else if (boost::regex_match(wire_name, what, re_BYP_B)) {
} else if (boost::regex_match(wire_name, what, re_FAN)) {
tile_delays[wireIndex] = 190;
} else if (boost::regex_match(wire_name, what, re_CLB_I1_6)) {
switch (what.str(2)[0]) {
case '1': tile_delays[wireIndex] = 280; break;
case '2': tile_delays[wireIndex] = 280; break;
case '3': tile_delays[wireIndex] = 180; break;
case '4': tile_delays[wireIndex] = 180; break;
case '5': tile_delays[wireIndex] = 80; break;
case '6': tile_delays[wireIndex] = 40; break;
default: throw;
}
}
}
} else if (boost::regex_match(ewi.mWireName, what, re_L)) {
std::string l(what[2]);
if (l == "H")
d.delay = 360;
else if (l == "VB")
d.delay = 300;
else if (l == "V")
d.delay = 350;
else
throw;
} else if (boost::regex_match(ewi.mWireName, what, re_BYP)) {
d.delay = 190;
} else if (boost::regex_match(ewi.mWireName, what, re_BYP_B)) {
} else if (boost::regex_match(ewi.mWireName, what, re_FAN)) {
d.delay = 190;
it = delay_lookup.emplace(tile_type_index, std::move(tile_delays)).first;
}
assert(it != delay_lookup.end());
DelayInfo d;
d.delay = it->second[tw.getWireIndex()];
wire_to_delay.emplace_back(std::move(d));
}
@ -600,20 +617,26 @@ IdString Arch::getPipName(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
#if 1
int x = chip_info->pip_data[pip.index].x;
int y = chip_info->pip_data[pip.index].y;
ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire());
ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire());
std::stringstream pip_name;
pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
return id(pip_name.str());
std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get();
std::replace(src_name.begin(), src_name.end(), '/', '.');
std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get();
std::replace(dst_name.begin(), dst_name.end(), '/', '.');
return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
#else
return id(chip_info->pip_data[pip.index].name.get());
#endif
//#if 1
// int x = chip_info->pip_data[pip.index].x;
// int y = chip_info->pip_data[pip.index].y;
//
// std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get();
// std::replace(src_name.begin(), src_name.end(), '/', '.');
//
// std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get();
// std::replace(dst_name.begin(), dst_name.end(), '/', '.');
//
// return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
//#else
// return id(chip_info->pip_data[pip.index].name.get());
//#endif
}
// -----------------------------------------------------------------------
@ -957,8 +980,22 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
if (cell->type == id_SLICE_LUT6) {
if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index)
return toPort == id_O || toPort == id_OQ;
if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) {
if (toPort == id_O) {
delay.delay = 124; // Tilo
return true;
}
if (toPort == id_OQ) {
delay.delay = 95; // Tas
return true;
}
}
if (fromPort == id_CLK) {
if (toPort == id_OQ) {
delay.delay = 456; // Tcko
return false; // No path CLK->OQ, but this fn is used for getting clkToQ delay
}
}
} else if (cell->type == id_BUFGCTRL) {
return true;
}

View File

@ -331,7 +331,7 @@ struct TorcInfo
construct_wire_to_tilewire(const Segments &segments, const Tiles &tiles,
std::unordered_map<Segments::SegmentReference, int> &segment_to_wire,
std::unordered_map<Tilewire, int> &trivial_to_wire);
static std::vector<DelayInfo> construct_wire_to_delay(const std::vector<Tilewire> &wire_to_tilewire,
static std::vector<DelayInfo> construct_wire_to_delay(const Tiles &tiles, const std::vector<Tilewire> &wire_to_tilewire,
const DDB &ddb);
static std::vector<Arc> construct_pip_to_arc(const std::vector<Tilewire> &wire_to_tilewire, const DDB &ddb,
std::vector<std::vector<int>> &wire_to_pips_uphill,
@ -677,6 +677,11 @@ 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());
@ -773,6 +778,11 @@ struct Arch : BaseCtx
return pip_to_net[pip.index];
}
WireId getConflictingPipWire(PipId pip) const
{
return WireId();
}
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());

View File

@ -70,6 +70,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
@ -78,6 +79,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
@ -86,6 +88,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

@ -4,5 +4,5 @@ COMP "led2" LOCATE = SITE "G14" LEVEL 1;
COMP "led3" LOCATE = SITE "D18" LEVEL 1;
COMP "clki" LOCATE = SITE "K17" LEVEL 1;
NET "clki" PERIOD = 8 nS ;
PIN "clki_pin" = BEL "clki$iob.PAD" PINNAME PAD;
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;

View File

@ -1,10 +1,14 @@
#!/bin/bash
set -ex
yosys blinky.ys
../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 250
../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 150
xdl -xdl2ncd blinky.xdl
bitgen -w blinky.ncd -g UnconstrainedPins:Allow
trce blinky.ncd -v 10
netgen -ofmt verilog -w blinky.ncd blinky_chip.v -tm blinky
iverilog -o blinky_tb blinky_chip.v blinky_tb.v -y/opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/simprims/ -insert_glbl true
vvp -N ./blinky_tb
#netgen -ofmt verilog -w blinky.ncd blinky_chip.v -tm blinky -insert_glbl true
#iverilog -o blinky_tb blinky_chip.v blinky_tb.v -y/opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/simprims/
#vvp -N ./blinky_tb
#xdl -xdl2ncd blinky.xdl -nopips blinky_map.ncd
#par -w blinky_map.ncd blinky_par.ncd blinky.pcf
#bitgen -w blinky_par.ncd -g UnconstrainedPins:Allow

View File

@ -5,7 +5,7 @@ module blinky (
output led2,
output led3
);
`include "ps7.vh"
//`include "ps7.vh"
BUFGCTRL clk_gb (
.I0(clki),
@ -19,7 +19,7 @@ module blinky (
);
localparam BITS = 4;
localparam LOG2DELAY = 21;
localparam LOG2DELAY = 23;
reg [BITS+LOG2DELAY-1:0] counter = 0;
reg [BITS-1:0] outcnt;
@ -29,5 +29,5 @@ module blinky (
outcnt <= counter >> LOG2DELAY;
end
assign {led0, led1, led2, led3} = outcnt ^ (outcnt >> 1);
assign {led0, led1, led2, led3} = outcnt /*^ (outcnt >> 1)*/;
endmodule

View File

@ -4,12 +4,12 @@ module blinky_tb;
wire led0, led1, led2, led3;
chip uut (
.\clki$iob.PAD.PAD (clk),
.\led0$iob.OUTBUF.OUT (led0),
.\led1$iob.OUTBUF.OUT (led1),
.\led2$iob.OUTBUF.OUT (led2),
.\led3$iob.OUTBUF.OUT (led3)
blinky uut (
.\clki.PAD.PAD (clk),
.\led0.OUTBUF.OUT (led0),
.\led1.OUTBUF.OUT (led1),
.\led2.OUTBUF.OUT (led2),
.\led3.OUTBUF.OUT (led3)
);
initial begin

View File

@ -259,17 +259,17 @@ std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::stri
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
{
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
replace_port(lut, ctx->id("I0"), lc, id_I1);
replace_port(lut, ctx->id("I0"), lc, id_I6);
if (get_net_or_empty(lut, id_I1))
replace_port(lut, id_I1, lc, id_I2);
replace_port(lut, id_I1, lc, id_I5);
if (get_net_or_empty(lut, id_I2))
replace_port(lut, id_I2, lc, id_I3);
replace_port(lut, id_I2, lc, id_I4);
if (get_net_or_empty(lut, id_I3))
replace_port(lut, id_I3, lc, id_I4);
replace_port(lut, id_I3, lc, id_I3);
if (get_net_or_empty(lut, id_I4))
replace_port(lut, id_I4, lc, id_I5);
replace_port(lut, id_I4, lc, id_I2);
if (get_net_or_empty(lut, id_I5))
replace_port(lut, id_I5, lc, id_I6);
replace_port(lut, id_I5, lc, id_I1);
if (no_dff) {
replace_port(lut, id_O, lc, id_O);
lc->params[ctx->id("DFF_ENABLE")] = "0";

View File

@ -104,8 +104,27 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
const auto &src_info = torc_info->tiles.getTileInfo(src_tw.getTileIndex());
const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
const auto &dst_info = torc_info->tiles.getTileInfo(dst_tw.getTileIndex());
return 100 * (abs(src_info.getCol() - dst_info.getCol()) + abs(src_info.getRow() - dst_info.getRow()));
auto abs_delta_x = (abs(src_info.getCol() - dst_info.getCol()) + 1)/ 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs
auto abs_delta_y = abs(src_info.getRow() - dst_info.getRow());
#if 1
auto div_LH = std::div(abs_delta_x, 12);
auto div_LV = std::div(abs_delta_y, 18);
auto div_LVB = std::div(div_LV.rem, 12);
auto div_H6 = std::div(div_LH.rem, 6);
auto div_V6 = std::div(div_LVB.rem, 6);
auto div_H4 = std::div(div_H6.rem, 4);
auto div_V4 = std::div(div_V6.rem, 4);
auto div_H2 = std::div(div_H4.rem, 2);
auto div_V2 = std::div(div_V4.rem, 2);
auto num_H1 = div_H2.rem;
auto num_V1 = div_V2.rem;
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350
+ (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210
+ (div_H2.quot + div_V2.quot) * 170
+ (num_H1 + num_V1) * 150;
#else
return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y);
#endif
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@ -113,8 +132,27 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
const auto &driver = net_info->driver;
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));
auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
#if 1
auto div_LH = std::div(abs_delta_x, 12);
auto div_LV = std::div(abs_delta_y, 18);
auto div_LVB = std::div(div_LV.rem, 12);
auto div_H6 = std::div(div_LH.rem, 6);
auto div_V6 = std::div(div_LVB.rem, 6);
auto div_H4 = std::div(div_H6.rem, 4);
auto div_V4 = std::div(div_V6.rem, 4);
auto div_H2 = std::div(div_H4.rem, 2);
auto div_V2 = std::div(div_V4.rem, 2);
auto num_H1 = div_H2.rem;
auto num_V1 = div_V2.rem;
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350
+ (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210
+ (div_H2.quot + div_V2.quot) * 170
+ (num_H1 + num_V1) * 150;
#else
return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y);
#endif
}
NEXTPNR_NAMESPACE_END

View File

@ -421,7 +421,7 @@ static void pack_io(Context *ctx)
}
} else {
// Create a IOBUF buffer
std::unique_ptr<CellInfo> ice_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx) + "$iob");
std::unique_ptr<CellInfo> ice_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
nxio_to_sb(ctx, ci, ice_cell.get());
new_cells.push_back(std::move(ice_cell));
sb = new_cells.back().get();

3
xc7/picorv32.pcf Normal file
View File

@ -0,0 +1,3 @@
NET "clk" PERIOD = 8 nS ;
PIN "clk_pin" = BEL "clk.PAD" PINNAME PAD;
PIN "clk_pin" CLOCK_DEDICATED_ROUTE = FALSE;

View File

@ -3,6 +3,7 @@ set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys picorv32.ys
../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl
#../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
#icetime -d hx8k -t picorv32.asc
../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl --pcf picorv32.pcf --freq 150
xdl -xdl2ncd picorv32.xdl
#bitgen -w blinky.ncd -g UnconstrainedPins:Allow
trce picorv32.ncd -v 10