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