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