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