/*
* Intel CPU Microcode Update Driver for Linux
*
* Copyright (C) 2000-2004 Tigran Aivazian
*
* This driver allows to upgrade microcode on Intel processors
* belonging to IA-32 family - PentiumPro, Pentium II,
* Pentium III, Xeon, Pentium 4, etc.
*
* Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual,
* Order Number 245472 or free download from:
*
* http://developer.intel.com/design/pentium4/manuals/245472.htm
*
* For more information, go to http://www.urbanmyth.org/microcode
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com>
* Initial release.
* 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com>
* Added read() support + cleanups.
* 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com>
* Added 'device trimming' support. open(O_WRONLY) zeroes
* and frees the saved copy of applied microcode.
* 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com>
* Made to use devfs (/dev/cpu/microcode) + cleanups.
* 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com>
* Added misc device support (now uses both devfs and misc).
* Added MICROCODE_IOCFREE ioctl to clear memory.
* 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com>
* Messages for error cases (non Intel & no suitable microcode).
* 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
* Removed ->release(). Removed exclusive open and status bitmap.
* Added microcode_rwsem to serialize read()/write()/ioctl().
* Removed global kernel lock usage.
* 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
* Write 0 to 0x8B msr and then cpuid before reading revision,
* so that it works even if there were no update done by the
* BIOS. Otherwise, reading from 0x8B gives junk (which happened
* to be 0 on my machine which is why it worked even when I
* disabled update by the BIOS)
* Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
* 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
* Tigran Aivazian <tigran@veritas.com>
* Intel Pentium 4 processor support and bugfixes.
* 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
* Bugfix for HT (Hyper-Threading) enabled processors
* whereby processor resources are shared by all logical processors
* in a single CPU package.
* 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
* Tigran Aivazian <tigran@veritas.com>,
* Serialize updates as required on HT processors due to speculative
* nature of implementation.
* 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
* Fix the panic when writing zero-length microcode chunk.
* 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
* Jun Nakajima <jun.nakajima@intel.com>
* Support for the microcode updates in the new format.
* 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
* Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
* because we no longer hold a copy of applied microcode
* in kernel memory.
* 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
* Fix sigmatch() macro to handle old CPUs with pf == 0.
* Thanks to Stuart Swales for pointing out this bug.
*/
#include <xen/config.h>
#include <xen/lib.h>
#include <xen/kernel.h>
#include <xen/init.h>
#include <xen/sched.h>
#include <xen/smp.h>
#include <xen/spinlock.h>
#include <asm/current.h>
#include <asm/msr.h>
#include pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long *//*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2021 Symbiflow Authors
*
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "log.h"
#include "nextpnr.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
// All legal routes involved at most 2 sites, the source site and the sink
// site. The source site and sink sites may be the same, but that is not
// dedicated routing, that is intra site routing.
//
// Dedicated routing must leave the sink site, traverse some routing and
// terminate at another site. Routing that "flys" over a site is expressed as
// a psuedo-pip connected the relevant site pin wires, rather than traversing
// the site.
enum WireNodeState
{
IN_SINK_SITE = 0,
IN_ROUTING = 1,
IN_SOURCE_SITE = 2
};
struct WireNode
{
WireId wire;
WireNodeState state;
int depth;
};
// Maximum depth that a dedicate interconnect is considered.
//
// Routing networks with depth <= kMaxDepth is considers a dedicated
// interconnect.
constexpr int kMaxDepth = 20;
void DedicatedInterconnect::init(const Context *ctx)
{
this->ctx = ctx;
if (ctx->debug) {
log_info("Finding dedicated interconnect!\n");
}
find_dedicated_interconnect();
if (ctx->debug) {
print_dedicated_interconnect();
}
}
bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel, IdString dst_bel_pin,
bool site_only) const
{
std::vector<WireNode> nodes_to_expand;
WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin);
const auto &src_wire_data = ctx->wire_info(src_wire);
NPNR_ASSERT(src_wire_data.site != -1);
WireId dst_wire = ctx->getBelPinWire(dst_bel, dst_bel_pin);
if (src_wire == dst_wire) {
return true;
}
const auto &dst_wire_data = ctx->wire_info(dst_wire);
NPNR_ASSERT(dst_wire_data.site != -1);
WireNode wire_node;
wire_node.wire = src_wire;
wire_node.state = IN_SOURCE_SITE;
wire_node.depth = 0;
nodes_to_expand.push_back(wire_node);
while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipDstWire(pip);
if (wire == WireId()) {
continue;
}
if (ctx->debug) {
log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
}
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
// FIXME: Consider removing kMaxDepth and use kMaxSources?
return false;
}
auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
if (ctx->is_site_port(pip)) {
if (site_only) {
// When routing site only, don't allow site ports.
continue;
}
switch (node_to_expand.state) {
case IN_SOURCE_SITE:
NPNR_ASSERT(wire_data.site == -1);
next_node.state = IN_ROUTING;
break;
case IN_ROUTING:
NPNR_ASSERT(wire_data.site != -1);
if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
// Dedicated routing won't have straight loops,
// general routing looks like that.
#ifdef DEBUG_EXPANSION
log_info(" - Not dedicated site routing because loop!");
#endif
return false;
}
next_node.state = IN_SINK_SITE;
break;
case IN_SINK_SITE:
// Once entering a site, do not leave it again.
// This path is not a legal route!
expand_node = false;
break;
default:
// Unreachable!!!
NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
if (next_node.state == IN_SINK_SITE) {
for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
if (bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) {
if (ctx->debug) {
log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", ctx->nameOfBel(src_bel),
src_bel_pin.c_str(ctx), ctx->nameOfBel(dst_bel), dst_bel_pin.c_str(ctx));
}
return true;
}
}
}
}
}
return false;
}
bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port,
NetInfo *net) const
{
const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type;
type_bel_pin.bel_index = driver_bel.index;
Loc driver_loc = ctx->getBelLocation(driver_bel);
for (IdString driver_bel_pin : ctx->getBelPinsForCellPin(cell, driver_port)) {
type_bel_pin.bel_pin = driver_bel_pin;
auto iter = sources.find(type_bel_pin);
if (iter == sources.end()) {
// This BEL pin doesn't have a dedicate interconnect.
continue;
}
for (const PortRef &port_ref : net->users) {
NPNR_ASSERT(port_ref.cell != nullptr);
if (port_ref.cell->bel == BelId()) {
// FIXME: This should actually return "unknown!" because the
// sink is unplaced. Once the sink is placed, this constraint
// can be evaluated.
if (ctx->debug) {
log_info("BEL %s is not valid because sink cell %s/%s is not placed\n", ctx->nameOfBel(driver_bel),
port_ref.cell->name.c_str(ctx), port_ref.port.c_str(ctx));
}
return false;
}
BelId sink_bel = port_ref.cell->bel;
const auto &sink_bel_data = bel_info(ctx->chip_info, sink_bel);
Loc sink_loc = ctx->getBelLocation(port_ref.cell->bel);
if (sink_bel.tile == driver_bel.tile && sink_bel_data.site == driver_bel_data.site) {
// This might site local routing. See if it can be routed
for (IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) {
if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin, /*site_only=*/true)) {
return false;
}
}
continue;
}
DeltaTileTypeBelPin sink_type_bel_pin;
sink_type_bel_pin.delta_x = sink_loc.x - driver_loc.x;
sink_type_bel_pin.delta_y = sink_loc.y - driver_loc.y;
sink_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
sink_type_bel_pin.type_bel_pin.bel_index = sink_bel.index;
for (IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) {
sink_type_bel_pin.type_bel_pin.bel_pin = sink_bel_pin;
// Do fast routing check to see if the pair of driver and sink
// every are valid.
if (iter->second.count(sink_type_bel_pin) == 0) {
if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", ctx->nameOfBel(driver_bel),
driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel), sink_bel_pin.c_str(ctx));
}
return false;
}
// Do detailed routing check to ensure driver can reach sink.
//
// FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare.
if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin, /*site_only=*/false)) {
if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n",
ctx->nameOfBel(driver_bel), driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel),
sink_bel_pin.c_str(ctx));
}
return false;
}
}
}
}
return true;
}
bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name,
NetInfo *net) const
{
const auto &bel_data = bel_info(ctx->chip_info, bel);
Loc bel_loc = ctx->getBelLocation(bel);
BelId driver_bel = net->driver.cell->bel;
for (IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) {
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type;
type_bel_pin.bel_index = bel.index;
type_bel_pin.bel_pin = bel_pin;
auto iter = sinks.find(type_bel_pin);
if (iter == sinks.end()) {
// This BEL pin doesn't have a dedicate interconnect.
continue;
}
if (driver_bel == BelId()) {
// FIXME: This should actually return "unknown!" because the
// driver is unplaced. Once the driver is placed, this constraint
// can be evaluated.
if (ctx->debug) {
log_info("BEL %s is not valid because driver cell %s/%s is not placed\n", ctx->nameOfBel(bel),
net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
}
return false;
}
const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
if (bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) {
// This is a site local routing, even though this is a sink
// with a dedicated interconnect.
continue;
}
Loc driver_loc = ctx->getBelLocation(driver_bel);
DeltaTileTypeBelPin driver_type_bel_pin;
driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x;
driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y;
driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type;
driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index;
driver_type_bel_pin.type_bel_pin.bel_pin =
get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port));
// Do fast routing check to see if the pair of driver and sink
// every are valid.
if (iter->second.count(driver_type_bel_pin) == 0) {
if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", ctx->nameOfBel(bel),
bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
}
return false;
}
// Do detailed routing check to ensure driver can reach sink.
//
// FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare.
if (!check_routing(driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, bel, bel_pin, /*site_only=*/false)) {
if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n",
ctx->nameOfBel(bel), bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
}
return false;
}
}
return true;
}
bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) const
{
NPNR_ASSERT(bel != BelId());
for (const auto &port_pair : cell->ports) {
IdString port_name = port_pair.first;
NetInfo *net = port_pair.second.net;
if (net == nullptr) {
continue;
}
// This net doesn't have a driver, probably not valid?
NPNR_ASSERT(net->driver.cell != nullptr);
// Only check sink BELs.
if (net->driver.cell == cell && net->driver.port == port_name) {
if (!is_driver_on_net_valid(bel, cell, port_name, net)) {
return false;
}
} else {
if (!is_sink_on_net_valid(bel, cell, port_name, net)) {
return false;
}
}
}
return true;
}
void DedicatedInterconnect::explain_bel_status(BelId bel, const CellInfo *cell) const
{
NPNR_ASSERT(bel != BelId());
for (const auto &port_pair : cell->ports) {
IdString port_name = port_pair.first;
NetInfo *net = port_pair.second.net;
if (net == nullptr) {
continue;
}
// This net doesn't have a driver, probably not valid?
NPNR_ASSERT(net->driver.cell != nullptr);
// Only check sink BELs.
if (net->driver.cell == cell && net->driver.port == port_name) {
if (!is_driver_on_net_valid(bel, cell, port_name, net)) {
log_info("Driver %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx),
net->name.c_str(ctx));
}
} else {
if (!is_sink_on_net_valid(bel, cell, port_name, net)) {
log_info("Sink %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx),
net->name.c_str(ctx));
}
}
}
}
void DedicatedInterconnect::print_dedicated_interconnect() const
{
log_info("Found %zu sinks with dedicated interconnect\n", sinks.size());
log_info("Found %zu sources with dedicated interconnect\n", sources.size());
std::vector<TileTypeBelPin> sorted_keys;
for (const auto &sink_to_srcs : sinks) {
sorted_keys.push_back(sink_to_srcs.first);
}
for (const auto &src_to_sinks : sources) {
sorted_keys.push_back(src_to_sinks.first);
}
std::sort(sorted_keys.begin(), sorted_keys.end());
for (const auto &key : sorted_keys) {
auto iter = sinks.find(key);
if (iter != sinks.end()) {
auto dst = key;
for (const auto &src_delta : iter->second) {
auto src = src_delta.type_bel_pin;
auto delta_x = src_delta.delta_x;
auto delta_y = src_delta.delta_y;
const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type];
const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index];
IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]);
IdString src_bel_pin = src.bel_pin;
const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin;
log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", IdString(src_tile_type.name).c_str(ctx),
src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx),
src_bel_pin.c_str(ctx), delta_x, delta_y, IdString(dst_tile_type.name).c_str(ctx),
dst_site_type.c_str(ctx), dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx),
dst_bel_pin.c_str(ctx));
}
} else {
auto src = key;
for (const auto &dst_delta : sources.at(key)) {
auto dst = dst_delta.type_bel_pin;
auto delta_x = dst_delta.delta_x;
auto delta_y = dst_delta.delta_y;
const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type];
const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index];
IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]);
IdString src_bel_pin = src.bel_pin;
const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin;
log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n", IdString(src_tile_type.name).c_str(ctx),
src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx),
src_bel_pin.c_str(ctx), IdString(dst_tile_type.name).c_str(ctx), dst_site_type.c_str(ctx),
dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx), dst_bel_pin.c_str(ctx), delta_x,
delta_y);
}
}
}
}
void DedicatedInterconnect::find_dedicated_interconnect()
{
for (BelId bel : ctx->getBels()) {
const auto &bel_data = bel_info(ctx->chip_info, bel);
if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
if (bel_data.synthetic) {
continue;
}
for (int i = 0; i < bel_data.num_bel_wires; ++i) {
if (bel_data.types[i] != PORT_IN) {
continue;
}
WireId wire;
wire.tile = bel.tile;
wire.index = bel_data.wires[i];
expand_sink_bel(bel, IdString(bel_data.ports[i]), wire);
}
}
std::unordered_set<TileTypeBelPin> seen_pins;
for (auto sink_pair : sinks) {
for (auto src : sink_pair.second) {
seen_pins.emplace(src.type_bel_pin);
}
}
for (BelId bel : ctx->getBels()) {
const auto &bel_data = bel_info(ctx->chip_info, bel);
if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
if (bel_data.synthetic) {
continue;
}
for (int i = 0; i < bel_data.num_bel_wires; ++i) {
if (bel_data.types[i] != PORT_OUT) {
continue;
}
IdString bel_pin(bel_data.ports[i]);
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type;
type_bel_pin.bel_index = bel.index;
type_bel_pin.bel_pin = bel_pin;
// Don't visit src pins already handled in the sink expansion!
if (seen_pins.count(type_bel_pin)) {
continue;
}
WireId wire;
wire.tile = bel.tile;
wire.index = bel_data.wires[i];
expand_source_bel(bel, bel_pin, wire);
}
}
}
void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, WireId sink_wire)
{
NPNR_ASSERT(sink_bel != BelId());
#ifdef DEBUG_EXPANSION
log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx));
#endif
std::vector<WireNode> nodes_to_expand;
const auto &sink_wire_data = ctx->wire_info(sink_wire);
NPNR_ASSERT(sink_wire_data.site != -1);
WireNode wire_node;
wire_node.wire = sink_wire;
wire_node.state = IN_SINK_SITE;
wire_node.depth = 0;
nodes_to_expand.push_back(wire_node);
Loc sink_loc = ctx->getBelLocation(sink_bel);
std::unordered_set<DeltaTileTypeBelPin> srcs;
while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
for (PipId pip : ctx->getPipsUphill(node_to_expand.wire)) {
if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipSrcWire(pip);
if (wire == WireId()) {
continue;
}
#ifdef DEBUG_EXPANSION
log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
// FIXME: Consider removing kMaxDepth and use kMaxSources?
#ifdef DEBUG_EXPANSION
log_info(" - Exceeded max depth!\n");
#endif
return;
}
auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
if (ctx->is_site_port(pip)) {
switch (node_to_expand.state) {
case IN_SINK_SITE:
NPNR_ASSERT(wire_data.site == -1);
next_node.state = IN_ROUTING;
break;
case IN_ROUTING:
NPNR_ASSERT(wire_data.site != -1);
if (wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) {
// Dedicated routing won't have straight loops,
// general routing looks like that.
#ifdef DEBUG_EXPANSION
log_info(" - Not dedicated site routing because loop!");
#endif
return;
}
next_node.state = IN_SOURCE_SITE;
break;
case IN_SOURCE_SITE:
// Once entering a site, do not leave it again.
// This path is not a legal route!
expand_node = false;
break;
default:
// Unreachable!!!
NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
if (next_node.state == IN_SOURCE_SITE) {
for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
BelId src_bel = bel_pin.bel;
auto const &bel_data = bel_info(ctx->chip_info, src_bel);
if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
if (bel_data.synthetic) {
continue;
}
if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_OUT) {
continue;
}
#ifdef DEBUG_EXPANSION
log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx));
#endif
Loc src_loc = ctx->getBelLocation(src_bel);
DeltaTileTypeBelPin delta_type_bel_pin;
delta_type_bel_pin.delta_x = src_loc.x - sink_loc.x;
delta_type_bel_pin.delta_y = src_loc.y - sink_loc.y;
delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;
delta_type_bel_pin.type_bel_pin.bel_index = src_bel.index;
delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin;
srcs.emplace(delta_type_bel_pin);
}
}
}
}
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
type_bel_pin.bel_index = sink_bel.index;
type_bel_pin.bel_pin = sink_pin;
auto result = sinks.emplace(type_bel_pin, srcs);
if (!result.second) {
// type_bel_pin was already present! Add any new sources from this
// sink type (if any);
for (auto src : srcs) {
result.first->second.emplace(src);
}
}
}
void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, WireId src_wire)
{
NPNR_ASSERT(src_bel != BelId());
#ifdef DEBUG_EXPANSION
log_info("Expanding from %s/%s\n", ctx->nameOfBel(src_bel), src_pin.c_str(ctx));
#endif
std::vector<WireNode> nodes_to_expand;
const auto &src_wire_data = ctx->wire_info(src_wire);
NPNR_ASSERT(src_wire_data.site != -1);
WireNode wire_node;
wire_node.wire = src_wire;
wire_node.state = IN_SOURCE_SITE;
wire_node.depth = 0;
nodes_to_expand.push_back(wire_node);
Loc src_loc = ctx->getBelLocation(src_bel);
std::unordered_set<DeltaTileTypeBelPin> dsts;
while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipDstWire(pip);
if (wire == WireId()) {
continue;
}
#ifdef DEBUG_EXPANSION
log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
// FIXME: Consider removing kMaxDepth and use kMaxSources?
#ifdef DEBUG_EXPANSION
log_info(" - Exceeded max depth!\n");
#endif
return;
}
auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
if (ctx->is_site_port(pip)) {
switch (node_to_expand.state) {
case IN_SOURCE_SITE:
NPNR_ASSERT(wire_data.site == -1);
next_node.state = IN_ROUTING;
break;
case IN_ROUTING:
NPNR_ASSERT(wire_data.site != -1);
if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
// Dedicated routing won't have straight loops,
// general routing looks like that.
#ifdef DEBUG_EXPANSION
log_info(" - Not dedicated site routing because loop!");
#endif
return;
}
next_node.state = IN_SINK_SITE;
break;
case IN_SINK_SITE:
// Once entering a site, do not leave it again.
// This path is not a legal route!
expand_node = false;
break;
default:
// Unreachable!!!
NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
if (next_node.state == IN_SINK_SITE) {
for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
BelId sink_bel = bel_pin.bel;
auto const &bel_data = bel_info(ctx->chip_info, sink_bel);
if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
if (bel_data.synthetic) {
continue;
}
if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) {
continue;
}
#ifdef DEBUG_EXPANSION
log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx));
#endif
Loc sink_loc = ctx->getBelLocation(sink_bel);
DeltaTileTypeBelPin delta_type_bel_pin;
delta_type_bel_pin.delta_x = sink_loc.x - src_loc.x;
delta_type_bel_pin.delta_y = sink_loc.y - src_loc.y;
delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
delta_type_bel_pin.type_bel_pin.bel_index = sink_bel.index;
delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin;
dsts.emplace(delta_type_bel_pin);
}
}
}
}
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;
type_bel_pin.bel_index = src_bel.index;
type_bel_pin.bel_pin = src_pin;
auto result = sources.emplace(type_bel_pin, dsts);
if (!result.second) {
// type_bel_pin was already present! Add any new sources from this
// sink type (if any);
for (auto dst : dsts) {
result.first->second.emplace(dst);
}
}
}
NEXTPNR_NAMESPACE_END