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