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