timing.cc revision 2867
1/*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Steve Reinhardt
29 */
30
31#include "arch/utility.hh"
32#include "cpu/exetrace.hh"
33#include "cpu/simple/timing.hh"
34#include "mem/packet_impl.hh"
35#include "sim/builder.hh"
36
37using namespace std;
38using namespace TheISA;
39
40
41void
42TimingSimpleCPU::init()
43{
44    //Create Memory Ports (conect them up)
45    Port *mem_dport = mem->getPort("");
46    dcachePort.setPeer(mem_dport);
47    mem_dport->setPeer(&dcachePort);
48
49    Port *mem_iport = mem->getPort("");
50    icachePort.setPeer(mem_iport);
51    mem_iport->setPeer(&icachePort);
52
53    BaseCPU::init();
54#if FULL_SYSTEM
55    for (int i = 0; i < threadContexts.size(); ++i) {
56        ThreadContext *tc = threadContexts[i];
57
58        // initialize CPU, including PC
59        TheISA::initCPU(tc, tc->readCpuId());
60    }
61#endif
62}
63
64Tick
65TimingSimpleCPU::CpuPort::recvAtomic(Packet *pkt)
66{
67    panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
68    return curTick;
69}
70
71void
72TimingSimpleCPU::CpuPort::recvFunctional(Packet *pkt)
73{
74    panic("TimingSimpleCPU doesn't expect recvFunctional callback!");
75}
76
77void
78TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
79{
80    if (status == RangeChange)
81        return;
82
83    panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
84}
85
86TimingSimpleCPU::TimingSimpleCPU(Params *p)
87    : BaseSimpleCPU(p), icachePort(this), dcachePort(this)
88{
89    _status = Idle;
90    ifetch_pkt = dcache_pkt = NULL;
91    drainEvent = NULL;
92    fetchEvent = NULL;
93    state = SimObject::Timing;
94}
95
96
97TimingSimpleCPU::~TimingSimpleCPU()
98{
99}
100
101void
102TimingSimpleCPU::serialize(ostream &os)
103{
104    SERIALIZE_ENUM(_status);
105    BaseSimpleCPU::serialize(os);
106}
107
108void
109TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
110{
111    UNSERIALIZE_ENUM(_status);
112    BaseSimpleCPU::unserialize(cp, section);
113}
114
115bool
116TimingSimpleCPU::drain(Event *drain_event)
117{
118    // TimingSimpleCPU is ready to drain if it's not waiting for
119    // an access to complete.
120    if (status() == Idle || status() == Running || status() == SwitchedOut) {
121        changeState(SimObject::DrainedTiming);
122        return true;
123    } else {
124        changeState(SimObject::Draining);
125        drainEvent = drain_event;
126        return false;
127    }
128}
129
130void
131TimingSimpleCPU::resume()
132{
133    if (_status != SwitchedOut && _status != Idle) {
134        // Delete the old event if it existed.
135        if (fetchEvent) {
136            assert(!fetchEvent->scheduled());
137            delete fetchEvent;
138        }
139
140        fetchEvent =
141            new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
142        fetchEvent->schedule(curTick);
143    }
144}
145
146void
147TimingSimpleCPU::setMemoryMode(State new_mode)
148{
149    assert(new_mode == SimObject::Timing);
150}
151
152void
153TimingSimpleCPU::switchOut()
154{
155    assert(status() == Running || status() == Idle);
156    _status = SwitchedOut;
157
158    // If we've been scheduled to resume but are then told to switch out,
159    // we'll need to cancel it.
160    if (fetchEvent && fetchEvent->scheduled())
161        fetchEvent->deschedule();
162}
163
164
165void
166TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
167{
168    BaseCPU::takeOverFrom(oldCPU);
169
170    // if any of this CPU's ThreadContexts are active, mark the CPU as
171    // running and schedule its tick event.
172    for (int i = 0; i < threadContexts.size(); ++i) {
173        ThreadContext *tc = threadContexts[i];
174        if (tc->status() == ThreadContext::Active && _status != Running) {
175            _status = Running;
176            break;
177        }
178    }
179}
180
181
182void
183TimingSimpleCPU::activateContext(int thread_num, int delay)
184{
185    assert(thread_num == 0);
186    assert(thread);
187
188    assert(_status == Idle);
189
190    notIdleFraction++;
191    _status = Running;
192    // kick things off by initiating the fetch of the next instruction
193    fetchEvent =
194        new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
195    fetchEvent->schedule(curTick + cycles(delay));
196}
197
198
199void
200TimingSimpleCPU::suspendContext(int thread_num)
201{
202    assert(thread_num == 0);
203    assert(thread);
204
205    assert(_status == Running);
206
207    // just change status to Idle... if status != Running,
208    // completeInst() will not initiate fetch of next instruction.
209
210    notIdleFraction--;
211    _status = Idle;
212}
213
214
215template <class T>
216Fault
217TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
218{
219    // need to fill in CPU & thread IDs here
220    Request *data_read_req = new Request();
221    data_read_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE
222    data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC());
223
224    if (traceData) {
225        traceData->setAddr(data_read_req->getVaddr());
226    }
227
228   // translate to physical address
229    Fault fault = thread->translateDataReadReq(data_read_req);
230
231    // Now do the access.
232    if (fault == NoFault) {
233        Packet *data_read_pkt =
234            new Packet(data_read_req, Packet::ReadReq, Packet::Broadcast);
235        data_read_pkt->dataDynamic<T>(new T);
236
237        if (!dcachePort.sendTiming(data_read_pkt)) {
238            _status = DcacheRetry;
239            dcache_pkt = data_read_pkt;
240        } else {
241            _status = DcacheWaitResponse;
242            dcache_pkt = NULL;
243        }
244    }
245
246    // This will need a new way to tell if it has a dcache attached.
247    if (data_read_req->getFlags() & UNCACHEABLE)
248        recordEvent("Uncached Read");
249
250    return fault;
251}
252
253#ifndef DOXYGEN_SHOULD_SKIP_THIS
254
255template
256Fault
257TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
258
259template
260Fault
261TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
262
263template
264Fault
265TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
266
267template
268Fault
269TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
270
271#endif //DOXYGEN_SHOULD_SKIP_THIS
272
273template<>
274Fault
275TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
276{
277    return read(addr, *(uint64_t*)&data, flags);
278}
279
280template<>
281Fault
282TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
283{
284    return read(addr, *(uint32_t*)&data, flags);
285}
286
287
288template<>
289Fault
290TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
291{
292    return read(addr, (uint32_t&)data, flags);
293}
294
295
296template <class T>
297Fault
298TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
299{
300    // need to fill in CPU & thread IDs here
301    Request *data_write_req = new Request();
302    data_write_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE
303    data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC());
304
305    // translate to physical address
306    Fault fault = thread->translateDataWriteReq(data_write_req);
307    // Now do the access.
308    if (fault == NoFault) {
309        Packet *data_write_pkt =
310            new Packet(data_write_req, Packet::WriteReq, Packet::Broadcast);
311        data_write_pkt->allocate();
312        data_write_pkt->set(data);
313
314        if (!dcachePort.sendTiming(data_write_pkt)) {
315            _status = DcacheRetry;
316            dcache_pkt = data_write_pkt;
317        } else {
318            _status = DcacheWaitResponse;
319            dcache_pkt = NULL;
320        }
321    }
322
323    // This will need a new way to tell if it's hooked up to a cache or not.
324    if (data_write_req->getFlags() & UNCACHEABLE)
325        recordEvent("Uncached Write");
326
327    // If the write needs to have a fault on the access, consider calling
328    // changeStatus() and changing it to "bad addr write" or something.
329    return fault;
330}
331
332
333#ifndef DOXYGEN_SHOULD_SKIP_THIS
334template
335Fault
336TimingSimpleCPU::write(uint64_t data, Addr addr,
337                       unsigned flags, uint64_t *res);
338
339template
340Fault
341TimingSimpleCPU::write(uint32_t data, Addr addr,
342                       unsigned flags, uint64_t *res);
343
344template
345Fault
346TimingSimpleCPU::write(uint16_t data, Addr addr,
347                       unsigned flags, uint64_t *res);
348
349template
350Fault
351TimingSimpleCPU::write(uint8_t data, Addr addr,
352                       unsigned flags, uint64_t *res);
353
354#endif //DOXYGEN_SHOULD_SKIP_THIS
355
356template<>
357Fault
358TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
359{
360    return write(*(uint64_t*)&data, addr, flags, res);
361}
362
363template<>
364Fault
365TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
366{
367    return write(*(uint32_t*)&data, addr, flags, res);
368}
369
370
371template<>
372Fault
373TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
374{
375    return write((uint32_t)data, addr, flags, res);
376}
377
378
379void
380TimingSimpleCPU::fetch()
381{
382    checkForInterrupts();
383
384    // need to fill in CPU & thread IDs here
385    Request *ifetch_req = new Request();
386    ifetch_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE
387    Fault fault = setupFetchRequest(ifetch_req);
388
389    ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast);
390    ifetch_pkt->dataStatic(&inst);
391
392    if (fault == NoFault) {
393        if (!icachePort.sendTiming(ifetch_pkt)) {
394            // Need to wait for retry
395            _status = IcacheRetry;
396        } else {
397            // Need to wait for cache to respond
398            _status = IcacheWaitResponse;
399            // ownership of packet transferred to memory system
400            ifetch_pkt = NULL;
401        }
402    } else {
403        // fetch fault: advance directly to next instruction (fault handler)
404        advanceInst(fault);
405    }
406}
407
408
409void
410TimingSimpleCPU::advanceInst(Fault fault)
411{
412    advancePC(fault);
413
414    if (_status == Running) {
415        // kick off fetch of next instruction... callback from icache
416        // response will cause that instruction to be executed,
417        // keeping the CPU running.
418        fetch();
419    }
420}
421
422
423void
424TimingSimpleCPU::completeIfetch(Packet *pkt)
425{
426    // received a response from the icache: execute the received
427    // instruction
428    assert(pkt->result == Packet::Success);
429    assert(_status == IcacheWaitResponse);
430
431    _status = Running;
432
433    delete pkt->req;
434    delete pkt;
435
436    if (getState() == SimObject::Draining) {
437        completeDrain();
438        return;
439    }
440
441    preExecute();
442    if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
443        // load or store: just send to dcache
444        Fault fault = curStaticInst->initiateAcc(this, traceData);
445        if (fault == NoFault) {
446            // successfully initiated access: instruction will
447            // complete in dcache response callback
448            assert(_status == DcacheWaitResponse);
449        } else {
450            // fault: complete now to invoke fault handler
451            postExecute();
452            advanceInst(fault);
453        }
454    } else {
455        // non-memory instruction: execute completely now
456        Fault fault = curStaticInst->execute(this, traceData);
457        postExecute();
458        advanceInst(fault);
459    }
460}
461
462
463bool
464TimingSimpleCPU::IcachePort::recvTiming(Packet *pkt)
465{
466    if (cpu->_status == DcacheWaitResponse)
467        cpu->completeDataAccess(pkt);
468    else if (cpu->_status == IcacheWaitResponse)
469        cpu->completeIfetch(pkt);
470    else
471        assert("OOPS" && 0);
472    return true;
473}
474
475void
476TimingSimpleCPU::IcachePort::recvRetry()
477{
478    // we shouldn't get a retry unless we have a packet that we're
479    // waiting to transmit
480    assert(cpu->ifetch_pkt != NULL);
481    assert(cpu->_status == IcacheRetry);
482    Packet *tmp = cpu->ifetch_pkt;
483    if (sendTiming(tmp)) {
484        cpu->_status = IcacheWaitResponse;
485        cpu->ifetch_pkt = NULL;
486    }
487}
488
489void
490TimingSimpleCPU::completeDataAccess(Packet *pkt)
491{
492    // received a response from the dcache: complete the load or store
493    // instruction
494    assert(pkt->result == Packet::Success);
495    assert(_status == DcacheWaitResponse);
496    _status = Running;
497
498    if (getState() == SimObject::Draining) {
499        completeDrain();
500
501        delete pkt->req;
502        delete pkt;
503
504        return;
505    }
506
507    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
508
509    delete pkt->req;
510    delete pkt;
511
512    postExecute();
513    advanceInst(fault);
514}
515
516
517void
518TimingSimpleCPU::completeDrain()
519{
520    DPRINTF(Config, "Done draining\n");
521    changeState(SimObject::DrainedTiming);
522    drainEvent->process();
523}
524
525bool
526TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt)
527{
528    cpu->completeDataAccess(pkt);
529    return true;
530}
531
532void
533TimingSimpleCPU::DcachePort::recvRetry()
534{
535    // we shouldn't get a retry unless we have a packet that we're
536    // waiting to transmit
537    assert(cpu->dcache_pkt != NULL);
538    assert(cpu->_status == DcacheRetry);
539    Packet *tmp = cpu->dcache_pkt;
540    if (sendTiming(tmp)) {
541        cpu->_status = DcacheWaitResponse;
542        cpu->dcache_pkt = NULL;
543    }
544}
545
546
547////////////////////////////////////////////////////////////////////////
548//
549//  TimingSimpleCPU Simulation Object
550//
551BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
552
553    Param<Counter> max_insts_any_thread;
554    Param<Counter> max_insts_all_threads;
555    Param<Counter> max_loads_any_thread;
556    Param<Counter> max_loads_all_threads;
557    SimObjectParam<MemObject *> mem;
558
559#if FULL_SYSTEM
560    SimObjectParam<AlphaITB *> itb;
561    SimObjectParam<AlphaDTB *> dtb;
562    SimObjectParam<System *> system;
563    Param<int> cpu_id;
564    Param<Tick> profile;
565#else
566    SimObjectParam<Process *> workload;
567#endif // FULL_SYSTEM
568
569    Param<int> clock;
570
571    Param<bool> defer_registration;
572    Param<int> width;
573    Param<bool> function_trace;
574    Param<Tick> function_trace_start;
575    Param<bool> simulate_stalls;
576
577END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
578
579BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
580
581    INIT_PARAM(max_insts_any_thread,
582               "terminate when any thread reaches this inst count"),
583    INIT_PARAM(max_insts_all_threads,
584               "terminate when all threads have reached this inst count"),
585    INIT_PARAM(max_loads_any_thread,
586               "terminate when any thread reaches this load count"),
587    INIT_PARAM(max_loads_all_threads,
588               "terminate when all threads have reached this load count"),
589    INIT_PARAM(mem, "memory"),
590
591#if FULL_SYSTEM
592    INIT_PARAM(itb, "Instruction TLB"),
593    INIT_PARAM(dtb, "Data TLB"),
594    INIT_PARAM(system, "system object"),
595    INIT_PARAM(cpu_id, "processor ID"),
596    INIT_PARAM(profile, ""),
597#else
598    INIT_PARAM(workload, "processes to run"),
599#endif // FULL_SYSTEM
600
601    INIT_PARAM(clock, "clock speed"),
602    INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
603    INIT_PARAM(width, "cpu width"),
604    INIT_PARAM(function_trace, "Enable function trace"),
605    INIT_PARAM(function_trace_start, "Cycle to start function trace"),
606    INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
607
608END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
609
610
611CREATE_SIM_OBJECT(TimingSimpleCPU)
612{
613    TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
614    params->name = getInstanceName();
615    params->numberOfThreads = 1;
616    params->max_insts_any_thread = max_insts_any_thread;
617    params->max_insts_all_threads = max_insts_all_threads;
618    params->max_loads_any_thread = max_loads_any_thread;
619    params->max_loads_all_threads = max_loads_all_threads;
620    params->deferRegistration = defer_registration;
621    params->clock = clock;
622    params->functionTrace = function_trace;
623    params->functionTraceStart = function_trace_start;
624    params->mem = mem;
625
626#if FULL_SYSTEM
627    params->itb = itb;
628    params->dtb = dtb;
629    params->system = system;
630    params->cpu_id = cpu_id;
631    params->profile = profile;
632#else
633    params->process = workload;
634#endif
635
636    TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
637    return cpu;
638}
639
640REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
641
642