xbar.cc revision 3074
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
42Port *
43Bus::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    // if_name ignored?  forced to be empty?
54    int id = interfaces.size();
55    BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id);
56    interfaces.push_back(bp);
57    return bp;
58}
59
60/** Get the ranges of anyone other buses that we are connected to. */
61void
62Bus::init()
63{
64    std::vector<Port*>::iterator intIter;
65
66    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
67        (*intIter)->sendStatusChange(Port::RangeChange);
68}
69
70
71/** Function called by the port when the bus is receiving a Timing
72 * transaction.*/
73bool
74Bus::recvTiming(Packet *pkt)
75{
76    Port *port;
77    DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n",
78            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
79
80    short dest = pkt->getDest();
81    if (dest == Packet::Broadcast) {
82        if ( timingSnoopPhase1(pkt) )
83        {
84            timingSnoopPhase2(pkt);
85            port = findPort(pkt->getAddr(), pkt->getSrc());
86        }
87        else
88        {
89            //Snoop didn't succeed
90            retryList.push_back(interfaces[pkt->getSrc()]);
91            return false;
92        }
93    } else {
94        assert(dest >= 0 && dest < interfaces.size());
95        assert(dest != pkt->getSrc()); // catch infinite loops
96        port = interfaces[dest];
97    }
98    if (port->sendTiming(pkt))  {
99        // packet was successfully sent, just return true.
100        return true;
101    }
102
103    // packet not successfully sent
104    retryList.push_back(interfaces[pkt->getSrc()]);
105    return false;
106}
107
108void
109Bus::recvRetry(int id)
110{
111    // Go through all the elements on the list calling sendRetry on each
112    // This is not very efficient at all but it works. Ultimately we should end
113    // up with something that is more intelligent.
114    int initialSize = retryList.size();
115    int i;
116    Port *p;
117
118    for (i = 0; i < initialSize; i++) {
119        assert(retryList.size() > 0);
120        p = retryList.front();
121        retryList.pop_front();
122        p->sendRetry();
123    }
124}
125
126
127Port *
128Bus::findPort(Addr addr, int id)
129{
130    /* An interval tree would be a better way to do this. --ali. */
131    int dest_id = -1;
132    int i = 0;
133    bool found = false;
134    AddrRangeIter iter;
135
136    while (i < portList.size() && !found)
137    {
138        if (portList[i].range == addr) {
139            dest_id = portList[i].portId;
140            found = true;
141            DPRINTF(Bus, "  found addr 0x%llx on device %d\n", addr, dest_id);
142        }
143        i++;
144    }
145
146    // Check if this matches the default range
147    if (dest_id == -1) {
148        for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) {
149            if (*iter == addr) {
150                DPRINTF(Bus, "  found addr 0x%llx on default\n", addr);
151                return defaultPort;
152            }
153        }
154        panic("Unable to find destination for addr: %llx", addr);
155    }
156
157
158    // we shouldn't be sending this back to where it came from
159    assert(dest_id != id);
160
161    return interfaces[dest_id];
162}
163
164std::vector<int>
165Bus::findSnoopPorts(Addr addr, int id)
166{
167    int i = 0;
168    AddrRangeIter iter;
169    std::vector<int> ports;
170
171    while (i < portSnoopList.size())
172    {
173        if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) {
174            //Careful  to not overlap ranges
175            //or snoop will be called more than once on the port
176            ports.push_back(portSnoopList[i].portId);
177            DPRINTF(Bus, "  found snoop addr 0x%llx on device%d\n", addr,
178                    portSnoopList[i].portId);
179        }
180        i++;
181    }
182    return ports;
183}
184
185void
186Bus::atomicSnoop(Packet *pkt)
187{
188    std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc());
189
190    while (!ports.empty())
191    {
192        interfaces[ports.back()]->sendAtomic(pkt);
193        ports.pop_back();
194    }
195}
196
197bool
198Bus::timingSnoopPhase1(Packet *pkt)
199{
200    std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc());
201    bool success = true;
202
203    while (!ports.empty() && success)
204    {
205        snoopCallbacks.push_back(ports.back());
206        success = interfaces[ports.back()]->sendTiming(pkt);
207        ports.pop_back();
208    }
209    if (!success)
210    {
211        while (!snoopCallbacks.empty())
212        {
213            interfaces[snoopCallbacks.back()]->sendStatusChange(Port::SnoopSquash);
214            snoopCallbacks.pop_back();
215        }
216        return false;
217    }
218    return true;
219}
220
221void
222Bus::timingSnoopPhase2(Packet *pkt)
223{
224    bool success;
225    pkt->flags |= SNOOP_COMMIT;
226    while (!snoopCallbacks.empty())
227    {
228        success = interfaces[snoopCallbacks.back()]->sendTiming(pkt);
229        //We should not fail on snoop callbacks
230        assert(success);
231        snoopCallbacks.pop_back();
232    }
233}
234
235/** Function called by the port when the bus is receiving a Atomic
236 * transaction.*/
237Tick
238Bus::recvAtomic(Packet *pkt)
239{
240    DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n",
241            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
242    assert(pkt->getDest() == Packet::Broadcast);
243    atomicSnoop(pkt);
244    return findPort(pkt->getAddr(), pkt->getSrc())->sendAtomic(pkt);
245}
246
247/** Function called by the port when the bus is receiving a Functional
248 * transaction.*/
249void
250Bus::recvFunctional(Packet *pkt)
251{
252    DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
253            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
254    assert(pkt->getDest() == Packet::Broadcast);
255    findPort(pkt->getAddr(), pkt->getSrc())->sendFunctional(pkt);
256}
257
258/** Function called by the port when the bus is receiving a status change.*/
259void
260Bus::recvStatusChange(Port::Status status, int id)
261{
262    AddrRangeList ranges;
263    AddrRangeList snoops;
264    int x;
265    AddrRangeIter iter;
266
267    assert(status == Port::RangeChange &&
268           "The other statuses need to be implemented.");
269
270    DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
271
272    if (id == defaultId) {
273        defaultRange.clear();
274        defaultPort->getPeerAddressRanges(ranges, snoops);
275        assert(snoops.size() == 0);
276        for(iter = ranges.begin(); iter != ranges.end(); iter++) {
277            defaultRange.push_back(*iter);
278            DPRINTF(BusAddrRanges, "Adding range %llx - %llx for default range\n",
279                    iter->start, iter->end);
280        }
281    } else {
282
283        assert((id < interfaces.size() && id >= 0) || id == -1);
284        Port *port = interfaces[id];
285        std::vector<DevMap>::iterator portIter;
286        std::vector<DevMap>::iterator snoopIter;
287
288        // Clean out any previously existent ids
289        for (portIter = portList.begin(); portIter != portList.end(); ) {
290            if (portIter->portId == id)
291                portIter = portList.erase(portIter);
292            else
293                portIter++;
294        }
295
296        for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) {
297            if (snoopIter->portId == id)
298                snoopIter = portSnoopList.erase(snoopIter);
299            else
300                snoopIter++;
301        }
302
303        port->getPeerAddressRanges(ranges, snoops);
304
305        for(iter = snoops.begin(); iter != snoops.end(); iter++) {
306            DevMap dm;
307            dm.portId = id;
308            dm.range = *iter;
309
310            DPRINTF(BusAddrRanges, "Adding snoop range %llx - %llx for id %d\n",
311                    dm.range.start, dm.range.end, id);
312            portSnoopList.push_back(dm);
313        }
314
315        for(iter = ranges.begin(); iter != ranges.end(); iter++) {
316            DevMap dm;
317            dm.portId = id;
318            dm.range = *iter;
319
320            DPRINTF(BusAddrRanges, "Adding range %llx - %llx for id %d\n",
321                    dm.range.start, dm.range.end, id);
322            portList.push_back(dm);
323        }
324    }
325    DPRINTF(MMU, "port list has %d entries\n", portList.size());
326
327    // tell all our peers that our address range has changed.
328    // Don't tell the device that caused this change, it already knows
329    for (x = 0; x < interfaces.size(); x++)
330        if (x != id)
331            interfaces[x]->sendStatusChange(Port::RangeChange);
332
333    if (id != defaultId && defaultPort)
334        defaultPort->sendStatusChange(Port::RangeChange);
335}
336
337void
338Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id)
339{
340    std::vector<DevMap>::iterator portIter;
341    AddrRangeIter dflt_iter;
342    bool subset;
343
344    resp.clear();
345    snoop.clear();
346
347    DPRINTF(BusAddrRanges, "received address range request, returning:\n");
348
349    for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end();
350            dflt_iter++) {
351        resp.push_back(*dflt_iter);
352        DPRINTF(BusAddrRanges, "  -- %#llX : %#llX\n",dflt_iter->start,
353                dflt_iter->end);
354    }
355    for (portIter = portList.begin(); portIter != portList.end(); portIter++) {
356        subset = false;
357        for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end();
358                dflt_iter++) {
359            if ((portIter->range.start < dflt_iter->start &&
360                portIter->range.end >= dflt_iter->start) ||
361               (portIter->range.start < dflt_iter->end &&
362                portIter->range.end >= dflt_iter->end))
363                fatal("Devices can not set ranges that itersect the default set\
364                        but are not a subset of the default set.\n");
365            if (portIter->range.start >= dflt_iter->start &&
366                portIter->range.end <= dflt_iter->end) {
367                subset = true;
368                DPRINTF(BusAddrRanges, "  -- %#llX : %#llX is a SUBSET\n",
369                    portIter->range.start, portIter->range.end);
370            }
371        }
372        if (portIter->portId != id && !subset) {
373            resp.push_back(portIter->range);
374            DPRINTF(BusAddrRanges, "  -- %#llX : %#llX\n",
375                    portIter->range.start, portIter->range.end);
376        }
377    }
378}
379
380BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus)
381
382    Param<int> bus_id;
383
384END_DECLARE_SIM_OBJECT_PARAMS(Bus)
385
386BEGIN_INIT_SIM_OBJECT_PARAMS(Bus)
387    INIT_PARAM(bus_id, "a globally unique bus id")
388END_INIT_SIM_OBJECT_PARAMS(Bus)
389
390CREATE_SIM_OBJECT(Bus)
391{
392    return new Bus(getInstanceName(), bus_id);
393}
394
395REGISTER_SIM_OBJECT("Bus", Bus)
396