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);
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 Port *peer;
214 if (icachePort.getPeer() == NULL) {
215 peer = oldCPU->getPort("icache_port")->getPeer();
216 icachePort.setPeer(peer);
217 } else {
218 peer = icachePort.getPeer();
219 }
220 peer->setPeer(&icachePort);
221
222 if (dcachePort.getPeer() == NULL) {
223 peer = oldCPU->getPort("dcache_port")->getPeer();
224 dcachePort.setPeer(peer);
225 } else {
226 peer = dcachePort.getPeer();
227 }
228 peer->setPeer(&dcachePort);
229}
230
231
232void
233TimingSimpleCPU::activateContext(int thread_num, int delay)
234{
235 assert(thread_num == 0);
236 assert(thread);
237
238 assert(_status == Idle);
239
240 notIdleFraction++;
241 _status = Running;
242
243#if FULL_SYSTEM
244 // Connect the ThreadContext's memory ports (Functional/Virtual
245 // Ports)
246 tc->connectMemPorts();
247#endif
248
249 // kick things off by initiating the fetch of the next instruction
250 fetchEvent =
251 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
252 fetchEvent->schedule(nextCycle(curTick + cycles(delay)));
253}
254
255
256void
257TimingSimpleCPU::suspendContext(int thread_num)
258{
259 assert(thread_num == 0);
260 assert(thread);
261
262 assert(_status == Running);
263
264 // just change status to Idle... if status != Running,
265 // completeInst() will not initiate fetch of next instruction.
266
267 notIdleFraction--;
268 _status = Idle;
269}
270
271
272template <class T>
273Fault
274TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
275{
276 Request *req =
277 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
278 cpu_id, /* thread ID */ 0);
279
280 if (traceData) {
281 traceData->setAddr(req->getVaddr());
282 }
283
284 // translate to physical address
285 Fault fault = thread->translateDataReadReq(req);
286
287 // Now do the access.
288 if (fault == NoFault) {
289 PacketPtr pkt =
290 new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
291 pkt->dataDynamic<T>(new T);
292
293 if (!dcachePort.sendTiming(pkt)) {
294 _status = DcacheRetry;
295 dcache_pkt = pkt;
296 } else {
297 _status = DcacheWaitResponse;
298 // memory system takes ownership of packet
299 dcache_pkt = NULL;
300 }
301 } else {
302 delete req;
303 }
304
305 // This will need a new way to tell if it has a dcache attached.
306 if (req->isUncacheable())
307 recordEvent("Uncached Read");
308
309 return fault;
310}
311
312#ifndef DOXYGEN_SHOULD_SKIP_THIS
313
314template
315Fault
316TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
317
318template
319Fault
320TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
321
322template
323Fault
324TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
325
326template
327Fault
328TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
329
330template
331Fault
332TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
333
334#endif //DOXYGEN_SHOULD_SKIP_THIS
335
336template<>
337Fault
338TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
339{
340 return read(addr, *(uint64_t*)&data, flags);
341}
342
343template<>
344Fault
345TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
346{
347 return read(addr, *(uint32_t*)&data, flags);
348}
349
350
351template<>
352Fault
353TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
354{
355 return read(addr, (uint32_t&)data, flags);
356}
357
358
359template <class T>
360Fault
361TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
362{
363 Request *req =
364 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
365 cpu_id, /* thread ID */ 0);
366
367 if (traceData) {
368 traceData->setAddr(req->getVaddr());
369 }
370
371 // translate to physical address
372 Fault fault = thread->translateDataWriteReq(req);
373
374 // Now do the access.
375 if (fault == NoFault) {
376 assert(dcache_pkt == NULL);
368 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
377 if (req->isSwap())
378 dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
379 else
380 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
381 dcache_pkt->allocate();
382 dcache_pkt->set(data);
383
384 bool do_access = true; // flag to suppress cache access
385
386 if (req->isLocked()) {
387 do_access = TheISA::handleLockedWrite(thread, req);
388 }
389 if (req->isCondSwap()) {
390 assert(res);
391 req->setExtraData(*res);
392 }
393
394 if (do_access) {
395 if (!dcachePort.sendTiming(dcache_pkt)) {
396 _status = DcacheRetry;
397 } else {
398 _status = DcacheWaitResponse;
399 // memory system takes ownership of packet
400 dcache_pkt = NULL;
401 }
402 }
403 } else {
404 delete req;
405 }
406
407 // This will need a new way to tell if it's hooked up to a cache or not.
408 if (req->isUncacheable())
409 recordEvent("Uncached Write");
410
411 // If the write needs to have a fault on the access, consider calling
412 // changeStatus() and changing it to "bad addr write" or something.
413 return fault;
414}
415
416
417#ifndef DOXYGEN_SHOULD_SKIP_THIS
418template
419Fault
420TimingSimpleCPU::write(uint64_t data, Addr addr,
421 unsigned flags, uint64_t *res);
422
423template
424Fault
425TimingSimpleCPU::write(uint32_t data, Addr addr,
426 unsigned flags, uint64_t *res);
427
428template
429Fault
430TimingSimpleCPU::write(uint16_t data, Addr addr,
431 unsigned flags, uint64_t *res);
432
433template
434Fault
435TimingSimpleCPU::write(uint8_t data, Addr addr,
436 unsigned flags, uint64_t *res);
437
438#endif //DOXYGEN_SHOULD_SKIP_THIS
439
440template<>
441Fault
442TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
443{
444 return write(*(uint64_t*)&data, addr, flags, res);
445}
446
447template<>
448Fault
449TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
450{
451 return write(*(uint32_t*)&data, addr, flags, res);
452}
453
454
455template<>
456Fault
457TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
458{
459 return write((uint32_t)data, addr, flags, res);
460}
461
462
463void
464TimingSimpleCPU::fetch()
465{
466 if (!curStaticInst || !curStaticInst->isDelayedCommit())
467 checkForInterrupts();
468
469 Request *ifetch_req = new Request();
470 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
471 Fault fault = setupFetchRequest(ifetch_req);
472
473 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
474 ifetch_pkt->dataStatic(&inst);
475
476 if (fault == NoFault) {
477 if (!icachePort.sendTiming(ifetch_pkt)) {
478 // Need to wait for retry
479 _status = IcacheRetry;
480 } else {
481 // Need to wait for cache to respond
482 _status = IcacheWaitResponse;
483 // ownership of packet transferred to memory system
484 ifetch_pkt = NULL;
485 }
486 } else {
487 delete ifetch_req;
488 delete ifetch_pkt;
489 // fetch fault: advance directly to next instruction (fault handler)
490 advanceInst(fault);
491 }
492
493 numCycles += curTick - previousTick;
494 previousTick = curTick;
495}
496
497
498void
499TimingSimpleCPU::advanceInst(Fault fault)
500{
501 advancePC(fault);
502
503 if (_status == Running) {
504 // kick off fetch of next instruction... callback from icache
505 // response will cause that instruction to be executed,
506 // keeping the CPU running.
507 fetch();
508 }
509}
510
511
512void
513TimingSimpleCPU::completeIfetch(PacketPtr pkt)
514{
515 // received a response from the icache: execute the received
516 // instruction
517 assert(pkt->result == Packet::Success);
518 assert(_status == IcacheWaitResponse);
519
520 _status = Running;
521
522 numCycles += curTick - previousTick;
523 previousTick = curTick;
524
525 if (getState() == SimObject::Draining) {
526 delete pkt->req;
527 delete pkt;
528
529 completeDrain();
530 return;
531 }
532
533 preExecute();
534 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
535 // load or store: just send to dcache
536 Fault fault = curStaticInst->initiateAcc(this, traceData);
537 if (_status != Running) {
538 // instruction will complete in dcache response callback
539 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
540 assert(fault == NoFault);
541 } else {
542 if (fault == NoFault) {
543 // early fail on store conditional: complete now
544 assert(dcache_pkt != NULL);
545 fault = curStaticInst->completeAcc(dcache_pkt, this,
546 traceData);
547 delete dcache_pkt->req;
548 delete dcache_pkt;
549 dcache_pkt = NULL;
550 }
551 postExecute();
552 advanceInst(fault);
553 }
554 } else {
555 // non-memory instruction: execute completely now
556 Fault fault = curStaticInst->execute(this, traceData);
557 postExecute();
558 advanceInst(fault);
559 }
560
561 delete pkt->req;
562 delete pkt;
563}
564
565void
566TimingSimpleCPU::IcachePort::ITickEvent::process()
567{
568 cpu->completeIfetch(pkt);
569}
570
571bool
572TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
573{
574 if (pkt->isResponse()) {
575 // delay processing of returned data until next CPU clock edge
576 Tick mem_time = pkt->req->getTime();
577 Tick next_tick = cpu->nextCycle(mem_time);
578
579 if (next_tick == curTick)
580 cpu->completeIfetch(pkt);
581 else
582 tickEvent.schedule(pkt, next_tick);
583
584 return true;
585 }
586 else {
587 //Snooping a Coherence Request, do nothing
588 return true;
589 }
590}
591
592void
593TimingSimpleCPU::IcachePort::recvRetry()
594{
595 // we shouldn't get a retry unless we have a packet that we're
596 // waiting to transmit
597 assert(cpu->ifetch_pkt != NULL);
598 assert(cpu->_status == IcacheRetry);
599 PacketPtr tmp = cpu->ifetch_pkt;
600 if (sendTiming(tmp)) {
601 cpu->_status = IcacheWaitResponse;
602 cpu->ifetch_pkt = NULL;
603 }
604}
605
606void
607TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
608{
609 // received a response from the dcache: complete the load or store
610 // instruction
611 assert(pkt->result == Packet::Success);
612 assert(_status == DcacheWaitResponse);
613 _status = Running;
614
615 numCycles += curTick - previousTick;
616 previousTick = curTick;
617
618 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
619
620 if (pkt->isRead() && pkt->req->isLocked()) {
621 TheISA::handleLockedRead(thread, pkt->req);
622 }
623
624 delete pkt->req;
625 delete pkt;
626
627 postExecute();
628
629 if (getState() == SimObject::Draining) {
630 advancePC(fault);
631 completeDrain();
632
633 return;
634 }
635
636 advanceInst(fault);
637}
638
639
640void
641TimingSimpleCPU::completeDrain()
642{
643 DPRINTF(Config, "Done draining\n");
644 changeState(SimObject::Drained);
645 drainEvent->process();
646}
647
648bool
649TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
650{
651 if (pkt->isResponse()) {
652 // delay processing of returned data until next CPU clock edge
653 Tick mem_time = pkt->req->getTime();
654 Tick next_tick = cpu->nextCycle(mem_time);
655
656 if (next_tick == curTick)
657 cpu->completeDataAccess(pkt);
658 else
659 tickEvent.schedule(pkt, next_tick);
660
661 return true;
662 }
663 else {
664 //Snooping a coherence req, do nothing
665 return true;
666 }
667}
668
669void
670TimingSimpleCPU::DcachePort::DTickEvent::process()
671{
672 cpu->completeDataAccess(pkt);
673}
674
675void
676TimingSimpleCPU::DcachePort::recvRetry()
677{
678 // we shouldn't get a retry unless we have a packet that we're
679 // waiting to transmit
680 assert(cpu->dcache_pkt != NULL);
681 assert(cpu->_status == DcacheRetry);
682 PacketPtr tmp = cpu->dcache_pkt;
683 if (sendTiming(tmp)) {
684 cpu->_status = DcacheWaitResponse;
685 // memory system takes ownership of packet
686 cpu->dcache_pkt = NULL;
687 }
688}
689
690
691////////////////////////////////////////////////////////////////////////
692//
693// TimingSimpleCPU Simulation Object
694//
695BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
696
697 Param<Counter> max_insts_any_thread;
698 Param<Counter> max_insts_all_threads;
699 Param<Counter> max_loads_any_thread;
700 Param<Counter> max_loads_all_threads;
701 Param<Tick> progress_interval;
702 SimObjectParam<System *> system;
703 Param<int> cpu_id;
704
705#if FULL_SYSTEM
706 SimObjectParam<TheISA::ITB *> itb;
707 SimObjectParam<TheISA::DTB *> dtb;
708 Param<Tick> profile;
709
710 Param<bool> do_quiesce;
711 Param<bool> do_checkpoint_insts;
712 Param<bool> do_statistics_insts;
713#else
714 SimObjectParam<Process *> workload;
715#endif // FULL_SYSTEM
716
717 Param<int> clock;
718 Param<int> phase;
719
720 Param<bool> defer_registration;
721 Param<int> width;
722 Param<bool> function_trace;
723 Param<Tick> function_trace_start;
724 Param<bool> simulate_stalls;
725
726END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
727
728BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
729
730 INIT_PARAM(max_insts_any_thread,
731 "terminate when any thread reaches this inst count"),
732 INIT_PARAM(max_insts_all_threads,
733 "terminate when all threads have reached this inst count"),
734 INIT_PARAM(max_loads_any_thread,
735 "terminate when any thread reaches this load count"),
736 INIT_PARAM(max_loads_all_threads,
737 "terminate when all threads have reached this load count"),
738 INIT_PARAM(progress_interval, "Progress interval"),
739 INIT_PARAM(system, "system object"),
740 INIT_PARAM(cpu_id, "processor ID"),
741
742#if FULL_SYSTEM
743 INIT_PARAM(itb, "Instruction TLB"),
744 INIT_PARAM(dtb, "Data TLB"),
745 INIT_PARAM(profile, ""),
746 INIT_PARAM(do_quiesce, ""),
747 INIT_PARAM(do_checkpoint_insts, ""),
748 INIT_PARAM(do_statistics_insts, ""),
749#else
750 INIT_PARAM(workload, "processes to run"),
751#endif // FULL_SYSTEM
752
753 INIT_PARAM(clock, "clock speed"),
754 INIT_PARAM_DFLT(phase, "clock phase", 0),
755 INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
756 INIT_PARAM(width, "cpu width"),
757 INIT_PARAM(function_trace, "Enable function trace"),
758 INIT_PARAM(function_trace_start, "Cycle to start function trace"),
759 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
760
761END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
762
763
764CREATE_SIM_OBJECT(TimingSimpleCPU)
765{
766 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
767 params->name = getInstanceName();
768 params->numberOfThreads = 1;
769 params->max_insts_any_thread = max_insts_any_thread;
770 params->max_insts_all_threads = max_insts_all_threads;
771 params->max_loads_any_thread = max_loads_any_thread;
772 params->max_loads_all_threads = max_loads_all_threads;
773 params->progress_interval = progress_interval;
774 params->deferRegistration = defer_registration;
775 params->clock = clock;
776 params->phase = phase;
777 params->functionTrace = function_trace;
778 params->functionTraceStart = function_trace_start;
779 params->system = system;
780 params->cpu_id = cpu_id;
781
782#if FULL_SYSTEM
783 params->itb = itb;
784 params->dtb = dtb;
785 params->profile = profile;
786 params->do_quiesce = do_quiesce;
787 params->do_checkpoint_insts = do_checkpoint_insts;
788 params->do_statistics_insts = do_statistics_insts;
789#else
790 params->process = workload;
791#endif
792
793 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
794 return cpu;
795}
796
797REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
798