timing.cc revision 4224:7e828583f2cb
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/locked_mem.hh"
32#include "arch/utility.hh"
33#include "base/bigint.hh"
34#include "cpu/exetrace.hh"
35#include "cpu/simple/timing.hh"
36#include "mem/packet.hh"
37#include "mem/packet_access.hh"
38#include "sim/builder.hh"
39#include "sim/system.hh"
40
41using namespace std;
42using namespace TheISA;
43
44Port *
45TimingSimpleCPU::getPort(const std::string &if_name, int idx)
46{
47    if (if_name == "dcache_port")
48        return &dcachePort;
49    else if (if_name == "icache_port")
50        return &icachePort;
51    else
52        panic("No Such Port\n");
53}
54
55void
56TimingSimpleCPU::init()
57{
58    BaseCPU::init();
59#if FULL_SYSTEM
60    for (int i = 0; i < threadContexts.size(); ++i) {
61        ThreadContext *tc = threadContexts[i];
62
63        // initialize CPU, including PC
64        TheISA::initCPU(tc, tc->readCpuId());
65    }
66#endif
67}
68
69Tick
70TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
71{
72    panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
73    return curTick;
74}
75
76void
77TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
78{
79    //No internal storage to update, jusst return
80    return;
81}
82
83void
84TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
85{
86    if (status == RangeChange) {
87        if (!snoopRangeSent) {
88            snoopRangeSent = true;
89            sendStatusChange(Port::RangeChange);
90        }
91        return;
92    }
93
94    panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
95}
96
97
98void
99TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
100{
101    pkt = _pkt;
102    Event::schedule(t);
103}
104
105TimingSimpleCPU::TimingSimpleCPU(Params *p)
106    : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock),
107      cpu_id(p->cpu_id)
108{
109    _status = Idle;
110
111    icachePort.snoopRangeSent = false;
112    dcachePort.snoopRangeSent = false;
113
114    ifetch_pkt = dcache_pkt = NULL;
115    drainEvent = NULL;
116    fetchEvent = NULL;
117    previousTick = 0;
118    changeState(SimObject::Running);
119}
120
121
122TimingSimpleCPU::~TimingSimpleCPU()
123{
124}
125
126void
127TimingSimpleCPU::serialize(ostream &os)
128{
129    SimObject::State so_state = SimObject::getState();
130    SERIALIZE_ENUM(so_state);
131    BaseSimpleCPU::serialize(os);
132}
133
134void
135TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
136{
137    SimObject::State so_state;
138    UNSERIALIZE_ENUM(so_state);
139    BaseSimpleCPU::unserialize(cp, section);
140}
141
142unsigned int
143TimingSimpleCPU::drain(Event *drain_event)
144{
145    // TimingSimpleCPU is ready to drain if it's not waiting for
146    // an access to complete.
147    if (status() == Idle || status() == Running || status() == SwitchedOut) {
148        changeState(SimObject::Drained);
149        return 0;
150    } else {
151        changeState(SimObject::Draining);
152        drainEvent = drain_event;
153        return 1;
154    }
155}
156
157void
158TimingSimpleCPU::resume()
159{
160    if (_status != SwitchedOut && _status != Idle) {
161        assert(system->getMemoryMode() == System::Timing);
162
163        // Delete the old event if it existed.
164        if (fetchEvent) {
165            if (fetchEvent->scheduled())
166                fetchEvent->deschedule();
167
168            delete fetchEvent;
169        }
170
171        fetchEvent =
172            new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
173        fetchEvent->schedule(nextCycle());
174    }
175
176    changeState(SimObject::Running);
177    previousTick = curTick;
178}
179
180void
181TimingSimpleCPU::switchOut()
182{
183    assert(status() == Running || status() == Idle);
184    _status = SwitchedOut;
185    numCycles += curTick - previousTick;
186
187    // If we've been scheduled to resume but are then told to switch out,
188    // we'll need to cancel it.
189    if (fetchEvent && fetchEvent->scheduled())
190        fetchEvent->deschedule();
191}
192
193
194void
195TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
196{
197    BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
198
199    // if any of this CPU's ThreadContexts are active, mark the CPU as
200    // running and schedule its tick event.
201    for (int i = 0; i < threadContexts.size(); ++i) {
202        ThreadContext *tc = threadContexts[i];
203        if (tc->status() == ThreadContext::Active && _status != Running) {
204            _status = Running;
205            break;
206        }
207    }
208
209    if (_status != Running) {
210        _status = Idle;
211    }
212}
213
214
215void
216TimingSimpleCPU::activateContext(int thread_num, int delay)
217{
218    assert(thread_num == 0);
219    assert(thread);
220
221    assert(_status == Idle);
222
223    notIdleFraction++;
224    _status = Running;
225
226    // kick things off by initiating the fetch of the next instruction
227    fetchEvent =
228        new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
229    fetchEvent->schedule(nextCycle(curTick + cycles(delay)));
230}
231
232
233void
234TimingSimpleCPU::suspendContext(int thread_num)
235{
236    assert(thread_num == 0);
237    assert(thread);
238
239    assert(_status == Running);
240
241    // just change status to Idle... if status != Running,
242    // completeInst() will not initiate fetch of next instruction.
243
244    notIdleFraction--;
245    _status = Idle;
246}
247
248
249template <class T>
250Fault
251TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
252{
253    Request *req =
254        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
255                    cpu_id, /* thread ID */ 0);
256
257    if (traceData) {
258        traceData->setAddr(req->getVaddr());
259    }
260
261   // translate to physical address
262    Fault fault = thread->translateDataReadReq(req);
263
264    // Now do the access.
265    if (fault == NoFault) {
266        PacketPtr pkt =
267            new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
268        pkt->dataDynamic<T>(new T);
269
270        if (!dcachePort.sendTiming(pkt)) {
271            _status = DcacheRetry;
272            dcache_pkt = pkt;
273        } else {
274            _status = DcacheWaitResponse;
275            // memory system takes ownership of packet
276            dcache_pkt = NULL;
277        }
278
279        // This will need a new way to tell if it has a dcache attached.
280        if (req->isUncacheable())
281            recordEvent("Uncached Read");
282    } else {
283        delete req;
284    }
285
286    return fault;
287}
288
289#ifndef DOXYGEN_SHOULD_SKIP_THIS
290
291template
292Fault
293TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
294
295template
296Fault
297TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
298
299template
300Fault
301TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
302
303template
304Fault
305TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
306
307template
308Fault
309TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
310
311template
312Fault
313TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
314
315#endif //DOXYGEN_SHOULD_SKIP_THIS
316
317template<>
318Fault
319TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
320{
321    return read(addr, *(uint64_t*)&data, flags);
322}
323
324template<>
325Fault
326TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
327{
328    return read(addr, *(uint32_t*)&data, flags);
329}
330
331
332template<>
333Fault
334TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
335{
336    return read(addr, (uint32_t&)data, flags);
337}
338
339
340template <class T>
341Fault
342TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
343{
344    Request *req =
345        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
346                    cpu_id, /* thread ID */ 0);
347
348    if (traceData) {
349        traceData->setAddr(req->getVaddr());
350    }
351
352    // translate to physical address
353    Fault fault = thread->translateDataWriteReq(req);
354
355    // Now do the access.
356    if (fault == NoFault) {
357        assert(dcache_pkt == NULL);
358        if (req->isSwap())
359            dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
360        else
361            dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
362        dcache_pkt->allocate();
363        dcache_pkt->set(data);
364
365        bool do_access = true;  // flag to suppress cache access
366
367        if (req->isLocked()) {
368            do_access = TheISA::handleLockedWrite(thread, req);
369        }
370        if (req->isCondSwap()) {
371             assert(res);
372             req->setExtraData(*res);
373        }
374
375        if (do_access) {
376            if (!dcachePort.sendTiming(dcache_pkt)) {
377                _status = DcacheRetry;
378            } else {
379                _status = DcacheWaitResponse;
380                // memory system takes ownership of packet
381                dcache_pkt = NULL;
382            }
383        }
384        // This will need a new way to tell if it's hooked up to a cache or not.
385        if (req->isUncacheable())
386            recordEvent("Uncached Write");
387    } else {
388        delete req;
389    }
390
391
392    // If the write needs to have a fault on the access, consider calling
393    // changeStatus() and changing it to "bad addr write" or something.
394    return fault;
395}
396
397
398#ifndef DOXYGEN_SHOULD_SKIP_THIS
399template
400Fault
401TimingSimpleCPU::write(Twin32_t data, Addr addr,
402                       unsigned flags, uint64_t *res);
403
404template
405Fault
406TimingSimpleCPU::write(Twin64_t data, Addr addr,
407                       unsigned flags, uint64_t *res);
408
409template
410Fault
411TimingSimpleCPU::write(uint64_t data, Addr addr,
412                       unsigned flags, uint64_t *res);
413
414template
415Fault
416TimingSimpleCPU::write(uint32_t data, Addr addr,
417                       unsigned flags, uint64_t *res);
418
419template
420Fault
421TimingSimpleCPU::write(uint16_t data, Addr addr,
422                       unsigned flags, uint64_t *res);
423
424template
425Fault
426TimingSimpleCPU::write(uint8_t data, Addr addr,
427                       unsigned flags, uint64_t *res);
428
429#endif //DOXYGEN_SHOULD_SKIP_THIS
430
431template<>
432Fault
433TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
434{
435    return write(*(uint64_t*)&data, addr, flags, res);
436}
437
438template<>
439Fault
440TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
441{
442    return write(*(uint32_t*)&data, addr, flags, res);
443}
444
445
446template<>
447Fault
448TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
449{
450    return write((uint32_t)data, addr, flags, res);
451}
452
453
454void
455TimingSimpleCPU::fetch()
456{
457    if (!curStaticInst || !curStaticInst->isDelayedCommit())
458        checkForInterrupts();
459
460    Request *ifetch_req = new Request();
461    ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
462    Fault fault = setupFetchRequest(ifetch_req);
463
464    ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
465    ifetch_pkt->dataStatic(&inst);
466
467    if (fault == NoFault) {
468        if (!icachePort.sendTiming(ifetch_pkt)) {
469            // Need to wait for retry
470            _status = IcacheRetry;
471        } else {
472            // Need to wait for cache to respond
473            _status = IcacheWaitResponse;
474            // ownership of packet transferred to memory system
475            ifetch_pkt = NULL;
476        }
477    } else {
478        delete ifetch_req;
479        delete ifetch_pkt;
480        // fetch fault: advance directly to next instruction (fault handler)
481        advanceInst(fault);
482    }
483
484    numCycles += curTick - previousTick;
485    previousTick = curTick;
486}
487
488
489void
490TimingSimpleCPU::advanceInst(Fault fault)
491{
492    advancePC(fault);
493
494    if (_status == Running) {
495        // kick off fetch of next instruction... callback from icache
496        // response will cause that instruction to be executed,
497        // keeping the CPU running.
498        fetch();
499    }
500}
501
502
503void
504TimingSimpleCPU::completeIfetch(PacketPtr pkt)
505{
506    // received a response from the icache: execute the received
507    // instruction
508    assert(pkt->result == Packet::Success);
509    assert(_status == IcacheWaitResponse);
510
511    _status = Running;
512
513    numCycles += curTick - previousTick;
514    previousTick = curTick;
515
516    if (getState() == SimObject::Draining) {
517        delete pkt->req;
518        delete pkt;
519
520        completeDrain();
521        return;
522    }
523
524    preExecute();
525    if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
526        // load or store: just send to dcache
527        Fault fault = curStaticInst->initiateAcc(this, traceData);
528        if (_status != Running) {
529            // instruction will complete in dcache response callback
530            assert(_status == DcacheWaitResponse || _status == DcacheRetry);
531            assert(fault == NoFault);
532        } else {
533            if (fault == NoFault) {
534                // early fail on store conditional: complete now
535                assert(dcache_pkt != NULL);
536                fault = curStaticInst->completeAcc(dcache_pkt, this,
537                                                   traceData);
538                delete dcache_pkt->req;
539                delete dcache_pkt;
540                dcache_pkt = NULL;
541            }
542            postExecute();
543            advanceInst(fault);
544        }
545    } else {
546        // non-memory instruction: execute completely now
547        Fault fault = curStaticInst->execute(this, traceData);
548        postExecute();
549        advanceInst(fault);
550    }
551
552    delete pkt->req;
553    delete pkt;
554}
555
556void
557TimingSimpleCPU::IcachePort::ITickEvent::process()
558{
559    cpu->completeIfetch(pkt);
560}
561
562bool
563TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
564{
565    if (pkt->isResponse()) {
566        // delay processing of returned data until next CPU clock edge
567        Tick mem_time = pkt->req->getTime();
568        Tick next_tick = cpu->nextCycle(mem_time);
569
570        if (next_tick == curTick)
571            cpu->completeIfetch(pkt);
572        else
573            tickEvent.schedule(pkt, next_tick);
574
575        return true;
576    }
577    else {
578        //Snooping a Coherence Request, do nothing
579        return true;
580    }
581}
582
583void
584TimingSimpleCPU::IcachePort::recvRetry()
585{
586    // we shouldn't get a retry unless we have a packet that we're
587    // waiting to transmit
588    assert(cpu->ifetch_pkt != NULL);
589    assert(cpu->_status == IcacheRetry);
590    PacketPtr tmp = cpu->ifetch_pkt;
591    if (sendTiming(tmp)) {
592        cpu->_status = IcacheWaitResponse;
593        cpu->ifetch_pkt = NULL;
594    }
595}
596
597void
598TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
599{
600    // received a response from the dcache: complete the load or store
601    // instruction
602    assert(pkt->result == Packet::Success);
603    assert(_status == DcacheWaitResponse);
604    _status = Running;
605
606    numCycles += curTick - previousTick;
607    previousTick = curTick;
608
609    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
610
611    if (pkt->isRead() && pkt->req->isLocked()) {
612        TheISA::handleLockedRead(thread, pkt->req);
613    }
614
615    delete pkt->req;
616    delete pkt;
617
618    postExecute();
619
620    if (getState() == SimObject::Draining) {
621        advancePC(fault);
622        completeDrain();
623
624        return;
625    }
626
627    advanceInst(fault);
628}
629
630
631void
632TimingSimpleCPU::completeDrain()
633{
634    DPRINTF(Config, "Done draining\n");
635    changeState(SimObject::Drained);
636    drainEvent->process();
637}
638
639void
640TimingSimpleCPU::DcachePort::setPeer(Port *port)
641{
642    Port::setPeer(port);
643
644#if FULL_SYSTEM
645    // Update the ThreadContext's memory ports (Functional/Virtual
646    // Ports)
647    cpu->tcBase()->connectMemPorts();
648#endif
649}
650
651bool
652TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
653{
654    if (pkt->isResponse()) {
655        // delay processing of returned data until next CPU clock edge
656        Tick mem_time = pkt->req->getTime();
657        Tick next_tick = cpu->nextCycle(mem_time);
658
659        if (next_tick == curTick)
660            cpu->completeDataAccess(pkt);
661        else
662            tickEvent.schedule(pkt, next_tick);
663
664        return true;
665    }
666    else {
667        //Snooping a coherence req, do nothing
668        return true;
669    }
670}
671
672void
673TimingSimpleCPU::DcachePort::DTickEvent::process()
674{
675    cpu->completeDataAccess(pkt);
676}
677
678void
679TimingSimpleCPU::DcachePort::recvRetry()
680{
681    // we shouldn't get a retry unless we have a packet that we're
682    // waiting to transmit
683    assert(cpu->dcache_pkt != NULL);
684    assert(cpu->_status == DcacheRetry);
685    PacketPtr tmp = cpu->dcache_pkt;
686    if (sendTiming(tmp)) {
687        cpu->_status = DcacheWaitResponse;
688        // memory system takes ownership of packet
689        cpu->dcache_pkt = NULL;
690    }
691}
692
693
694////////////////////////////////////////////////////////////////////////
695//
696//  TimingSimpleCPU Simulation Object
697//
698BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
699
700    Param<Counter> max_insts_any_thread;
701    Param<Counter> max_insts_all_threads;
702    Param<Counter> max_loads_any_thread;
703    Param<Counter> max_loads_all_threads;
704    Param<Tick> progress_interval;
705    SimObjectParam<System *> system;
706    Param<int> cpu_id;
707
708#if FULL_SYSTEM
709    SimObjectParam<TheISA::ITB *> itb;
710    SimObjectParam<TheISA::DTB *> dtb;
711    Param<Tick> profile;
712
713    Param<bool> do_quiesce;
714    Param<bool> do_checkpoint_insts;
715    Param<bool> do_statistics_insts;
716#else
717    SimObjectParam<Process *> workload;
718#endif // FULL_SYSTEM
719
720    Param<int> clock;
721    Param<int> phase;
722
723    Param<bool> defer_registration;
724    Param<int> width;
725    Param<bool> function_trace;
726    Param<Tick> function_trace_start;
727    Param<bool> simulate_stalls;
728
729END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
730
731BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
732
733    INIT_PARAM(max_insts_any_thread,
734               "terminate when any thread reaches this inst count"),
735    INIT_PARAM(max_insts_all_threads,
736               "terminate when all threads have reached this inst count"),
737    INIT_PARAM(max_loads_any_thread,
738               "terminate when any thread reaches this load count"),
739    INIT_PARAM(max_loads_all_threads,
740               "terminate when all threads have reached this load count"),
741    INIT_PARAM(progress_interval, "Progress interval"),
742    INIT_PARAM(system, "system object"),
743    INIT_PARAM(cpu_id, "processor ID"),
744
745#if FULL_SYSTEM
746    INIT_PARAM(itb, "Instruction TLB"),
747    INIT_PARAM(dtb, "Data TLB"),
748    INIT_PARAM(profile, ""),
749    INIT_PARAM(do_quiesce, ""),
750    INIT_PARAM(do_checkpoint_insts, ""),
751    INIT_PARAM(do_statistics_insts, ""),
752#else
753    INIT_PARAM(workload, "processes to run"),
754#endif // FULL_SYSTEM
755
756    INIT_PARAM(clock, "clock speed"),
757    INIT_PARAM_DFLT(phase, "clock phase", 0),
758    INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
759    INIT_PARAM(width, "cpu width"),
760    INIT_PARAM(function_trace, "Enable function trace"),
761    INIT_PARAM(function_trace_start, "Cycle to start function trace"),
762    INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
763
764END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
765
766
767CREATE_SIM_OBJECT(TimingSimpleCPU)
768{
769    TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
770    params->name = getInstanceName();
771    params->numberOfThreads = 1;
772    params->max_insts_any_thread = max_insts_any_thread;
773    params->max_insts_all_threads = max_insts_all_threads;
774    params->max_loads_any_thread = max_loads_any_thread;
775    params->max_loads_all_threads = max_loads_all_threads;
776    params->progress_interval = progress_interval;
777    params->deferRegistration = defer_registration;
778    params->clock = clock;
779    params->phase = phase;
780    params->functionTrace = function_trace;
781    params->functionTraceStart = function_trace_start;
782    params->system = system;
783    params->cpu_id = cpu_id;
784
785#if FULL_SYSTEM
786    params->itb = itb;
787    params->dtb = dtb;
788    params->profile = profile;
789    params->do_quiesce = do_quiesce;
790    params->do_checkpoint_insts = do_checkpoint_insts;
791    params->do_statistics_insts = do_statistics_insts;
792#else
793    params->process = workload;
794#endif
795
796    TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
797    return cpu;
798}
799
800REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
801
802