Merge pull request #985 from antmicro/interchange-lut-constants
[interchange] Tying unused LUT inputs according to architecture
This commit is contained in:
commit
4ecbf6c6e9
@ -301,6 +301,12 @@ void Arch::init()
|
||||
for (size_t tile_type = 0; tile_type < chip_info->tile_types.size(); ++tile_type) {
|
||||
pseudo_pip_data.init_tile_type(getCtx(), tile_type);
|
||||
}
|
||||
|
||||
// Warn if there is no preferred constant net defined in the architecture
|
||||
IdString const_net_name(getCtx()->chip_info->constants->best_constant_net);
|
||||
if (const_net_name == IdString()) {
|
||||
log_warning("The architecture does not specify preferred constant net. Using VCC as default.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@ -874,17 +880,31 @@ static void prepare_sites_for_routing(Context *ctx)
|
||||
|
||||
// Fixup LUT vcc pins.
|
||||
IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name);
|
||||
IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name);
|
||||
|
||||
IdString const_net_name(ctx->chip_info->constants->best_constant_net);
|
||||
NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name);
|
||||
|
||||
// FIXME: Use VCC if the architecture does not device the best constant
|
||||
if (const_net_name == IdString()) {
|
||||
const_net_name = vcc_net_name;
|
||||
}
|
||||
|
||||
for (BelId bel : ctx->getBels()) {
|
||||
CellInfo *cell = ctx->getBoundBelCell(bel);
|
||||
if (cell == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->lut_cell.vcc_pins.empty()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto &it : cell->lut_cell.pin_connections) {
|
||||
const auto &bel_pin = it.first;
|
||||
const auto &conn = it.second;
|
||||
|
||||
// Connected to an active signal or unconnected
|
||||
if (conn == LutCell::PinConnection::Signal || conn == LutCell::PinConnection::Unconnected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto bel_pin : cell->lut_cell.vcc_pins) {
|
||||
// We can't rely on bel pins not clashing with cell names (for Xilinx they use different naming schemes, for
|
||||
// Nexus they are the same) so add a prefix to the bel pin name to disambiguate it
|
||||
IdString cell_pin = ctx->id(stringf("%s_PHYS", ctx->nameOf(bel_pin)));
|
||||
@ -896,17 +916,37 @@ static void prepare_sites_for_routing(Context *ctx)
|
||||
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (ctx->verbose) {
|
||||
log_info("%s must be tied to VCC, tying now\n", ctx->nameOfWire(lut_pin_wire));
|
||||
log_info("%s must be tied to %s, tying now\n", ctx->nameOfWire(lut_pin_wire),
|
||||
LutCell::nameOfPinConnection(conn).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
IdString tie_net_name;
|
||||
switch (conn) {
|
||||
case LutCell::PinConnection::Vcc:
|
||||
tie_net_name = vcc_net_name;
|
||||
break;
|
||||
case LutCell::PinConnection::Gnd:
|
||||
tie_net_name = gnd_net_name;
|
||||
break;
|
||||
case LutCell::PinConnection::Const:
|
||||
tie_net_name = const_net_name;
|
||||
break;
|
||||
default:
|
||||
// Should never happen
|
||||
NPNR_ASSERT_FALSE(
|
||||
stringf("Invalid LUT cell pin connection '%s'", LutCell::nameOfPinConnection(conn).c_str())
|
||||
.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = cell->ports.emplace(cell_pin, port_info);
|
||||
if (result.second) {
|
||||
cell->cell_bel_pins[cell_pin].push_back(bel_pin);
|
||||
ctx->connectPort(vcc_net_name, cell->name, cell_pin);
|
||||
ctx->connectPort(tie_net_name, cell->name, cell_pin);
|
||||
cell->const_ports.emplace(cell_pin);
|
||||
} else {
|
||||
NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(vcc_net_name));
|
||||
NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(tie_net_name));
|
||||
auto result2 = cell->cell_bel_pins.emplace(cell_pin, std::vector<IdString>({bel_pin}));
|
||||
NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
|
||||
NPNR_ASSERT(result2.first->second.size() == 1);
|
||||
|
@ -47,14 +47,6 @@ bool rotate_and_merge_lut_equation(std::vector<LogicLevel> *result, const LutBel
|
||||
// This address line is 0, so don't translate this bit to the cell
|
||||
// address.
|
||||
if ((bel_address & (1 << bel_pin_idx)) == 0) {
|
||||
// This pin is unused, so the line will be tied high, this
|
||||
// address is unreachable.
|
||||
//
|
||||
// FIXME: The assumption is that unused pins are tied VCC.
|
||||
// This is not generally true.
|
||||
//
|
||||
// Use Arch::prefered_constant_net_type to determine what
|
||||
// constant net should be used for unused pins.
|
||||
if ((used_pins & (1 << bel_pin_idx)) == 0) {
|
||||
address_reachable = false;
|
||||
break;
|
||||
@ -132,6 +124,26 @@ struct LutPin
|
||||
bool operator<(const LutPin &other) const { return max_pin < other.max_pin; }
|
||||
};
|
||||
|
||||
const std::string LutCell::nameOfPinConnection(LutCell::PinConnection conn)
|
||||
{
|
||||
switch (conn) {
|
||||
case PinConnection::Unconnected:
|
||||
return std::string("unconnected");
|
||||
case PinConnection::Gnd:
|
||||
return std::string("Gnd");
|
||||
case PinConnection::Vcc:
|
||||
return std::string("Vcc");
|
||||
case PinConnection::Const:
|
||||
return std::string("Const");
|
||||
case PinConnection::Signal:
|
||||
return std::string("Signal");
|
||||
default:
|
||||
// Should never happen
|
||||
NPNR_ASSERT_FALSE("Invalid value of LutCell::PinConnection");
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t LutMapper::check_wires(const Context *ctx) const
|
||||
{
|
||||
// Unlike the 3 argument version of check_wires, this version needs to
|
||||
@ -184,12 +196,7 @@ uint32_t LutMapper::check_wires(const std::vector<std::vector<int32_t>> &bel_to_
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: The assumption is that unused pins are tied VCC.
|
||||
// This is not generally true.
|
||||
//
|
||||
// Use Arch::prefered_constant_net_type to determine what
|
||||
// constant net should be used for unused pins.
|
||||
uint32_t vcc_mask = 0;
|
||||
uint32_t pin_mask = 0;
|
||||
|
||||
DynamicBitarray<> wire_equation;
|
||||
wire_equation.resize(2);
|
||||
@ -248,11 +255,11 @@ uint32_t LutMapper::check_wires(const std::vector<std::vector<int32_t>> &bel_to_
|
||||
|
||||
bool good_for_wire = valid_pin_for_wire && !invalid_pin_for_wire;
|
||||
if (!good_for_wire) {
|
||||
vcc_mask |= (1 << pin_idx);
|
||||
pin_mask |= (1 << pin_idx);
|
||||
}
|
||||
}
|
||||
|
||||
return vcc_mask;
|
||||
return pin_mask;
|
||||
}
|
||||
|
||||
bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping,
|
||||
@ -381,27 +388,19 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping
|
||||
}
|
||||
|
||||
// Not all LUT inputs are used
|
||||
uint32_t vcc_pins = 0;
|
||||
uint32_t pin_mask = 0;
|
||||
if (cells.size() != element.lut_bels.size()) {
|
||||
// Look to see if wires can be run from element inputs to unused
|
||||
// outputs. If not, block the BEL pin by tying to VCC.
|
||||
//
|
||||
// FIXME: The assumption is that unused pins are tied VCC.
|
||||
// This is not generally true.
|
||||
//
|
||||
// Use Arch::prefered_constant_net_type to determine what
|
||||
// constant net should be used for unused pins.
|
||||
vcc_pins = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, blocked_luts);
|
||||
#if defined(DEBUG_LUT_ROTATION)
|
||||
log_info("vcc_pins = 0x%x", vcc_pins);
|
||||
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
|
||||
CellInfo *cell = cells[cell_idx];
|
||||
log(", %s => %s", ctx->nameOfBel(cell->bel), cell->name.c_str(ctx));
|
||||
}
|
||||
log("\n");
|
||||
#endif
|
||||
pin_mask = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, blocked_luts);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_LUT_ROTATION)
|
||||
log_info("Cell bindings:\n");
|
||||
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
|
||||
CellInfo *cell = cells[cell_idx];
|
||||
log_info(" - %s => %s\n", ctx->nameOfBel(cell->bel), cell->name.c_str(ctx));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill in the LUT mapping result
|
||||
|
||||
// Push new cell -> BEL pin maps out to cells now that equations have been
|
||||
@ -422,28 +421,39 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping
|
||||
cell.belPins[cellPin] = belPin;
|
||||
}
|
||||
|
||||
cell.lutCell.vcc_pins.clear();
|
||||
cell.lutCell.pin_connections.clear();
|
||||
|
||||
// All LUT inputs used
|
||||
if (cells.size() == element.lut_bels.size()) {
|
||||
for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) {
|
||||
if ((used_pins & (1 << bel_pin_idx)) == 0) {
|
||||
NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1);
|
||||
cell.lutCell.vcc_pins.emplace(lutBel.pins.at(bel_pin_idx));
|
||||
cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Const);
|
||||
} else {
|
||||
cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only some LUT inputs used
|
||||
else {
|
||||
for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) {
|
||||
if ((vcc_pins & (1 << bel_pin_idx)) != 0) {
|
||||
if ((pin_mask & (1 << bel_pin_idx)) != 0) {
|
||||
NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1);
|
||||
auto pin = lutBel.pins.at(bel_pin_idx);
|
||||
cell.lutCell.vcc_pins.emplace(pin);
|
||||
cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Const);
|
||||
} else {
|
||||
cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG_LUT_ROTATION)
|
||||
log_info("Pin connections for LUT cell %s:\n", cellInfo->name.c_str(ctx));
|
||||
for (const auto &it : cell.lutCell.pin_connections) {
|
||||
log_info(" - %s : %s\n", it.first.c_str(ctx), LutCell::nameOfPinConnection(it.second).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
lut_mapping->cells.push_back(cell);
|
||||
}
|
||||
|
||||
@ -456,7 +466,7 @@ void check_equation(const LutCell &lut_cell, const dict<IdString, IdString> &cel
|
||||
std::vector<int8_t> pin_map;
|
||||
pin_map.resize(lut_bel.pins.size(), -1);
|
||||
|
||||
NPNR_ASSERT(lut_cell.pins.size() < std::numeric_limits<decltype(pin_map)::value_type>::max());
|
||||
NPNR_ASSERT(lut_cell.pins.size() < (size_t)std::numeric_limits<decltype(pin_map)::value_type>::max());
|
||||
|
||||
for (size_t cell_pin_idx = 0; cell_pin_idx < lut_cell.pins.size(); ++cell_pin_idx) {
|
||||
IdString cell_pin = lut_cell.pins[cell_pin_idx];
|
||||
|
@ -42,11 +42,22 @@ enum LogicLevel
|
||||
|
||||
struct LutCell
|
||||
{
|
||||
enum class PinConnection
|
||||
{
|
||||
Unconnected,
|
||||
Gnd,
|
||||
Vcc,
|
||||
Const,
|
||||
Signal
|
||||
};
|
||||
|
||||
// LUT cell pins for equation, LSB first.
|
||||
std::vector<IdString> pins;
|
||||
pool<IdString> lut_pins;
|
||||
pool<IdString> vcc_pins;
|
||||
dict<IdString, PinConnection> pin_connections;
|
||||
DynamicBitarray<> equation;
|
||||
|
||||
static const std::string nameOfPinConnection(PinConnection conn);
|
||||
};
|
||||
|
||||
struct LutBel
|
||||
|
@ -133,8 +133,20 @@ SiteArch::SiteArch(const SiteInformation *site_info)
|
||||
}
|
||||
|
||||
// Create list of out of site sources and sinks.
|
||||
|
||||
bool have_vcc_pins = false;
|
||||
bool have_gnd_pins = false;
|
||||
|
||||
IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name);
|
||||
IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name);
|
||||
|
||||
IdString const_net_name(ctx->chip_info->constants->best_constant_net);
|
||||
NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name);
|
||||
|
||||
// FIXME: Use VCC if the architecture does not device the best constant
|
||||
if (const_net_name == IdString()) {
|
||||
const_net_name = vcc_net_name;
|
||||
}
|
||||
|
||||
for (CellInfo *cell : site_info->cells_in_site) {
|
||||
for (const auto &pin_pair : cell->cell_bel_pins) {
|
||||
if (!cell->ports.count(pin_pair.first))
|
||||
@ -145,8 +157,18 @@ SiteArch::SiteArch(const SiteInformation *site_info)
|
||||
}
|
||||
}
|
||||
|
||||
if (!cell->lut_cell.vcc_pins.empty()) {
|
||||
have_vcc_pins = true;
|
||||
for (const auto &conn : cell->lut_cell.pin_connections) {
|
||||
if (conn.second == LutCell::PinConnection::Vcc) {
|
||||
have_vcc_pins = true;
|
||||
} else if (conn.second == LutCell::PinConnection::Gnd) {
|
||||
have_gnd_pins = true;
|
||||
} else if (conn.second == LutCell::PinConnection::Const) {
|
||||
if (const_net_name == vcc_net_name) {
|
||||
have_vcc_pins = true;
|
||||
} else if (const_net_name == gnd_net_name) {
|
||||
have_gnd_pins = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,10 +261,9 @@ SiteArch::SiteArch(const SiteInformation *site_info)
|
||||
}
|
||||
}
|
||||
|
||||
IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name);
|
||||
NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get();
|
||||
auto iter = nets.find(vcc_net);
|
||||
if (iter == nets.end() && have_vcc_pins) {
|
||||
auto vcc_iter = nets.find(vcc_net);
|
||||
if (vcc_iter == nets.end() && have_vcc_pins) {
|
||||
// VCC net isn't present, add it.
|
||||
SiteNetInfo net_info;
|
||||
net_info.net = vcc_net;
|
||||
@ -250,13 +271,53 @@ SiteArch::SiteArch(const SiteInformation *site_info)
|
||||
net_info.driver.net = vcc_net;
|
||||
auto result = nets.emplace(vcc_net, net_info);
|
||||
NPNR_ASSERT(result.second);
|
||||
iter = result.first;
|
||||
vcc_iter = result.first;
|
||||
}
|
||||
|
||||
NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get();
|
||||
auto gnd_iter = nets.find(gnd_net);
|
||||
if (gnd_iter == nets.end() && have_gnd_pins) {
|
||||
// GND net isn't present, add it.
|
||||
SiteNetInfo net_info;
|
||||
net_info.net = gnd_net;
|
||||
net_info.driver.type = SiteWire::OUT_OF_SITE_SOURCE;
|
||||
net_info.driver.net = gnd_net;
|
||||
auto result = nets.emplace(gnd_net, net_info);
|
||||
NPNR_ASSERT(result.second);
|
||||
gnd_iter = result.first;
|
||||
}
|
||||
|
||||
for (CellInfo *cell : site_info->cells_in_site) {
|
||||
for (IdString vcc_pin : cell->lut_cell.vcc_pins) {
|
||||
SiteWire wire = getBelPinWire(cell->bel, vcc_pin);
|
||||
iter->second.users.emplace(wire);
|
||||
for (const auto &it : cell->lut_cell.pin_connections) {
|
||||
const auto &pin = it.first;
|
||||
const auto &conn = it.second;
|
||||
|
||||
if (conn == LutCell::PinConnection::Unconnected || conn == LutCell::PinConnection::Signal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conn == LutCell::PinConnection::Vcc) {
|
||||
SiteWire wire = getBelPinWire(cell->bel, pin);
|
||||
vcc_iter->second.users.emplace(wire);
|
||||
} else if (conn == LutCell::PinConnection::Gnd) {
|
||||
SiteWire wire = getBelPinWire(cell->bel, pin);
|
||||
gnd_iter->second.users.emplace(wire);
|
||||
} else if (conn == LutCell::PinConnection::Const) {
|
||||
SiteWire wire = getBelPinWire(cell->bel, pin);
|
||||
if (const_net_name == vcc_net_name) {
|
||||
vcc_iter->second.users.emplace(wire);
|
||||
}
|
||||
if (const_net_name == gnd_net_name) {
|
||||
gnd_iter->second.users.emplace(wire);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (ctx->verbose) {
|
||||
log_info("Tying %s.%s to %s\n", cell->name.c_str(ctx), pin.c_str(ctx),
|
||||
LutCell::nameOfPinConnection(conn).c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,8 +138,8 @@ bool SiteLutMappingResult::apply(const SiteInformation &siteInfo)
|
||||
}
|
||||
|
||||
// LUT data
|
||||
// FIXME: Is there any other info that is being updated than vcc_pins ?
|
||||
cellInfo->lut_cell.vcc_pins = std::move(cell.lutCell.vcc_pins);
|
||||
// FIXME: Is there any other info that is being updated than pin_connections ?
|
||||
cellInfo->lut_cell.pin_connections = std::move(cell.lutCell.pin_connections);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
Loading…
Reference in New Issue
Block a user