RubyPort.cc revision 6882:898047a3672c
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
34uint16_t RubyPort::m_num_ports = 0;
35
36RubyPort::RequestMap RubyPort::pending_cpu_requests;
37
38RubyPort::RubyPort(const Params *p)
39    : MemObject(p),
40      funcMemPort(csprintf("%s-funcmem_port", name()), this)
41{
42    m_version = p->version;
43    assert(m_version != -1);
44
45    m_controller = NULL;
46    m_mandatory_q_ptr = NULL;
47
48    m_port_id = m_num_ports++;
49    m_request_cnt = 0;
50    m_hit_callback = ruby_hit_callback;
51    pio_port = NULL;
52    assert(m_num_ports <= 2048); // see below for reason
53}
54
55void RubyPort::init()
56{
57    assert(m_controller != NULL);
58    m_mandatory_q_ptr = m_controller->getMandatoryQueue();
59}
60
61Port *
62RubyPort::getPort(const std::string &if_name, int idx)
63{
64    if (if_name == "port") {
65        return new M5Port(csprintf("%s-port%d", name(), idx), this);
66    } else if (if_name == "pio_port") {
67        //
68        // ensure there is only one pio port
69        //
70        assert(pio_port == NULL);
71
72        pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx),
73                                     this);
74
75        return pio_port;
76    } else if (if_name == "funcmem_port") {
77        return &funcMemPort;
78    }
79    return NULL;
80}
81
82RubyPort::PioPort::PioPort(const std::string &_name,
83                           RubyPort *_port)
84    : SimpleTimingPort(_name, _port)
85{
86    DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name);
87    ruby_port = _port;
88}
89
90RubyPort::M5Port::M5Port(const std::string &_name,
91                         RubyPort *_port)
92    : SimpleTimingPort(_name, _port)
93{
94    DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name);
95    ruby_port = _port;
96}
97
98Tick
99RubyPort::PioPort::recvAtomic(PacketPtr pkt)
100{
101    panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
102    return 0;
103}
104
105
106Tick
107RubyPort::M5Port::recvAtomic(PacketPtr pkt)
108{
109    panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
110    return 0;
111}
112
113
114bool
115RubyPort::PioPort::recvTiming(PacketPtr pkt)
116{
117    //
118    // In FS mode, ruby memory will receive pio responses from devices and
119    // it must forward these responses back to the particular CPU.
120    //
121    DPRINTF(MemoryAccess,
122            "Pio response for address %#x\n",
123            pkt->getAddr());
124
125    assert(pkt->isResponse());
126
127    //
128    // First we must retrieve the request port from the sender State
129    //
130    RubyPort::SenderState *senderState =
131      safe_cast<RubyPort::SenderState *>(pkt->senderState);
132    M5Port *port = senderState->port;
133    assert(port != NULL);
134
135    // pop the sender state from the packet
136    pkt->senderState = senderState->saved;
137    delete senderState;
138
139    port->sendTiming(pkt);
140
141    return true;
142}
143
144bool
145RubyPort::M5Port::recvTiming(PacketPtr pkt)
146{
147    DPRINTF(MemoryAccess,
148            "Timing access caught for address %#x\n",
149            pkt->getAddr());
150
151    //dsm: based on SimpleTimingPort::recvTiming(pkt);
152
153    //
154    // After checking for pio responses, the remainder of packets
155    // received by ruby should only be M5 requests, which should never
156    // get nacked.  There used to be code to hanldle nacks here, but
157    // I'm pretty sure it didn't work correctly with the drain code,
158    // so that would need to be fixed if we ever added it back.
159    //
160    assert(pkt->isRequest());
161
162    if (pkt->memInhibitAsserted()) {
163        warn("memInhibitAsserted???");
164        // snooper will supply based on copy of packet
165        // still target's responsibility to delete packet
166        delete pkt;
167        return true;
168    }
169
170    //
171    // Check for pio requests and directly send them to the dedicated
172    // pio port.
173    //
174    if (!isPhysMemAddress(pkt->getAddr())) {
175        assert(ruby_port->pio_port != NULL);
176
177        //
178        // Save the port in the sender state object to be used later to
179        // route the response
180        //
181        pkt->senderState = new SenderState(this, pkt->senderState);
182
183        return ruby_port->pio_port->sendTiming(pkt);
184    }
185
186    //
187    // For DMA and CPU requests, translate them to ruby requests before
188    // sending them to our assigned ruby port.
189    //
190    RubyRequestType type = RubyRequestType_NULL;
191    Addr pc = 0;
192    if (pkt->isRead()) {
193        if (pkt->req->isInstFetch()) {
194            type = RubyRequestType_IFETCH;
195            pc = pkt->req->getPC();
196        } else {
197            type = RubyRequestType_LD;
198        }
199    } else if (pkt->isWrite()) {
200        type = RubyRequestType_ST;
201    } else if (pkt->isReadWrite()) {
202        type = RubyRequestType_RMW_Write;
203    }
204
205    RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(),
206                             pkt->getSize(), pc, type,
207                             RubyAccessMode_Supervisor);
208
209    // Submit the ruby request
210    int64_t req_id = ruby_port->makeRequest(ruby_request);
211    if (req_id == -1) {
212        return false;
213    }
214
215    // Save the request for the callback
216    RubyPort::pending_cpu_requests[req_id] = new RequestCookie(pkt, this);
217
218    return true;
219}
220
221void
222RubyPort::ruby_hit_callback(int64_t req_id)
223{
224    //
225    // Note: This single fuction can be called by cpu and dma ports,
226    // as well as the functional port.
227    //
228    RequestMap::iterator i = pending_cpu_requests.find(req_id);
229    if (i == pending_cpu_requests.end())
230        panic("could not find pending request %d\n", req_id);
231
232    RequestCookie *cookie = i->second;
233    pending_cpu_requests.erase(i);
234
235    Packet *pkt = cookie->pkt;
236    M5Port *port = cookie->m5Port;
237    delete cookie;
238
239    port->hitCallback(pkt);
240}
241
242void
243RubyPort::M5Port::hitCallback(PacketPtr pkt)
244{
245
246    bool needsResponse = pkt->needsResponse();
247
248    DPRINTF(MemoryAccess, "Hit callback needs response %d\n",
249            needsResponse);
250
251    ruby_port->funcMemPort.sendFunctional(pkt);
252
253    // turn packet around to go back to requester if response expected
254    if (needsResponse) {
255        // recvAtomic() should already have turned packet into
256        // atomic response
257        assert(pkt->isResponse());
258        DPRINTF(MemoryAccess, "Sending packet back over port\n");
259        sendTiming(pkt);
260    } else {
261        delete pkt;
262    }
263    DPRINTF(MemoryAccess, "Hit callback done!\n");
264}
265
266bool
267RubyPort::M5Port::sendTiming(PacketPtr pkt)
268{
269    schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
270    return true;
271}
272
273bool
274RubyPort::PioPort::sendTiming(PacketPtr pkt)
275{
276    schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
277    return true;
278}
279
280bool
281RubyPort::M5Port::isPhysMemAddress(Addr addr)
282{
283    AddrRangeList physMemAddrList;
284    bool snoop = false;
285    ruby_port->funcMemPort.getPeerAddressRanges(physMemAddrList, snoop);
286    for(AddrRangeIter iter = physMemAddrList.begin();
287        iter != physMemAddrList.end();
288        iter++) {
289        if (addr >= iter->start && addr <= iter->end) {
290            DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n",
291                    iter->start, iter->end);
292            return true;
293        }
294    }
295    assert(isPioAddress(addr));
296    return false;
297}
298
299bool
300RubyPort::M5Port::isPioAddress(Addr addr)
301{
302    AddrRangeList pioAddrList;
303    bool snoop = false;
304    if (ruby_port->pio_port == NULL) {
305        return false;
306    }
307
308    ruby_port->pio_port->getPeerAddressRanges(pioAddrList, snoop);
309    for(AddrRangeIter iter = pioAddrList.begin();
310        iter != pioAddrList.end();
311        iter++) {
312        if (addr >= iter->start && addr <= iter->end) {
313            DPRINTF(MemoryAccess, "Pio request found in %#llx - %#llx range\n",
314                    iter->start, iter->end);
315            return true;
316        }
317    }
318    return false;
319}
320
321