RubyPort.cc revision 7035:b78b3a9e205f
1
2/*
3 * Copyright (c) 2009 Advanced Micro Devices, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met: redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer;
10 * redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution;
13 * neither the name of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "mem/physical.hh"
31#include "mem/ruby/system/RubyPort.hh"
32#include "mem/ruby/slicc_interface/AbstractController.hh"
33#include "cpu/rubytest/RubyTester.hh"
34
35RubyPort::RubyPort(const Params *p)
36    : MemObject(p)
37{
38    m_version = p->version;
39    assert(m_version != -1);
40
41    physmem = p->physmem;
42
43    m_controller = NULL;
44    m_mandatory_q_ptr = NULL;
45
46    m_request_cnt = 0;
47    pio_port = NULL;
48    physMemPort = NULL;
49}
50
51void RubyPort::init()
52{
53    assert(m_controller != NULL);
54    m_mandatory_q_ptr = m_controller->getMandatoryQueue();
55}
56
57Port *
58RubyPort::getPort(const std::string &if_name, int idx)
59{
60    if (if_name == "port") {
61        return new M5Port(csprintf("%s-port%d", name(), idx), this);
62    } else if (if_name == "pio_port") {
63        //
64        // ensure there is only one pio port
65        //
66        assert(pio_port == NULL);
67
68        pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx),
69                                     this);
70
71        return pio_port;
72    } else if (if_name == "physMemPort") {
73        //
74        // RubyPort should only have one port to physical memory
75        //
76        assert (physMemPort == NULL);
77
78        physMemPort = new M5Port(csprintf("%s-physMemPort", name()),
79                                 this);
80
81        return physMemPort;
82    } else if (if_name == "functional") {
83        //
84        // Calls for the functional port only want to access functional memory.
85        // Therefore, directly pass these calls ports to physmem.
86        //
87        assert(physmem != NULL);
88        return physmem->getPort(if_name, idx);
89    }
90    return NULL;
91}
92
93RubyPort::PioPort::PioPort(const std::string &_name,
94                           RubyPort *_port)
95    : SimpleTimingPort(_name, _port)
96{
97    DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name);
98    ruby_port = _port;
99}
100
101RubyPort::M5Port::M5Port(const std::string &_name,
102                         RubyPort *_port)
103    : SimpleTimingPort(_name, _port)
104{
105    DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name);
106    ruby_port = _port;
107}
108
109Tick
110RubyPort::PioPort::recvAtomic(PacketPtr pkt)
111{
112    panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
113    return 0;
114}
115
116
117Tick
118RubyPort::M5Port::recvAtomic(PacketPtr pkt)
119{
120    panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
121    return 0;
122}
123
124
125bool
126RubyPort::PioPort::recvTiming(PacketPtr pkt)
127{
128    //
129    // In FS mode, ruby memory will receive pio responses from devices and
130    // it must forward these responses back to the particular CPU.
131    //
132    DPRINTF(MemoryAccess,
133            "Pio response for address %#x\n",
134            pkt->getAddr());
135
136    assert(pkt->isResponse());
137
138    //
139    // First we must retrieve the request port from the sender State
140    //
141    RubyPort::SenderState *senderState =
142      safe_cast<RubyPort::SenderState *>(pkt->senderState);
143    M5Port *port = senderState->port;
144    assert(port != NULL);
145
146    // pop the sender state from the packet
147    pkt->senderState = senderState->saved;
148    delete senderState;
149
150    port->sendTiming(pkt);
151
152    return true;
153}
154
155bool
156RubyPort::M5Port::recvTiming(PacketPtr pkt)
157{
158    DPRINTF(MemoryAccess,
159            "Timing access caught for address %#x\n",
160            pkt->getAddr());
161
162    //dsm: based on SimpleTimingPort::recvTiming(pkt);
163
164    //
165    // The received packets should only be M5 requests, which should never
166    // get nacked.  There used to be code to hanldle nacks here, but
167    // I'm pretty sure it didn't work correctly with the drain code,
168    // so that would need to be fixed if we ever added it back.
169    //
170    assert(pkt->isRequest());
171
172    if (pkt->memInhibitAsserted()) {
173        warn("memInhibitAsserted???");
174        // snooper will supply based on copy of packet
175        // still target's responsibility to delete packet
176        delete pkt;
177        return true;
178    }
179
180    //
181    // Save the port in the sender state object to be used later to
182    // route the response
183    //
184    pkt->senderState = new SenderState(this, pkt->senderState);
185
186    //
187    // Check for pio requests and directly send them to the dedicated
188    // pio port.
189    //
190    if (!isPhysMemAddress(pkt->getAddr())) {
191        assert(ruby_port->pio_port != NULL);
192        DPRINTF(MemoryAccess,
193                "Request for address 0x%#x is assumed to be a pio request\n",
194                pkt->getAddr());
195
196        return ruby_port->pio_port->sendTiming(pkt);
197    }
198
199    //
200    // For DMA and CPU requests, translate them to ruby requests before
201    // sending them to our assigned ruby port.
202    //
203    RubyRequestType type = RubyRequestType_NULL;
204
205    //
206    // If valid, copy the pc to the ruby request
207    //
208    Addr pc = 0;
209    if (pkt->req->hasPC()) {
210        pc = pkt->req->getPC();
211    }
212
213    if (pkt->isLLSC()) {
214        if (pkt->isWrite()) {
215            DPRINTF(MemoryAccess, "Issuing SC\n");
216            type = RubyRequestType_Locked_Write;
217        } else {
218            DPRINTF(MemoryAccess, "Issuing LL\n");
219            assert(pkt->isRead());
220            type = RubyRequestType_Locked_Read;
221        }
222    } else {
223        if (pkt->isRead()) {
224            if (pkt->req->isInstFetch()) {
225                type = RubyRequestType_IFETCH;
226            } else {
227                type = RubyRequestType_LD;
228            }
229        } else if (pkt->isWrite()) {
230            type = RubyRequestType_ST;
231        } else if (pkt->isReadWrite()) {
232            //
233            // Fix me.  This conditional will never be executed because
234            // isReadWrite() is just an OR of isRead() and isWrite().
235            // Furthermore, just because the packet is a read/write request does
236            // not necessary mean it is a read-modify-write atomic operation.
237            //
238            type = RubyRequestType_RMW_Write;
239        } else {
240            panic("Unsupported ruby packet type\n");
241        }
242    }
243
244    RubyRequest ruby_request(pkt->getAddr(),
245                             pkt->getPtr<uint8_t>(),
246                             pkt->getSize(),
247                             pc,
248                             type,
249                             RubyAccessMode_Supervisor,
250                             pkt);
251
252    // Submit the ruby request
253    RequestStatus requestStatus = ruby_port->makeRequest(ruby_request);
254
255    //
256    // If the request successfully issued or the SC request completed because
257    // exclusive permission was lost, then we should return true.
258    // Otherwise, we need to delete the senderStatus we just created and return
259    // false.
260    //
261    if ((requestStatus == RequestStatus_Issued) ||
262        (requestStatus == RequestStatus_LlscFailed)) {
263
264        //
265        // The communicate to M5 whether the SC command succeeded by seting the
266        // packet's extra data.
267        //
268        if (pkt->isLLSC() && pkt->isWrite()) {
269            if (requestStatus == RequestStatus_LlscFailed) {
270                DPRINTF(MemoryAccess, "SC failed and request completed\n");
271                pkt->req->setExtraData(0);
272            } else {
273                pkt->req->setExtraData(1);
274            }
275        }
276        return true;
277    }
278
279    DPRINTF(MemoryAccess,
280            "Request for address #x did not issue because %s\n",
281            pkt->getAddr(),
282            RequestStatus_to_string(requestStatus));
283
284    SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
285    pkt->senderState = senderState->saved;
286    delete senderState;
287    return false;
288}
289
290void
291RubyPort::ruby_hit_callback(PacketPtr pkt)
292{
293    //
294    // Retrieve the request port from the sender State
295    //
296    RubyPort::SenderState *senderState =
297        safe_cast<RubyPort::SenderState *>(pkt->senderState);
298    M5Port *port = senderState->port;
299    assert(port != NULL);
300
301    // pop the sender state from the packet
302    pkt->senderState = senderState->saved;
303    delete senderState;
304
305    port->hitCallback(pkt);
306}
307
308void
309RubyPort::M5Port::hitCallback(PacketPtr pkt)
310{
311
312    bool needsResponse = pkt->needsResponse();
313
314    DPRINTF(MemoryAccess, "Hit callback needs response %d\n",
315            needsResponse);
316
317    ruby_port->physMemPort->sendAtomic(pkt);
318
319    // turn packet around to go back to requester if response expected
320    if (needsResponse) {
321        // sendAtomic() should already have turned packet into
322        // atomic response
323        assert(pkt->isResponse());
324        DPRINTF(MemoryAccess, "Sending packet back over port\n");
325        sendTiming(pkt);
326    } else {
327        delete pkt;
328    }
329    DPRINTF(MemoryAccess, "Hit callback done!\n");
330}
331
332bool
333RubyPort::M5Port::sendTiming(PacketPtr pkt)
334{
335    schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
336    return true;
337}
338
339bool
340RubyPort::PioPort::sendTiming(PacketPtr pkt)
341{
342    schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
343    return true;
344}
345
346bool
347RubyPort::M5Port::isPhysMemAddress(Addr addr)
348{
349    AddrRangeList physMemAddrList;
350    bool snoop = false;
351    ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
352    for(AddrRangeIter iter = physMemAddrList.begin();
353        iter != physMemAddrList.end();
354        iter++) {
355        if (addr >= iter->start && addr <= iter->end) {
356            DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n",
357                    iter->start, iter->end);
358            return true;
359        }
360    }
361    return false;
362}
363