Skip to content

Commit

Permalink
Merge pull request The-OpenROAD-Project#4673 from QuantamHD/tt_rsz_swap
Browse files Browse the repository at this point in the history
rsz: Makes pin swap equivalence algorithm more robust
  • Loading branch information
maliberty authored Mar 14, 2024
2 parents 72fe7db + 7d2359a commit 41d6ebc
Show file tree
Hide file tree
Showing 9 changed files with 1,643 additions and 671 deletions.
247 changes: 167 additions & 80 deletions src/rsz/src/RepairSetup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,11 @@ bool RepairSetup::swapPins(PathRef *drvr_path,
LibertyPort *swap_port = input_port;
sta::LibertyPortSet ports;

//Skip output to output paths
if (input_port->direction()->isOutput()) {
return false;
}

// Results for > 2 input gates are unpredictable. Only swap pins for
// 2 input gates for now.
int input_port_count = 0;
Expand All @@ -513,7 +518,7 @@ bool RepairSetup::swapPins(PathRef *drvr_path,
}
}
if (input_port_count > 2) {
return false;
return false;
}

// Check if we have already dealt with this instance
Expand All @@ -526,13 +531,14 @@ bool RepairSetup::swapPins(PathRef *drvr_path,
}

// Find the equivalent pins for a cell (simple implementation for now)
// stash them
if (equiv_pin_map_.find(cell) == equiv_pin_map_.end()) {
equivCellPins(cell, ports);
equiv_pin_map_.insert(cell, ports);
// stash them. Ports are unique to a cell so we can just cache by port
// and that should apply to all instances of that cell with this input_port.
if (equiv_pin_map_.find(input_port) == equiv_pin_map_.end()) {
equivCellPins(cell, input_port, ports);
equiv_pin_map_.insert(input_port, ports);
}
ports = equiv_pin_map_[cell];
if (ports.size() > 1) {
ports = equiv_pin_map_[input_port];
if (!ports.empty()) {
resizer_->findSwapPinCandidate(input_port, drvr_port, load_cap,
dcalc_ap, &swap_port);
if (!sta::LibertyPort::equiv(swap_port, input_port)) {
Expand Down Expand Up @@ -887,76 +893,114 @@ RepairSetup::fanout(Vertex *vertex)
return fanout;
}

void
RepairSetup::getEquivPortList2(sta::FuncExpr *expr,
sta::LibertyPortSet &ports,
sta::LibertyPortSet &inv_ports,
sta::FuncExpr::Operator &status)
bool RepairSetup::simulateExpr(
sta::FuncExpr* expr,
sta::UnorderedMap<const LibertyPort*, std::vector<bool>>& port_stimulus,
size_t table_index)
{
using Operator = sta::FuncExpr::Operator ;
const Operator curr_op = expr->op();
using Operator = sta::FuncExpr::Operator;
const Operator curr_op = expr->op();

switch (curr_op) {
case Operator::op_not:
return !simulateExpr(expr->left(), port_stimulus, table_index);
case Operator::op_and:
return simulateExpr(expr->left(), port_stimulus, table_index)
&& simulateExpr(expr->right(), port_stimulus, table_index);
case Operator::op_or:
return simulateExpr(expr->left(), port_stimulus, table_index)
|| simulateExpr(expr->right(), port_stimulus, table_index);
case Operator::op_xor:
return simulateExpr(expr->left(), port_stimulus, table_index)
^ simulateExpr(expr->right(), port_stimulus, table_index);
case Operator::op_one:
return true;
case Operator::op_zero:
return false;
case Operator::op_port:
return port_stimulus[expr->port()][table_index];
}

if (curr_op == Operator::op_not) {
getEquivPortList2(expr->left(), inv_ports, ports, status);
}
else if (status == Operator::op_zero &&
(curr_op == Operator::op_and ||
curr_op == Operator::op_or ||
curr_op == Operator::op_xor)) {
// Start parsing the equivalent pins (if it is simple or/and/xor)
status = curr_op;
getEquivPortList2(expr->left(), ports, inv_ports, status);
if (status == Operator::op_port) {
return;
}
getEquivPortList2(expr->right(), ports, inv_ports, status);
if (status == Operator::op_port) {
return;
}
status = Operator::op_one;
}
else if (status == curr_op) {
// handle > 2 input scenarios (up to any arbitrary number)
getEquivPortList2(expr->left(), ports, inv_ports, status);
if (status == Operator::op_port) {
return;
}
getEquivPortList2(expr->right(), ports, inv_ports, status);
if (status == Operator::op_port) {
return;
}
}
else if (curr_op == Operator::op_port && expr->port() != nullptr) {
ports.insert(expr->port());
}
else {
status = Operator::op_port; // moved to some other operator.
ports.clear();
inv_ports.clear();
}
logger_->error(RSZ, 91, "unrecognized expr op from OpenSTA");
}

void
RepairSetup::getEquivPortList(sta::FuncExpr *expr, sta::LibertyPortSet &ports)
std::vector<bool> RepairSetup::simulateExpr(
sta::FuncExpr* expr,
sta::UnorderedMap<const LibertyPort*, std::vector<bool>>& port_stimulus)
{
size_t table_length = 0x1 << port_stimulus.size();
std::vector<bool> result;
result.resize(table_length);
for (size_t i = 0; i < table_length; i++) {
result[i] = simulateExpr(expr, port_stimulus, i);
}

return result;
}

bool RepairSetup::isPortEqiv(sta::FuncExpr* expr,
const LibertyCell* cell,
const LibertyPort* port_a,
const LibertyPort* port_b)
{
sta::FuncExpr::Operator status = sta::FuncExpr::op_zero;
ports.clear();
sta::LibertyPortSet inv_ports;
getEquivPortList2(expr, ports, inv_ports, status);
if (inv_ports.size() > ports.size()) {
ports = inv_ports;
if (port_a->libertyCell() != cell || port_b->libertyCell() != cell) {
return false;
}

sta::LibertyCellPortIterator port_iter(cell);
sta::UnorderedMap<const LibertyPort*, std::vector<bool>> port_stimulus;
size_t input_port_count = 0;
while (port_iter.hasNext()) {
LibertyPort* port = port_iter.next();
if (port->direction()->isInput()) {
++input_port_count;
port_stimulus[port] = {};
}
if (status == sta::FuncExpr::op_port || ports.size() == 1) {
ports.clear();
}

if (input_port_count > 16) {
// Not worth manually simulating all these values.
// Probably need to do SAT solving or something else instead.
return false;
}

// Generate stimulus for the ports
size_t var_index = 0;
for (auto& it : port_stimulus) {
size_t truth_table_length = 0x1 << input_port_count;
std::vector<bool>& variable_stimulus = it.second;
variable_stimulus.resize(truth_table_length, false);
for (int i = 0; i < truth_table_length; i++) {
variable_stimulus[i] = static_cast<bool>((i >> var_index) & 0x1);
}
var_index++;
}

std::vector<bool> result_no_swap = simulateExpr(expr, port_stimulus);

// Swap pins
std::swap(port_stimulus.at(port_a), port_stimulus.at(port_b));

std::vector<bool> result_with_swap = simulateExpr(expr, port_stimulus);

// Check if truth tables are equivalent post swap. If they are then pins
// are equivalent.
for (size_t i = 0; i < result_no_swap.size(); i++) {
if (result_no_swap[i] != result_with_swap[i]) {
return false;
}
}

return true;
}

// Lets just look at the first list for now.
// We may want to cache this information somwhere (by building it up for the whole
// library).
// Or just generate it when the cell is being created (depending on agreement).
void
RepairSetup::equivCellPins(const LibertyCell *cell, sta::LibertyPortSet &ports)
void RepairSetup::equivCellPins(const LibertyCell* cell,
LibertyPort* input_port,
sta::LibertyPortSet& ports)
{
if (cell->hasSequentials() || cell->isIsolationCell()) {
ports.clear();
Expand All @@ -966,25 +1010,61 @@ RepairSetup::equivCellPins(const LibertyCell *cell, sta::LibertyPortSet &ports)
int outputs = 0;
int inputs = 0;

// count number of output ports. Skip ports with > 1 output for now.
// count number of output ports.
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
if (port->direction()->isOutput()) {
++outputs;
++outputs;
} else {
++inputs;
}
}

if (outputs == 1 && inputs >= 2) {
sta::LibertyCellPortIterator port_iter2(cell);
while (port_iter2.hasNext()) {
LibertyPort *port = port_iter2.next();
sta::FuncExpr *expr = port->function();
if (expr != nullptr) {
getEquivPortList(expr, ports);
}
if (outputs >= 1 && inputs >= 2) {
sta::LibertyCellPortIterator port_iter2(cell);
while (port_iter2.hasNext()) {
LibertyPort* candidate_port = port_iter2.next();
if (!candidate_port->direction()->isInput()) {
continue;
}

sta::LibertyCellPortIterator output_port_iter(cell);
std::optional<bool> is_equivalent;
// Loop through all the output ports and make sure they are equivalent
// under swaps of candidate_port and input_port. For multi-ouput gates
// like full adders.
while (output_port_iter.hasNext()) {
LibertyPort* output_candidate_port = output_port_iter.next();
sta::FuncExpr* output_expr = output_candidate_port->function();
if (!output_candidate_port->direction()->isOutput()) {
continue;
}

if (output_expr == nullptr) {
continue;
}

if (input_port == candidate_port) {
continue;
}

bool is_equivalent_result
= isPortEqiv(output_expr, cell, input_port, candidate_port);

if (!is_equivalent.has_value()) {
is_equivalent = is_equivalent_result;
continue;
}

is_equivalent = is_equivalent.value() && is_equivalent_result;
}

// candidate_port is equivalent to input_port under all output ports
// of this cell.
if (is_equivalent.has_value() && is_equivalent.value()) {
ports.insert(candidate_port);
}
}
}
}

Expand All @@ -999,13 +1079,20 @@ RepairSetup::reportSwappablePins()
sta::LibertyCellIterator cell_iter(library);
while (cell_iter.hasNext()) {
sta::LibertyCell* cell = cell_iter.next();
sta::LibertyPortSet ports;
equivCellPins(cell, ports);
std::ostringstream ostr;
for (auto port : ports) {
ostr << ' ' << port->name();
sta::LibertyCellPortIterator port_iter(cell);
while (port_iter.hasNext()) {
LibertyPort* port = port_iter.next();
if (!port->direction()->isInput()) {
continue;
}
sta::LibertyPortSet ports;
equivCellPins(cell, port, ports);
std::ostringstream ostr;
for (auto port : ports) {
ostr << ' ' << port->name();
}
logger_->report("{}/{} ->{}", cell->name(), port->name(), ostr.str());
}
logger_->report("{} ->{}", cell->name(), ostr.str());
}
}
}
Expand Down
24 changes: 17 additions & 7 deletions src/rsz/src/RepairSetup.hh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
///////////////////////////////////////////////////////////////////////////////

#pragma once
#include <boost/functional/hash.hpp>
#include <unordered_set>

#include "db_sta/dbNetwork.hh"
#include "db_sta/dbSta.hh"
#include "sta/FuncExpr.hh"
Expand Down Expand Up @@ -104,12 +106,20 @@ private:
bool skip_gate_cloning);
void debugCheckMultipleBuffers(PathRef &path,
PathExpanded *expanded);

void getEquivPortList2(sta::FuncExpr *expr, sta::LibertyPortSet &ports,
sta::LibertyPortSet &inv_ports,
sta::FuncExpr::Operator &status);
void getEquivPortList(sta::FuncExpr *expr, sta::LibertyPortSet &ports);
void equivCellPins(const LibertyCell *cell, sta::LibertyPortSet &ports);
bool simulateExpr(
sta::FuncExpr* expr,
sta::UnorderedMap<const LibertyPort*, std::vector<bool>>& port_stimulus,
size_t table_index);
std::vector<bool> simulateExpr(
sta::FuncExpr* expr,
sta::UnorderedMap<const LibertyPort*, std::vector<bool>>& port_stimulus);
bool isPortEqiv(sta::FuncExpr* expr,
const LibertyCell* cell,
const LibertyPort* port_a,
const LibertyPort* port_b);
void equivCellPins(const LibertyCell* cell,
LibertyPort* input_port,
sta::LibertyPortSet& ports);
bool swapPins(PathRef *drvr_path, int drvr_index, PathExpanded *expanded);
bool upsizeDrvr(PathRef *drvr_path,
int drvr_index,
Expand Down Expand Up @@ -168,7 +178,7 @@ private:
const MinMax *min_;
const MinMax *max_;

sta::UnorderedMap<LibertyCell *, sta::LibertyPortSet> equiv_pin_map_;
sta::UnorderedMap<LibertyPort*, sta::LibertyPortSet> equiv_pin_map_;

static constexpr int decreasing_slack_max_passes_ = 50;
static constexpr int rebuffer_max_fanout_ = 20;
Expand Down
Loading

0 comments on commit 41d6ebc

Please sign in to comment.