timing.cc revision 6043
112953Sgabeblack@google.com/*
212953Sgabeblack@google.com * Copyright (c) 2002-2005 The Regents of The University of Michigan
312953Sgabeblack@google.com * All rights reserved.
412953Sgabeblack@google.com *
512953Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
612953Sgabeblack@google.com * modification, are permitted provided that the following conditions are
712953Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
812953Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
912953Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
1012953Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1112953Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1212953Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1312953Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1412953Sgabeblack@google.com * this software without specific prior written permission.
1512953Sgabeblack@google.com *
1612953Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712953Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812953Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912953Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012953Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112953Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212953Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312953Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412953Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512953Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612953Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712953Sgabeblack@google.com *
2812953Sgabeblack@google.com * Authors: Steve Reinhardt
2912953Sgabeblack@google.com */
3012953Sgabeblack@google.com
3112953Sgabeblack@google.com#include "arch/locked_mem.hh"
3212953Sgabeblack@google.com#include "arch/mmaped_ipr.hh"
3312953Sgabeblack@google.com#include "arch/utility.hh"
3412953Sgabeblack@google.com#include "base/bigint.hh"
3512953Sgabeblack@google.com#include "cpu/exetrace.hh"
3612953Sgabeblack@google.com#include "cpu/simple/timing.hh"
3712953Sgabeblack@google.com#include "mem/packet.hh"
3812953Sgabeblack@google.com#include "mem/packet_access.hh"
3912953Sgabeblack@google.com#include "params/TimingSimpleCPU.hh"
4012953Sgabeblack@google.com#include "sim/system.hh"
4112953Sgabeblack@google.com
4213180Sgabeblack@google.comusing namespace std;
4313180Sgabeblack@google.comusing namespace TheISA;
4413180Sgabeblack@google.com
4512953Sgabeblack@google.comPort *
4612953Sgabeblack@google.comTimingSimpleCPU::getPort(const std::string &if_name, int idx)
4712953Sgabeblack@google.com{
4812953Sgabeblack@google.com    if (if_name == "dcache_port")
4912953Sgabeblack@google.com        return &dcachePort;
5012953Sgabeblack@google.com    else if (if_name == "icache_port")
5112953Sgabeblack@google.com        return &icachePort;
5212953Sgabeblack@google.com    else
5312953Sgabeblack@google.com        panic("No Such Port\n");
5412953Sgabeblack@google.com}
5512953Sgabeblack@google.com
5612953Sgabeblack@google.comvoid
5712953Sgabeblack@google.comTimingSimpleCPU::init()
5813180Sgabeblack@google.com{
5913180Sgabeblack@google.com    BaseCPU::init();
6012953Sgabeblack@google.com#if FULL_SYSTEM
6112953Sgabeblack@google.com    for (int i = 0; i < threadContexts.size(); ++i) {
6212953Sgabeblack@google.com        ThreadContext *tc = threadContexts[i];
6312953Sgabeblack@google.com
6412953Sgabeblack@google.com        // initialize CPU, including PC
6512953Sgabeblack@google.com        TheISA::initCPU(tc, _cpuId);
6612953Sgabeblack@google.com    }
6712953Sgabeblack@google.com#endif
6812953Sgabeblack@google.com}
6912953Sgabeblack@google.com
7012953Sgabeblack@google.comTick
7112953Sgabeblack@google.comTimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
7212953Sgabeblack@google.com{
7312953Sgabeblack@google.com    panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
7412953Sgabeblack@google.com    return curTick;
7512953Sgabeblack@google.com}
7612953Sgabeblack@google.com
7712953Sgabeblack@google.comvoid
7812953Sgabeblack@google.comTimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
7912953Sgabeblack@google.com{
8012953Sgabeblack@google.com    //No internal storage to update, jusst return
8112953Sgabeblack@google.com    return;
8212953Sgabeblack@google.com}
8312953Sgabeblack@google.com
8412953Sgabeblack@google.comvoid
8512953Sgabeblack@google.comTimingSimpleCPU::CpuPort::recvStatusChange(Status status)
8612953Sgabeblack@google.com{
8712953Sgabeblack@google.com    if (status == RangeChange) {
8812953Sgabeblack@google.com        if (!snoopRangeSent) {
8912953Sgabeblack@google.com            snoopRangeSent = true;
9012953Sgabeblack@google.com            sendStatusChange(Port::RangeChange);
9112998Sgabeblack@google.com        }
9212998Sgabeblack@google.com        return;
9312998Sgabeblack@google.com    }
9412998Sgabeblack@google.com
9512998Sgabeblack@google.com    panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
9612998Sgabeblack@google.com}
9713092Sgabeblack@google.com
9812998Sgabeblack@google.com
9912953Sgabeblack@google.comvoid
10012998Sgabeblack@google.comTimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
10112953Sgabeblack@google.com{
10212953Sgabeblack@google.com    pkt = _pkt;
10312953Sgabeblack@google.com    cpu->schedule(this, t);
10412953Sgabeblack@google.com}
10512953Sgabeblack@google.com
10612953Sgabeblack@google.comTimingSimpleCPU::TimingSimpleCPU(TimingSimpleCPUParams *p)
10712953Sgabeblack@google.com    : BaseSimpleCPU(p), fetchTranslation(this), icachePort(this, p->clock),
10813180Sgabeblack@google.com    dcachePort(this, p->clock), fetchEvent(this)
10913180Sgabeblack@google.com{
11012953Sgabeblack@google.com    _status = Idle;
11112953Sgabeblack@google.com
11212953Sgabeblack@google.com    icachePort.snoopRangeSent = false;
11312953Sgabeblack@google.com    dcachePort.snoopRangeSent = false;
11412953Sgabeblack@google.com
11512953Sgabeblack@google.com    ifetch_pkt = dcache_pkt = NULL;
11612953Sgabeblack@google.com    drainEvent = NULL;
11712953Sgabeblack@google.com    previousTick = 0;
11812953Sgabeblack@google.com    changeState(SimObject::Running);
11912953Sgabeblack@google.com}
12012953Sgabeblack@google.com
12112953Sgabeblack@google.com
12212953Sgabeblack@google.comTimingSimpleCPU::~TimingSimpleCPU()
12312953Sgabeblack@google.com{
12412953Sgabeblack@google.com}
12512953Sgabeblack@google.com
12612953Sgabeblack@google.comvoid
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    DPRINTF(SimpleCPU, "Resume\n");
161    if (_status != SwitchedOut && _status != Idle) {
162        assert(system->getMemoryMode() == Enums::timing);
163
164        if (fetchEvent.scheduled())
165           deschedule(fetchEvent);
166
167        schedule(fetchEvent, nextCycle());
168    }
169
170    changeState(SimObject::Running);
171}
172
173void
174TimingSimpleCPU::switchOut()
175{
176    assert(_status == Running || _status == Idle);
177    _status = SwitchedOut;
178    numCycles += tickToCycles(curTick - previousTick);
179
180    // If we've been scheduled to resume but are then told to switch out,
181    // we'll need to cancel it.
182    if (fetchEvent.scheduled())
183        deschedule(fetchEvent);
184}
185
186
187void
188TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
189{
190    BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
191
192    // if any of this CPU's ThreadContexts are active, mark the CPU as
193    // running and schedule its tick event.
194    for (int i = 0; i < threadContexts.size(); ++i) {
195        ThreadContext *tc = threadContexts[i];
196        if (tc->status() == ThreadContext::Active && _status != Running) {
197            _status = Running;
198            break;
199        }
200    }
201
202    if (_status != Running) {
203        _status = Idle;
204    }
205    assert(threadContexts.size() == 1);
206    previousTick = curTick;
207}
208
209
210void
211TimingSimpleCPU::activateContext(int thread_num, int delay)
212{
213    DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);
214
215    assert(thread_num == 0);
216    assert(thread);
217
218    assert(_status == Idle);
219
220    notIdleFraction++;
221    _status = Running;
222
223    // kick things off by initiating the fetch of the next instruction
224    schedule(fetchEvent, nextCycle(curTick + ticks(delay)));
225}
226
227
228void
229TimingSimpleCPU::suspendContext(int thread_num)
230{
231    DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
232
233    assert(thread_num == 0);
234    assert(thread);
235
236    if (_status == Idle)
237        return;
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
248bool
249TimingSimpleCPU::handleReadPacket(PacketPtr pkt)
250{
251    RequestPtr req = pkt->req;
252    if (req->isMmapedIpr()) {
253        Tick delay;
254        delay = TheISA::handleIprRead(thread->getTC(), pkt);
255        new IprEvent(pkt, this, nextCycle(curTick + delay));
256        _status = DcacheWaitResponse;
257        dcache_pkt = NULL;
258    } else if (!dcachePort.sendTiming(pkt)) {
259        _status = DcacheRetry;
260        dcache_pkt = pkt;
261    } else {
262        _status = DcacheWaitResponse;
263        // memory system takes ownership of packet
264        dcache_pkt = NULL;
265    }
266    return dcache_pkt == NULL;
267}
268
269void
270TimingSimpleCPU::sendData(Fault fault, RequestPtr req,
271        uint8_t *data, uint64_t *res, bool read)
272{
273    _status = Running;
274    if (fault != NoFault) {
275        delete data;
276        delete req;
277
278        translationFault(fault);
279        return;
280    }
281    PacketPtr pkt;
282    buildPacket(pkt, req, read);
283    pkt->dataDynamic<uint8_t>(data);
284    if (req->getFlags().isSet(Request::NO_ACCESS)) {
285        assert(!dcache_pkt);
286        pkt->makeResponse();
287        completeDataAccess(pkt);
288    } else if (read) {
289        handleReadPacket(pkt);
290    } else {
291        bool do_access = true;  // flag to suppress cache access
292
293        if (req->isLocked()) {
294            do_access = TheISA::handleLockedWrite(thread, req);
295        } else if (req->isCondSwap()) {
296            assert(res);
297            req->setExtraData(*res);
298        }
299
300        if (do_access) {
301            dcache_pkt = pkt;
302            handleWritePacket();
303        } else {
304            _status = DcacheWaitResponse;
305            completeDataAccess(pkt);
306        }
307    }
308}
309
310void
311TimingSimpleCPU::sendSplitData(Fault fault1, Fault fault2,
312        RequestPtr req1, RequestPtr req2, RequestPtr req,
313        uint8_t *data, bool read)
314{
315    _status = Running;
316    if (fault1 != NoFault || fault2 != NoFault) {
317        delete data;
318        delete req1;
319        delete req2;
320        if (fault1 != NoFault)
321            translationFault(fault1);
322        else if (fault2 != NoFault)
323            translationFault(fault2);
324        return;
325    }
326    PacketPtr pkt1, pkt2;
327    buildSplitPacket(pkt1, pkt2, req1, req2, req, data, read);
328    if (req->getFlags().isSet(Request::NO_ACCESS)) {
329        assert(!dcache_pkt);
330        pkt1->makeResponse();
331        completeDataAccess(pkt1);
332    } else if (read) {
333        if (handleReadPacket(pkt1)) {
334            SplitFragmentSenderState * send_state =
335                dynamic_cast<SplitFragmentSenderState *>(pkt1->senderState);
336            send_state->clearFromParent();
337            if (handleReadPacket(pkt2)) {
338                send_state = dynamic_cast<SplitFragmentSenderState *>(
339                        pkt1->senderState);
340                send_state->clearFromParent();
341            }
342        }
343    } else {
344        dcache_pkt = pkt1;
345        if (handleWritePacket()) {
346            SplitFragmentSenderState * send_state =
347                dynamic_cast<SplitFragmentSenderState *>(pkt1->senderState);
348            send_state->clearFromParent();
349            dcache_pkt = pkt2;
350            if (handleWritePacket()) {
351                send_state = dynamic_cast<SplitFragmentSenderState *>(
352                        pkt1->senderState);
353                send_state->clearFromParent();
354            }
355        }
356    }
357}
358
359void
360TimingSimpleCPU::translationFault(Fault fault)
361{
362    numCycles += tickToCycles(curTick - previousTick);
363    previousTick = curTick;
364
365    if (traceData) {
366        // Since there was a fault, we shouldn't trace this instruction.
367        delete traceData;
368        traceData = NULL;
369    }
370
371    postExecute();
372
373    if (getState() == SimObject::Draining) {
374        advancePC(fault);
375        completeDrain();
376    } else {
377        advanceInst(fault);
378    }
379}
380
381void
382TimingSimpleCPU::buildPacket(PacketPtr &pkt, RequestPtr req, bool read)
383{
384    MemCmd cmd;
385    if (read) {
386        cmd = MemCmd::ReadReq;
387        if (req->isLocked())
388            cmd = MemCmd::LoadLockedReq;
389    } else {
390        cmd = MemCmd::WriteReq;
391        if (req->isLocked()) {
392            cmd = MemCmd::StoreCondReq;
393        } else if (req->isSwap()) {
394            cmd = MemCmd::SwapReq;
395        }
396    }
397    pkt = new Packet(req, cmd, Packet::Broadcast);
398}
399
400void
401TimingSimpleCPU::buildSplitPacket(PacketPtr &pkt1, PacketPtr &pkt2,
402        RequestPtr req1, RequestPtr req2, RequestPtr req,
403        uint8_t *data, bool read)
404{
405    pkt1 = pkt2 = NULL;
406
407    assert(!req1->isMmapedIpr() && !req2->isMmapedIpr());
408
409    if (req->getFlags().isSet(Request::NO_ACCESS)) {
410        buildPacket(pkt1, req, read);
411        return;
412    }
413
414    buildPacket(pkt1, req1, read);
415    buildPacket(pkt2, req2, read);
416
417    req->setPhys(req1->getPaddr(), req->getSize(), req1->getFlags());
418    PacketPtr pkt = new Packet(req, pkt1->cmd.responseCommand(),
419                               Packet::Broadcast);
420
421    pkt->dataDynamic<uint8_t>(data);
422    pkt1->dataStatic<uint8_t>(data);
423    pkt2->dataStatic<uint8_t>(data + req1->getSize());
424
425    SplitMainSenderState * main_send_state = new SplitMainSenderState;
426    pkt->senderState = main_send_state;
427    main_send_state->fragments[0] = pkt1;
428    main_send_state->fragments[1] = pkt2;
429    main_send_state->outstanding = 2;
430    pkt1->senderState = new SplitFragmentSenderState(pkt, 0);
431    pkt2->senderState = new SplitFragmentSenderState(pkt, 1);
432}
433
434template <class T>
435Fault
436TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
437{
438    Fault fault;
439    const int asid = 0;
440    const int thread_id = 0;
441    const Addr pc = thread->readPC();
442    int block_size = dcachePort.peerBlockSize();
443    int data_size = sizeof(T);
444
445    RequestPtr req  = new Request(asid, addr, data_size,
446                                  flags, pc, _cpuId, thread_id);
447
448    Addr split_addr = roundDown(addr + data_size - 1, block_size);
449    assert(split_addr <= addr || split_addr - addr < block_size);
450
451
452    _status = DTBWaitResponse;
453    if (split_addr > addr) {
454        RequestPtr req1, req2;
455        assert(!req->isLocked() && !req->isSwap());
456        req->splitOnVaddr(split_addr, req1, req2);
457
458        typedef SplitDataTranslation::WholeTranslationState WholeState;
459        WholeState *state = new WholeState(req1, req2, req,
460                                           (uint8_t *)(new T), BaseTLB::Read);
461        thread->dtb->translateTiming(req1, tc,
462                new SplitDataTranslation(this, 0, state), BaseTLB::Read);
463        thread->dtb->translateTiming(req2, tc,
464                new SplitDataTranslation(this, 1, state), BaseTLB::Read);
465    } else {
466        DataTranslation *translation =
467            new DataTranslation(this, (uint8_t *)(new T), NULL, BaseTLB::Read);
468        thread->dtb->translateTiming(req, tc, translation, BaseTLB::Read);
469    }
470
471    if (traceData) {
472        traceData->setData(data);
473        traceData->setAddr(addr);
474    }
475
476    // This will need a new way to tell if it has a dcache attached.
477    if (req->isUncacheable())
478        recordEvent("Uncached Read");
479
480    return NoFault;
481}
482
483#ifndef DOXYGEN_SHOULD_SKIP_THIS
484
485template
486Fault
487TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
488
489template
490Fault
491TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
492
493template
494Fault
495TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
496
497template
498Fault
499TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
500
501template
502Fault
503TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
504
505template
506Fault
507TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
508
509#endif //DOXYGEN_SHOULD_SKIP_THIS
510
511template<>
512Fault
513TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
514{
515    return read(addr, *(uint64_t*)&data, flags);
516}
517
518template<>
519Fault
520TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
521{
522    return read(addr, *(uint32_t*)&data, flags);
523}
524
525
526template<>
527Fault
528TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
529{
530    return read(addr, (uint32_t&)data, flags);
531}
532
533bool
534TimingSimpleCPU::handleWritePacket()
535{
536    RequestPtr req = dcache_pkt->req;
537    if (req->isMmapedIpr()) {
538        Tick delay;
539        delay = TheISA::handleIprWrite(thread->getTC(), dcache_pkt);
540        new IprEvent(dcache_pkt, this, nextCycle(curTick + delay));
541        _status = DcacheWaitResponse;
542        dcache_pkt = NULL;
543    } else if (!dcachePort.sendTiming(dcache_pkt)) {
544        _status = DcacheRetry;
545    } else {
546        _status = DcacheWaitResponse;
547        // memory system takes ownership of packet
548        dcache_pkt = NULL;
549    }
550    return dcache_pkt == NULL;
551}
552
553template <class T>
554Fault
555TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
556{
557    const int asid = 0;
558    const int thread_id = 0;
559    const Addr pc = thread->readPC();
560    int block_size = dcachePort.peerBlockSize();
561    int data_size = sizeof(T);
562
563    RequestPtr req = new Request(asid, addr, data_size,
564                                 flags, pc, _cpuId, thread_id);
565
566    Addr split_addr = roundDown(addr + data_size - 1, block_size);
567    assert(split_addr <= addr || split_addr - addr < block_size);
568
569    T *dataP = new T;
570    *dataP = TheISA::htog(data);
571    _status = DTBWaitResponse;
572    if (split_addr > addr) {
573        RequestPtr req1, req2;
574        assert(!req->isLocked() && !req->isSwap());
575        req->splitOnVaddr(split_addr, req1, req2);
576
577        typedef SplitDataTranslation::WholeTranslationState WholeState;
578        WholeState *state = new WholeState(req1, req2, req,
579                (uint8_t *)dataP, BaseTLB::Write);
580        thread->dtb->translateTiming(req1, tc,
581                new SplitDataTranslation(this, 0, state), BaseTLB::Write);
582        thread->dtb->translateTiming(req2, tc,
583                new SplitDataTranslation(this, 1, state), BaseTLB::Write);
584    } else {
585        DataTranslation *translation =
586            new DataTranslation(this, (uint8_t *)dataP, res, BaseTLB::Write);
587        thread->dtb->translateTiming(req, tc, translation, BaseTLB::Write);
588    }
589
590    if (traceData) {
591        traceData->setAddr(req->getVaddr());
592        traceData->setData(data);
593    }
594
595    // This will need a new way to tell if it's hooked up to a cache or not.
596    if (req->isUncacheable())
597        recordEvent("Uncached Write");
598
599    // If the write needs to have a fault on the access, consider calling
600    // changeStatus() and changing it to "bad addr write" or something.
601    return NoFault;
602}
603
604
605#ifndef DOXYGEN_SHOULD_SKIP_THIS
606template
607Fault
608TimingSimpleCPU::write(Twin32_t data, Addr addr,
609                       unsigned flags, uint64_t *res);
610
611template
612Fault
613TimingSimpleCPU::write(Twin64_t data, Addr addr,
614                       unsigned flags, uint64_t *res);
615
616template
617Fault
618TimingSimpleCPU::write(uint64_t data, Addr addr,
619                       unsigned flags, uint64_t *res);
620
621template
622Fault
623TimingSimpleCPU::write(uint32_t data, Addr addr,
624                       unsigned flags, uint64_t *res);
625
626template
627Fault
628TimingSimpleCPU::write(uint16_t data, Addr addr,
629                       unsigned flags, uint64_t *res);
630
631template
632Fault
633TimingSimpleCPU::write(uint8_t data, Addr addr,
634                       unsigned flags, uint64_t *res);
635
636#endif //DOXYGEN_SHOULD_SKIP_THIS
637
638template<>
639Fault
640TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
641{
642    return write(*(uint64_t*)&data, addr, flags, res);
643}
644
645template<>
646Fault
647TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
648{
649    return write(*(uint32_t*)&data, addr, flags, res);
650}
651
652
653template<>
654Fault
655TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
656{
657    return write((uint32_t)data, addr, flags, res);
658}
659
660
661void
662TimingSimpleCPU::fetch()
663{
664    DPRINTF(SimpleCPU, "Fetch\n");
665
666    if (!curStaticInst || !curStaticInst->isDelayedCommit())
667        checkForInterrupts();
668
669    checkPcEventQueue();
670
671    bool fromRom = isRomMicroPC(thread->readMicroPC());
672
673    if (!fromRom && !curMacroStaticInst) {
674        Request *ifetch_req = new Request();
675        ifetch_req->setThreadContext(_cpuId, /* thread ID */ 0);
676        setupFetchRequest(ifetch_req);
677        thread->itb->translateTiming(ifetch_req, tc, &fetchTranslation,
678                BaseTLB::Execute);
679    } else {
680        _status = IcacheWaitResponse;
681        completeIfetch(NULL);
682
683        numCycles += tickToCycles(curTick - previousTick);
684        previousTick = curTick;
685    }
686}
687
688
689void
690TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc)
691{
692    if (fault == NoFault) {
693        ifetch_pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
694        ifetch_pkt->dataStatic(&inst);
695
696        if (!icachePort.sendTiming(ifetch_pkt)) {
697            // Need to wait for retry
698            _status = IcacheRetry;
699        } else {
700            // Need to wait for cache to respond
701            _status = IcacheWaitResponse;
702            // ownership of packet transferred to memory system
703            ifetch_pkt = NULL;
704        }
705    } else {
706        delete req;
707        // fetch fault: advance directly to next instruction (fault handler)
708        advanceInst(fault);
709    }
710
711    numCycles += tickToCycles(curTick - previousTick);
712    previousTick = curTick;
713}
714
715
716void
717TimingSimpleCPU::advanceInst(Fault fault)
718{
719    if (fault != NoFault || !stayAtPC)
720        advancePC(fault);
721
722    if (_status == Running) {
723        // kick off fetch of next instruction... callback from icache
724        // response will cause that instruction to be executed,
725        // keeping the CPU running.
726        fetch();
727    }
728}
729
730
731void
732TimingSimpleCPU::completeIfetch(PacketPtr pkt)
733{
734    DPRINTF(SimpleCPU, "Complete ICache Fetch\n");
735
736    // received a response from the icache: execute the received
737    // instruction
738
739    assert(!pkt || !pkt->isError());
740    assert(_status == IcacheWaitResponse);
741
742    _status = Running;
743
744    numCycles += tickToCycles(curTick - previousTick);
745    previousTick = curTick;
746
747    if (getState() == SimObject::Draining) {
748        if (pkt) {
749            delete pkt->req;
750            delete pkt;
751        }
752
753        completeDrain();
754        return;
755    }
756
757    preExecute();
758    if (curStaticInst &&
759            curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
760        // load or store: just send to dcache
761        Fault fault = curStaticInst->initiateAcc(this, traceData);
762        if (_status != Running) {
763            // instruction will complete in dcache response callback
764            assert(_status == DcacheWaitResponse ||
765                    _status == DcacheRetry || DTBWaitResponse);
766            assert(fault == NoFault);
767        } else {
768            if (fault != NoFault && traceData) {
769                // If there was a fault, we shouldn't trace this instruction.
770                delete traceData;
771                traceData = NULL;
772            }
773
774            postExecute();
775            // @todo remove me after debugging with legion done
776            if (curStaticInst && (!curStaticInst->isMicroop() ||
777                        curStaticInst->isFirstMicroop()))
778                instCnt++;
779            advanceInst(fault);
780        }
781    } else if (curStaticInst) {
782        // non-memory instruction: execute completely now
783        Fault fault = curStaticInst->execute(this, traceData);
784
785        // keep an instruction count
786        if (fault == NoFault)
787            countInst();
788        else if (traceData) {
789            // If there was a fault, we shouldn't trace this instruction.
790            delete traceData;
791            traceData = NULL;
792        }
793
794        postExecute();
795        // @todo remove me after debugging with legion done
796        if (curStaticInst && (!curStaticInst->isMicroop() ||
797                    curStaticInst->isFirstMicroop()))
798            instCnt++;
799        advanceInst(fault);
800    } else {
801        advanceInst(NoFault);
802    }
803
804    if (pkt) {
805        delete pkt->req;
806        delete pkt;
807    }
808}
809
810void
811TimingSimpleCPU::IcachePort::ITickEvent::process()
812{
813    cpu->completeIfetch(pkt);
814}
815
816bool
817TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
818{
819    if (pkt->isResponse() && !pkt->wasNacked()) {
820        // delay processing of returned data until next CPU clock edge
821        Tick next_tick = cpu->nextCycle(curTick);
822
823        if (next_tick == curTick)
824            cpu->completeIfetch(pkt);
825        else
826            tickEvent.schedule(pkt, next_tick);
827
828        return true;
829    }
830    else if (pkt->wasNacked()) {
831        assert(cpu->_status == IcacheWaitResponse);
832        pkt->reinitNacked();
833        if (!sendTiming(pkt)) {
834            cpu->_status = IcacheRetry;
835            cpu->ifetch_pkt = pkt;
836        }
837    }
838    //Snooping a Coherence Request, do nothing
839    return true;
840}
841
842void
843TimingSimpleCPU::IcachePort::recvRetry()
844{
845    // we shouldn't get a retry unless we have a packet that we're
846    // waiting to transmit
847    assert(cpu->ifetch_pkt != NULL);
848    assert(cpu->_status == IcacheRetry);
849    PacketPtr tmp = cpu->ifetch_pkt;
850    if (sendTiming(tmp)) {
851        cpu->_status = IcacheWaitResponse;
852        cpu->ifetch_pkt = NULL;
853    }
854}
855
856void
857TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
858{
859    // received a response from the dcache: complete the load or store
860    // instruction
861    assert(!pkt->isError());
862
863    numCycles += tickToCycles(curTick - previousTick);
864    previousTick = curTick;
865
866    if (pkt->senderState) {
867        SplitFragmentSenderState * send_state =
868            dynamic_cast<SplitFragmentSenderState *>(pkt->senderState);
869        assert(send_state);
870        delete pkt->req;
871        delete pkt;
872        PacketPtr big_pkt = send_state->bigPkt;
873        delete send_state;
874
875        SplitMainSenderState * main_send_state =
876            dynamic_cast<SplitMainSenderState *>(big_pkt->senderState);
877        assert(main_send_state);
878        // Record the fact that this packet is no longer outstanding.
879        assert(main_send_state->outstanding != 0);
880        main_send_state->outstanding--;
881
882        if (main_send_state->outstanding) {
883            return;
884        } else {
885            delete main_send_state;
886            big_pkt->senderState = NULL;
887            pkt = big_pkt;
888        }
889    }
890
891    assert(_status == DcacheWaitResponse || _status == DTBWaitResponse);
892    _status = Running;
893
894    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
895
896    // keep an instruction count
897    if (fault == NoFault)
898        countInst();
899    else if (traceData) {
900        // If there was a fault, we shouldn't trace this instruction.
901        delete traceData;
902        traceData = NULL;
903    }
904
905    // the locked flag may be cleared on the response packet, so check
906    // pkt->req and not pkt to see if it was a load-locked
907    if (pkt->isRead() && pkt->req->isLocked()) {
908        TheISA::handleLockedRead(thread, pkt->req);
909    }
910
911    delete pkt->req;
912    delete pkt;
913
914    postExecute();
915
916    if (getState() == SimObject::Draining) {
917        advancePC(fault);
918        completeDrain();
919
920        return;
921    }
922
923    advanceInst(fault);
924}
925
926
927void
928TimingSimpleCPU::completeDrain()
929{
930    DPRINTF(Config, "Done draining\n");
931    changeState(SimObject::Drained);
932    drainEvent->process();
933}
934
935void
936TimingSimpleCPU::DcachePort::setPeer(Port *port)
937{
938    Port::setPeer(port);
939
940#if FULL_SYSTEM
941    // Update the ThreadContext's memory ports (Functional/Virtual
942    // Ports)
943    cpu->tcBase()->connectMemPorts(cpu->tcBase());
944#endif
945}
946
947bool
948TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
949{
950    if (pkt->isResponse() && !pkt->wasNacked()) {
951        // delay processing of returned data until next CPU clock edge
952        Tick next_tick = cpu->nextCycle(curTick);
953
954        if (next_tick == curTick) {
955            cpu->completeDataAccess(pkt);
956        } else {
957            tickEvent.schedule(pkt, next_tick);
958        }
959
960        return true;
961    }
962    else if (pkt->wasNacked()) {
963        assert(cpu->_status == DcacheWaitResponse);
964        pkt->reinitNacked();
965        if (!sendTiming(pkt)) {
966            cpu->_status = DcacheRetry;
967            cpu->dcache_pkt = pkt;
968        }
969    }
970    //Snooping a Coherence Request, do nothing
971    return true;
972}
973
974void
975TimingSimpleCPU::DcachePort::DTickEvent::process()
976{
977    cpu->completeDataAccess(pkt);
978}
979
980void
981TimingSimpleCPU::DcachePort::recvRetry()
982{
983    // we shouldn't get a retry unless we have a packet that we're
984    // waiting to transmit
985    assert(cpu->dcache_pkt != NULL);
986    assert(cpu->_status == DcacheRetry);
987    PacketPtr tmp = cpu->dcache_pkt;
988    if (tmp->senderState) {
989        // This is a packet from a split access.
990        SplitFragmentSenderState * send_state =
991            dynamic_cast<SplitFragmentSenderState *>(tmp->senderState);
992        assert(send_state);
993        PacketPtr big_pkt = send_state->bigPkt;
994
995        SplitMainSenderState * main_send_state =
996            dynamic_cast<SplitMainSenderState *>(big_pkt->senderState);
997        assert(main_send_state);
998
999        if (sendTiming(tmp)) {
1000            // If we were able to send without retrying, record that fact
1001            // and try sending the other fragment.
1002            send_state->clearFromParent();
1003            int other_index = main_send_state->getPendingFragment();
1004            if (other_index > 0) {
1005                tmp = main_send_state->fragments[other_index];
1006                cpu->dcache_pkt = tmp;
1007                if ((big_pkt->isRead() && cpu->handleReadPacket(tmp)) ||
1008                        (big_pkt->isWrite() && cpu->handleWritePacket())) {
1009                    main_send_state->fragments[other_index] = NULL;
1010                }
1011            } else {
1012                cpu->_status = DcacheWaitResponse;
1013                // memory system takes ownership of packet
1014                cpu->dcache_pkt = NULL;
1015            }
1016        }
1017    } else if (sendTiming(tmp)) {
1018        cpu->_status = DcacheWaitResponse;
1019        // memory system takes ownership of packet
1020        cpu->dcache_pkt = NULL;
1021    }
1022}
1023
1024TimingSimpleCPU::IprEvent::IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu,
1025    Tick t)
1026    : pkt(_pkt), cpu(_cpu)
1027{
1028    cpu->schedule(this, t);
1029}
1030
1031void
1032TimingSimpleCPU::IprEvent::process()
1033{
1034    cpu->completeDataAccess(pkt);
1035}
1036
1037const char *
1038TimingSimpleCPU::IprEvent::description() const
1039{
1040    return "Timing Simple CPU Delay IPR event";
1041}
1042
1043
1044void
1045TimingSimpleCPU::printAddr(Addr a)
1046{
1047    dcachePort.printAddr(a);
1048}
1049
1050
1051////////////////////////////////////////////////////////////////////////
1052//
1053//  TimingSimpleCPU Simulation Object
1054//
1055TimingSimpleCPU *
1056TimingSimpleCPUParams::create()
1057{
1058    numThreads = 1;
1059#if !FULL_SYSTEM
1060    if (workload.size() != 1)
1061        panic("only one workload allowed");
1062#endif
1063    return new TimingSimpleCPU(this);
1064}
1065