3 memory system fixes:
[gem5.git] / src / mem / bus.cc
1 /*
2 * Copyright (c) 2006 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Ali Saidi
29 */
30
31 /**
32 * @file
33 * Definition of a bus object.
34 */
35
36
37 #include "base/misc.hh"
38 #include "base/trace.hh"
39 #include "mem/bus.hh"
40 #include "sim/builder.hh"
41
42 Port *
43 Bus::getPort(const std::string &if_name, int idx)
44 {
45 if (if_name == "default") {
46 if (defaultPort == NULL) {
47 defaultPort = new BusPort(csprintf("%s-default",name()), this,
48 defaultId);
49 return defaultPort;
50 } else
51 fatal("Default port already set\n");
52 }
53
54 // if_name ignored? forced to be empty?
55 int id = interfaces.size();
56 BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id);
57 interfaces.push_back(bp);
58 return bp;
59 }
60
61 /** Get the ranges of anyone other buses that we are connected to. */
62 void
63 Bus::init()
64 {
65 std::vector<BusPort*>::iterator intIter;
66
67 for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
68 (*intIter)->sendStatusChange(Port::RangeChange);
69 }
70
71 Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus)
72 {}
73
74 void Bus::BusFreeEvent::process()
75 {
76 bus->recvRetry(-1);
77 }
78
79 const char * Bus::BusFreeEvent::description()
80 {
81 return "bus became available";
82 }
83
84 void Bus::occupyBus(PacketPtr pkt)
85 {
86 //Bring tickNextIdle up to the present tick
87 //There is some potential ambiguity where a cycle starts, which might make
88 //a difference when devices are acting right around a cycle boundary. Using
89 //a < allows things which happen exactly on a cycle boundary to take up only
90 //the following cycle. Anthing that happens later will have to "wait" for
91 //the end of that cycle, and then start using the bus after that.
92 while (tickNextIdle < curTick)
93 tickNextIdle += clock;
94
95 // The packet will be sent. Figure out how long it occupies the bus, and
96 // how much of that time is for the first "word", aka bus width.
97 int numCycles = 0;
98 // Requests need one cycle to send an address
99 if (pkt->isRequest())
100 numCycles++;
101 else if (pkt->isResponse() || pkt->hasData()) {
102 // If a packet has data, it needs ceil(size/width) cycles to send it
103 // We're using the "adding instead of dividing" trick again here
104 if (pkt->hasData()) {
105 int dataSize = pkt->getSize();
106 for (int transmitted = 0; transmitted < dataSize;
107 transmitted += width) {
108 numCycles++;
109 }
110 } else {
111 // If the packet didn't have data, it must have been a response.
112 // Those use the bus for one cycle to send their data.
113 numCycles++;
114 }
115 }
116
117 // The first word will be delivered after the current tick, the delivery
118 // of the address if any, and one bus cycle to deliver the data
119 pkt->firstWordTime =
120 tickNextIdle +
121 pkt->isRequest() ? clock : 0 +
122 clock;
123
124 //Advance it numCycles bus cycles.
125 //XXX Should this use the repeated addition trick as well?
126 tickNextIdle += (numCycles * clock);
127 if (!busIdle.scheduled()) {
128 busIdle.schedule(tickNextIdle);
129 } else {
130 busIdle.reschedule(tickNextIdle);
131 }
132 DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
133 curTick, tickNextIdle);
134
135 // The bus will become idle once the current packet is delivered.
136 pkt->finishTime = tickNextIdle;
137 }
138
139 /** Function called by the port when the bus is receiving a Timing
140 * transaction.*/
141 bool
142 Bus::recvTiming(PacketPtr pkt)
143 {
144 Port *port;
145 DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n",
146 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
147
148 BusPort *pktPort;
149 if (pkt->getSrc() == defaultId)
150 pktPort = defaultPort;
151 else pktPort = interfaces[pkt->getSrc()];
152
153 // If the bus is busy, or other devices are in line ahead of the current
154 // one, put this device on the retry list.
155 if (tickNextIdle > curTick ||
156 (retryList.size() && (!inRetry || pktPort != retryList.front()))) {
157 addToRetryList(pktPort);
158 return false;
159 }
160
161 short dest = pkt->getDest();
162
163 // Make sure to clear the snoop commit flag so it doesn't think an
164 // access has been handled twice.
165 if (dest == Packet::Broadcast) {
166 port = findPort(pkt->getAddr(), pkt->getSrc());
167 pkt->flags &= ~SNOOP_COMMIT;
168 if (timingSnoop(pkt, port ? port : interfaces[pkt->getSrc()])) {
169 bool success;
170
171 pkt->flags |= SNOOP_COMMIT;
172 success = timingSnoop(pkt, port ? port : interfaces[pkt->getSrc()]);
173 assert(success);
174
175 if (pkt->flags & SATISFIED) {
176 //Cache-Cache transfer occuring
177 if (inRetry) {
178 retryList.front()->onRetryList(false);
179 retryList.pop_front();
180 inRetry = false;
181 }
182 occupyBus(pkt);
183 return true;
184 }
185 } else {
186 //Snoop didn't succeed
187 DPRINTF(Bus, "Adding a retry to RETRY list %i\n", pktPort);
188 addToRetryList(pktPort);
189 return false;
190 }
191 } else {
192 assert(dest >= 0 && dest < interfaces.size());
193 assert(dest != pkt->getSrc()); // catch infinite loops
194 port = interfaces[dest];
195 }
196
197 occupyBus(pkt);
198
199 if (port) {
200 if (port->sendTiming(pkt)) {
201 // Packet was successfully sent. Return true.
202 // Also take care of retries
203 if (inRetry) {
204 DPRINTF(Bus, "Remove retry from list %i\n", retryList.front());
205 retryList.front()->onRetryList(false);
206 retryList.pop_front();
207 inRetry = false;
208 }
209 return true;
210 }
211
212 // Packet not successfully sent. Leave or put it on the retry list.
213 DPRINTF(Bus, "Adding a retry to RETRY list %i\n", pktPort);
214 addToRetryList(pktPort);
215 return false;
216 }
217 else {
218 //Forwarding up from responder, just return true;
219 return true;
220 }
221 }
222
223 void
224 Bus::recvRetry(int id)
225 {
226 DPRINTF(Bus, "Received a retry\n");
227 // If there's anything waiting, and the bus isn't busy...
228 if (retryList.size() && curTick >= tickNextIdle) {
229 //retryingPort = retryList.front();
230 inRetry = true;
231 DPRINTF(Bus, "Sending a retry\n");
232 retryList.front()->sendRetry();
233 // If inRetry is still true, sendTiming wasn't called
234 if (inRetry)
235 {
236 retryList.front()->onRetryList(false);
237 retryList.pop_front();
238 inRetry = false;
239
240 //Bring tickNextIdle up to the present
241 while (tickNextIdle < curTick)
242 tickNextIdle += clock;
243
244 //Burn a cycle for the missed grant.
245 tickNextIdle += clock;
246
247 if (!busIdle.scheduled()) {
248 busIdle.schedule(tickNextIdle);
249 } else {
250 busIdle.reschedule(tickNextIdle);
251 }
252 }
253 }
254 //If we weren't able to drain before, we might be able to now.
255 if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) {
256 drainEvent->process();
257 // Clear the drain event once we're done with it.
258 drainEvent = NULL;
259 }
260 }
261
262 Port *
263 Bus::findPort(Addr addr, int id)
264 {
265 /* An interval tree would be a better way to do this. --ali. */
266 int dest_id = -1;
267 AddrRangeIter iter;
268 range_map<Addr,int>::iterator i;
269
270 i = portMap.find(RangeSize(addr,1));
271 if (i != portMap.end())
272 dest_id = i->second;
273
274 // Check if this matches the default range
275 if (dest_id == -1) {
276 for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) {
277 if (*iter == addr) {
278 DPRINTF(Bus, " found addr %#llx on default\n", addr);
279 return defaultPort;
280 }
281 }
282
283 if (responderSet) {
284 panic("Unable to find destination for addr (user set default "
285 "responder): %#llx", addr);
286 } else {
287 DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use "
288 "default port", addr);
289
290 return defaultPort;
291 }
292 }
293
294
295 // we shouldn't be sending this back to where it came from
296 // do the snoop access and then we should terminate
297 // the cyclical call.
298 if (dest_id == id)
299 return 0;
300
301 return interfaces[dest_id];
302 }
303
304 std::vector<int>
305 Bus::findSnoopPorts(Addr addr, int id)
306 {
307 int i = 0;
308 AddrRangeIter iter;
309 std::vector<int> ports;
310
311 while (i < portSnoopList.size())
312 {
313 if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) {
314 //Careful to not overlap ranges
315 //or snoop will be called more than once on the port
316
317 //@todo Fix this hack because ranges are overlapping
318 //need to make sure we dont't create overlapping ranges
319 bool hack_overlap = false;
320 int size = ports.size();
321 for (int j=0; j < size; j++) {
322 if (ports[j] == portSnoopList[i].portId)
323 hack_overlap = true;
324 }
325
326 if (!hack_overlap)
327 ports.push_back(portSnoopList[i].portId);
328 // DPRINTF(Bus, " found snoop addr %#llx on device%d\n", addr,
329 // portSnoopList[i].portId);
330 }
331 i++;
332 }
333 return ports;
334 }
335
336 Tick
337 Bus::atomicSnoop(PacketPtr pkt, Port *responder)
338 {
339 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc());
340 Tick response_time = 0;
341
342 while (!ports.empty())
343 {
344 if (interfaces[ports.back()] != responder) {
345 Tick response = interfaces[ports.back()]->sendAtomic(pkt);
346 if (response) {
347 assert(!response_time); //Multiple responders
348 response_time = response;
349 }
350 }
351 ports.pop_back();
352 }
353 return response_time;
354 }
355
356 void
357 Bus::functionalSnoop(PacketPtr pkt, Port *responder)
358 {
359 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc());
360
361 //The packet may be changed by another bus on snoops, restore the id after each
362 int id = pkt->getSrc();
363 while (!ports.empty() && pkt->result != Packet::Success)
364 {
365 if (interfaces[ports.back()] != responder)
366 interfaces[ports.back()]->sendFunctional(pkt);
367 ports.pop_back();
368 pkt->setSrc(id);
369 }
370 }
371
372 bool
373 Bus::timingSnoop(PacketPtr pkt, Port* responder)
374 {
375 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc());
376 bool success = true;
377
378 while (!ports.empty() && success)
379 {
380 if (interfaces[ports.back()] != responder) //Don't call if responder also, once will do
381 success = interfaces[ports.back()]->sendTiming(pkt);
382 ports.pop_back();
383 }
384
385 return success;
386 }
387
388
389 /** Function called by the port when the bus is receiving a Atomic
390 * transaction.*/
391 Tick
392 Bus::recvAtomic(PacketPtr pkt)
393 {
394 DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n",
395 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
396 assert(pkt->getDest() == Packet::Broadcast);
397 pkt->flags |= SNOOP_COMMIT;
398
399 // Assume one bus cycle in order to get through. This may have
400 // some clock skew issues yet again...
401 pkt->finishTime = curTick + clock;
402
403 Port *port = findPort(pkt->getAddr(), pkt->getSrc());
404 Tick snoopTime = atomicSnoop(pkt, port ? port : interfaces[pkt->getSrc()]);
405
406 if (snoopTime)
407 return snoopTime; //Snoop satisfies it
408 else if (port)
409 return port->sendAtomic(pkt);
410 else
411 return 0;
412 }
413
414 /** Function called by the port when the bus is receiving a Functional
415 * transaction.*/
416 void
417 Bus::recvFunctional(PacketPtr pkt)
418 {
419 DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
420 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
421 assert(pkt->getDest() == Packet::Broadcast);
422 pkt->flags |= SNOOP_COMMIT;
423
424 Port* port = findPort(pkt->getAddr(), pkt->getSrc());
425 functionalSnoop(pkt, port ? port : interfaces[pkt->getSrc()]);
426
427 // If the snooping found what we were looking for, we're done.
428 if (pkt->result != Packet::Success && port) {
429 port->sendFunctional(pkt);
430 }
431 }
432
433 /** Function called by the port when the bus is receiving a status change.*/
434 void
435 Bus::recvStatusChange(Port::Status status, int id)
436 {
437 AddrRangeList ranges;
438 AddrRangeList snoops;
439 int x;
440 AddrRangeIter iter;
441
442 assert(status == Port::RangeChange &&
443 "The other statuses need to be implemented.");
444
445 DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
446
447 if (id == defaultId) {
448 defaultRange.clear();
449 // Only try to update these ranges if the user set a default responder.
450 if (responderSet) {
451 defaultPort->getPeerAddressRanges(ranges, snoops);
452 assert(snoops.size() == 0);
453 for(iter = ranges.begin(); iter != ranges.end(); iter++) {
454 defaultRange.push_back(*iter);
455 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
456 iter->start, iter->end);
457 }
458 }
459 } else {
460
461 assert((id < interfaces.size() && id >= 0) || id == defaultId);
462 Port *port = interfaces[id];
463 range_map<Addr,int>::iterator portIter;
464 std::vector<DevMap>::iterator snoopIter;
465
466 // Clean out any previously existent ids
467 for (portIter = portMap.begin(); portIter != portMap.end(); ) {
468 if (portIter->second == id)
469 portMap.erase(portIter++);
470 else
471 portIter++;
472 }
473
474 for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) {
475 if (snoopIter->portId == id)
476 snoopIter = portSnoopList.erase(snoopIter);
477 else
478 snoopIter++;
479 }
480
481 port->getPeerAddressRanges(ranges, snoops);
482
483 for(iter = snoops.begin(); iter != snoops.end(); iter++) {
484 DevMap dm;
485 dm.portId = id;
486 dm.range = *iter;
487
488 //@todo, make sure we don't overlap ranges
489 DPRINTF(BusAddrRanges, "Adding snoop range %#llx - %#llx for id %d\n",
490 dm.range.start, dm.range.end, id);
491 portSnoopList.push_back(dm);
492 }
493
494 for(iter = ranges.begin(); iter != ranges.end(); iter++) {
495 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
496 iter->start, iter->end, id);
497 if (portMap.insert(*iter, id) == portMap.end())
498 panic("Two devices with same range\n");
499
500 }
501 }
502 DPRINTF(MMU, "port list has %d entries\n", portMap.size());
503
504 // tell all our peers that our address range has changed.
505 // Don't tell the device that caused this change, it already knows
506 for (x = 0; x < interfaces.size(); x++)
507 if (x != id)
508 interfaces[x]->sendStatusChange(Port::RangeChange);
509
510 if (id != defaultId && defaultPort)
511 defaultPort->sendStatusChange(Port::RangeChange);
512 }
513
514 void
515 Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id)
516 {
517 std::vector<DevMap>::iterator snoopIter;
518 range_map<Addr,int>::iterator portIter;
519 AddrRangeIter dflt_iter;
520 bool subset;
521
522 resp.clear();
523 snoop.clear();
524
525 DPRINTF(BusAddrRanges, "received address range request, returning:\n");
526
527 for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end();
528 dflt_iter++) {
529 resp.push_back(*dflt_iter);
530 DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start,
531 dflt_iter->end);
532 }
533 for (portIter = portMap.begin(); portIter != portMap.end(); portIter++) {
534 subset = false;
535 for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end();
536 dflt_iter++) {
537 if ((portIter->first.start < dflt_iter->start &&
538 portIter->first.end >= dflt_iter->start) ||
539 (portIter->first.start < dflt_iter->end &&
540 portIter->first.end >= dflt_iter->end))
541 fatal("Devices can not set ranges that itersect the default set\
542 but are not a subset of the default set.\n");
543 if (portIter->first.start >= dflt_iter->start &&
544 portIter->first.end <= dflt_iter->end) {
545 subset = true;
546 DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n",
547 portIter->first.start, portIter->first.end);
548 }
549 }
550 if (portIter->second != id && !subset) {
551 resp.push_back(portIter->first);
552 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",
553 portIter->first.start, portIter->first.end);
554 }
555 }
556
557 for (snoopIter = portSnoopList.begin();
558 snoopIter != portSnoopList.end(); snoopIter++)
559 {
560 if (snoopIter->portId != id) {
561 snoop.push_back(snoopIter->range);
562 DPRINTF(BusAddrRanges, " -- Snoop: %#llx : %#llx\n",
563 snoopIter->range.start, snoopIter->range.end);
564 //@todo We need to properly insert snoop ranges
565 //not overlapping the ranges (multiple)
566 }
567 }
568 }
569
570 unsigned int
571 Bus::drain(Event * de)
572 {
573 //We should check that we're not "doing" anything, and that noone is
574 //waiting. We might be idle but have someone waiting if the device we
575 //contacted for a retry didn't actually retry.
576 if (curTick >= tickNextIdle && retryList.size() == 0) {
577 return 0;
578 } else {
579 drainEvent = de;
580 return 1;
581 }
582 }
583
584 BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus)
585
586 Param<int> bus_id;
587 Param<int> clock;
588 Param<int> width;
589 Param<bool> responder_set;
590
591 END_DECLARE_SIM_OBJECT_PARAMS(Bus)
592
593 BEGIN_INIT_SIM_OBJECT_PARAMS(Bus)
594 INIT_PARAM(bus_id, "a globally unique bus id"),
595 INIT_PARAM(clock, "bus clock speed"),
596 INIT_PARAM(width, "width of the bus (bits)"),
597 INIT_PARAM(responder_set, "Is a default responder set by the user")
598 END_INIT_SIM_OBJECT_PARAMS(Bus)
599
600 CREATE_SIM_OBJECT(Bus)
601 {
602 return new Bus(getInstanceName(), bus_id, clock, width, responder_set);
603 }
604
605 REGISTER_SIM_OBJECT("Bus", Bus)