coherent_xbar.cc revision 9095
1/*
2 * Copyright (c) 2011-2012 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2006 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Ali Saidi
41 *          Andreas Hansson
42 *          William Wang
43 */
44
45/**
46 * @file
47 * Definition of a bus object.
48 */
49
50#include "base/misc.hh"
51#include "base/trace.hh"
52#include "debug/BusAddrRanges.hh"
53#include "debug/CoherentBus.hh"
54#include "mem/coherent_bus.hh"
55
56CoherentBus::CoherentBus(const CoherentBusParams *p)
57    : BaseBus(p), reqLayer(*this, ".reqLayer", p->clock),
58      respLayer(*this, ".respLayer", p->clock),
59      snoopRespLayer(*this, ".snoopRespLayer", p->clock)
60{
61    // create the ports based on the size of the master and slave
62    // vector ports, and the presence of the default port, the ports
63    // are enumerated starting from zero
64    for (int i = 0; i < p->port_master_connection_count; ++i) {
65        std::string portName = csprintf("%s.master[%d]", name(), i);
66        MasterPort* bp = new CoherentBusMasterPort(portName, *this, i);
67        masterPorts.push_back(bp);
68    }
69
70    // see if we have a default slave device connected and if so add
71    // our corresponding master port
72    if (p->port_default_connection_count) {
73        defaultPortID = masterPorts.size();
74        std::string portName = name() + ".default";
75        MasterPort* bp = new CoherentBusMasterPort(portName, *this,
76                                                   defaultPortID);
77        masterPorts.push_back(bp);
78    }
79
80    // create the slave ports, once again starting at zero
81    for (int i = 0; i < p->port_slave_connection_count; ++i) {
82        std::string portName = csprintf("%s.slave[%d]", name(), i);
83        SlavePort* bp = new CoherentBusSlavePort(portName, *this, i);
84        slavePorts.push_back(bp);
85    }
86
87    clearPortCache();
88}
89
90void
91CoherentBus::init()
92{
93    // iterate over our slave ports and determine which of our
94    // neighbouring master ports are snooping and add them as snoopers
95    for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
96         ++p) {
97        // check if the connected master port is snooping
98        if ((*p)->isSnooping()) {
99            DPRINTF(BusAddrRanges, "Adding snooping master %s\n",
100                    (*p)->getMasterPort().name());
101            snoopPorts.push_back(*p);
102        }
103    }
104
105    if (snoopPorts.empty())
106        warn("CoherentBus %s has no snooping ports attached!\n", name());
107}
108
109bool
110CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
111{
112    // determine the source port based on the id
113    SlavePort *src_port = slavePorts[slave_port_id];
114
115    // remember if the packet is an express snoop
116    bool is_express_snoop = pkt->isExpressSnoop();
117
118    // test if the bus should be considered occupied for the current
119    // port, and exclude express snoops from the check
120    if (!is_express_snoop && !reqLayer.tryTiming(src_port)) {
121        DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
122                src_port->name(), pkt->cmdString(), pkt->getAddr());
123        return false;
124    }
125
126    DPRINTF(CoherentBus, "recvTimingReq: src %s %s expr %d 0x%x\n",
127            src_port->name(), pkt->cmdString(), is_express_snoop,
128            pkt->getAddr());
129
130    // set the source port for routing of the response
131    pkt->setSrc(slave_port_id);
132
133    Tick headerFinishTime = is_express_snoop ? 0 : calcPacketTiming(pkt);
134    Tick packetFinishTime = is_express_snoop ? 0 : pkt->finishTime;
135
136    // uncacheable requests need never be snooped
137    if (!pkt->req->isUncacheable()) {
138        // the packet is a memory-mapped request and should be
139        // broadcasted to our snoopers but the source
140        forwardTiming(pkt, slave_port_id);
141    }
142
143    // remember if we add an outstanding req so we can undo it if
144    // necessary, if the packet needs a response, we should add it
145    // as outstanding and express snoops never fail so there is
146    // not need to worry about them
147    bool add_outstanding = !is_express_snoop && pkt->needsResponse();
148
149    // keep track that we have an outstanding request packet
150    // matching this request, this is used by the coherency
151    // mechanism in determining what to do with snoop responses
152    // (in recvTimingSnoop)
153    if (add_outstanding) {
154        // we should never have an exsiting request outstanding
155        assert(outstandingReq.find(pkt->req) == outstandingReq.end());
156        outstandingReq.insert(pkt->req);
157    }
158
159    // since it is a normal request, determine the destination
160    // based on the address and attempt to send the packet
161    bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
162
163    // if this is an express snoop, we are done at this point
164    if (is_express_snoop) {
165        assert(success);
166    } else {
167        // for normal requests, check if successful
168        if (!success)  {
169            // inhibited packets should never be forced to retry
170            assert(!pkt->memInhibitAsserted());
171
172            // if it was added as outstanding and the send failed, then
173            // erase it again
174            if (add_outstanding)
175                outstandingReq.erase(pkt->req);
176
177            DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
178                    src_port->name(), pkt->cmdString(), pkt->getAddr());
179
180            // update the bus state and schedule an idle event
181            reqLayer.failedTiming(src_port, headerFinishTime);
182        } else {
183            // update the bus state and schedule an idle event
184            reqLayer.succeededTiming(packetFinishTime);
185        }
186    }
187
188    return success;
189}
190
191bool
192CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
193{
194    // determine the source port based on the id
195    MasterPort *src_port = masterPorts[master_port_id];
196
197    // test if the bus should be considered occupied for the current
198    // port
199    if (!respLayer.tryTiming(src_port)) {
200        DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
201                src_port->name(), pkt->cmdString(), pkt->getAddr());
202        return false;
203    }
204
205    DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x\n",
206            src_port->name(), pkt->cmdString(), pkt->getAddr());
207
208    calcPacketTiming(pkt);
209    Tick packetFinishTime = pkt->finishTime;
210
211    // the packet is a normal response to a request that we should
212    // have seen passing through the bus
213    assert(outstandingReq.find(pkt->req) != outstandingReq.end());
214
215    // remove it as outstanding
216    outstandingReq.erase(pkt->req);
217
218    // send the packet to the destination through one of our slave
219    // ports, as determined by the destination field
220    bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
221
222    // currently it is illegal to block responses... can lead to
223    // deadlock
224    assert(success);
225
226    respLayer.succeededTiming(packetFinishTime);
227
228    return true;
229}
230
231void
232CoherentBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
233{
234    DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x\n",
235            masterPorts[master_port_id]->name(), pkt->cmdString(),
236            pkt->getAddr());
237
238    // we should only see express snoops from caches
239    assert(pkt->isExpressSnoop());
240
241    // set the source port for routing of the response
242    pkt->setSrc(master_port_id);
243
244    // forward to all snoopers
245    forwardTiming(pkt, InvalidPortID);
246
247    // a snoop request came from a connected slave device (one of
248    // our master ports), and if it is not coming from the slave
249    // device responsible for the address range something is
250    // wrong, hence there is nothing further to do as the packet
251    // would be going back to where it came from
252    assert(master_port_id == findPort(pkt->getAddr()));
253}
254
255bool
256CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
257{
258    // determine the source port based on the id
259    SlavePort* src_port = slavePorts[slave_port_id];
260
261    // test if the bus should be considered occupied for the current
262    // port
263    if (!snoopRespLayer.tryTiming(src_port)) {
264        DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
265                src_port->name(), pkt->cmdString(), pkt->getAddr());
266        return false;
267    }
268
269    DPRINTF(CoherentBus, "recvTimingSnoop: src %s %s 0x%x\n",
270            src_port->name(), pkt->cmdString(), pkt->getAddr());
271
272    // get the destination from the packet
273    PortID dest = pkt->getDest();
274
275    // responses are never express snoops
276    assert(!pkt->isExpressSnoop());
277
278    calcPacketTiming(pkt);
279    Tick packetFinishTime = pkt->finishTime;
280
281    // determine if the response is from a snoop request we
282    // created as the result of a normal request (in which case it
283    // should be in the outstandingReq), or if we merely forwarded
284    // someone else's snoop request
285    if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
286        // this is a snoop response to a snoop request we
287        // forwarded, e.g. coming from the L1 and going to the L2
288        // this should be forwarded as a snoop response
289        bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
290        assert(success);
291    } else {
292        // we got a snoop response on one of our slave ports,
293        // i.e. from a coherent master connected to the bus, and
294        // since we created the snoop request as part of
295        // recvTiming, this should now be a normal response again
296        outstandingReq.erase(pkt->req);
297
298        // this is a snoop response from a coherent master, with a
299        // destination field set on its way through the bus as
300        // request, hence it should never go back to where the
301        // snoop response came from, but instead to where the
302        // original request came from
303        assert(slave_port_id != dest);
304
305        // as a normal response, it should go back to a master
306        // through one of our slave ports
307        bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
308
309        // currently it is illegal to block responses... can lead
310        // to deadlock
311        assert(success);
312    }
313
314    snoopRespLayer.succeededTiming(packetFinishTime);
315
316    return true;
317}
318
319
320void
321CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
322{
323    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
324        SlavePort *p = *s;
325        // we could have gotten this request from a snooping master
326        // (corresponding to our own slave port that is also in
327        // snoopPorts) and should not send it back to where it came
328        // from
329        if (exclude_slave_port_id == InvalidPortID ||
330            p->getId() != exclude_slave_port_id) {
331            // cache is not allowed to refuse snoop
332            p->sendTimingSnoopReq(pkt);
333        }
334    }
335}
336
337void
338CoherentBus::recvRetry()
339{
340    // responses and snoop responses never block on forwarding them,
341    // so the retry will always be coming from a port to which we
342    // tried to forward a request
343    reqLayer.recvRetry();
344}
345
346Tick
347CoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
348{
349    DPRINTF(CoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
350            slavePorts[slave_port_id]->name(), pkt->getAddr(),
351            pkt->cmdString());
352
353    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
354    Tick snoop_response_latency = 0;
355
356    // uncacheable requests need never be snooped
357    if (!pkt->req->isUncacheable()) {
358        // forward to all snoopers but the source
359        std::pair<MemCmd, Tick> snoop_result =
360            forwardAtomic(pkt, slave_port_id);
361        snoop_response_cmd = snoop_result.first;
362        snoop_response_latency = snoop_result.second;
363    }
364
365    // even if we had a snoop response, we must continue and also
366    // perform the actual request at the destination
367    PortID dest_id = findPort(pkt->getAddr());
368
369    // forward the request to the appropriate destination
370    Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
371
372    // if we got a response from a snooper, restore it here
373    if (snoop_response_cmd != MemCmd::InvalidCmd) {
374        // no one else should have responded
375        assert(!pkt->isResponse());
376        pkt->cmd = snoop_response_cmd;
377        response_latency = snoop_response_latency;
378    }
379
380    pkt->finishTime = curTick() + response_latency;
381    return response_latency;
382}
383
384Tick
385CoherentBus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
386{
387    DPRINTF(CoherentBus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
388            masterPorts[master_port_id]->name(), pkt->getAddr(),
389            pkt->cmdString());
390
391    // forward to all snoopers
392    std::pair<MemCmd, Tick> snoop_result =
393        forwardAtomic(pkt, InvalidPortID);
394    MemCmd snoop_response_cmd = snoop_result.first;
395    Tick snoop_response_latency = snoop_result.second;
396
397    if (snoop_response_cmd != MemCmd::InvalidCmd)
398        pkt->cmd = snoop_response_cmd;
399
400    pkt->finishTime = curTick() + snoop_response_latency;
401    return snoop_response_latency;
402}
403
404std::pair<MemCmd, Tick>
405CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
406{
407    // the packet may be changed on snoops, record the original
408    // command to enable us to restore it between snoops so that
409    // additional snoops can take place properly
410    MemCmd orig_cmd = pkt->cmd;
411    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
412    Tick snoop_response_latency = 0;
413
414    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
415        SlavePort *p = *s;
416        // we could have gotten this request from a snooping master
417        // (corresponding to our own slave port that is also in
418        // snoopPorts) and should not send it back to where it came
419        // from
420        if (exclude_slave_port_id == InvalidPortID ||
421            p->getId() != exclude_slave_port_id) {
422            Tick latency = p->sendAtomicSnoop(pkt);
423            // in contrast to a functional access, we have to keep on
424            // going as all snoopers must be updated even if we get a
425            // response
426            if (pkt->isResponse()) {
427                // response from snoop agent
428                assert(pkt->cmd != orig_cmd);
429                assert(pkt->memInhibitAsserted());
430                // should only happen once
431                assert(snoop_response_cmd == MemCmd::InvalidCmd);
432                // save response state
433                snoop_response_cmd = pkt->cmd;
434                snoop_response_latency = latency;
435                // restore original packet state for remaining snoopers
436                pkt->cmd = orig_cmd;
437            }
438        }
439    }
440
441    // the packet is restored as part of the loop and any potential
442    // snoop response is part of the returned pair
443    return std::make_pair(snoop_response_cmd, snoop_response_latency);
444}
445
446void
447CoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
448{
449    if (!pkt->isPrint()) {
450        // don't do DPRINTFs on PrintReq as it clutters up the output
451        DPRINTF(CoherentBus,
452                "recvFunctional: packet src %s addr 0x%x cmd %s\n",
453                slavePorts[slave_port_id]->name(), pkt->getAddr(),
454                pkt->cmdString());
455    }
456
457    // uncacheable requests need never be snooped
458    if (!pkt->req->isUncacheable()) {
459        // forward to all snoopers but the source
460        forwardFunctional(pkt, slave_port_id);
461    }
462
463    // there is no need to continue if the snooping has found what we
464    // were looking for and the packet is already a response
465    if (!pkt->isResponse()) {
466        PortID dest_id = findPort(pkt->getAddr());
467
468        masterPorts[dest_id]->sendFunctional(pkt);
469    }
470}
471
472void
473CoherentBus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
474{
475    if (!pkt->isPrint()) {
476        // don't do DPRINTFs on PrintReq as it clutters up the output
477        DPRINTF(CoherentBus,
478                "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
479                masterPorts[master_port_id]->name(), pkt->getAddr(),
480                pkt->cmdString());
481    }
482
483    // forward to all snoopers
484    forwardFunctional(pkt, InvalidPortID);
485}
486
487void
488CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
489{
490    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
491        SlavePort *p = *s;
492        // we could have gotten this request from a snooping master
493        // (corresponding to our own slave port that is also in
494        // snoopPorts) and should not send it back to where it came
495        // from
496        if (exclude_slave_port_id == InvalidPortID ||
497            p->getId() != exclude_slave_port_id)
498            p->sendFunctionalSnoop(pkt);
499
500        // if we get a response we are done
501        if (pkt->isResponse()) {
502            break;
503        }
504    }
505}
506
507unsigned int
508CoherentBus::drain(Event *de)
509{
510    // sum up the individual layers
511    return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
512}
513
514CoherentBus *
515CoherentBusParams::create()
516{
517    return new CoherentBus(this);
518}
519