coherent_xbar.cc revision 10821
1/*
2 * Copyright (c) 2011-2015 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 crossbar object.
48 */
49
50#include "base/misc.hh"
51#include "base/trace.hh"
52#include "debug/AddrRanges.hh"
53#include "debug/CoherentXBar.hh"
54#include "mem/coherent_xbar.hh"
55#include "sim/system.hh"
56
57CoherentXBar::CoherentXBar(const CoherentXBarParams *p)
58    : BaseXBar(p), system(p->system), snoopFilter(p->snoop_filter),
59      snoopResponseLatency(p->snoop_response_latency)
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 CoherentXBarMasterPort(portName, *this, i);
67        masterPorts.push_back(bp);
68        reqLayers.push_back(new ReqLayer(*bp, *this,
69                                         csprintf(".reqLayer%d", i)));
70        snoopLayers.push_back(new SnoopRespLayer(*bp, *this,
71                                                 csprintf(".snoopLayer%d", i)));
72    }
73
74    // see if we have a default slave device connected and if so add
75    // our corresponding master port
76    if (p->port_default_connection_count) {
77        defaultPortID = masterPorts.size();
78        std::string portName = name() + ".default";
79        MasterPort* bp = new CoherentXBarMasterPort(portName, *this,
80                                                   defaultPortID);
81        masterPorts.push_back(bp);
82        reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
83                                             defaultPortID)));
84        snoopLayers.push_back(new SnoopRespLayer(*bp, *this,
85                                                 csprintf(".snoopLayer%d",
86                                                          defaultPortID)));
87    }
88
89    // create the slave ports, once again starting at zero
90    for (int i = 0; i < p->port_slave_connection_count; ++i) {
91        std::string portName = csprintf("%s.slave[%d]", name(), i);
92        SlavePort* bp = new CoherentXBarSlavePort(portName, *this, i);
93        slavePorts.push_back(bp);
94        respLayers.push_back(new RespLayer(*bp, *this,
95                                           csprintf(".respLayer%d", i)));
96        snoopRespPorts.push_back(new SnoopRespPort(*bp, *this));
97    }
98
99    if (snoopFilter)
100        snoopFilter->setSlavePorts(slavePorts);
101
102    clearPortCache();
103}
104
105CoherentXBar::~CoherentXBar()
106{
107    for (auto l: reqLayers)
108        delete l;
109    for (auto l: respLayers)
110        delete l;
111    for (auto l: snoopLayers)
112        delete l;
113    for (auto p: snoopRespPorts)
114        delete p;
115}
116
117void
118CoherentXBar::init()
119{
120    // the base class is responsible for determining the block size
121    BaseXBar::init();
122
123    // iterate over our slave ports and determine which of our
124    // neighbouring master ports are snooping and add them as snoopers
125    for (const auto& p: slavePorts) {
126        // check if the connected master port is snooping
127        if (p->isSnooping()) {
128            DPRINTF(AddrRanges, "Adding snooping master %s\n",
129                    p->getMasterPort().name());
130            snoopPorts.push_back(p);
131        }
132    }
133
134    if (snoopPorts.empty())
135        warn("CoherentXBar %s has no snooping ports attached!\n", name());
136}
137
138bool
139CoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
140{
141    // determine the source port based on the id
142    SlavePort *src_port = slavePorts[slave_port_id];
143
144    // remember if the packet is an express snoop
145    bool is_express_snoop = pkt->isExpressSnoop();
146    bool is_inhibited = pkt->memInhibitAsserted();
147    // for normal requests, going downstream, the express snoop flag
148    // and the inhibited flag should always be the same
149    assert(is_express_snoop == is_inhibited);
150
151    // determine the destination based on the address
152    PortID master_port_id = findPort(pkt->getAddr());
153
154    // test if the crossbar should be considered occupied for the current
155    // port, and exclude express snoops from the check
156    if (!is_express_snoop && !reqLayers[master_port_id]->tryTiming(src_port)) {
157        DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x BUSY\n",
158                src_port->name(), pkt->cmdString(), pkt->getAddr());
159        return false;
160    }
161
162    DPRINTF(CoherentXBar, "recvTimingReq: src %s %s expr %d 0x%x\n",
163            src_port->name(), pkt->cmdString(), is_express_snoop,
164            pkt->getAddr());
165
166    // store size and command as they might be modified when
167    // forwarding the packet
168    unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
169    unsigned int pkt_cmd = pkt->cmdToIndex();
170
171    // store the old header delay so we can restore it if needed
172    Tick old_header_delay = pkt->headerDelay;
173
174    // a request sees the frontend and forward latency
175    Tick xbar_delay = (frontendLatency + forwardLatency) * clockPeriod();
176
177    // set the packet header and payload delay
178    calcPacketTiming(pkt, xbar_delay);
179
180    // determine how long to be crossbar layer is busy
181    Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay;
182
183    if (!system->bypassCaches()) {
184        // the packet is a memory-mapped request and should be
185        // broadcasted to our snoopers but the source
186        if (snoopFilter) {
187            // check with the snoop filter where to forward this packet
188            auto sf_res = snoopFilter->lookupRequest(pkt, *src_port);
189            // If SnoopFilter is enabled, the total time required by a packet
190            // to be delivered through the xbar has to be charged also with
191            // to lookup latency of the snoop filter (sf_res.second).
192            pkt->headerDelay += sf_res.second * clockPeriod();
193            packetFinishTime += sf_res.second * clockPeriod();
194            DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x"\
195                    " SF size: %i lat: %i\n", src_port->name(),
196                    pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
197                    sf_res.second);
198            forwardTiming(pkt, slave_port_id, sf_res.first);
199        } else {
200            forwardTiming(pkt, slave_port_id);
201        }
202    }
203
204    // remember if the packet will generate a snoop response
205    const bool expect_snoop_resp = !is_inhibited && pkt->memInhibitAsserted();
206    const bool expect_response = pkt->needsResponse() &&
207        !pkt->memInhibitAsserted();
208
209    // Note: Cannot create a copy of the full packet, here.
210    MemCmd orig_cmd(pkt->cmd);
211
212    // since it is a normal request, attempt to send the packet
213    bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
214
215    if (snoopFilter && !system->bypassCaches()) {
216        // The packet may already be overwritten by the sendTimingReq function.
217        // The snoop filter needs to see the original request *and* the return
218        // status of the send operation, so we need to recreate the original
219        // request.  Atomic mode does not have the issue, as there the send
220        // operation and the response happen instantaneously and don't need two
221        // phase tracking.
222        MemCmd tmp_cmd(pkt->cmd);
223        pkt->cmd = orig_cmd;
224        // Let the snoop filter know about the success of the send operation
225        snoopFilter->updateRequest(pkt, *src_port, !success);
226        pkt->cmd = tmp_cmd;
227    }
228
229    // check if we were successful in sending the packet onwards
230    if (!success)  {
231        // express snoops and inhibited packets should never be forced
232        // to retry
233        assert(!is_express_snoop);
234        assert(!pkt->memInhibitAsserted());
235
236        // restore the header delay
237        pkt->headerDelay = old_header_delay;
238
239        DPRINTF(CoherentXBar, "recvTimingReq: src %s %s 0x%x RETRY\n",
240                src_port->name(), pkt->cmdString(), pkt->getAddr());
241
242        // update the layer state and schedule an idle event
243        reqLayers[master_port_id]->failedTiming(src_port,
244                                                clockEdge(Cycles(1)));
245    } else {
246        // express snoops currently bypass the crossbar state entirely
247        if (!is_express_snoop) {
248            // if this particular request will generate a snoop
249            // response
250            if (expect_snoop_resp) {
251                // we should never have an exsiting request outstanding
252                assert(outstandingSnoop.find(pkt->req) ==
253                       outstandingSnoop.end());
254                outstandingSnoop.insert(pkt->req);
255
256                // basic sanity check on the outstanding snoops
257                panic_if(outstandingSnoop.size() > 512,
258                         "Outstanding snoop requests exceeded 512\n");
259            }
260
261            // remember where to route the normal response to
262            if (expect_response || expect_snoop_resp) {
263                assert(routeTo.find(pkt->req) == routeTo.end());
264                routeTo[pkt->req] = slave_port_id;
265
266                panic_if(routeTo.size() > 512,
267                         "Routing table exceeds 512 packets\n");
268            }
269
270            // update the layer state and schedule an idle event
271            reqLayers[master_port_id]->succeededTiming(packetFinishTime);
272        }
273
274        // stats updates only consider packets that were successfully sent
275        pktCount[slave_port_id][master_port_id]++;
276        pktSize[slave_port_id][master_port_id] += pkt_size;
277        transDist[pkt_cmd]++;
278
279        if (is_express_snoop)
280            snoops++;
281    }
282
283    return success;
284}
285
286bool
287CoherentXBar::recvTimingResp(PacketPtr pkt, PortID master_port_id)
288{
289    // determine the source port based on the id
290    MasterPort *src_port = masterPorts[master_port_id];
291
292    // determine the destination
293    const auto route_lookup = routeTo.find(pkt->req);
294    assert(route_lookup != routeTo.end());
295    const PortID slave_port_id = route_lookup->second;
296    assert(slave_port_id != InvalidPortID);
297    assert(slave_port_id < respLayers.size());
298
299    // test if the crossbar should be considered occupied for the
300    // current port
301    if (!respLayers[slave_port_id]->tryTiming(src_port)) {
302        DPRINTF(CoherentXBar, "recvTimingResp: src %s %s 0x%x BUSY\n",
303                src_port->name(), pkt->cmdString(), pkt->getAddr());
304        return false;
305    }
306
307    DPRINTF(CoherentXBar, "recvTimingResp: src %s %s 0x%x\n",
308            src_port->name(), pkt->cmdString(), pkt->getAddr());
309
310    // store size and command as they might be modified when
311    // forwarding the packet
312    unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
313    unsigned int pkt_cmd = pkt->cmdToIndex();
314
315    // a response sees the response latency
316    Tick xbar_delay = responseLatency * clockPeriod();
317
318    // set the packet header and payload delay
319    calcPacketTiming(pkt, xbar_delay);
320
321    // determine how long to be crossbar layer is busy
322    Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay;
323
324    if (snoopFilter && !system->bypassCaches()) {
325        // let the snoop filter inspect the response and update its state
326        snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
327    }
328
329    // send the packet through the destination slave port
330    bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt);
331
332    // currently it is illegal to block responses... can lead to
333    // deadlock
334    assert(success);
335
336    // remove the request from the routing table
337    routeTo.erase(route_lookup);
338
339    respLayers[slave_port_id]->succeededTiming(packetFinishTime);
340
341    // stats updates
342    pktCount[slave_port_id][master_port_id]++;
343    pktSize[slave_port_id][master_port_id] += pkt_size;
344    transDist[pkt_cmd]++;
345
346    return true;
347}
348
349void
350CoherentXBar::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
351{
352    DPRINTF(CoherentXBar, "recvTimingSnoopReq: src %s %s 0x%x\n",
353            masterPorts[master_port_id]->name(), pkt->cmdString(),
354            pkt->getAddr());
355
356    // update stats here as we know the forwarding will succeed
357    transDist[pkt->cmdToIndex()]++;
358    snoops++;
359
360    // we should only see express snoops from caches
361    assert(pkt->isExpressSnoop());
362
363    // remeber if the packet is inhibited so we can see if it changes
364    const bool is_inhibited = pkt->memInhibitAsserted();
365
366    if (snoopFilter) {
367        // let the Snoop Filter work its magic and guide probing
368        auto sf_res = snoopFilter->lookupSnoop(pkt);
369        // No timing here: packetFinishTime += sf_res.second * clockPeriod();
370        DPRINTF(CoherentXBar, "recvTimingSnoopReq: src %s %s 0x%x"\
371                " SF size: %i lat: %i\n", masterPorts[master_port_id]->name(),
372                pkt->cmdString(), pkt->getAddr(), sf_res.first.size(),
373                sf_res.second);
374
375        // forward to all snoopers
376        forwardTiming(pkt, InvalidPortID, sf_res.first);
377    } else {
378        forwardTiming(pkt, InvalidPortID);
379    }
380
381    // if we can expect a response, remember how to route it
382    if (!is_inhibited && pkt->memInhibitAsserted()) {
383        assert(routeTo.find(pkt->req) == routeTo.end());
384        routeTo[pkt->req] = master_port_id;
385    }
386
387    // a snoop request came from a connected slave device (one of
388    // our master ports), and if it is not coming from the slave
389    // device responsible for the address range something is
390    // wrong, hence there is nothing further to do as the packet
391    // would be going back to where it came from
392    assert(master_port_id == findPort(pkt->getAddr()));
393}
394
395bool
396CoherentXBar::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
397{
398    // determine the source port based on the id
399    SlavePort* src_port = slavePorts[slave_port_id];
400
401    // get the destination
402    const auto route_lookup = routeTo.find(pkt->req);
403    assert(route_lookup != routeTo.end());
404    const PortID dest_port_id = route_lookup->second;
405    assert(dest_port_id != InvalidPortID);
406
407    // determine if the response is from a snoop request we
408    // created as the result of a normal request (in which case it
409    // should be in the outstandingSnoop), or if we merely forwarded
410    // someone else's snoop request
411    const bool forwardAsSnoop = outstandingSnoop.find(pkt->req) ==
412        outstandingSnoop.end();
413
414    // test if the crossbar should be considered occupied for the
415    // current port, note that the check is bypassed if the response
416    // is being passed on as a normal response since this is occupying
417    // the response layer rather than the snoop response layer
418    if (forwardAsSnoop) {
419        assert(dest_port_id < snoopLayers.size());
420        if (!snoopLayers[dest_port_id]->tryTiming(src_port)) {
421            DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
422                    src_port->name(), pkt->cmdString(), pkt->getAddr());
423            return false;
424        }
425    } else {
426        // get the master port that mirrors this slave port internally
427        MasterPort* snoop_port = snoopRespPorts[slave_port_id];
428        assert(dest_port_id < respLayers.size());
429        if (!respLayers[dest_port_id]->tryTiming(snoop_port)) {
430            DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
431                    snoop_port->name(), pkt->cmdString(), pkt->getAddr());
432            return false;
433        }
434    }
435
436    DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x\n",
437            src_port->name(), pkt->cmdString(), pkt->getAddr());
438
439    // store size and command as they might be modified when
440    // forwarding the packet
441    unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
442    unsigned int pkt_cmd = pkt->cmdToIndex();
443
444    // responses are never express snoops
445    assert(!pkt->isExpressSnoop());
446
447    // a snoop response sees the snoop response latency, and if it is
448    // forwarded as a normal response, the response latency
449    Tick xbar_delay =
450        (forwardAsSnoop ? snoopResponseLatency : responseLatency) *
451        clockPeriod();
452
453    // set the packet header and payload delay
454    calcPacketTiming(pkt, xbar_delay);
455
456    // determine how long to be crossbar layer is busy
457    Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay;
458
459    // forward it either as a snoop response or a normal response
460    if (forwardAsSnoop) {
461        // this is a snoop response to a snoop request we forwarded,
462        // e.g. coming from the L1 and going to the L2, and it should
463        // be forwarded as a snoop response
464
465        if (snoopFilter) {
466            // update the probe filter so that it can properly track the line
467            snoopFilter->updateSnoopForward(pkt, *slavePorts[slave_port_id],
468                                            *masterPorts[dest_port_id]);
469        }
470
471        bool success M5_VAR_USED =
472            masterPorts[dest_port_id]->sendTimingSnoopResp(pkt);
473        pktCount[slave_port_id][dest_port_id]++;
474        pktSize[slave_port_id][dest_port_id] += pkt_size;
475        assert(success);
476
477        snoopLayers[dest_port_id]->succeededTiming(packetFinishTime);
478    } else {
479        // we got a snoop response on one of our slave ports,
480        // i.e. from a coherent master connected to the crossbar, and
481        // since we created the snoop request as part of recvTiming,
482        // this should now be a normal response again
483        outstandingSnoop.erase(pkt->req);
484
485        // this is a snoop response from a coherent master, hence it
486        // should never go back to where the snoop response came from,
487        // but instead to where the original request came from
488        assert(slave_port_id != dest_port_id);
489
490        if (snoopFilter) {
491            // update the probe filter so that it can properly track the line
492            snoopFilter->updateSnoopResponse(pkt, *slavePorts[slave_port_id],
493                                    *slavePorts[dest_port_id]);
494        }
495
496        DPRINTF(CoherentXBar, "recvTimingSnoopResp: src %s %s 0x%x"\
497                " FWD RESP\n", src_port->name(), pkt->cmdString(),
498                pkt->getAddr());
499
500        // as a normal response, it should go back to a master through
501        // one of our slave ports, at this point we are ignoring the
502        // fact that the response layer could be busy and do not touch
503        // its state
504        bool success M5_VAR_USED =
505            slavePorts[dest_port_id]->sendTimingResp(pkt);
506
507        // @todo Put the response in an internal FIFO and pass it on
508        // to the response layer from there
509
510        // currently it is illegal to block responses... can lead
511        // to deadlock
512        assert(success);
513
514        respLayers[dest_port_id]->succeededTiming(packetFinishTime);
515    }
516
517    // remove the request from the routing table
518    routeTo.erase(route_lookup);
519
520    // stats updates
521    transDist[pkt_cmd]++;
522    snoops++;
523
524    return true;
525}
526
527
528void
529CoherentXBar::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id,
530                           const std::vector<SlavePort*>& dests)
531{
532    DPRINTF(CoherentXBar, "%s for %s address %x size %d\n", __func__,
533            pkt->cmdString(), pkt->getAddr(), pkt->getSize());
534
535    // snoops should only happen if the system isn't bypassing caches
536    assert(!system->bypassCaches());
537
538    unsigned fanout = 0;
539
540    for (const auto& p: dests) {
541        // we could have gotten this request from a snooping master
542        // (corresponding to our own slave port that is also in
543        // snoopPorts) and should not send it back to where it came
544        // from
545        if (exclude_slave_port_id == InvalidPortID ||
546            p->getId() != exclude_slave_port_id) {
547            // cache is not allowed to refuse snoop
548            p->sendTimingSnoopReq(pkt);
549            fanout++;
550        }
551    }
552
553    // Stats for fanout of this forward operation
554    snoopFanout.sample(fanout);
555}
556
557void
558CoherentXBar::recvReqRetry(PortID master_port_id)
559{
560    // responses and snoop responses never block on forwarding them,
561    // so the retry will always be coming from a port to which we
562    // tried to forward a request
563    reqLayers[master_port_id]->recvRetry();
564}
565
566Tick
567CoherentXBar::recvAtomic(PacketPtr pkt, PortID slave_port_id)
568{
569    DPRINTF(CoherentXBar, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
570            slavePorts[slave_port_id]->name(), pkt->getAddr(),
571            pkt->cmdString());
572
573    unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
574    unsigned int pkt_cmd = pkt->cmdToIndex();
575
576    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
577    Tick snoop_response_latency = 0;
578
579    if (!system->bypassCaches()) {
580        // forward to all snoopers but the source
581        std::pair<MemCmd, Tick> snoop_result;
582        if (snoopFilter) {
583            // check with the snoop filter where to forward this packet
584            auto sf_res =
585                snoopFilter->lookupRequest(pkt, *slavePorts[slave_port_id]);
586            snoop_response_latency += sf_res.second * clockPeriod();
587            DPRINTF(CoherentXBar, "%s: src %s %s 0x%x"\
588                    " SF size: %i lat: %i\n", __func__,
589                    slavePorts[slave_port_id]->name(), pkt->cmdString(),
590                    pkt->getAddr(), sf_res.first.size(), sf_res.second);
591            snoop_result = forwardAtomic(pkt, slave_port_id, InvalidPortID,
592                                         sf_res.first);
593        } else {
594            snoop_result = forwardAtomic(pkt, slave_port_id);
595        }
596        snoop_response_cmd = snoop_result.first;
597        snoop_response_latency += snoop_result.second;
598    }
599
600    // even if we had a snoop response, we must continue and also
601    // perform the actual request at the destination
602    PortID master_port_id = findPort(pkt->getAddr());
603
604    // stats updates for the request
605    pktCount[slave_port_id][master_port_id]++;
606    pktSize[slave_port_id][master_port_id] += pkt_size;
607    transDist[pkt_cmd]++;
608
609    // forward the request to the appropriate destination
610    Tick response_latency = masterPorts[master_port_id]->sendAtomic(pkt);
611
612    // Lower levels have replied, tell the snoop filter
613    if (snoopFilter && !system->bypassCaches() && pkt->isResponse()) {
614        snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]);
615    }
616
617    // if we got a response from a snooper, restore it here
618    if (snoop_response_cmd != MemCmd::InvalidCmd) {
619        // no one else should have responded
620        assert(!pkt->isResponse());
621        pkt->cmd = snoop_response_cmd;
622        response_latency = snoop_response_latency;
623    }
624
625    // add the response data
626    if (pkt->isResponse()) {
627        pkt_size = pkt->hasData() ? pkt->getSize() : 0;
628        pkt_cmd = pkt->cmdToIndex();
629
630        // stats updates
631        pktCount[slave_port_id][master_port_id]++;
632        pktSize[slave_port_id][master_port_id] += pkt_size;
633        transDist[pkt_cmd]++;
634    }
635
636    // @todo: Not setting header time
637    pkt->payloadDelay = response_latency;
638    return response_latency;
639}
640
641Tick
642CoherentXBar::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
643{
644    DPRINTF(CoherentXBar, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
645            masterPorts[master_port_id]->name(), pkt->getAddr(),
646            pkt->cmdString());
647
648    // add the request snoop data
649    snoops++;
650
651    // forward to all snoopers
652    std::pair<MemCmd, Tick> snoop_result;
653    Tick snoop_response_latency = 0;
654    if (snoopFilter) {
655        auto sf_res = snoopFilter->lookupSnoop(pkt);
656        snoop_response_latency += sf_res.second * clockPeriod();
657        DPRINTF(CoherentXBar, "%s: src %s %s 0x%x SF size: %i lat: %i\n",
658                __func__, masterPorts[master_port_id]->name(), pkt->cmdString(),
659                pkt->getAddr(), sf_res.first.size(), sf_res.second);
660        snoop_result = forwardAtomic(pkt, InvalidPortID, master_port_id,
661                                     sf_res.first);
662    } else {
663        snoop_result = forwardAtomic(pkt, InvalidPortID);
664    }
665    MemCmd snoop_response_cmd = snoop_result.first;
666    snoop_response_latency += snoop_result.second;
667
668    if (snoop_response_cmd != MemCmd::InvalidCmd)
669        pkt->cmd = snoop_response_cmd;
670
671    // add the response snoop data
672    if (pkt->isResponse()) {
673        snoops++;
674    }
675
676    // @todo: Not setting header time
677    pkt->payloadDelay = snoop_response_latency;
678    return snoop_response_latency;
679}
680
681std::pair<MemCmd, Tick>
682CoherentXBar::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id,
683                           PortID source_master_port_id,
684                           const std::vector<SlavePort*>& dests)
685{
686    // the packet may be changed on snoops, record the original
687    // command to enable us to restore it between snoops so that
688    // additional snoops can take place properly
689    MemCmd orig_cmd = pkt->cmd;
690    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
691    Tick snoop_response_latency = 0;
692
693    // snoops should only happen if the system isn't bypassing caches
694    assert(!system->bypassCaches());
695
696    unsigned fanout = 0;
697
698    for (const auto& p: dests) {
699        // we could have gotten this request from a snooping master
700        // (corresponding to our own slave port that is also in
701        // snoopPorts) and should not send it back to where it came
702        // from
703        if (exclude_slave_port_id != InvalidPortID &&
704            p->getId() == exclude_slave_port_id)
705            continue;
706
707        Tick latency = p->sendAtomicSnoop(pkt);
708        fanout++;
709
710        // in contrast to a functional access, we have to keep on
711        // going as all snoopers must be updated even if we get a
712        // response
713        if (!pkt->isResponse())
714            continue;
715
716        // response from snoop agent
717        assert(pkt->cmd != orig_cmd);
718        assert(pkt->memInhibitAsserted());
719        // should only happen once
720        assert(snoop_response_cmd == MemCmd::InvalidCmd);
721        // save response state
722        snoop_response_cmd = pkt->cmd;
723        snoop_response_latency = latency;
724
725        if (snoopFilter) {
726            // Handle responses by the snoopers and differentiate between
727            // responses to requests from above and snoops from below
728            if (source_master_port_id != InvalidPortID) {
729                // Getting a response for a snoop from below
730                assert(exclude_slave_port_id == InvalidPortID);
731                snoopFilter->updateSnoopForward(pkt, *p,
732                             *masterPorts[source_master_port_id]);
733            } else {
734                // Getting a response for a request from above
735                assert(source_master_port_id == InvalidPortID);
736                snoopFilter->updateSnoopResponse(pkt, *p,
737                             *slavePorts[exclude_slave_port_id]);
738            }
739        }
740        // restore original packet state for remaining snoopers
741        pkt->cmd = orig_cmd;
742    }
743
744    // Stats for fanout
745    snoopFanout.sample(fanout);
746
747    // the packet is restored as part of the loop and any potential
748    // snoop response is part of the returned pair
749    return std::make_pair(snoop_response_cmd, snoop_response_latency);
750}
751
752void
753CoherentXBar::recvFunctional(PacketPtr pkt, PortID slave_port_id)
754{
755    if (!pkt->isPrint()) {
756        // don't do DPRINTFs on PrintReq as it clutters up the output
757        DPRINTF(CoherentXBar,
758                "recvFunctional: packet src %s addr 0x%x cmd %s\n",
759                slavePorts[slave_port_id]->name(), pkt->getAddr(),
760                pkt->cmdString());
761    }
762
763    if (!system->bypassCaches()) {
764        // forward to all snoopers but the source
765        forwardFunctional(pkt, slave_port_id);
766    }
767
768    // there is no need to continue if the snooping has found what we
769    // were looking for and the packet is already a response
770    if (!pkt->isResponse()) {
771        PortID dest_id = findPort(pkt->getAddr());
772
773        masterPorts[dest_id]->sendFunctional(pkt);
774    }
775}
776
777void
778CoherentXBar::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
779{
780    if (!pkt->isPrint()) {
781        // don't do DPRINTFs on PrintReq as it clutters up the output
782        DPRINTF(CoherentXBar,
783                "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
784                masterPorts[master_port_id]->name(), pkt->getAddr(),
785                pkt->cmdString());
786    }
787
788    // forward to all snoopers
789    forwardFunctional(pkt, InvalidPortID);
790}
791
792void
793CoherentXBar::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
794{
795    // snoops should only happen if the system isn't bypassing caches
796    assert(!system->bypassCaches());
797
798    for (const auto& p: snoopPorts) {
799        // we could have gotten this request from a snooping master
800        // (corresponding to our own slave port that is also in
801        // snoopPorts) and should not send it back to where it came
802        // from
803        if (exclude_slave_port_id == InvalidPortID ||
804            p->getId() != exclude_slave_port_id)
805            p->sendFunctionalSnoop(pkt);
806
807        // if we get a response we are done
808        if (pkt->isResponse()) {
809            break;
810        }
811    }
812}
813
814unsigned int
815CoherentXBar::drain(DrainManager *dm)
816{
817    // sum up the individual layers
818    unsigned int total = 0;
819    for (auto l: reqLayers)
820        total += l->drain(dm);
821    for (auto l: respLayers)
822        total += l->drain(dm);
823    for (auto l: snoopLayers)
824        total += l->drain(dm);
825    return total;
826}
827
828void
829CoherentXBar::regStats()
830{
831    // register the stats of the base class and our layers
832    BaseXBar::regStats();
833    for (auto l: reqLayers)
834        l->regStats();
835    for (auto l: respLayers)
836        l->regStats();
837    for (auto l: snoopLayers)
838        l->regStats();
839
840    snoops
841        .name(name() + ".snoops")
842        .desc("Total snoops (count)")
843    ;
844
845    snoopFanout
846        .init(0, snoopPorts.size(), 1)
847        .name(name() + ".snoop_fanout")
848        .desc("Request fanout histogram")
849    ;
850}
851
852CoherentXBar *
853CoherentXBarParams::create()
854{
855    return new CoherentXBar(this);
856}
857