void CoreRecorder::recordAccess(uint64_t startCycle) {
    assert(eventRecorder.hasRecord());
    TimingRecord tr = eventRecorder.popRecord();
    TimingEvent* origPrevResp = prevRespEvent;

    assert(startCycle >= prevRespCycle);
    assert(tr.reqCycle >= startCycle);

    if (IsGet(tr.type)) {
        uint64_t delay = tr.reqCycle - prevRespCycle;
        TimingEvent* ev = new (eventRecorder) TimingCoreEvent(delay, prevRespCycle - gapCycles, this);
        ev->setMinStartCycle(prevRespCycle);
        prevRespEvent->addChild(ev, eventRecorder)->addChild(tr.startEvent, eventRecorder);
        prevRespEvent = tr.endEvent;
        prevRespCycle = tr.respCycle;
        assert(prevRespEvent);
    } else {
        assert(IsPut(tr.type));
        // Link previous response and this req directly (don't even create a new event)
        DelayEvent* dr = new (eventRecorder) DelayEvent(tr.reqCycle - prevRespCycle);
        dr->setMinStartCycle(prevRespCycle);
        prevRespEvent->addChild(dr, eventRecorder)->addChild(tr.startEvent, eventRecorder);
        //tr.endEvent not linked to anything, it's a PUT
    }

    origPrevResp->produceCrossings(&eventRecorder);
    eventRecorder.getCrossingStack().clear();
}
예제 #2
0
파일: timing_event.cpp 프로젝트: 8l/zsim
void TimingEvent::produceCrossings(EventRecorder* evRec) {
    assert(domain != -1);
    //assert(dynamic_cast<CrossingEvent*>(this) == nullptr); //careful, expensive...
    auto pcLambda = [this, evRec](TimingEvent** childPtr) {
        TimingEvent* c = *childPtr;
        if (c->domain != domain) *childPtr = handleCrossing(c, evRec, true);
        c->produceCrossings(evRec);
    };
    visitChildren< decltype(pcLambda) > (pcLambda);
}
예제 #3
0
double TimerEx::GetAvgTime( TimerType type, LPCWSTR timerId, bool stall )
{
	_ASSERT( "init not called or called with NULL" && (m_pDev != NULL) );

	TimingEvent* te = NULL;

	if( NULL != m_Current )
		te = m_Current->GetTimer( timerId );

	if( NULL == te )
		te = GetTimer( timerId );
	
	return ( NULL != te ) ? te->GetAvgTime(type, stall) : 0.0;
}
void CoreRecorder::recordAccess(uint64_t startCycle) {
    assert(eventRecorder.numRecords() <= 2);
    TimingRecord tr = eventRecorder.getRecord(0);
    TimingEvent* origPrevResp = prevRespEvent;

    if (tr.type == PUTS || tr.type == PUTX) {
        //info("Handling PUT+GET");
        assert(eventRecorder.numRecords() == 2);
        TimingRecord tr1 = eventRecorder.getRecord(1);
        assert(tr1.type == GETX || tr1.type == GETS);
        assert(startCycle >= prevRespCycle);
        assert(tr1.reqCycle >= startCycle);
        assert(tr.reqCycle >= startCycle);

        uint64_t delay = startCycle - prevRespCycle;
        TimingCoreEvent* ev = new (eventRecorder) TimingCoreEvent(delay, prevRespCycle - gapCycles, this);
        ev->setMinStartCycle(prevRespCycle);
        prevRespEvent->addChild(ev, eventRecorder);
        DelayEvent* dr = new (eventRecorder) DelayEvent(tr.reqCycle-startCycle);
        DelayEvent* dr1 = new (eventRecorder) DelayEvent(tr1.reqCycle-startCycle);
        dr->setMinStartCycle(startCycle);
        dr1->setMinStartCycle(startCycle);
        ev->addChild(dr, eventRecorder)->addChild(tr.startEvent, eventRecorder);
        ev->addChild(dr1, eventRecorder)->addChild(tr1.startEvent, eventRecorder);

        //tr.endEvent not linked to anything
        prevRespEvent = tr1.endEvent;
        prevRespCycle = tr1.respCycle;

    } else {
        //info("Handling single GET");
        assert(tr.type == GETX || tr.type == GETS);
        assert(eventRecorder.numRecords() == 1);
        uint64_t delay = tr.reqCycle - prevRespCycle;
        TimingEvent* ev = new (eventRecorder) TimingCoreEvent(delay, prevRespCycle - gapCycles, this);
        ev->setMinStartCycle(prevRespCycle);
        prevRespEvent->addChild(ev, eventRecorder)->addChild(tr.startEvent, eventRecorder);
        prevRespEvent = tr.endEvent;
        prevRespCycle = tr.respCycle;
    }

    origPrevResp->produceCrossings(&eventRecorder);
    eventRecorder.getCrossingStack().clear();
    eventRecorder.clearRecords();
}
예제 #5
0
TimingEvent* TimerEx::GetTimer( LPCWSTR timerId )
{
	if( NULL == timerId )
		return m_Root;

	_ASSERT( "init not called" && (m_pDev != NULL) );

	size_t len = wcslen(timerId);
	size_t seperator = wcscspn( timerId, L"/|\\" );
	if( seperator < len )
	{
		LPWSTR idCopy = new WCHAR[len+1];
		wcscpy_s( idCopy, len+1, timerId );
		idCopy[seperator] = 0;
		TimingEvent* te = m_Root;
		while( te )
		{
			if( !wcscmp(idCopy, te->m_name ) )
			{
				te = te->GetTimerRec( &idCopy[seperator+1] );
				delete[] idCopy;
				return te;
			}
			te = te->m_next;
		}
		delete[] idCopy;
	}
	else
	{
		TimingEvent* te = m_Root;
		while( te )
		{
			if( !wcscmp(timerId, te->m_name ) )
				return te;
			te = te->m_next;
		}
	}
	return NULL;
}
예제 #6
0
// when this function is called we know we're working on a copy of the name, so we can "destruct" it
TimingEvent* TimingEvent::GetTimerRec( LPWSTR timerId ) 
{
	size_t len = wcslen(timerId);
	size_t seperator = wcscspn( timerId, L"/|\\" );
	if( seperator<len )
		timerId[seperator] = 0;

	TimingEvent* te = m_firstChild;
	while( te )
	{
		if( !wcscmp(timerId, te->m_name ) )
		{
			if( seperator<len )
				te = te->GetTimerRec( &timerId[seperator+1] );

			return te;
		}
		te = te->m_next;
	}

	return NULL;
}
예제 #7
0
TimingEvent* TimingEvent::GetTimer(LPCWSTR timerId)
{
	size_t len = wcslen(timerId);
	size_t seperator = wcscspn( timerId, L"/|\\" );

	if( seperator<len )
	{
		LPWSTR idCopy = new WCHAR[len+1];
		wcscpy_s( idCopy, len+1, timerId );
		idCopy[seperator] = 0;

		TimingEvent* te = m_firstChild;
		while( te )
		{
			if( !wcscmp(idCopy, te->m_name ) )
			{
				te = te->GetTimerRec( &idCopy[seperator+1] );
				delete[] idCopy;
				return te;
			}
			te = te->m_next;
		}

		delete[] idCopy;
	}
	else
	{
		TimingEvent* te = m_firstChild;
		while( te )
		{
			if( !wcscmp(timerId, te->m_name ) )
				return te;
			te = te->m_next;
		}
	}
	return NULL;
}
예제 #8
0
void TimerEx::Start( LPCWSTR timerId )
{
	_ASSERT( "init not called or called with NULL" && (m_pDev != NULL) );

	TimingEvent* te = (NULL == m_Current) ? GetTimer(timerId) : m_Current->GetTimer(timerId);
	if( NULL == te )
	{
		// create new timer event
		if( NULL == m_Unused )
		{
			te = new TimingEvent();
		}
		else
		{
			te = m_Unused;
			m_Unused = te->m_next;
			te->m_next = NULL;
		}

		te->SetName( timerId );
		te->m_parent = m_Current;

		// now look where to insert it
		TimingEvent* lu = NULL;
		if( NULL == m_Current )
		{
			TimingEvent* tmp = m_Root;
			while( tmp )
			{
				if( tmp->m_used )
					lu = tmp;
				tmp = tmp->m_next;
			}
		}
		else
		{
			lu = m_Current->FindLastChildUsed();
		}

		if( NULL != lu )
		{
			te->m_next = lu->m_next;
			lu->m_next = te;
		}
		else
		{
			if( NULL == m_Current )
			{
				te->m_next = m_Root;
				m_Root = te;
			}
			else
			{
				te->m_next = m_Current->m_firstChild;
				m_Current->m_firstChild = te;
			}

		}
	}

	m_Current = te;
	m_Current->Start();
}
예제 #9
0
// TODO(dsm): This is copied verbatim from Cache. We should split Cache into different methods, then call those.
uint64_t TimingCache::access(MemReq& req) {
    EventRecorder* evRec = zinfo->eventRecorders[req.srcId];
    assert_msg(evRec, "TimingCache is not connected to TimingCore");
    uint32_t initialRecords = evRec->numRecords();

    bool hasWritebackRecord = false;
    TimingRecord writebackRecord;
    bool hasAccessRecord = false;
    TimingRecord accessRecord;
    uint64_t evDoneCycle = 0;
    
    uint64_t respCycle = req.cycle;
    bool skipAccess = cc->startAccess(req); //may need to skip access due to races (NOTE: may change req.type!)
    if (likely(!skipAccess)) {
        bool updateReplacement = (req.type == GETS) || (req.type == GETX);
        int32_t lineId = array->lookup(req.lineAddr, &req, updateReplacement);
        respCycle += accLat;

        if (lineId == -1 /*&& cc->shouldAllocate(req)*/) {
            assert(cc->shouldAllocate(req)); //dsm: for now, we don't deal with non-inclusion in TimingCache

            //Make space for new line
            Address wbLineAddr;
            lineId = array->preinsert(req.lineAddr, &req, &wbLineAddr); //find the lineId to replace
            trace(Cache, "[%s] Evicting 0x%lx", name.c_str(), wbLineAddr);

            //Evictions are not in the critical path in any sane implementation -- we do not include their delays
            //NOTE: We might be "evicting" an invalid line for all we know. Coherence controllers will know what to do
            evDoneCycle = cc->processEviction(req, wbLineAddr, lineId, respCycle); //if needed, send invalidates/downgrades to lower level, and wb to upper level

            array->postinsert(req.lineAddr, &req, lineId); //do the actual insertion. NOTE: Now we must split insert into a 2-phase thing because cc unlocks us.

            if (evRec->numRecords() > initialRecords) {
                assert_msg(evRec->numRecords() == initialRecords + 1, "evRec records on eviction %ld", evRec->numRecords());
                writebackRecord = evRec->getRecord(initialRecords);
                hasWritebackRecord = true;
                evRec->popRecord();
            }
        }

        uint64_t getDoneCycle = respCycle;
        respCycle = cc->processAccess(req, lineId, respCycle, &getDoneCycle);

        if (evRec->numRecords() > initialRecords) {
            assert_msg(evRec->numRecords() == initialRecords + 1, "evRec records %ld", evRec->numRecords());
            accessRecord = evRec->getRecord(initialRecords);
            hasAccessRecord = true;
            evRec->popRecord();
        }

        // At this point we have all the info we need to hammer out the timing record
        TimingRecord tr = {req.lineAddr << lineBits, req.cycle, respCycle, req.type, NULL, NULL}; //note the end event is the response, not the wback

        if (getDoneCycle - req.cycle == accLat) {
            // Hit
            assert(!hasWritebackRecord);
            assert(!hasAccessRecord);
            uint64_t hitLat = respCycle - req.cycle; // accLat + invLat
            HitEvent* ev = new (evRec) HitEvent(this, hitLat, domain);
            ev->setMinStartCycle(req.cycle);
            tr.startEvent = tr.endEvent = ev;
        } else {
            assert_msg(getDoneCycle == respCycle, "gdc %ld rc %ld", getDoneCycle, respCycle);

            // Miss events:
            // MissStart (does high-prio lookup) -> getEvent || evictionEvent || replEvent (if needed) -> MissWriteback

            MissStartEvent* mse = new (evRec) MissStartEvent(this, accLat, domain);
            MissResponseEvent* mre = new (evRec) MissResponseEvent(this, mse, domain);
            MissWritebackEvent* mwe = new (evRec) MissWritebackEvent(this, mse, accLat, domain);

            mse->setMinStartCycle(req.cycle);
            mre->setMinStartCycle(getDoneCycle);
            mwe->setMinStartCycle(MAX(evDoneCycle, getDoneCycle));

            // Tie two events to an optional timing record
            // TODO: Promote to evRec if this is more generally useful
            auto connect = [evRec](const TimingRecord* r, TimingEvent* startEv, TimingEvent* endEv, uint64_t startCycle, uint64_t endCycle) {
                assert_msg(startCycle <= endCycle, "start > end? %ld %ld", startCycle, endCycle);
                if (r) {
                    assert_msg(startCycle <= r->reqCycle, "%ld / %ld", startCycle, r->reqCycle);
                    assert_msg(r->respCycle <= endCycle, "%ld %ld %ld %ld", startCycle, r->reqCycle, r->respCycle, endCycle);
                    uint64_t upLat = r->reqCycle - startCycle;
                    uint64_t downLat = endCycle - r->respCycle;

                    if (upLat) {
                        DelayEvent* dUp = new (evRec) DelayEvent(upLat);
                        dUp->setMinStartCycle(startCycle);
                        startEv->addChild(dUp, evRec)->addChild(r->startEvent, evRec);
                    } else {
                        startEv->addChild(r->startEvent, evRec);
                    }

                    if (downLat) {
                        DelayEvent* dDown = new (evRec) DelayEvent(downLat);
                        dDown->setMinStartCycle(r->respCycle);
                        r->endEvent->addChild(dDown, evRec)->addChild(endEv, evRec);
                    } else {
                        r->endEvent->addChild(endEv, evRec);
                    }
                } else {
                    if (startCycle == endCycle) {
                        startEv->addChild(endEv, evRec);
                    } else {
                        DelayEvent* dEv = new (evRec) DelayEvent(endCycle - startCycle);
                        dEv->setMinStartCycle(startCycle);
                        startEv->addChild(dEv, evRec)->addChild(endEv, evRec);
                    }
                }
            };

            // Get path
            connect(hasAccessRecord? &accessRecord : NULL, mse, mre, req.cycle + accLat, getDoneCycle);
            mre->addChild(mwe, evRec);

            // Eviction path
            if (evDoneCycle) {
                connect(hasWritebackRecord? &writebackRecord : NULL, mse, mwe, req.cycle + accLat, evDoneCycle);
            }

            // Replacement path
            if (evDoneCycle && cands > ways) {
                uint32_t replLookups = (cands + (ways-1))/ways - 1; // e.g., with 4 ways, 5-8 -> 1, 9-12 -> 2, etc.
                assert(replLookups);

                uint32_t fringeAccs = ways - 1;
                uint32_t accsSoFar = 0;

                TimingEvent* p = mse;

                // Candidate lookup events
                while (accsSoFar < replLookups) {
                    uint32_t preDelay = accsSoFar? 0 : tagLat;
                    uint32_t postDelay = tagLat - MIN(tagLat - 1, fringeAccs);
                    uint32_t accs = MIN(fringeAccs, replLookups - accsSoFar);
                    //info("ReplAccessEvent rl %d fa %d preD %d postD %d accs %d", replLookups, fringeAccs, preDelay, postDelay, accs);
                    ReplAccessEvent* raEv = new (evRec) ReplAccessEvent(this, accs, preDelay, postDelay, domain);
                    raEv->setMinStartCycle(req.cycle /*lax...*/);
                    accsSoFar += accs;
                    p->addChild(raEv, evRec);
                    p = raEv;
                    fringeAccs *= ways - 1;
                }

                // Swap events -- typically, one read and one write work for 1-2 swaps. Exact number depends on layout.
                ReplAccessEvent* rdEv = new (evRec) ReplAccessEvent(this, 1, tagLat, tagLat, domain);
                rdEv->setMinStartCycle(req.cycle /*lax...*/);
                ReplAccessEvent* wrEv = new (evRec) ReplAccessEvent(this, 1, 0, 0, domain);
                wrEv->setMinStartCycle(req.cycle /*lax...*/);

                p->addChild(rdEv, evRec)->addChild(wrEv, evRec)->addChild(mwe, evRec);
            }


            tr.startEvent = mse;
            tr.endEvent = mre; // note the end event is the response, not the wback
        }
        evRec->pushRecord(tr);
    }

    cc->endAccess(req);

    assert_msg(respCycle >= req.cycle, "[%s] resp < req? 0x%lx type %s childState %s, respCycle %ld reqCycle %ld",
            name.c_str(), req.lineAddr, AccessTypeName(req.type), MESIStateName(*req.state), respCycle, req.cycle);
    return respCycle;
}