Port: Align port names in C++ and Python
[gem5.git] / src / mem / coherent_bus.cc
1 /*
2 * Copyright (c) 2011-2012 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 bus object.
48 */
49
50 #include "base/misc.hh"
51 #include "base/trace.hh"
52 #include "debug/BusAddrRanges.hh"
53 #include "debug/CoherentBus.hh"
54 #include "mem/coherent_bus.hh"
55
56 CoherentBus::CoherentBus(const CoherentBusParams *p)
57 : BaseBus(p), reqLayer(*this, ".reqLayer", p->clock),
58 respLayer(*this, ".respLayer", p->clock),
59 snoopRespLayer(*this, ".snoopRespLayer", p->clock)
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 CoherentBusMasterPort(portName, *this, i);
67 masterPorts.push_back(bp);
68 }
69
70 // see if we have a default slave device connected and if so add
71 // our corresponding master port
72 if (p->port_default_connection_count) {
73 defaultPortID = masterPorts.size();
74 std::string portName = name() + ".default";
75 MasterPort* bp = new CoherentBusMasterPort(portName, *this,
76 defaultPortID);
77 masterPorts.push_back(bp);
78 }
79
80 // create the slave ports, once again starting at zero
81 for (int i = 0; i < p->port_slave_connection_count; ++i) {
82 std::string portName = csprintf("%s.slave[%d]", name(), i);
83 SlavePort* bp = new CoherentBusSlavePort(portName, *this, i);
84 slavePorts.push_back(bp);
85 }
86
87 clearPortCache();
88 }
89
90 void
91 CoherentBus::init()
92 {
93 // iterate over our slave ports and determine which of our
94 // neighbouring master ports are snooping and add them as snoopers
95 for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
96 ++p) {
97 // check if the connected master port is snooping
98 if ((*p)->isSnooping()) {
99 DPRINTF(BusAddrRanges, "Adding snooping master %s\n",
100 (*p)->getMasterPort().name());
101 snoopPorts.push_back(*p);
102 }
103 }
104
105 if (snoopPorts.empty())
106 warn("CoherentBus %s has no snooping ports attached!\n", name());
107 }
108
109 bool
110 CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
111 {
112 // determine the source port based on the id
113 SlavePort *src_port = slavePorts[slave_port_id];
114
115 // remember if the packet is an express snoop
116 bool is_express_snoop = pkt->isExpressSnoop();
117
118 // test if the bus should be considered occupied for the current
119 // port, and exclude express snoops from the check
120 if (!is_express_snoop && !reqLayer.tryTiming(src_port)) {
121 DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
122 src_port->name(), pkt->cmdString(), pkt->getAddr());
123 return false;
124 }
125
126 DPRINTF(CoherentBus, "recvTimingReq: src %s %s expr %d 0x%x\n",
127 src_port->name(), pkt->cmdString(), is_express_snoop,
128 pkt->getAddr());
129
130 // set the source port for routing of the response
131 pkt->setSrc(slave_port_id);
132
133 Tick headerFinishTime = is_express_snoop ? 0 : calcPacketTiming(pkt);
134 Tick packetFinishTime = is_express_snoop ? 0 : pkt->finishTime;
135
136 // uncacheable requests need never be snooped
137 if (!pkt->req->isUncacheable()) {
138 // the packet is a memory-mapped request and should be
139 // broadcasted to our snoopers but the source
140 forwardTiming(pkt, slave_port_id);
141 }
142
143 // remember if we add an outstanding req so we can undo it if
144 // necessary, if the packet needs a response, we should add it
145 // as outstanding and express snoops never fail so there is
146 // not need to worry about them
147 bool add_outstanding = !is_express_snoop && pkt->needsResponse();
148
149 // keep track that we have an outstanding request packet
150 // matching this request, this is used by the coherency
151 // mechanism in determining what to do with snoop responses
152 // (in recvTimingSnoop)
153 if (add_outstanding) {
154 // we should never have an exsiting request outstanding
155 assert(outstandingReq.find(pkt->req) == outstandingReq.end());
156 outstandingReq.insert(pkt->req);
157 }
158
159 // since it is a normal request, determine the destination
160 // based on the address and attempt to send the packet
161 bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
162
163 // if this is an express snoop, we are done at this point
164 if (is_express_snoop) {
165 assert(success);
166 } else {
167 // for normal requests, check if successful
168 if (!success) {
169 // inhibited packets should never be forced to retry
170 assert(!pkt->memInhibitAsserted());
171
172 // if it was added as outstanding and the send failed, then
173 // erase it again
174 if (add_outstanding)
175 outstandingReq.erase(pkt->req);
176
177 DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
178 src_port->name(), pkt->cmdString(), pkt->getAddr());
179
180 // update the bus state and schedule an idle event
181 reqLayer.failedTiming(src_port, headerFinishTime);
182 } else {
183 // update the bus state and schedule an idle event
184 reqLayer.succeededTiming(packetFinishTime);
185 }
186 }
187
188 return success;
189 }
190
191 bool
192 CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
193 {
194 // determine the source port based on the id
195 MasterPort *src_port = masterPorts[master_port_id];
196
197 // test if the bus should be considered occupied for the current
198 // port
199 if (!respLayer.tryTiming(src_port)) {
200 DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
201 src_port->name(), pkt->cmdString(), pkt->getAddr());
202 return false;
203 }
204
205 DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x\n",
206 src_port->name(), pkt->cmdString(), pkt->getAddr());
207
208 calcPacketTiming(pkt);
209 Tick packetFinishTime = pkt->finishTime;
210
211 // the packet is a normal response to a request that we should
212 // have seen passing through the bus
213 assert(outstandingReq.find(pkt->req) != outstandingReq.end());
214
215 // remove it as outstanding
216 outstandingReq.erase(pkt->req);
217
218 // send the packet to the destination through one of our slave
219 // ports, as determined by the destination field
220 bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
221
222 // currently it is illegal to block responses... can lead to
223 // deadlock
224 assert(success);
225
226 respLayer.succeededTiming(packetFinishTime);
227
228 return true;
229 }
230
231 void
232 CoherentBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
233 {
234 DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x\n",
235 masterPorts[master_port_id]->name(), pkt->cmdString(),
236 pkt->getAddr());
237
238 // we should only see express snoops from caches
239 assert(pkt->isExpressSnoop());
240
241 // set the source port for routing of the response
242 pkt->setSrc(master_port_id);
243
244 // forward to all snoopers
245 forwardTiming(pkt, InvalidPortID);
246
247 // a snoop request came from a connected slave device (one of
248 // our master ports), and if it is not coming from the slave
249 // device responsible for the address range something is
250 // wrong, hence there is nothing further to do as the packet
251 // would be going back to where it came from
252 assert(master_port_id == findPort(pkt->getAddr()));
253 }
254
255 bool
256 CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
257 {
258 // determine the source port based on the id
259 SlavePort* src_port = slavePorts[slave_port_id];
260
261 // test if the bus should be considered occupied for the current
262 // port
263 if (!snoopRespLayer.tryTiming(src_port)) {
264 DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
265 src_port->name(), pkt->cmdString(), pkt->getAddr());
266 return false;
267 }
268
269 DPRINTF(CoherentBus, "recvTimingSnoop: src %s %s 0x%x\n",
270 src_port->name(), pkt->cmdString(), pkt->getAddr());
271
272 // get the destination from the packet
273 PortID dest = pkt->getDest();
274
275 // responses are never express snoops
276 assert(!pkt->isExpressSnoop());
277
278 calcPacketTiming(pkt);
279 Tick packetFinishTime = pkt->finishTime;
280
281 // determine if the response is from a snoop request we
282 // created as the result of a normal request (in which case it
283 // should be in the outstandingReq), or if we merely forwarded
284 // someone else's snoop request
285 if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
286 // this is a snoop response to a snoop request we
287 // forwarded, e.g. coming from the L1 and going to the L2
288 // this should be forwarded as a snoop response
289 bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
290 assert(success);
291 } else {
292 // we got a snoop response on one of our slave ports,
293 // i.e. from a coherent master connected to the bus, and
294 // since we created the snoop request as part of
295 // recvTiming, this should now be a normal response again
296 outstandingReq.erase(pkt->req);
297
298 // this is a snoop response from a coherent master, with a
299 // destination field set on its way through the bus as
300 // request, hence it should never go back to where the
301 // snoop response came from, but instead to where the
302 // original request came from
303 assert(slave_port_id != dest);
304
305 // as a normal response, it should go back to a master
306 // through one of our slave ports
307 bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
308
309 // currently it is illegal to block responses... can lead
310 // to deadlock
311 assert(success);
312 }
313
314 snoopRespLayer.succeededTiming(packetFinishTime);
315
316 return true;
317 }
318
319
320 void
321 CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
322 {
323 for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
324 SlavePort *p = *s;
325 // we could have gotten this request from a snooping master
326 // (corresponding to our own slave port that is also in
327 // snoopPorts) and should not send it back to where it came
328 // from
329 if (exclude_slave_port_id == InvalidPortID ||
330 p->getId() != exclude_slave_port_id) {
331 // cache is not allowed to refuse snoop
332 p->sendTimingSnoopReq(pkt);
333 }
334 }
335 }
336
337 void
338 CoherentBus::recvRetry()
339 {
340 // responses and snoop responses never block on forwarding them,
341 // so the retry will always be coming from a port to which we
342 // tried to forward a request
343 reqLayer.recvRetry();
344 }
345
346 Tick
347 CoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
348 {
349 DPRINTF(CoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
350 slavePorts[slave_port_id]->name(), pkt->getAddr(),
351 pkt->cmdString());
352
353 MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
354 Tick snoop_response_latency = 0;
355
356 // uncacheable requests need never be snooped
357 if (!pkt->req->isUncacheable()) {
358 // forward to all snoopers but the source
359 std::pair<MemCmd, Tick> snoop_result =
360 forwardAtomic(pkt, slave_port_id);
361 snoop_response_cmd = snoop_result.first;
362 snoop_response_latency = snoop_result.second;
363 }
364
365 // even if we had a snoop response, we must continue and also
366 // perform the actual request at the destination
367 PortID dest_id = findPort(pkt->getAddr());
368
369 // forward the request to the appropriate destination
370 Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
371
372 // if we got a response from a snooper, restore it here
373 if (snoop_response_cmd != MemCmd::InvalidCmd) {
374 // no one else should have responded
375 assert(!pkt->isResponse());
376 pkt->cmd = snoop_response_cmd;
377 response_latency = snoop_response_latency;
378 }
379
380 pkt->finishTime = curTick() + response_latency;
381 return response_latency;
382 }
383
384 Tick
385 CoherentBus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
386 {
387 DPRINTF(CoherentBus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
388 masterPorts[master_port_id]->name(), pkt->getAddr(),
389 pkt->cmdString());
390
391 // forward to all snoopers
392 std::pair<MemCmd, Tick> snoop_result =
393 forwardAtomic(pkt, InvalidPortID);
394 MemCmd snoop_response_cmd = snoop_result.first;
395 Tick snoop_response_latency = snoop_result.second;
396
397 if (snoop_response_cmd != MemCmd::InvalidCmd)
398 pkt->cmd = snoop_response_cmd;
399
400 pkt->finishTime = curTick() + snoop_response_latency;
401 return snoop_response_latency;
402 }
403
404 std::pair<MemCmd, Tick>
405 CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
406 {
407 // the packet may be changed on snoops, record the original
408 // command to enable us to restore it between snoops so that
409 // additional snoops can take place properly
410 MemCmd orig_cmd = pkt->cmd;
411 MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
412 Tick snoop_response_latency = 0;
413
414 for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
415 SlavePort *p = *s;
416 // we could have gotten this request from a snooping master
417 // (corresponding to our own slave port that is also in
418 // snoopPorts) and should not send it back to where it came
419 // from
420 if (exclude_slave_port_id == InvalidPortID ||
421 p->getId() != exclude_slave_port_id) {
422 Tick latency = p->sendAtomicSnoop(pkt);
423 // in contrast to a functional access, we have to keep on
424 // going as all snoopers must be updated even if we get a
425 // response
426 if (pkt->isResponse()) {
427 // response from snoop agent
428 assert(pkt->cmd != orig_cmd);
429 assert(pkt->memInhibitAsserted());
430 // should only happen once
431 assert(snoop_response_cmd == MemCmd::InvalidCmd);
432 // save response state
433 snoop_response_cmd = pkt->cmd;
434 snoop_response_latency = latency;
435 // restore original packet state for remaining snoopers
436 pkt->cmd = orig_cmd;
437 }
438 }
439 }
440
441 // the packet is restored as part of the loop and any potential
442 // snoop response is part of the returned pair
443 return std::make_pair(snoop_response_cmd, snoop_response_latency);
444 }
445
446 void
447 CoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
448 {
449 if (!pkt->isPrint()) {
450 // don't do DPRINTFs on PrintReq as it clutters up the output
451 DPRINTF(CoherentBus,
452 "recvFunctional: packet src %s addr 0x%x cmd %s\n",
453 slavePorts[slave_port_id]->name(), pkt->getAddr(),
454 pkt->cmdString());
455 }
456
457 // uncacheable requests need never be snooped
458 if (!pkt->req->isUncacheable()) {
459 // forward to all snoopers but the source
460 forwardFunctional(pkt, slave_port_id);
461 }
462
463 // there is no need to continue if the snooping has found what we
464 // were looking for and the packet is already a response
465 if (!pkt->isResponse()) {
466 PortID dest_id = findPort(pkt->getAddr());
467
468 masterPorts[dest_id]->sendFunctional(pkt);
469 }
470 }
471
472 void
473 CoherentBus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
474 {
475 if (!pkt->isPrint()) {
476 // don't do DPRINTFs on PrintReq as it clutters up the output
477 DPRINTF(CoherentBus,
478 "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
479 masterPorts[master_port_id]->name(), pkt->getAddr(),
480 pkt->cmdString());
481 }
482
483 // forward to all snoopers
484 forwardFunctional(pkt, InvalidPortID);
485 }
486
487 void
488 CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
489 {
490 for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
491 SlavePort *p = *s;
492 // we could have gotten this request from a snooping master
493 // (corresponding to our own slave port that is also in
494 // snoopPorts) and should not send it back to where it came
495 // from
496 if (exclude_slave_port_id == InvalidPortID ||
497 p->getId() != exclude_slave_port_id)
498 p->sendFunctionalSnoop(pkt);
499
500 // if we get a response we are done
501 if (pkt->isResponse()) {
502 break;
503 }
504 }
505 }
506
507 unsigned int
508 CoherentBus::drain(Event *de)
509 {
510 // sum up the individual layers
511 return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
512 }
513
514 CoherentBus *
515 CoherentBusParams::create()
516 {
517 return new CoherentBus(this);
518 }