coherent_xbar.cc revision 4912
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#include <algorithm>
37#include <limits>
38
39#include "base/misc.hh"
40#include "base/trace.hh"
41#include "mem/bus.hh"
42#include "sim/builder.hh"
43
44Port *
45Bus::getPort(const std::string &if_name, int idx)
46{
47    if (if_name == "default") {
48        if (defaultPort == NULL) {
49            defaultPort = new BusPort(csprintf("%s-default",name()), this,
50                                      defaultId);
51            cachedBlockSizeValid = false;
52            return defaultPort;
53        } else
54            fatal("Default port already set\n");
55    }
56    int id;
57    if (if_name == "functional") {
58        if (!funcPort) {
59            id = maxId++;
60            funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id);
61            funcPortId = id;
62            interfaces[id] = funcPort;
63        }
64        return funcPort;
65    }
66
67    // if_name ignored?  forced to be empty?
68    id = maxId++;
69    assert(maxId < std::numeric_limits<typeof(maxId)>::max());
70    BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id);
71    interfaces[id] = bp;
72    cachedBlockSizeValid = false;
73    return bp;
74}
75
76void
77Bus::deletePortRefs(Port *p)
78{
79
80    BusPort *bp =  dynamic_cast<BusPort*>(p);
81    if (bp == NULL)
82        panic("Couldn't convert Port* to BusPort*\n");
83    // If this is our one functional port
84    if (funcPort == bp)
85        return;
86    interfaces.erase(bp->getId());
87    delete bp;
88}
89
90/** Get the ranges of anyone other buses that we are connected to. */
91void
92Bus::init()
93{
94    m5::hash_map<short,BusPort*>::iterator intIter;
95
96    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
97        intIter->second->sendStatusChange(Port::RangeChange);
98}
99
100Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus)
101{}
102
103void Bus::BusFreeEvent::process()
104{
105    bus->recvRetry(-1);
106}
107
108const char * Bus::BusFreeEvent::description()
109{
110    return "bus became available";
111}
112
113void Bus::occupyBus(PacketPtr pkt)
114{
115    //Bring tickNextIdle up to the present tick
116    //There is some potential ambiguity where a cycle starts, which might make
117    //a difference when devices are acting right around a cycle boundary. Using
118    //a < allows things which happen exactly on a cycle boundary to take up
119    //only the following cycle. Anything that happens later will have to "wait"
120    //for the end of that cycle, and then start using the bus after that.
121    if (tickNextIdle < curTick) {
122        tickNextIdle = curTick;
123        if (tickNextIdle % clock != 0)
124            tickNextIdle = curTick - (curTick % clock) + clock;
125    }
126
127    // The packet will be sent. Figure out how long it occupies the bus, and
128    // how much of that time is for the first "word", aka bus width.
129    int numCycles = 0;
130    // Requests need one cycle to send an address
131    if (pkt->isRequest())
132        numCycles++;
133    else if (pkt->isResponse() || pkt->hasData()) {
134        // If a packet has data, it needs ceil(size/width) cycles to send it
135        // We're using the "adding instead of dividing" trick again here
136        if (pkt->hasData()) {
137            int dataSize = pkt->getSize();
138            numCycles += dataSize/width;
139            if (dataSize % width)
140                numCycles++;
141        } else {
142            // If the packet didn't have data, it must have been a response.
143            // Those use the bus for one cycle to send their data.
144            numCycles++;
145        }
146    }
147
148    // The first word will be delivered after the current tick, the delivery
149    // of the address if any, and one bus cycle to deliver the data
150    pkt->firstWordTime =
151        tickNextIdle +
152        pkt->isRequest() ? clock : 0 +
153        clock;
154
155    //Advance it numCycles bus cycles.
156    //XXX Should this use the repeated addition trick as well?
157    tickNextIdle += (numCycles * clock);
158    if (!busIdle.scheduled()) {
159        busIdle.schedule(tickNextIdle);
160    } else {
161        busIdle.reschedule(tickNextIdle);
162    }
163    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
164            curTick, tickNextIdle);
165
166    // The bus will become idle once the current packet is delivered.
167    pkt->finishTime = tickNextIdle;
168}
169
170/** Function called by the port when the bus is receiving a Timing
171 * transaction.*/
172bool
173Bus::recvTiming(PacketPtr pkt)
174{
175    short src = pkt->getSrc();
176    DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n",
177            src, pkt->getDest(), pkt->getAddr(), pkt->cmdString());
178
179    BusPort *src_port = (src == defaultId) ? defaultPort : interfaces[src];
180
181    // If the bus is busy, or other devices are in line ahead of the current
182    // one, put this device on the retry list.
183    if (!(pkt->isResponse() || pkt->isExpressSnoop()) &&
184        (tickNextIdle > curTick ||
185         (retryList.size() && (!inRetry || src_port != retryList.front()))))
186    {
187        addToRetryList(src_port);
188        DPRINTF(Bus, "recvTiming: Bus is busy, returning false\n");
189        return false;
190    }
191
192    occupyBus(pkt);
193
194    short dest = pkt->getDest();
195    int dest_port_id;
196    Port *dest_port;
197
198    if (dest == Packet::Broadcast) {
199        dest_port_id = findPort(pkt->getAddr());
200        dest_port = interfaces[dest_port_id];
201        for (SnoopIter s_iter = snoopPorts.begin();
202             s_iter != snoopPorts.end();
203             s_iter++) {
204            BusPort *p = *s_iter;
205            if (p != dest_port && p != src_port) {
206#ifndef NDEBUG
207                // cache is not allowed to refuse snoop
208                bool success = p->sendTiming(pkt);
209                assert(success);
210#else
211                // avoid unused variable warning
212                p->sendTiming(pkt);
213#endif
214            }
215        }
216    } else {
217        assert(dest >= 0 && dest < maxId);
218        assert(dest != src); // catch infinite loops
219        dest_port_id = dest;
220        dest_port = interfaces[dest_port_id];
221    }
222
223    if (dest_port_id == src) {
224        // Must be forwarded snoop up from below...
225        assert(dest == Packet::Broadcast);
226    } else {
227        // send to actual target
228        if (!dest_port->sendTiming(pkt))  {
229            // Packet not successfully sent. Leave or put it on the retry list.
230            // illegal to block responses... can lead to deadlock
231            assert(!pkt->isResponse());
232            DPRINTF(Bus, "Adding2 a retry to RETRY list %d\n", src);
233            addToRetryList(src_port);
234            return false;
235        }
236        // send OK, fall through
237    }
238
239    // Packet was successfully sent.
240    // Also take care of retries
241    if (inRetry) {
242        DPRINTF(Bus, "Remove retry from list %d\n", src);
243        retryList.front()->onRetryList(false);
244        retryList.pop_front();
245        inRetry = false;
246    }
247    return true;
248}
249
250void
251Bus::recvRetry(int id)
252{
253    // If there's anything waiting, and the bus isn't busy...
254    if (retryList.size() && curTick >= tickNextIdle) {
255        //retryingPort = retryList.front();
256        inRetry = true;
257        DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name());
258        retryList.front()->sendRetry();
259        // If inRetry is still true, sendTiming wasn't called
260        if (inRetry)
261        {
262            retryList.front()->onRetryList(false);
263            retryList.pop_front();
264            inRetry = false;
265
266            //Bring tickNextIdle up to the present
267            while (tickNextIdle < curTick)
268                tickNextIdle += clock;
269
270            //Burn a cycle for the missed grant.
271            tickNextIdle += clock;
272
273            busIdle.reschedule(tickNextIdle, true);
274        }
275    }
276    //If we weren't able to drain before, we might be able to now.
277    if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) {
278        drainEvent->process();
279        // Clear the drain event once we're done with it.
280        drainEvent = NULL;
281    }
282}
283
284int
285Bus::findPort(Addr addr)
286{
287    /* An interval tree would be a better way to do this. --ali. */
288    int dest_id = -1;
289
290    PortIter i = portMap.find(RangeSize(addr,1));
291    if (i != portMap.end())
292        dest_id = i->second;
293
294    // Check if this matches the default range
295    if (dest_id == -1) {
296        for (AddrRangeIter iter = defaultRange.begin();
297             iter != defaultRange.end(); iter++) {
298            if (*iter == addr) {
299                DPRINTF(Bus, "  found addr %#llx on default\n", addr);
300                return defaultId;
301            }
302        }
303
304        if (responderSet) {
305            panic("Unable to find destination for addr (user set default "
306                  "responder): %#llx", addr);
307        } else {
308            DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use "
309                    "default port", addr);
310
311            return defaultId;
312        }
313    }
314
315    return dest_id;
316}
317
318
319/** Function called by the port when the bus is receiving a Atomic
320 * transaction.*/
321Tick
322Bus::recvAtomic(PacketPtr pkt)
323{
324    DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n",
325            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
326    assert(pkt->getDest() == Packet::Broadcast);
327    assert(pkt->isRequest());
328
329    // Variables for recording original command and snoop response (if
330    // any)... if a snooper respondes, we will need to restore
331    // original command so that additional snoops can take place
332    // properly
333    MemCmd orig_cmd = pkt->cmd;
334    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
335    Tick snoop_response_latency = 0;
336    int orig_src = pkt->getSrc();
337
338    int target_port_id = findPort(pkt->getAddr());
339    Port *target_port = interfaces[target_port_id];
340
341    SnoopIter s_end = snoopPorts.end();
342    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
343        BusPort *p = *s_iter;
344        // same port should not have both target addresses and snooping
345        assert(p != target_port);
346        if (p->getId() != pkt->getSrc()) {
347            Tick latency = p->sendAtomic(pkt);
348            if (pkt->isResponse()) {
349                // response from snoop agent
350                assert(pkt->cmd != orig_cmd);
351                assert(pkt->memInhibitAsserted());
352                // should only happen once
353                assert(snoop_response_cmd == MemCmd::InvalidCmd);
354                // save response state
355                snoop_response_cmd = pkt->cmd;
356                snoop_response_latency = latency;
357                // restore original packet state for remaining snoopers
358                pkt->cmd = orig_cmd;
359                pkt->setSrc(orig_src);
360                pkt->setDest(Packet::Broadcast);
361            }
362        }
363    }
364
365    Tick response_latency = 0;
366
367    // we can get requests sent up from the memory side of the bus for
368    // snooping... don't send them back down!
369    if (target_port_id != pkt->getSrc()) {
370        response_latency = target_port->sendAtomic(pkt);
371    }
372
373    // if we got a response from a snooper, restore it here
374    if (snoop_response_cmd != MemCmd::InvalidCmd) {
375        // no one else should have responded
376        assert(!pkt->isResponse());
377        assert(pkt->cmd == orig_cmd);
378        pkt->cmd = snoop_response_cmd;
379        response_latency = snoop_response_latency;
380    }
381
382    // why do we have this packet field and the return value both???
383    pkt->finishTime = curTick + response_latency;
384    return response_latency;
385}
386
387/** Function called by the port when the bus is receiving a Functional
388 * transaction.*/
389void
390Bus::recvFunctional(PacketPtr pkt)
391{
392    DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
393            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
394    assert(pkt->getDest() == Packet::Broadcast);
395
396    int port_id = findPort(pkt->getAddr());
397    Port *port = interfaces[port_id];
398    // The packet may be changed by another bus on snoops, restore the
399    // id after each
400    int src_id = pkt->getSrc();
401
402    assert(pkt->isRequest()); // hasn't already been satisfied
403
404    for (SnoopIter s_iter = snoopPorts.begin();
405         s_iter != snoopPorts.end();
406         s_iter++) {
407        BusPort *p = *s_iter;
408        if (p != port && p->getId() != src_id) {
409            p->sendFunctional(pkt);
410        }
411        if (pkt->isResponse()) {
412            break;
413        }
414        pkt->setSrc(src_id);
415    }
416
417    // If the snooping hasn't found what we were looking for, keep going.
418    if (!pkt->isResponse() && port_id != pkt->getSrc()) {
419        port->sendFunctional(pkt);
420    }
421}
422
423/** Function called by the port when the bus is receiving a status change.*/
424void
425Bus::recvStatusChange(Port::Status status, int id)
426{
427    AddrRangeList ranges;
428    bool snoops;
429    AddrRangeIter iter;
430
431    assert(status == Port::RangeChange &&
432           "The other statuses need to be implemented.");
433
434    DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
435
436    if (id == defaultId) {
437        defaultRange.clear();
438        // Only try to update these ranges if the user set a default responder.
439        if (responderSet) {
440            defaultPort->getPeerAddressRanges(ranges, snoops);
441            assert(snoops == false);
442            for(iter = ranges.begin(); iter != ranges.end(); iter++) {
443                defaultRange.push_back(*iter);
444                DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
445                        iter->start, iter->end);
446            }
447        }
448    } else {
449
450        assert((id < maxId && id >= 0) || id == defaultId);
451        BusPort *port = interfaces[id];
452
453        // Clean out any previously existent ids
454        for (PortIter portIter = portMap.begin();
455             portIter != portMap.end(); ) {
456            if (portIter->second == id)
457                portMap.erase(portIter++);
458            else
459                portIter++;
460        }
461
462        for (SnoopIter s_iter = snoopPorts.begin();
463             s_iter != snoopPorts.end(); ) {
464            if ((*s_iter)->getId() == id)
465                s_iter = snoopPorts.erase(s_iter);
466            else
467                s_iter++;
468        }
469
470        port->getPeerAddressRanges(ranges, snoops);
471
472        if (snoops) {
473            DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id);
474            snoopPorts.push_back(port);
475        }
476
477        for (iter = ranges.begin(); iter != ranges.end(); iter++) {
478            DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
479                    iter->start, iter->end, id);
480            if (portMap.insert(*iter, id) == portMap.end())
481                panic("Two devices with same range\n");
482
483        }
484    }
485    DPRINTF(MMU, "port list has %d entries\n", portMap.size());
486
487    // tell all our peers that our address range has changed.
488    // Don't tell the device that caused this change, it already knows
489    m5::hash_map<short,BusPort*>::iterator intIter;
490
491    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
492        if (intIter->first != id && intIter->first != funcPortId)
493            intIter->second->sendStatusChange(Port::RangeChange);
494
495    if (id != defaultId && defaultPort)
496        defaultPort->sendStatusChange(Port::RangeChange);
497}
498
499void
500Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id)
501{
502    resp.clear();
503    snoop = false;
504
505    DPRINTF(BusAddrRanges, "received address range request, returning:\n");
506
507    for (AddrRangeIter dflt_iter = defaultRange.begin();
508         dflt_iter != defaultRange.end(); dflt_iter++) {
509        resp.push_back(*dflt_iter);
510        DPRINTF(BusAddrRanges, "  -- Dflt: %#llx : %#llx\n",dflt_iter->start,
511                dflt_iter->end);
512    }
513    for (PortIter portIter = portMap.begin();
514         portIter != portMap.end(); portIter++) {
515        bool subset = false;
516        for (AddrRangeIter dflt_iter = defaultRange.begin();
517             dflt_iter != defaultRange.end(); dflt_iter++) {
518            if ((portIter->first.start < dflt_iter->start &&
519                portIter->first.end >= dflt_iter->start) ||
520               (portIter->first.start < dflt_iter->end &&
521                portIter->first.end >= dflt_iter->end))
522                fatal("Devices can not set ranges that itersect the default set\
523                        but are not a subset of the default set.\n");
524            if (portIter->first.start >= dflt_iter->start &&
525                portIter->first.end <= dflt_iter->end) {
526                subset = true;
527                DPRINTF(BusAddrRanges, "  -- %#llx : %#llx is a SUBSET\n",
528                    portIter->first.start, portIter->first.end);
529            }
530        }
531        if (portIter->second != id && !subset) {
532            resp.push_back(portIter->first);
533            DPRINTF(BusAddrRanges, "  -- %#llx : %#llx\n",
534                    portIter->first.start, portIter->first.end);
535        }
536    }
537
538    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end();
539         s_iter++) {
540        if ((*s_iter)->getId() != id) {
541            snoop = true;
542            break;
543        }
544    }
545}
546
547int
548Bus::findBlockSize(int id)
549{
550    if (cachedBlockSizeValid)
551        return cachedBlockSize;
552
553    int max_bs = -1;
554
555    for (PortIter portIter = portMap.begin();
556         portIter != portMap.end(); portIter++) {
557        int tmp_bs = interfaces[portIter->second]->peerBlockSize();
558        if (tmp_bs > max_bs)
559            max_bs = tmp_bs;
560    }
561    for (SnoopIter s_iter = snoopPorts.begin();
562         s_iter != snoopPorts.end(); s_iter++) {
563        int tmp_bs = (*s_iter)->peerBlockSize();
564        if (tmp_bs > max_bs)
565            max_bs = tmp_bs;
566    }
567    if (max_bs <= 0)
568        max_bs = defaultBlockSize;
569
570    if (max_bs != 64)
571        warn_once("Blocksize found to not be 64... hmm... probably not.\n");
572    cachedBlockSize = max_bs;
573    cachedBlockSizeValid = true;
574    return max_bs;
575}
576
577
578unsigned int
579Bus::drain(Event * de)
580{
581    //We should check that we're not "doing" anything, and that noone is
582    //waiting. We might be idle but have someone waiting if the device we
583    //contacted for a retry didn't actually retry.
584    if (curTick >= tickNextIdle && retryList.size() == 0) {
585        return 0;
586    } else {
587        drainEvent = de;
588        return 1;
589    }
590}
591
592void
593Bus::startup()
594{
595    if (tickNextIdle < curTick)
596        tickNextIdle = (curTick / clock) * clock + clock;
597}
598
599BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus)
600
601    Param<int> bus_id;
602    Param<int> clock;
603    Param<int> width;
604    Param<bool> responder_set;
605    Param<int> block_size;
606
607END_DECLARE_SIM_OBJECT_PARAMS(Bus)
608
609BEGIN_INIT_SIM_OBJECT_PARAMS(Bus)
610    INIT_PARAM(bus_id, "a globally unique bus id"),
611    INIT_PARAM(clock, "bus clock speed"),
612    INIT_PARAM(width, "width of the bus (bits)"),
613    INIT_PARAM(responder_set, "Is a default responder set by the user"),
614    INIT_PARAM(block_size, "Default blocksize if no device has one")
615END_INIT_SIM_OBJECT_PARAMS(Bus)
616
617CREATE_SIM_OBJECT(Bus)
618{
619    return new Bus(getInstanceName(), bus_id, clock, width, responder_set,
620            block_size);
621}
622
623REGISTER_SIM_OBJECT("Bus", Bus)
624