abc/src/base/seq/seqMaxMeanCycle.c

568 lines
18 KiB
C

/**CFile****************************************************************
FileName [seqMaxMeanCycle.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [Construction and manipulation of sequential AIGs.]
Synopsis [Efficient computation of maximum mean cycle times.]
Author [Aaron P. Hurst]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - May 15, 2006.]
Revision [$Id: seqMaxMeanCycle.c,v 1.00 2005/05/15 00:00:00 ahurst Exp $]
***********************************************************************/
#include "seqInt.h"
#include "hash.h"
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
struct Abc_ManTime_t_
{
Abc_Time_t tArrDef;
Abc_Time_t tReqDef;
Vec_Ptr_t * vArrs;
Vec_Ptr_t * vReqs;
};
typedef struct Seq_HowardData_t_
{
char visited;
int mark;
int policy;
float cycle;
float skew;
float delay;
} Seq_HowardData_t;
// accessing the arrival and required times of a node
static inline Abc_Time_t * Abc_NodeArrival( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vArrs->pArray[pNode->Id]; }
static inline Abc_Time_t * Abc_NodeRequired( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vReqs->pArray[pNode->Id]; }
Hash_Ptr_t * Seq_NtkPathDelays( Abc_Ntk_t * pNtk, int fVerbose );
void Seq_NtkMergePios( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays, int fVerbose );
void Seq_NtkHowardLoop( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays,
Hash_Ptr_t * hNodeData, int node,
int *howardDepth, float *howardDelay, int *howardSink,
float *maxMeanCycle);
void Abc_NtkDfsReverse_rec2( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Vec_Ptr_t * vEndpoints );
#define Seq_NtkGetPathDelay( hFwdDelays, from, to ) \
(Hash_PtrExists(hFwdDelays, from)?Hash_FltEntry( ((Hash_Flt_t *)Hash_PtrEntry(hFwdDelays, from, 0)), to, 0):0 )
#define HOWARD_EPSILON 1e-3
#define ZERO_SLOP 1e-5
#define REMOVE_ZERO_SLOP( x ) \
(x = (x > -ZERO_SLOP && x < ZERO_SLOP)?0:x)
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
/**Function*************************************************************
Synopsis [Computes maximum mean cycle time.]
Description [Uses Howard's algorithm.]
SideEffects []
SeeAlso []
***********************************************************************/
float Seq_NtkHoward( Abc_Ntk_t * pNtk, int fVerbose ) {
Abc_Obj_t * pObj;
Hash_Ptr_t * hFwdDelays;
Hash_Flt_t * hOutgoing;
Hash_Ptr_Entry_t * pSourceEntry, * pNodeEntry;
Hash_Flt_Entry_t * pSinkEntry;
int i, j, iteration = 0;
int source, sink;
int fChanged;
int howardDepth, howardSink = 0;
float delay, howardDelay, t;
float maxMeanCycle = -ABC_INFINITY;
Hash_Ptr_t * hNodeData;
Seq_HowardData_t * pNodeData, * pSourceData, * pSinkData;
// gather timing constraints
hFwdDelays = Seq_NtkPathDelays( pNtk, fVerbose );
Seq_NtkMergePios( pNtk, hFwdDelays, fVerbose );
// initialize data, create initial policy
hNodeData = Hash_PtrAlloc( hFwdDelays->nSize );
Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) {
Hash_PtrWriteEntry( hNodeData, pSourceEntry->key,
(pNodeData = ALLOC(Seq_HowardData_t, 1)) );
pNodeData->skew = 0.0;
pNodeData->policy = 0;
hOutgoing = (Hash_Flt_t *)(pSourceEntry->data);
assert(hOutgoing);
Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) {
sink = pSinkEntry->key;
delay = pSinkEntry->data;
if (delay > pNodeData->skew) {
pNodeData->policy = sink;
pNodeData->skew = delay;
}
}
}
// iteratively refine policy
do {
iteration++;
fChanged = 0;
howardDelay = 0.0;
howardDepth = 0;
// reset data
Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) {
pNodeData = (Seq_HowardData_t *)pNodeEntry->data;
pNodeData->skew = -ABC_INFINITY;
pNodeData->cycle = -ABC_INFINITY;
pNodeData->mark = 0;
pNodeData->visited = 0;
}
// find loops in policy graph
Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) {
pNodeData = (Seq_HowardData_t *)(pNodeEntry->data);
assert(pNodeData);
if (!pNodeData->visited)
Seq_NtkHowardLoop( pNtk, hFwdDelays,
hNodeData, pNodeEntry->key,
&howardDepth, &howardDelay, &howardSink, &maxMeanCycle);
}
if (!howardSink) {
return -1;
}
// improve policy by tightening loops
Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) {
source = pSourceEntry->key;
pSourceData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, source, 0 );
assert(pSourceData);
hOutgoing = (Hash_Flt_t *)(pSourceEntry->data);
assert(hOutgoing);
Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) {
sink = pSinkEntry->key;
pSinkData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, sink, 0 );
assert(pSinkData);
delay = pSinkEntry->data;
if (pSinkData->cycle > pSourceData->cycle + HOWARD_EPSILON) {
fChanged = 1;
pSourceData->cycle = pSinkData->cycle;
pSourceData->policy = sink;
}
}
}
// improve policy by correcting skews
if (!fChanged) {
Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) {
source = pSourceEntry->key;
pSourceData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, source, 0 );
assert(pSourceData);
hOutgoing = (Hash_Flt_t *)(pSourceEntry->data);
assert(hOutgoing);
Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) {
sink = pSinkEntry->key;
pSinkData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, sink, 0 );
assert(pSinkData);
delay = pSinkEntry->data;
if (pSinkData->cycle < 0.0 || pSinkData->cycle < pSourceData->cycle)
continue;
t = delay - pSinkData->cycle + pSinkData->skew;
if (t > pSourceData->skew + HOWARD_EPSILON) {
fChanged = 1;
pSourceData->skew = t;
pSourceData->policy = sink;
}
}
}
}
if (fVerbose) printf("Iteration %d \t Period = %.2f\n", iteration, maxMeanCycle);
} while (fChanged);
// set global skew, mmct
pNodeData = Hash_PtrEntry( hNodeData, -1, 0 );
pNtk->globalSkew = -pNodeData->skew;
pNtk->maxMeanCycle = maxMeanCycle;
// set endpoint skews
Vec_FltGrow( pNtk->vSkews, Abc_NtkLatchNum( pNtk ) );
pNtk->vSkews->nSize = Abc_NtkLatchNum( pNtk );
Abc_NtkForEachLatch( pNtk, pObj, i ) {
pNodeData = Hash_PtrEntry( hNodeData, pObj->Id, 0 );
// skews are set based on latch # NOT id #
Abc_NtkSetLatSkew( pNtk, i, pNodeData->skew );
}
// free node data
Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) {
pNodeData = (Seq_HowardData_t *)(pNodeEntry->data);
FREE( pNodeData );
}
Hash_PtrFree(hNodeData);
// free delay data
Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) {
Hash_FltFree( (Hash_Flt_t *)(pSourceEntry->data) );
}
Hash_PtrFree(hFwdDelays);
return maxMeanCycle;
}
/**Function*************************************************************
Synopsis [Computes the mean cycle times of current policy graph.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Seq_NtkHowardLoop( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays,
Hash_Ptr_t * hNodeData, int node,
int *howardDepth, float *howardDelay, int *howardSink,
float *maxMeanCycle) {
Seq_HowardData_t * pNodeData, *pToData;
float delay, t;
pNodeData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, node, 0 );
assert(pNodeData);
pNodeData->visited = 1;
pNodeData->mark = ++(*howardDepth);
pNodeData->delay = (*howardDelay);
if (pNodeData->policy) {
pToData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, pNodeData->policy, 0 );
assert(pToData);
delay = Seq_NtkGetPathDelay( hFwdDelays, node, pNodeData->policy );
assert(delay > 0.0);
(*howardDelay) += delay;
if (pToData->mark) {
t = (*howardDelay - pToData->delay) / (*howardDepth - pToData->mark + 1);
pNodeData->cycle = t;
pNodeData->skew = 0.0;
if (*maxMeanCycle < t) {
*maxMeanCycle = t;
*howardSink = pNodeData->policy;
}
} else {
if(!pToData->visited) {
Seq_NtkHowardLoop(pNtk, hFwdDelays, hNodeData, pNodeData->policy,
howardDepth, howardDelay, howardSink, maxMeanCycle);
}
if(pToData->cycle > 0) {
t = delay - pToData->cycle + pToData->skew;
pNodeData->skew = t;
pNodeData->cycle = pToData->cycle;
}
}
}
*howardDelay = pNodeData->delay;
pNodeData->mark = 0;
--(*howardDepth);
}
/**Function*************************************************************
Synopsis [Computes the register-to-register delays.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
Hash_Ptr_t * Seq_NtkPathDelays( Abc_Ntk_t * pNtk, int fVerbose ) {
Abc_Time_t * pTime, ** ppTimes;
Abc_Obj_t * pObj, * pDriver, * pStart, * pFanout;
Vec_Ptr_t * vNodes, * vEndpoints;
int i, j, nPaths = 0;
Hash_Flt_t * hOutgoing;
Hash_Ptr_t * hFwdDelays;
float nMaxPath = 0, nSumPath = 0;
extern void Abc_NtkTimePrepare( Abc_Ntk_t * pNtk );
extern void Abc_NodeDelayTraceArrival( Abc_Obj_t * pNode );
if (fVerbose) printf("Gathering path delays...\n");
hFwdDelays = Hash_PtrAlloc( Abc_NtkCiNum( pNtk ) );
assert( Abc_NtkIsMappedLogic(pNtk) );
Abc_NtkTimePrepare( pNtk );
ppTimes = (Abc_Time_t **)pNtk->pManTime->vArrs->pArray;
vNodes = Vec_PtrAlloc( 100 );
vEndpoints = Vec_PtrAlloc( 100 );
// set the initial times (i.e. ignore all inputs)
Abc_NtkForEachObj( pNtk, pObj, i) {
pTime = ppTimes[pObj->Id];
pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY;
}
// starting at each Ci, compute timing forward
Abc_NtkForEachCi( pNtk, pStart, j ) {
hOutgoing = Hash_FltAlloc( 10 );
Hash_PtrWriteEntry( hFwdDelays, pStart->Id, (void *)(hOutgoing) );
// seed the starting point of interest
pTime = ppTimes[pStart->Id];
pTime->Fall = pTime->Rise = pTime->Worst = 0.0;
// find a DFS ordering from the start
Abc_NtkIncrementTravId( pNtk );
Abc_NodeSetTravIdCurrent( pStart );
pObj = Abc_ObjFanout0Ntk(pStart);
Abc_ObjForEachFanout( pObj, pFanout, i )
Abc_NtkDfsReverse_rec2( pFanout, vNodes, vEndpoints );
if ( Abc_ObjIsCo( pStart ) )
Vec_PtrPush( vEndpoints, pStart );
// do timing analysis
for ( i = vNodes->nSize-1; i >= 0; --i )
Abc_NodeDelayTraceArrival( vNodes->pArray[i] );
// there is a path to each set of Co endpoints
Vec_PtrForEachEntry( vEndpoints, pObj, i )
{
assert(pObj);
assert( Abc_ObjIsCo( pObj ) );
pDriver = Abc_ObjFanin0(pObj);
pTime = Abc_NodeArrival(pDriver);
if ( pTime->Worst > 0 ) {
Hash_FltWriteEntry( hOutgoing, pObj->Id, pTime->Worst );
nPaths++;
// if (fVerbose) printf("\tpath %d,%d delay = %f\n", pStart->Id, pObj->Id, pTime->Worst);
nSumPath += pTime->Worst;
if (pTime->Worst > nMaxPath)
nMaxPath = pTime->Worst;
}
}
// clear the times that were altered
for ( i = 0; i < vNodes->nSize; i++ ) {
pObj = (Abc_Obj_t *)(vNodes->pArray[i]);
pTime = ppTimes[pObj->Id];
pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY;
}
pTime = ppTimes[pStart->Id];
pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY;
Vec_PtrClear( vNodes );
Vec_PtrClear( vEndpoints );
}
Vec_PtrFree( vNodes );
// rezero Cis (note: these should be restored to values if they were nonzero)
Abc_NtkForEachCi( pNtk, pObj, i) {
pTime = ppTimes[pObj->Id];
pTime->Fall = pTime->Rise = pTime->Worst = 0.0;
}
if (fVerbose) printf("Num. paths = %d\tMax. Path Delay = %.2f\tAvg. Path Delay = %.2f\n", nPaths, nMaxPath, nSumPath / nPaths);
return hFwdDelays;
}
/**Function*************************************************************
Synopsis [Merges all the Pios together into one ID = -1.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Seq_NtkMergePios( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays,
int fVerbose ) {
Abc_Obj_t * pObj;
Hash_Flt_Entry_t * pSinkEntry;
Hash_Ptr_Entry_t * pSourceEntry;
Hash_Flt_t * hOutgoing, * hPioSource;
int i, j;
int source, sink, nMerges = 0;
float delay = 0, max_delay = 0;
Vec_Int_t * vFreeList;
vFreeList = Vec_IntAlloc( 10 );
// create a new "-1" source entry for the Pios
hPioSource = Hash_FltAlloc( 100 );
Hash_PtrWriteEntry( hFwdDelays, -1, (void *)(hPioSource) );
// merge all edges with a Pio as a source
Abc_NtkForEachPi( pNtk, pObj, i ) {
source = pObj->Id;
hOutgoing = (Hash_Flt_t *)Hash_PtrEntry( hFwdDelays, source, 0 );
if (!hOutgoing) continue;
Hash_PtrForEachEntry( hOutgoing, pSinkEntry, j ) {
nMerges++;
sink = pSinkEntry->key;
delay = pSinkEntry->data;
if (Hash_FltEntry( hPioSource, sink, 1 ) < delay) {
Hash_FltWriteEntry( hPioSource, sink, delay );
}
}
Hash_FltFree( hOutgoing );
Hash_PtrRemove( hFwdDelays, source );
}
// merge all edges with a Pio as a sink
Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) {
hOutgoing = (Hash_Flt_t *)(pSourceEntry->data);
Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) {
sink = pSinkEntry->key;
delay = pSinkEntry->data;
max_delay = -ABC_INFINITY;
if (Abc_ObjIsPo( Abc_NtkObj( pNtk, sink ) )) {
nMerges++;
if (delay > max_delay)
max_delay = delay;
Vec_IntPush( vFreeList, sink );
}
}
if (max_delay != -ABC_INFINITY)
Hash_FltWriteEntry( hOutgoing, -1, delay );
// do freeing
while( vFreeList->nSize > 0 ) {
Hash_FltRemove( hOutgoing, Vec_IntPop( vFreeList ) );
}
}
if (fVerbose) printf("Merged %d paths into one Pio node\n", nMerges);
}
/**Function*************************************************************
Synopsis [This is a modification of routine from abcDfs.c]
Description [Recursive DFS from a starting point. Keeps the endpoints.]
SideEffects []
SeeAlso []
***********************************************************************/
void Abc_NtkDfsReverse_rec2( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Vec_Ptr_t * vEndpoints )
{
Abc_Obj_t * pFanout;
int i;
assert( !Abc_ObjIsNet(pNode) );
// if this node is already visited, skip
if ( Abc_NodeIsTravIdCurrent( pNode ) )
return;
// mark the node as visited
Abc_NodeSetTravIdCurrent( pNode );
// terminate at the Co
if ( Abc_ObjIsCo(pNode) ) {
Vec_PtrPush( vEndpoints, pNode );
return;
}
assert( Abc_ObjIsNode( pNode ) );
// visit the transitive fanin of the node
pNode = Abc_ObjFanout0Ntk(pNode);
Abc_ObjForEachFanout( pNode, pFanout, i )
Abc_NtkDfsReverse_rec2( pFanout, vNodes, vEndpoints );
// add the node after the fanins have been added
Vec_PtrPush( vNodes, pNode );
}
/**Function*************************************************************
Synopsis [Converts all skews into forward skews 0<skew<T.]
Description [Can also minimize total skew by changing global skew.]
SideEffects []
SeeAlso []
***********************************************************************/
void Seq_NtkSkewForward( Abc_Ntk_t * pNtk, float period, int fMinimize ) {
Abc_Obj_t * pObj;
int i;
float skew;
float currentSum = 0, bestSum = ABC_INFINITY;
float currentOffset = 0, nextStep, bestOffset = 0;
assert( pNtk->vSkews->nSize >= Abc_NtkLatchNum( pNtk )-1 );
if (fMinimize) {
// search all offsets for the one that minimizes sum of skews
while(currentOffset < period) {
currentSum = 0;
nextStep = period;
Abc_NtkForEachLatch( pNtk, pObj, i ) {
skew = Abc_NtkGetLatSkew( pNtk, i ) + currentOffset;
skew = (float)(skew - period*floor(skew/period));
currentSum += skew;
if (skew > ZERO_SLOP && skew < nextStep) {
nextStep = skew;
}
}
if (currentSum < bestSum) {
bestSum = currentSum;
bestOffset = currentOffset;
}
currentOffset += nextStep;
}
printf("Offseting all skews by %.2f\n", bestOffset);
}
// convert global skew into forward skew
pNtk->globalSkew = pNtk->globalSkew - bestOffset;
pNtk->globalSkew = (float)(pNtk->globalSkew - period*floor(pNtk->globalSkew/period));
assert(pNtk->globalSkew>= 0 && pNtk->globalSkew < period);
// convert endpoint skews into forward skews
Abc_NtkForEachLatch( pNtk, pObj, i ) {
skew = Abc_NtkGetLatSkew( pNtk, i ) + bestOffset;
skew = (float)(skew - period*floor(skew/period));
REMOVE_ZERO_SLOP( skew );
assert(skew >=0 && skew < period);
Abc_NtkSetLatSkew( pNtk, i, skew );
}
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////