Re-work LUT mapping logic to only put VCC pins when required.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
5dda3a14ff
commit
91ca5f110b
@ -794,6 +794,44 @@ static void prepare_sites_for_routing(Context *ctx)
|
||||
site_router.bindSiteRouting(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Fixup LUT vcc pins.
|
||||
IdString vcc_net_name(ctx->chip_info->constants->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 (auto bel_pin : cell->lut_cell.vcc_pins) {
|
||||
PortInfo port_info;
|
||||
port_info.name = bel_pin;
|
||||
port_info.type = PORT_IN;
|
||||
port_info.net = nullptr;
|
||||
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (ctx->verbose) {
|
||||
log_info("%s must be tied to VCC, tying now\n", ctx->nameOfWire(lut_pin_wire));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto result = cell->ports.emplace(bel_pin, port_info);
|
||||
if (result.second) {
|
||||
cell->cell_bel_pins[bel_pin].push_back(bel_pin);
|
||||
ctx->connectPort(vcc_net_name, cell->name, bel_pin);
|
||||
cell->const_ports.emplace(bel_pin);
|
||||
} else {
|
||||
NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(vcc_net_name));
|
||||
auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
|
||||
NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
|
||||
NPNR_ASSERT(result2.first->second.size() == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Arch::route()
|
||||
@ -818,10 +856,6 @@ bool Arch::route()
|
||||
log_error("FPGA interchange architecture does not support router '%s'\n", router.c_str());
|
||||
}
|
||||
|
||||
if (result) {
|
||||
result = route_vcc_to_unused_lut_pins();
|
||||
}
|
||||
|
||||
getCtx()->attrs[getCtx()->id("step")] = std::string("route");
|
||||
archInfoToAttributes();
|
||||
|
||||
@ -835,103 +869,6 @@ bool Arch::route()
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Arch::route_vcc_to_unused_lut_pins()
|
||||
{
|
||||
std::string router = str_or_default(settings, id("router"), defaultRouter);
|
||||
|
||||
HashTables::HashMap<WireId, const NetInfo *> bound_wires;
|
||||
for (auto &net_pair : nets) {
|
||||
const NetInfo *net = net_pair.second.get();
|
||||
for (auto &wire_pair : net->wires) {
|
||||
auto result = bound_wires.emplace(wire_pair.first, net);
|
||||
NPNR_ASSERT(result.first->second == net);
|
||||
|
||||
PipId pip = wire_pair.second.pip;
|
||||
if (pip == PipId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const PipInfoPOD &pip_data = pip_info(chip_info, pip);
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (getCtx()->verbose) {
|
||||
log_info("Pip %s in use, has %zu pseudo wires!\n", nameOfPip(pip), pip_data.pseudo_cell_wires.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
WireId wire;
|
||||
wire.tile = pip.tile;
|
||||
for (int32_t wire_index : pip_data.pseudo_cell_wires) {
|
||||
wire.index = wire_index;
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (getCtx()->verbose) {
|
||||
log_info("Marking wire %s as in use due to pseudo pip\n", nameOfWire(wire));
|
||||
}
|
||||
#endif
|
||||
auto result = bound_wires.emplace(wire, net);
|
||||
NPNR_ASSERT(result.first->second == net);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fixup LUT vcc pins.
|
||||
IdString vcc_net_name(chip_info->constants->vcc_net_name);
|
||||
for (BelId bel : getBels()) {
|
||||
CellInfo *cell = getBoundBelCell(bel);
|
||||
if (cell == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->lut_cell.vcc_pins.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto bel_pin : cell->lut_cell.vcc_pins) {
|
||||
PortInfo port_info;
|
||||
port_info.name = bel_pin;
|
||||
port_info.type = PORT_IN;
|
||||
port_info.net = nullptr;
|
||||
|
||||
WireId lut_pin_wire = getBelPinWire(bel, bel_pin);
|
||||
auto iter = bound_wires.find(lut_pin_wire);
|
||||
if (iter != bound_wires.end()) {
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (getCtx()->verbose) {
|
||||
log_info("%s is now used as a LUT route-through, not tying to VCC\n", nameOfWire(lut_pin_wire));
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LUT_MAPPING
|
||||
if (getCtx()->verbose) {
|
||||
log_info("%s is an unused LUT pin, tying to VCC\n", nameOfWire(lut_pin_wire));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto result = cell->ports.emplace(bel_pin, port_info);
|
||||
if (result.second) {
|
||||
cell->cell_bel_pins[bel_pin].push_back(bel_pin);
|
||||
connectPort(vcc_net_name, cell->name, bel_pin);
|
||||
cell->const_ports.emplace(bel_pin);
|
||||
} else {
|
||||
NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name));
|
||||
auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
|
||||
NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
|
||||
NPNR_ASSERT(result2.first->second.size() == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (router == "router1") {
|
||||
return router1(getCtx(), Router1Cfg(getCtx()));
|
||||
} else if (router == "router2") {
|
||||
router2(getCtx(), Router2Cfg(getCtx()));
|
||||
return true;
|
||||
} else {
|
||||
log_error("FPGA interchange architecture does not support router '%s'\n", router.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const { return {}; }
|
||||
|
@ -1064,7 +1064,6 @@ struct Arch : ArchAPI<ArchRanges>
|
||||
std::regex verilog_bin_constant;
|
||||
std::regex verilog_hex_constant;
|
||||
void read_lut_equation(DynamicBitarray<> *equation, const Property &equation_parameter) const;
|
||||
bool route_vcc_to_unused_lut_pins();
|
||||
|
||||
IdString id_GND;
|
||||
IdString id_VCC;
|
||||
|
@ -124,6 +124,81 @@ struct LutPin
|
||||
|
||||
//#define DEBUG_LUT_ROTATION
|
||||
|
||||
uint32_t LutMapper::check_wires(const std::vector<std::vector<int32_t>> &bel_to_cell_pin_remaps,
|
||||
const std::vector<const LutBel *> &lut_bels, uint32_t used_pins) const
|
||||
{
|
||||
std::vector<const LutBel *> unused_luts;
|
||||
for (auto &lut_bel_pair : element.lut_bels) {
|
||||
if (std::find(lut_bels.begin(), lut_bels.end(), &lut_bel_pair.second) == lut_bels.end()) {
|
||||
unused_luts.push_back(&lut_bel_pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t vcc_mask = 0;
|
||||
|
||||
DynamicBitarray<> wire_equation;
|
||||
wire_equation.resize(2);
|
||||
wire_equation.set(0, false);
|
||||
wire_equation.set(1, true);
|
||||
|
||||
std::vector<int32_t> wire_bel_to_cell_pin_map;
|
||||
std::vector<LogicLevel> equation_result;
|
||||
for (int32_t pin_idx = 0; pin_idx < (int32_t)element.pins.size(); ++pin_idx) {
|
||||
if (used_pins & (1 << pin_idx)) {
|
||||
// This pin is already used, so it cannot be used for a wire.
|
||||
continue;
|
||||
}
|
||||
|
||||
bool valid_pin_for_wire = false;
|
||||
bool invalid_pin_for_wire = false;
|
||||
|
||||
for (const LutBel *lut_bel : unused_luts) {
|
||||
if (pin_idx < lut_bel->min_pin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin_idx > lut_bel->max_pin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wire_bel_to_cell_pin_map.clear();
|
||||
wire_bel_to_cell_pin_map.resize(lut_bel->pins.size(), -1);
|
||||
wire_bel_to_cell_pin_map[lut_bel->pin_to_index.at(element.pins[pin_idx])] = 0;
|
||||
|
||||
equation_result.clear();
|
||||
equation_result.resize(element.width, LL_DontCare);
|
||||
|
||||
uint32_t used_pins_with_wire = used_pins | (1 << pin_idx);
|
||||
|
||||
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
|
||||
const CellInfo *cell = cells[cell_idx];
|
||||
auto &lut_bel_for_cell = *lut_bels[cell_idx];
|
||||
if (!rotate_and_merge_lut_equation(&equation_result, lut_bel_for_cell, cell->lut_cell.equation,
|
||||
bel_to_cell_pin_remaps[cell_idx], used_pins_with_wire)) {
|
||||
invalid_pin_for_wire = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid_pin_for_wire) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rotate_and_merge_lut_equation(&equation_result, *lut_bel, wire_equation, wire_bel_to_cell_pin_map,
|
||||
used_pins_with_wire)) {
|
||||
valid_pin_for_wire = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool good_for_wire = valid_pin_for_wire && !invalid_pin_for_wire;
|
||||
if (!good_for_wire) {
|
||||
vcc_mask |= (1 << pin_idx);
|
||||
}
|
||||
}
|
||||
|
||||
return vcc_mask;
|
||||
}
|
||||
|
||||
bool LutMapper::remap_luts(const Context *ctx)
|
||||
{
|
||||
std::unordered_map<NetInfo *, LutPin> lut_pin_map;
|
||||
@ -259,12 +334,43 @@ bool LutMapper::remap_luts(const Context *ctx)
|
||||
bel_pins.clear();
|
||||
bel_pins.push_back(lut_bel.pins[cell_to_bel_pin_remaps[cell_idx][pin_idx]]);
|
||||
}
|
||||
}
|
||||
|
||||
cell->lut_cell.vcc_pins.clear();
|
||||
for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.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->lut_cell.vcc_pins.emplace(lut_bel.pins.at(bel_pin_idx));
|
||||
if (cells.size() == element.lut_bels.size()) {
|
||||
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
|
||||
CellInfo *cell = cells[cell_idx];
|
||||
auto &lut_bel = *lut_bels[cell_idx];
|
||||
cell->lut_cell.vcc_pins.clear();
|
||||
for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.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->lut_cell.vcc_pins.emplace(lut_bel.pins.at(bel_pin_idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Look to see if wires can be run from element inputs to unused
|
||||
// outputs. If not, block the BEL pin by tying to VCC.
|
||||
uint32_t vcc_pins = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins);
|
||||
#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
|
||||
|
||||
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
|
||||
CellInfo *cell = cells[cell_idx];
|
||||
auto &lut_bel = *lut_bels[cell_idx];
|
||||
cell->lut_cell.vcc_pins.clear();
|
||||
for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) {
|
||||
if ((vcc_pins & (1 << bel_pin_idx)) != 0) {
|
||||
NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1);
|
||||
auto pin = lut_bel.pins.at(bel_pin_idx);
|
||||
cell->lut_cell.vcc_pins.emplace(pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,8 @@ struct LutMapper
|
||||
std::vector<CellInfo *> cells;
|
||||
|
||||
bool remap_luts(const Context *ctx);
|
||||
uint32_t check_wires(const std::vector<std::vector<int32_t>> &bel_to_cell_pin_remaps,
|
||||
const std::vector<const LutBel *> &lut_bels, uint32_t used_pins) const;
|
||||
};
|
||||
|
||||
// Rotate and merge a LUT equation into an array of levels.
|
||||
|
@ -125,6 +125,7 @@ SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site
|
||||
|
||||
// Create list of out of site sources and sinks.
|
||||
|
||||
bool have_vcc_pins = false;
|
||||
for (CellInfo *cell : site_info->cells_in_site) {
|
||||
for (const auto &pin_pair : cell->cell_bel_pins) {
|
||||
const PortInfo &port = cell->ports.at(pin_pair.first);
|
||||
@ -132,6 +133,10 @@ SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site
|
||||
nets.emplace(port.net, SiteNetInfo{port.net});
|
||||
}
|
||||
}
|
||||
|
||||
if (!cell->lut_cell.vcc_pins.empty()) {
|
||||
have_vcc_pins = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &net_pair : nets) {
|
||||
@ -222,6 +227,27 @@ SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
// VCC net isn't present, add it.
|
||||
SiteNetInfo net_info;
|
||||
net_info.net = vcc_net;
|
||||
net_info.driver.type = SiteWire::OUT_OF_SITE_SOURCE;
|
||||
net_info.driver.net = vcc_net;
|
||||
auto result = nets.emplace(vcc_net, net_info);
|
||||
NPNR_ASSERT(result.second);
|
||||
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 (auto &net_pair : nets) {
|
||||
SiteNetInfo *net_info = &net_pair.second;
|
||||
auto result = wire_to_nets.emplace(net_info->driver, SiteNetMap{net_info, 1});
|
||||
|
Loading…
Reference in New Issue
Block a user