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

Generated on Fri Jun 9 2017 13:03:48 for gem5 by doxygen 1.8.6