2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
// Copyright (c) 2019, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// 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 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
#include "Machine.hh"
|
|
|
|
|
#include "FuncExpr.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Search.hh"
|
|
|
|
|
#include "GatedClk.hh"
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
GatedClk::GatedClk(const StaState *sta) :
|
|
|
|
|
StaState(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
GatedClk::isGatedClkEnable(Vertex *vertex) const
|
|
|
|
|
{
|
|
|
|
|
bool is_gated_clk_enable;
|
|
|
|
|
const Pin *clk_pin;
|
|
|
|
|
LogicValue logic_active_value;
|
|
|
|
|
isGatedClkEnable(vertex,
|
|
|
|
|
is_gated_clk_enable, clk_pin, logic_active_value);
|
|
|
|
|
return is_gated_clk_enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
GatedClk::isGatedClkEnable(Vertex *enable_vertex,
|
|
|
|
|
bool &is_gated_clk_enable,
|
|
|
|
|
const Pin *&clk_pin,
|
|
|
|
|
LogicValue &logic_active_value) const
|
|
|
|
|
{
|
|
|
|
|
is_gated_clk_enable = false;
|
|
|
|
|
const Pin *enable_pin = enable_vertex->pin();
|
|
|
|
|
const Instance *inst = network_->instance(enable_pin);
|
|
|
|
|
LibertyPort *enable_port = network_->libertyPort(enable_pin);
|
|
|
|
|
EvalPred *eval_pred = search_->evalPred();
|
|
|
|
|
if (enable_port
|
|
|
|
|
&& enable_port->direction()->isInput()
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(enable_pin)
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(inst)
|
|
|
|
|
&& eval_pred->searchFrom(enable_vertex)) {
|
|
|
|
|
FuncExpr *func = NULL;
|
|
|
|
|
Vertex *gclk_vertex = NULL;
|
|
|
|
|
VertexOutEdgeIterator edge_iter(enable_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
gclk_vertex = edge->to(graph_);
|
|
|
|
|
if (edge->role() == TimingRole::combinational()
|
|
|
|
|
&& eval_pred->searchTo(gclk_vertex)
|
|
|
|
|
&& eval_pred->searchThru(edge)) {
|
|
|
|
|
const Pin *gclk_pin = gclk_vertex->pin();
|
|
|
|
|
LibertyPort *gclk_port = network_->libertyPort(gclk_pin);
|
|
|
|
|
if (gclk_port) {
|
|
|
|
|
func = gclk_port->function();
|
|
|
|
|
if (func)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (func
|
|
|
|
|
&& search_->isClock(gclk_vertex)
|
|
|
|
|
&& !search_->isClock(enable_vertex)) {
|
|
|
|
|
FuncExprPortIterator clk_port_iter(func);
|
|
|
|
|
while (clk_port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *clk_port = clk_port_iter.next();
|
|
|
|
|
if (clk_port != enable_port) {
|
|
|
|
|
bool is_clk_gate = false;
|
|
|
|
|
isClkGatingFunc(func, enable_port, clk_port,
|
|
|
|
|
is_clk_gate, logic_active_value);
|
|
|
|
|
if (is_clk_gate) {
|
|
|
|
|
clk_pin = network_->findPin(inst, clk_port);
|
|
|
|
|
if (clk_pin
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(clk_pin)
|
|
|
|
|
&& search_->isClock(graph_->pinLoadVertex(clk_pin))) {
|
|
|
|
|
is_gated_clk_enable = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
GatedClk::gatedClkEnables(Vertex *clk_vertex,
|
|
|
|
|
// Return value.
|
|
|
|
|
PinSet &enable_pins)
|
|
|
|
|
{
|
|
|
|
|
const Pin *clk_pin = clk_vertex->pin();
|
|
|
|
|
const Instance *inst = network_->instance(clk_pin);
|
|
|
|
|
LibertyPort *clk_port = network_->libertyPort(clk_pin);
|
|
|
|
|
EvalPred *eval_pred = search_->evalPred();
|
|
|
|
|
if (clk_port
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(clk_pin)
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(inst)
|
|
|
|
|
&& eval_pred->searchFrom(clk_vertex)) {
|
|
|
|
|
FuncExpr *func = NULL;
|
|
|
|
|
Vertex *gclk_vertex = NULL;
|
|
|
|
|
VertexOutEdgeIterator edge_iter(clk_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
gclk_vertex = edge->to(graph_);
|
|
|
|
|
if (edge->role() == TimingRole::combinational()
|
|
|
|
|
&& eval_pred->searchTo(gclk_vertex)
|
|
|
|
|
&& eval_pred->searchThru(edge)) {
|
|
|
|
|
const Pin *gclk_pin = gclk_vertex->pin();
|
|
|
|
|
LibertyPort *gclk_port = network_->libertyPort(gclk_pin);
|
|
|
|
|
if (gclk_port) {
|
|
|
|
|
func = gclk_port->function();
|
|
|
|
|
if (func) {
|
|
|
|
|
if (search_->isClock(gclk_vertex)) {
|
|
|
|
|
FuncExprPortIterator enable_port_iter(func);
|
|
|
|
|
while (enable_port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *enable_port = enable_port_iter.next();
|
|
|
|
|
if (enable_port != clk_port) {
|
|
|
|
|
bool is_clk_gate;
|
|
|
|
|
LogicValue logic_value;
|
|
|
|
|
isClkGatingFunc(func, enable_port, clk_port,
|
|
|
|
|
is_clk_gate, logic_value);
|
|
|
|
|
if (is_clk_gate) {
|
|
|
|
|
Pin *enable_pin = network_->findPin(inst, enable_port);
|
|
|
|
|
if (enable_pin
|
|
|
|
|
&& !sdc_->isDisableClockGatingCheck(enable_pin)
|
|
|
|
|
&& !search_->isClock(graph_->pinLoadVertex(enable_pin))) {
|
|
|
|
|
enable_pins.insert(enable_pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
GatedClk::isClkGatingFunc(FuncExpr *func,
|
|
|
|
|
LibertyPort *enable_port,
|
|
|
|
|
LibertyPort *clk_port,
|
|
|
|
|
bool &is_clk_gate,
|
|
|
|
|
LogicValue &logic_value) const
|
|
|
|
|
{
|
|
|
|
|
// The function should be in two-level SOP or POS form depending on "cost".
|
|
|
|
|
// We need to apply literal cofactor if any input port is constant and
|
|
|
|
|
// do "simple" logic minimization based on SOP and POS.
|
|
|
|
|
while (func->op() == FuncExpr::op_not)
|
|
|
|
|
func = func->left();
|
|
|
|
|
if (func->op() == FuncExpr::op_and)
|
|
|
|
|
logic_value = logic_one;
|
|
|
|
|
else if (func->op() == FuncExpr::op_or)
|
|
|
|
|
logic_value = logic_zero;
|
|
|
|
|
else {
|
|
|
|
|
is_clk_gate = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FuncExprSet funcs;
|
|
|
|
|
functionClkOperands(func, func->left(), funcs);
|
|
|
|
|
functionClkOperands(func, func->right(), funcs);
|
|
|
|
|
|
|
|
|
|
bool need_gating_check = false;
|
|
|
|
|
FuncExprSet::Iterator expr_iter(funcs);
|
|
|
|
|
while (expr_iter.hasNext()) {
|
|
|
|
|
FuncExpr *expr = expr_iter.next();
|
|
|
|
|
if (expr->op() == FuncExpr::op_not) {
|
|
|
|
|
if (expr->left()->op() == FuncExpr::op_port
|
|
|
|
|
&& expr->left()->port() == clk_port) {
|
|
|
|
|
need_gating_check = true;
|
|
|
|
|
logic_value = (logic_value == logic_one) ? logic_zero : logic_one;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (expr->op() == FuncExpr::op_port
|
|
|
|
|
&& expr->port() == clk_port) {
|
|
|
|
|
need_gating_check = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (need_gating_check) {
|
|
|
|
|
FuncExprSet::Iterator expr_iter2(funcs);
|
|
|
|
|
while (expr_iter2.hasNext()) {
|
|
|
|
|
FuncExpr *expr = expr_iter2.next();
|
|
|
|
|
FuncExprPortIterator en_port_iter(expr);
|
|
|
|
|
while (en_port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *port = en_port_iter.next();
|
|
|
|
|
if (port == enable_port) {
|
|
|
|
|
is_clk_gate = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
is_clk_gate = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
GatedClk::functionClkOperands(FuncExpr *root_expr,
|
|
|
|
|
FuncExpr *expr,
|
|
|
|
|
FuncExprSet &funcs) const
|
|
|
|
|
{
|
|
|
|
|
if (expr->op() != root_expr->op())
|
|
|
|
|
funcs.insert(expr);
|
|
|
|
|
else {
|
|
|
|
|
functionClkOperands(root_expr, expr->left(), funcs);
|
|
|
|
|
functionClkOperands(root_expr, expr->right(), funcs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransRiseFall *
|
|
|
|
|
GatedClk::gatedClkActiveTrans(LogicValue active_value,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
TransRiseFall *leading_tr;
|
|
|
|
|
switch (active_value) {
|
|
|
|
|
case logic_one:
|
|
|
|
|
case logic_unknown:
|
|
|
|
|
leading_tr = TransRiseFall::rise();
|
|
|
|
|
break;
|
|
|
|
|
case logic_zero:
|
|
|
|
|
leading_tr = TransRiseFall::fall();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
internalError("illegal gated clock active value");
|
|
|
|
|
leading_tr = TransRiseFall::rise();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (min_max == MinMax::max())
|
|
|
|
|
return leading_tr;
|
|
|
|
|
else
|
|
|
|
|
return leading_tr->opposite();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|