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