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