RubyPort.cc revision 8436
1/*
2 * Copyright (c) 2009 Advanced Micro Devices, Inc.
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
29#include "config/the_isa.hh"
30#if THE_ISA == X86_ISA
31#include "arch/x86/insts/microldstop.hh"
32#endif // X86_ISA
33#include "cpu/testers/rubytest/RubyTester.hh"
34#include "debug/Ruby.hh"
35#include "mem/protocol/AccessPermission.hh"
36#include "mem/ruby/slicc_interface/AbstractController.hh"
37#include "mem/ruby/system/RubyPort.hh"
38#include "mem/physical.hh"
39
40RubyPort::RubyPort(const Params *p)
41    : MemObject(p)
42{
43    m_version = p->version;
44    assert(m_version != -1);
45
46    physmem = p->physmem;
47
48    m_controller = NULL;
49    m_mandatory_q_ptr = NULL;
50
51    m_request_cnt = 0;
52    pio_port = NULL;
53    physMemPort = NULL;
54
55    m_usingRubyTester = p->using_ruby_tester;
56    access_phys_mem = p->access_phys_mem;
57
58    ruby_system = p->ruby_system;
59}
60
61void
62RubyPort::init()
63{
64    assert(m_controller != NULL);
65    m_mandatory_q_ptr = m_controller->getMandatoryQueue();
66}
67
68Port *
69RubyPort::getPort(const std::string &if_name, int idx)
70{
71    if (if_name == "port") {
72        return new M5Port(csprintf("%s-port%d", name(), idx), this,
73                          ruby_system, access_phys_mem);
74    }
75
76    if (if_name == "pio_port") {
77        // ensure there is only one pio port
78        assert(pio_port == NULL);
79
80        pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this);
81
82        return pio_port;
83    }
84
85    if (if_name == "physMemPort") {
86        // RubyPort should only have one port to physical memory
87        assert (physMemPort == NULL);
88
89        physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this,
90                                 ruby_system, access_phys_mem);
91
92        return physMemPort;
93    }
94
95    if (if_name == "functional") {
96        // Calls for the functional port only want to access
97        // functional memory.  Therefore, directly pass these calls
98        // ports to physmem.
99        assert(physmem != NULL);
100        return physmem->getPort(if_name, idx);
101    }
102
103    return NULL;
104}
105
106RubyPort::PioPort::PioPort(const std::string &_name,
107                           RubyPort *_port)
108    : SimpleTimingPort(_name, _port)
109{
110    DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name);
111    ruby_port = _port;
112}
113
114RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port,
115                         RubySystem *_system, bool _access_phys_mem)
116    : SimpleTimingPort(_name, _port)
117{
118    DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name);
119    ruby_port = _port;
120    ruby_system = _system;
121    _onRetryList = false;
122    access_phys_mem = _access_phys_mem;
123}
124
125Tick
126RubyPort::PioPort::recvAtomic(PacketPtr pkt)
127{
128    panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
129    return 0;
130}
131
132Tick
133RubyPort::M5Port::recvAtomic(PacketPtr pkt)
134{
135    panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
136    return 0;
137}
138
139
140bool
141RubyPort::PioPort::recvTiming(PacketPtr pkt)
142{
143    // In FS mode, ruby memory will receive pio responses from devices
144    // and it must forward these responses back to the particular CPU.
145    DPRINTF(RubyPort,  "Pio response for address %#x\n", pkt->getAddr());
146
147    assert(pkt->isResponse());
148
149    // First we must retrieve the request port from the sender State
150    RubyPort::SenderState *senderState =
151      safe_cast<RubyPort::SenderState *>(pkt->senderState);
152    M5Port *port = senderState->port;
153    assert(port != NULL);
154
155    // pop the sender state from the packet
156    pkt->senderState = senderState->saved;
157    delete senderState;
158
159    port->sendTiming(pkt);
160
161    return true;
162}
163
164bool
165RubyPort::M5Port::recvTiming(PacketPtr pkt)
166{
167    DPRINTF(RubyPort,
168            "Timing access caught for address %#x\n", pkt->getAddr());
169
170    //dsm: based on SimpleTimingPort::recvTiming(pkt);
171
172    // The received packets should only be M5 requests, which should never
173    // get nacked.  There used to be code to hanldle nacks here, but
174    // I'm pretty sure it didn't work correctly with the drain code,
175    // so that would need to be fixed if we ever added it back.
176    assert(pkt->isRequest());
177
178    if (pkt->memInhibitAsserted()) {
179        warn("memInhibitAsserted???");
180        // snooper will supply based on copy of packet
181        // still target's responsibility to delete packet
182        delete pkt;
183        return true;
184    }
185
186    // Save the port in the sender state object to be used later to
187    // route the response
188    pkt->senderState = new SenderState(this, pkt->senderState);
189
190    // Check for pio requests and directly send them to the dedicated
191    // pio port.
192    if (!isPhysMemAddress(pkt->getAddr())) {
193        assert(ruby_port->pio_port != NULL);
194        DPRINTF(RubyPort,
195                "Request for address 0x%#x is assumed to be a pio request\n",
196                pkt->getAddr());
197
198        return ruby_port->pio_port->sendTiming(pkt);
199    }
200
201    // For DMA and CPU requests, translate them to ruby requests before
202    // sending them to our assigned ruby port.
203    RubyRequestType type = RubyRequestType_NULL;
204
205    // If valid, copy the pc to the ruby request
206    Addr pc = 0;
207    if (pkt->req->hasPC()) {
208        pc = pkt->req->getPC();
209    }
210
211    if (pkt->isLLSC()) {
212        if (pkt->isWrite()) {
213            DPRINTF(RubyPort, "Issuing SC\n");
214            type = RubyRequestType_Store_Conditional;
215        } else {
216            DPRINTF(RubyPort, "Issuing LL\n");
217            assert(pkt->isRead());
218            type = RubyRequestType_Load_Linked;
219        }
220    } else if (pkt->req->isLocked()) {
221        if (pkt->isWrite()) {
222            DPRINTF(RubyPort, "Issuing Locked RMW Write\n");
223            type = RubyRequestType_Locked_RMW_Write;
224        } else {
225            DPRINTF(RubyPort, "Issuing Locked RMW Read\n");
226            assert(pkt->isRead());
227            type = RubyRequestType_Locked_RMW_Read;
228        }
229    } else {
230        if (pkt->isRead()) {
231            if (pkt->req->isInstFetch()) {
232                type = RubyRequestType_IFETCH;
233            } else {
234#if THE_ISA == X86_ISA
235                uint32_t flags = pkt->req->getFlags();
236                bool storeCheck = flags &
237                        (TheISA::StoreCheck << TheISA::FlagShift);
238#else
239                bool storeCheck = false;
240#endif // X86_ISA
241                if (storeCheck) {
242                    type = RubyRequestType_RMW_Read;
243                } else {
244                    type = RubyRequestType_LD;
245                }
246            }
247        } else if (pkt->isWrite()) {
248            //
249            // Note: M5 packets do not differentiate ST from RMW_Write
250            //
251            type = RubyRequestType_ST;
252        } else if (pkt->isFlush()) {
253            type = RubyRequestType_FLUSH;
254        } else {
255            panic("Unsupported ruby packet type\n");
256        }
257    }
258
259    RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(true),
260                             pkt->getSize(), pc, type,
261                             RubyAccessMode_Supervisor, pkt);
262
263    assert(ruby_request.m_PhysicalAddress.getOffset() + ruby_request.m_Size <=
264        RubySystem::getBlockSizeBytes());
265
266    // Submit the ruby request
267    RequestStatus requestStatus = ruby_port->makeRequest(ruby_request);
268
269    // If the request successfully issued then we should return true.
270    // Otherwise, we need to delete the senderStatus we just created and return
271    // false.
272    if (requestStatus == RequestStatus_Issued) {
273        DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr());
274        return true;
275    }
276
277    //
278    // Unless one is using the ruby tester, record the stalled M5 port for
279    // later retry when the sequencer becomes free.
280    //
281    if (!ruby_port->m_usingRubyTester) {
282        ruby_port->addToRetryList(this);
283    }
284
285    DPRINTF(RubyPort,
286            "Request for address %#x did not issue because %s\n",
287            pkt->getAddr(), RequestStatus_to_string(requestStatus));
288
289    SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
290    pkt->senderState = senderState->saved;
291    delete senderState;
292    return false;
293}
294
295bool
296RubyPort::M5Port::doFunctionalRead(PacketPtr pkt)
297{
298    Address address(pkt->getAddr());
299    Address line_address(address);
300    line_address.makeLineAddress();
301
302    AccessPermission accessPerm = AccessPermission_NotPresent;
303    int num_controllers = ruby_system->m_abs_cntrl_vec.size();
304
305    // In this loop, we try to figure which controller has a read only or
306    // a read write copy of the given address. Any valid copy would suffice
307    // for a functional read.
308
309    DPRINTF(RubyPort, "Functional Read request for %s\n",address);
310    for(int i = 0;i < num_controllers;++i)
311    {
312        accessPerm = ruby_system->m_abs_cntrl_vec[i]
313                                          ->getAccessPermission(line_address);
314        if(accessPerm == AccessPermission_Read_Only ||
315           accessPerm == AccessPermission_Read_Write)
316        {
317            unsigned startByte = address.getAddress() - line_address.getAddress();
318
319            uint8* data = pkt->getPtr<uint8_t>(true);
320            unsigned int size_in_bytes = pkt->getSize();
321            DataBlock& block = ruby_system->m_abs_cntrl_vec[i]
322                                                 ->getDataBlock(line_address);
323
324            DPRINTF(RubyPort, "reading from %s block %s\n",
325                    ruby_system->m_abs_cntrl_vec[i]->name(), block);
326            for (unsigned i = 0; i < size_in_bytes; ++i)
327            {
328                data[i] = block.getByte(i + startByte);
329            }
330            return true;
331        }
332    }
333    return false;
334}
335
336bool
337RubyPort::M5Port::doFunctionalWrite(PacketPtr pkt)
338{
339    Address addr(pkt->getAddr());
340    Address line_addr = line_address(addr);
341    AccessPermission accessPerm = AccessPermission_NotPresent;
342    int num_controllers = ruby_system->m_abs_cntrl_vec.size();
343
344    DPRINTF(RubyPort, "Functional Write request for %s\n",addr);
345
346    unsigned int num_ro = 0;
347    unsigned int num_rw = 0;
348    unsigned int num_busy = 0;
349
350    // In this loop we count the number of controllers that have the given
351    // address in read only, read write and busy states.
352    for(int i = 0;i < num_controllers;++i)
353    {
354        accessPerm = ruby_system->m_abs_cntrl_vec[i]->
355                                            getAccessPermission(line_addr);
356        if(accessPerm == AccessPermission_Read_Only) num_ro++;
357        else if(accessPerm == AccessPermission_Read_Write) num_rw++;
358        else if(accessPerm == AccessPermission_Busy) num_busy++;
359    }
360
361    // If the number of read write copies is more than 1, then there is bug in
362    // coherence protocol. Otherwise, if all copies are in stable states, i.e.
363    // num_busy == 0, we update all the copies. If there is at least one copy
364    // in busy state, then we check if there is read write copy. If yes, then
365    // also we let the access go through.
366
367    DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n",
368            num_busy, num_ro, num_rw);
369    assert(num_rw <= 1);
370    if((num_busy == 0 && num_ro > 0) || num_rw == 1)
371    {
372        uint8* data = pkt->getPtr<uint8_t>(true);
373        unsigned int size_in_bytes = pkt->getSize();
374        unsigned startByte = addr.getAddress() - line_addr.getAddress();
375
376        for(int i = 0; i < num_controllers;++i)
377        {
378            accessPerm = ruby_system->m_abs_cntrl_vec[i]->
379                                                getAccessPermission(line_addr);
380            if(accessPerm == AccessPermission_Read_Only ||
381               accessPerm == AccessPermission_Read_Write||
382               accessPerm == AccessPermission_Maybe_Stale)
383            {
384                DataBlock& block = ruby_system->m_abs_cntrl_vec[i]
385                                                      ->getDataBlock(line_addr);
386
387                DPRINTF(RubyPort, "%s\n",block);
388                for (unsigned i = 0; i < size_in_bytes; ++i)
389                {
390                  block.setByte(i + startByte, data[i]);
391                }
392                DPRINTF(RubyPort, "%s\n",block);
393            }
394        }
395        return true;
396    }
397    return false;
398}
399
400void
401RubyPort::M5Port::recvFunctional(PacketPtr pkt)
402{
403    DPRINTF(RubyPort, "Functional access caught for address %#x\n",
404                                                           pkt->getAddr());
405
406    // Check for pio requests and directly send them to the dedicated
407    // pio port.
408    if (!isPhysMemAddress(pkt->getAddr())) {
409        assert(ruby_port->pio_port != NULL);
410        DPRINTF(RubyPort, "Request for address 0x%#x is a pio request\n",
411                                                           pkt->getAddr());
412        panic("RubyPort::PioPort::recvFunctional() not implemented!\n");
413    }
414
415    assert(pkt->getAddr() + pkt->getSize() <=
416                line_address(Address(pkt->getAddr())).getAddress() +
417                RubySystem::getBlockSizeBytes());
418
419    bool accessSucceeded = false;
420    bool needsResponse = pkt->needsResponse();
421
422    // Do the functional access on ruby memory
423    if (pkt->isRead()) {
424        accessSucceeded = doFunctionalRead(pkt);
425    } else if (pkt->isWrite()) {
426        accessSucceeded = doFunctionalWrite(pkt);
427    } else {
428        panic("RubyPort: unsupported functional command %s\n",
429              pkt->cmdString());
430    }
431
432    // Unless the requester explicitly said otherwise, generate an error if
433    // the functional request failed
434    if (!accessSucceeded && !pkt->suppressFuncError()) {
435        fatal("Ruby functional %s failed for address %#x\n",
436              pkt->isWrite() ? "write" : "read", pkt->getAddr());
437    }
438
439    if (access_phys_mem) {
440        // The attached physmem contains the official version of data.
441        // The following command performs the real functional access.
442        // This line should be removed once Ruby supplies the official version
443        // of data.
444        ruby_port->physMemPort->sendFunctional(pkt);
445    }
446
447    // turn packet around to go back to requester if response expected
448    if (needsResponse) {
449        pkt->setFunctionalResponseStatus(accessSucceeded);
450        DPRINTF(RubyPort, "Sending packet back over port\n");
451        sendFunctional(pkt);
452    }
453    DPRINTF(RubyPort, "Functional access %s!\n",
454            accessSucceeded ? "successful":"failed");
455}
456
457void
458RubyPort::ruby_hit_callback(PacketPtr pkt)
459{
460    // Retrieve the request port from the sender State
461    RubyPort::SenderState *senderState =
462        safe_cast<RubyPort::SenderState *>(pkt->senderState);
463    M5Port *port = senderState->port;
464    assert(port != NULL);
465
466    // pop the sender state from the packet
467    pkt->senderState = senderState->saved;
468    delete senderState;
469
470    port->hitCallback(pkt);
471
472    //
473    // If we had to stall the M5Ports, wake them up because the sequencer
474    // likely has free resources now.
475    //
476    if (waitingOnSequencer) {
477        //
478        // Record the current list of ports to retry on a temporary list before
479        // calling sendRetry on those ports.  sendRetry will cause an
480        // immediate retry, which may result in the ports being put back on the
481        // list. Therefore we want to clear the retryList before calling
482        // sendRetry.
483        //
484        std::list<M5Port*> curRetryList(retryList);
485
486        retryList.clear();
487        waitingOnSequencer = false;
488
489        for (std::list<M5Port*>::iterator i = curRetryList.begin();
490             i != curRetryList.end(); ++i) {
491            DPRINTF(RubyPort,
492                    "Sequencer may now be free.  SendRetry to port %s\n",
493                    (*i)->name());
494            (*i)->onRetryList(false);
495            (*i)->sendRetry();
496        }
497    }
498}
499
500void
501RubyPort::M5Port::hitCallback(PacketPtr pkt)
502{
503    bool needsResponse = pkt->needsResponse();
504
505    //
506    // Unless specified at configuraiton, all responses except failed SC
507    // and Flush operations access M5 physical memory.
508    //
509    bool accessPhysMem = access_phys_mem;
510
511    if (pkt->isLLSC()) {
512        if (pkt->isWrite()) {
513            if (pkt->req->getExtraData() != 0) {
514                //
515                // Successful SC packets convert to normal writes
516                //
517                pkt->convertScToWrite();
518            } else {
519                //
520                // Failed SC packets don't access physical memory and thus
521                // the RubyPort itself must convert it to a response.
522                //
523                accessPhysMem = false;
524            }
525        } else {
526            //
527            // All LL packets convert to normal loads so that M5 PhysMem does
528            // not lock the blocks.
529            //
530            pkt->convertLlToRead();
531        }
532    }
533
534    //
535    // Flush requests don't access physical memory
536    //
537    if (pkt->isFlush()) {
538        accessPhysMem = false;
539    }
540
541    DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse);
542
543    if (accessPhysMem) {
544        ruby_port->physMemPort->sendAtomic(pkt);
545    } else if (needsResponse) {
546        pkt->makeResponse();
547    }
548
549    // turn packet around to go back to requester if response expected
550    if (needsResponse) {
551        DPRINTF(RubyPort, "Sending packet back over port\n");
552        sendTiming(pkt);
553    } else {
554        delete pkt;
555    }
556    DPRINTF(RubyPort, "Hit callback done!\n");
557}
558
559bool
560RubyPort::M5Port::sendTiming(PacketPtr pkt)
561{
562    //minimum latency, must be > 0
563    schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
564    return true;
565}
566
567bool
568RubyPort::PioPort::sendTiming(PacketPtr pkt)
569{
570    //minimum latency, must be > 0
571    schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
572    return true;
573}
574
575bool
576RubyPort::M5Port::isPhysMemAddress(Addr addr)
577{
578    AddrRangeList physMemAddrList;
579    bool snoop = false;
580    ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
581    for (AddrRangeIter iter = physMemAddrList.begin();
582         iter != physMemAddrList.end();
583         iter++) {
584        if (addr >= iter->start && addr <= iter->end) {
585            DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n",
586                    iter->start, iter->end);
587            return true;
588        }
589    }
590    return false;
591}
592
593unsigned
594RubyPort::M5Port::deviceBlockSize() const
595{
596    return (unsigned) RubySystem::getBlockSizeBytes();
597}
598