/**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 0vSkews->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 /// ////////////////////////////////////////////////////////////////////////