xbar.cc revision 9715
112SN/A/*
21762SN/A * Copyright (c) 2011-2013 ARM Limited
312SN/A * All rights reserved
412SN/A *
512SN/A * The license below extends only to copyright in the software and shall
612SN/A * not be construed as granting a license to any other intellectual
712SN/A * property including but not limited to intellectual property relating
812SN/A * to a hardware implementation of the functionality of the software
912SN/A * licensed hereunder.  You may use the software subject to the license
1012SN/A * terms below provided that you ensure that this notice is replicated
1112SN/A * unmodified and in its entirety in all distributions of the software,
1212SN/A * modified or unmodified, in source code or in binary form.
1312SN/A *
1412SN/A * Copyright (c) 2006 The Regents of The University of Michigan
1512SN/A * All rights reserved.
1612SN/A *
1712SN/A * Redistribution and use in source and binary forms, with or without
1812SN/A * modification, are permitted provided that the following conditions are
1912SN/A * met: redistributions of source code must retain the above copyright
2012SN/A * notice, this list of conditions and the following disclaimer;
2112SN/A * redistributions in binary form must reproduce the above copyright
2212SN/A * notice, this list of conditions and the following disclaimer in the
2312SN/A * documentation and/or other materials provided with the distribution;
2412SN/A * neither the name of the copyright holders nor the names of its
2512SN/A * contributors may be used to endorse or promote products derived from
2612SN/A * this software without specific prior written permission.
272665Ssaidi@eecs.umich.edu *
282665Ssaidi@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2912SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3012SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3112SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3212SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3312SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
345070Ssaidi@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
355090Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3612SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
378229Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
388229Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3912SN/A *
4012SN/A * Authors: Ali Saidi
4112SN/A *          Andreas Hansson
4212SN/A *          William Wang
435090Sgblack@eecs.umich.edu */
445090Sgblack@eecs.umich.edu
455090Sgblack@eecs.umich.edu/**
462976Sgblack@eecs.umich.edu * @file
472976Sgblack@eecs.umich.edu * Definition of a bus object.
482976Sgblack@eecs.umich.edu */
492976Sgblack@eecs.umich.edu
502976Sgblack@eecs.umich.edu#include "base/misc.hh"
515070Ssaidi@eecs.umich.edu#include "base/trace.hh"
522976Sgblack@eecs.umich.edu#include "debug/Bus.hh"
53468SN/A#include "debug/BusAddrRanges.hh"
547581SAli.Saidi@arm.com#include "debug/Drain.hh"
55468SN/A#include "mem/bus.hh"
5612SN/A
572479SN/ABaseBus::BaseBus(const BaseBusParams *p)
58360SN/A    : MemObject(p),
5912SN/A      headerCycles(p->header_cycles), width(p->width),
605070Ssaidi@eecs.umich.edu      gotAddrRanges(p->port_default_connection_count +
615070Ssaidi@eecs.umich.edu                          p->port_master_connection_count, false),
625070Ssaidi@eecs.umich.edu      gotAllAddrRanges(false), defaultPortID(InvalidPortID),
635090Sgblack@eecs.umich.edu      useDefaultRange(p->use_default_range),
645090Sgblack@eecs.umich.edu      blockSize(p->block_size)
6512SN/A{}
6612SN/A
6712SN/ABaseBus::~BaseBus()
685090Sgblack@eecs.umich.edu{
695090Sgblack@eecs.umich.edu    for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end();
703812Ssaidi@eecs.umich.edu         ++m) {
713812Ssaidi@eecs.umich.edu        delete *m;
723812Ssaidi@eecs.umich.edu    }
733812Ssaidi@eecs.umich.edu
7412SN/A    for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end();
755070Ssaidi@eecs.umich.edu         ++s) {
765070Ssaidi@eecs.umich.edu        delete *s;
773917Ssaidi@eecs.umich.edu    }
7812SN/A}
7912SN/A
802976Sgblack@eecs.umich.eduvoid
812976Sgblack@eecs.umich.eduBaseBus::init()
822976Sgblack@eecs.umich.edu{
8312SN/A    // determine the maximum peer block size, look at both the
8412SN/A    // connected master and slave modules
8512SN/A    uint32_t peer_block_size = 0;
86
87    for (MasterPortConstIter m = masterPorts.begin(); m != masterPorts.end();
88         ++m) {
89        peer_block_size = std::max((*m)->peerBlockSize(), peer_block_size);
90    }
91
92    for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end();
93         ++s) {
94        peer_block_size = std::max((*s)->peerBlockSize(), peer_block_size);
95    }
96
97    // if the peers do not have a block size, use the default value
98    // set through the bus parameters
99    if (peer_block_size != 0)
100        blockSize = peer_block_size;
101
102    // check if the block size is a value known to work
103    if (!(blockSize == 16 || blockSize == 32 || blockSize == 64 ||
104          blockSize == 128))
105        warn_once("Block size is neither 16, 32, 64 or 128 bytes.\n");
106}
107
108BaseMasterPort &
109BaseBus::getMasterPort(const std::string &if_name, PortID idx)
110{
111    if (if_name == "master" && idx < masterPorts.size()) {
112        // the master port index translates directly to the vector position
113        return *masterPorts[idx];
114    } else  if (if_name == "default") {
115        return *masterPorts[defaultPortID];
116    } else {
117        return MemObject::getMasterPort(if_name, idx);
118    }
119}
120
121BaseSlavePort &
122BaseBus::getSlavePort(const std::string &if_name, PortID idx)
123{
124    if (if_name == "slave" && idx < slavePorts.size()) {
125        // the slave port index translates directly to the vector position
126        return *slavePorts[idx];
127    } else {
128        return MemObject::getSlavePort(if_name, idx);
129    }
130}
131
132void
133BaseBus::calcPacketTiming(PacketPtr pkt)
134{
135    // the bus will be called at a time that is not necessarily
136    // coinciding with its own clock, so start by determining how long
137    // until the next clock edge (could be zero)
138    Tick offset = clockEdge() - curTick();
139
140    // determine how many cycles are needed to send the data
141    unsigned dataCycles = pkt->hasData() ? divCeil(pkt->getSize(), width) : 0;
142
143    // before setting the bus delay fields of the packet, ensure that
144    // the delay from any previous bus has been accounted for
145    if (pkt->busFirstWordDelay != 0 || pkt->busLastWordDelay != 0)
146        panic("Packet %s already has bus delay (%d, %d) that should be "
147              "accounted for.\n", pkt->cmdString(), pkt->busFirstWordDelay,
148              pkt->busLastWordDelay);
149
150    // The first word will be delivered on the cycle after the header.
151    pkt->busFirstWordDelay = (headerCycles + 1) * clockPeriod() + offset;
152
153    // Note that currently busLastWordDelay can be smaller than
154    // busFirstWordDelay if the packet has no data
155    pkt->busLastWordDelay = (headerCycles + dataCycles) * clockPeriod() +
156        offset;
157}
158
159template <typename SrcType, typename DstType>
160BaseBus::Layer<SrcType,DstType>::Layer(DstType& _port, BaseBus& _bus,
161                                       const std::string& _name) :
162    port(_port), bus(_bus), _name(_name), state(IDLE), drainManager(NULL),
163    retryingPort(NULL), waitingForPeer(NULL),
164    releaseEvent(this)
165{
166}
167
168template <typename SrcType, typename DstType>
169void BaseBus::Layer<SrcType,DstType>::occupyLayer(Tick until)
170{
171    // ensure the state is busy at this point, as the bus should
172    // transition from idle as soon as it has decided to forward the
173    // packet to prevent any follow-on calls to sendTiming seeing an
174    // unoccupied bus
175    assert(state == BUSY);
176
177    // until should never be 0 as express snoops never occupy the bus
178    assert(until != 0);
179    bus.schedule(releaseEvent, until);
180
181    // account for the occupied ticks
182    occupancy += until - curTick();
183
184    DPRINTF(BaseBus, "The bus is now busy from tick %d to %d\n",
185            curTick(), until);
186}
187
188template <typename SrcType, typename DstType>
189bool
190BaseBus::Layer<SrcType,DstType>::tryTiming(SrcType* src_port)
191{
192    // first we see if the layer is busy, next we check if we are in a
193    // retry with a port other than the current one, lastly we check
194    // if the destination port is already engaged in a transaction
195    // waiting for a retry from the peer
196    if (state == BUSY || (state == RETRY && src_port != retryingPort) ||
197        waitingForPeer != NULL) {
198        // put the port at the end of the retry list waiting for the
199        // layer to be freed up (and in the case of a busy peer, for
200        // that transaction to go through, and then the bus to free
201        // up)
202        waitingForLayer.push_back(src_port);
203        return false;
204    }
205
206    // update the state to busy
207    state = BUSY;
208
209    // reset the retrying port
210    retryingPort = NULL;
211
212    return true;
213}
214
215template <typename SrcType, typename DstType>
216void
217BaseBus::Layer<SrcType,DstType>::succeededTiming(Tick busy_time)
218{
219    // we should have gone from idle or retry to busy in the tryTiming
220    // test
221    assert(state == BUSY);
222
223    // occupy the bus accordingly
224    occupyLayer(busy_time);
225}
226
227template <typename SrcType, typename DstType>
228void
229BaseBus::Layer<SrcType,DstType>::failedTiming(SrcType* src_port,
230                                              Tick busy_time)
231{
232    // ensure no one got in between and tried to send something to
233    // this port
234    assert(waitingForPeer == NULL);
235
236    // if the source port is the current retrying one or not, we have
237    // failed in forwarding and should track that we are now waiting
238    // for the peer to send a retry
239    waitingForPeer = src_port;
240
241    // we should have gone from idle or retry to busy in the tryTiming
242    // test
243    assert(state == BUSY);
244
245    // occupy the bus accordingly
246    occupyLayer(busy_time);
247}
248
249template <typename SrcType, typename DstType>
250void
251BaseBus::Layer<SrcType,DstType>::releaseLayer()
252{
253    // releasing the bus means we should now be idle
254    assert(state == BUSY);
255    assert(!releaseEvent.scheduled());
256
257    // update the state
258    state = IDLE;
259
260    // bus layer is now idle, so if someone is waiting we can retry
261    if (!waitingForLayer.empty()) {
262        retryWaiting();
263    } else if (waitingForPeer == NULL && drainManager) {
264        DPRINTF(Drain, "Bus done draining, signaling drain manager\n");
265        //If we weren't able to drain before, do it now.
266        drainManager->signalDrainDone();
267        // Clear the drain event once we're done with it.
268        drainManager = NULL;
269    }
270}
271
272template <typename SrcType, typename DstType>
273void
274BaseBus::Layer<SrcType,DstType>::retryWaiting()
275{
276    // this should never be called with no one waiting
277    assert(!waitingForLayer.empty());
278
279    // we always go to retrying from idle
280    assert(state == IDLE);
281
282    // update the state
283    state = RETRY;
284
285    // set the retrying port to the front of the retry list and pop it
286    // off the list
287    assert(retryingPort == NULL);
288    retryingPort = waitingForLayer.front();
289    waitingForLayer.pop_front();
290
291    // tell the port to retry, which in some cases ends up calling the
292    // bus
293    retryingPort->sendRetry();
294
295    // If the bus is still in the retry state, sendTiming wasn't
296    // called in zero time (e.g. the cache does this), burn a cycle
297    if (state == RETRY) {
298        // update the state to busy and reset the retrying port, we
299        // have done our bit and sent the retry
300        state = BUSY;
301        retryingPort = NULL;
302
303        // occupy the bus layer until the next cycle ends
304        occupyLayer(bus.clockEdge(Cycles(1)));
305    }
306}
307
308template <typename SrcType, typename DstType>
309void
310BaseBus::Layer<SrcType,DstType>::recvRetry()
311{
312    // we should never get a retry without having failed to forward
313    // something to this port
314    assert(waitingForPeer != NULL);
315
316    // add the port where the failed packet originated to the front of
317    // the waiting ports for the layer, this allows us to call retry
318    // on the port immediately if the bus layer is idle
319    waitingForLayer.push_front(waitingForPeer);
320
321    // we are no longer waiting for the peer
322    waitingForPeer = NULL;
323
324    // if the bus layer is idle, retry this port straight away, if we
325    // are busy, then simply let the port wait for its turn
326    if (state == IDLE) {
327        retryWaiting();
328    } else {
329        assert(state == BUSY);
330    }
331}
332
333PortID
334BaseBus::findPort(Addr addr)
335{
336    // we should never see any address lookups before we've got the
337    // ranges of all connected slave modules
338    assert(gotAllAddrRanges);
339
340    // Check the cache
341    PortID dest_id = checkPortCache(addr);
342    if (dest_id != InvalidPortID)
343        return dest_id;
344
345    // Check the address map interval tree
346    PortMapConstIter i = portMap.find(addr);
347    if (i != portMap.end()) {
348        dest_id = i->second;
349        updatePortCache(dest_id, i->first);
350        return dest_id;
351    }
352
353    // Check if this matches the default range
354    if (useDefaultRange) {
355        if (defaultRange.contains(addr)) {
356            DPRINTF(BusAddrRanges, "  found addr %#llx on default\n",
357                    addr);
358            return defaultPortID;
359        }
360    } else if (defaultPortID != InvalidPortID) {
361        DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, "
362                "will use default port\n", addr);
363        return defaultPortID;
364    }
365
366    // we should use the range for the default port and it did not
367    // match, or the default port is not set
368    fatal("Unable to find destination for addr %#llx on bus %s\n", addr,
369          name());
370}
371
372/** Function called by the port when the bus is receiving a range change.*/
373void
374BaseBus::recvRangeChange(PortID master_port_id)
375{
376    DPRINTF(BusAddrRanges, "Received range change from slave port %s\n",
377            masterPorts[master_port_id]->getSlavePort().name());
378
379    // remember that we got a range from this master port and thus the
380    // connected slave module
381    gotAddrRanges[master_port_id] = true;
382
383    // update the global flag
384    if (!gotAllAddrRanges) {
385        // take a logical AND of all the ports and see if we got
386        // ranges from everyone
387        gotAllAddrRanges = true;
388        std::vector<bool>::const_iterator r = gotAddrRanges.begin();
389        while (gotAllAddrRanges &&  r != gotAddrRanges.end()) {
390            gotAllAddrRanges &= *r++;
391        }
392        if (gotAllAddrRanges)
393            DPRINTF(BusAddrRanges, "Got address ranges from all slaves\n");
394    }
395
396    // note that we could get the range from the default port at any
397    // point in time, and we cannot assume that the default range is
398    // set before the other ones are, so we do additional checks once
399    // all ranges are provided
400    if (master_port_id == defaultPortID) {
401        // only update if we are indeed checking ranges for the
402        // default port since the port might not have a valid range
403        // otherwise
404        if (useDefaultRange) {
405            AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges();
406
407            if (ranges.size() != 1)
408                fatal("Bus %s may only have a single default range",
409                      name());
410
411            defaultRange = ranges.front();
412        }
413    } else {
414        // the ports are allowed to update their address ranges
415        // dynamically, so remove any existing entries
416        if (gotAddrRanges[master_port_id]) {
417            for (PortMapIter p = portMap.begin(); p != portMap.end(); ) {
418                if (p->second == master_port_id)
419                    // erasing invalidates the iterator, so advance it
420                    // before the deletion takes place
421                    portMap.erase(p++);
422                else
423                    p++;
424            }
425        }
426
427        AddrRangeList ranges = masterPorts[master_port_id]->getAddrRanges();
428
429        for (AddrRangeConstIter r = ranges.begin(); r != ranges.end(); ++r) {
430            DPRINTF(BusAddrRanges, "Adding range %s for id %d\n",
431                    r->to_string(), master_port_id);
432            if (portMap.insert(*r, master_port_id) == portMap.end()) {
433                PortID conflict_id = portMap.find(*r)->second;
434                fatal("%s has two ports with same range:\n\t%s\n\t%s\n",
435                      name(),
436                      masterPorts[master_port_id]->getSlavePort().name(),
437                      masterPorts[conflict_id]->getSlavePort().name());
438            }
439        }
440    }
441
442    // if we have received ranges from all our neighbouring slave
443    // modules, go ahead and tell our connected master modules in
444    // turn, this effectively assumes a tree structure of the system
445    if (gotAllAddrRanges) {
446        DPRINTF(BusAddrRanges, "Aggregating bus ranges\n");
447        busRanges.clear();
448
449        // start out with the default range
450        if (useDefaultRange) {
451            if (!gotAddrRanges[defaultPortID])
452                fatal("Bus %s uses default range, but none provided",
453                      name());
454
455            busRanges.push_back(defaultRange);
456            DPRINTF(BusAddrRanges, "-- Adding default %s\n",
457                    defaultRange.to_string());
458        }
459
460        // merge all interleaved ranges and add any range that is not
461        // a subset of the default range
462        std::vector<AddrRange> intlv_ranges;
463        for (AddrRangeMap<PortID>::const_iterator r = portMap.begin();
464             r != portMap.end(); ++r) {
465            // if the range is interleaved then save it for now
466            if (r->first.interleaved()) {
467                // if we already got interleaved ranges that are not
468                // part of the same range, then first do a merge
469                // before we add the new one
470                if (!intlv_ranges.empty() &&
471                    !intlv_ranges.back().mergesWith(r->first)) {
472                    DPRINTF(BusAddrRanges, "-- Merging range from %d ranges\n",
473                            intlv_ranges.size());
474                    AddrRange merged_range(intlv_ranges);
475                    // next decide if we keep the merged range or not
476                    if (!(useDefaultRange &&
477                          merged_range.isSubset(defaultRange))) {
478                        busRanges.push_back(merged_range);
479                        DPRINTF(BusAddrRanges, "-- Adding merged range %s\n",
480                                merged_range.to_string());
481                    }
482                    intlv_ranges.clear();
483                }
484                intlv_ranges.push_back(r->first);
485            } else {
486                // keep the current range if not a subset of the default
487                if (!(useDefaultRange &&
488                      r->first.isSubset(defaultRange))) {
489                    busRanges.push_back(r->first);
490                    DPRINTF(BusAddrRanges, "-- Adding range %s\n",
491                            r->first.to_string());
492                }
493            }
494        }
495
496        // if there is still interleaved ranges waiting to be merged,
497        // go ahead and do it
498        if (!intlv_ranges.empty()) {
499            DPRINTF(BusAddrRanges, "-- Merging range from %d ranges\n",
500                    intlv_ranges.size());
501            AddrRange merged_range(intlv_ranges);
502            if (!(useDefaultRange && merged_range.isSubset(defaultRange))) {
503                busRanges.push_back(merged_range);
504                DPRINTF(BusAddrRanges, "-- Adding merged range %s\n",
505                        merged_range.to_string());
506            }
507        }
508
509        // also check that no range partially overlaps with the
510        // default range, this has to be done after all ranges are set
511        // as there are no guarantees for when the default range is
512        // update with respect to the other ones
513        if (useDefaultRange) {
514            for (AddrRangeConstIter r = busRanges.begin();
515                 r != busRanges.end(); ++r) {
516                // see if the new range is partially
517                // overlapping the default range
518                if (r->intersects(defaultRange) &&
519                    !r->isSubset(defaultRange))
520                    fatal("Range %s intersects the "                    \
521                          "default range of %s but is not a "           \
522                          "subset\n", r->to_string(), name());
523            }
524        }
525
526        // tell all our neighbouring master ports that our address
527        // ranges have changed
528        for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end();
529             ++s)
530            (*s)->sendRangeChange();
531    }
532
533    clearPortCache();
534}
535
536AddrRangeList
537BaseBus::getAddrRanges() const
538{
539    // we should never be asked without first having sent a range
540    // change, and the latter is only done once we have all the ranges
541    // of the connected devices
542    assert(gotAllAddrRanges);
543
544    // at the moment, this never happens, as there are no cycles in
545    // the range queries and no devices on the master side of a bus
546    // (CPU, cache, bridge etc) actually care about the ranges of the
547    // ports they are connected to
548
549    DPRINTF(BusAddrRanges, "Received address range request\n");
550
551    return busRanges;
552}
553
554unsigned
555BaseBus::deviceBlockSize() const
556{
557    return blockSize;
558}
559
560void
561BaseBus::regStats()
562{
563    using namespace Stats;
564
565    transDist
566        .init(MemCmd::NUM_MEM_CMDS)
567        .name(name() + ".trans_dist")
568        .desc("Transaction distribution")
569        .flags(nozero);
570
571    // get the string representation of the commands
572    for (int i = 0; i < MemCmd::NUM_MEM_CMDS; i++) {
573        MemCmd cmd(i);
574        const std::string &cstr = cmd.toString();
575        transDist.subname(i, cstr);
576    }
577
578    pktCount
579        .init(slavePorts.size(), masterPorts.size())
580        .name(name() + ".pkt_count")
581        .desc("Packet count per connected master and slave (bytes)")
582        .flags(total | nozero | nonan);
583
584    totPktSize
585        .init(slavePorts.size(), masterPorts.size())
586        .name(name() + ".tot_pkt_size")
587        .desc("Cumulative packet size per connected master and slave (bytes)")
588        .flags(total | nozero | nonan);
589
590    // both the packet count and total size are two-dimensional
591    // vectors, indexed by slave port id and master port id, thus the
592    // neighbouring master and slave, they do not differentiate what
593    // came from the master and was forwarded to the slave (requests
594    // and snoop responses) and what came from the slave and was
595    // forwarded to the master (responses and snoop requests)
596    for (int i = 0; i < slavePorts.size(); i++) {
597        pktCount.subname(i, slavePorts[i]->getMasterPort().name());
598        totPktSize.subname(i, slavePorts[i]->getMasterPort().name());
599        for (int j = 0; j < masterPorts.size(); j++) {
600            pktCount.ysubname(j, masterPorts[j]->getSlavePort().name());
601            totPktSize.ysubname(j, masterPorts[j]->getSlavePort().name());
602        }
603    }
604}
605
606template <typename SrcType, typename DstType>
607unsigned int
608BaseBus::Layer<SrcType,DstType>::drain(DrainManager *dm)
609{
610    //We should check that we're not "doing" anything, and that noone is
611    //waiting. We might be idle but have someone waiting if the device we
612    //contacted for a retry didn't actually retry.
613    if (state != IDLE) {
614        DPRINTF(Drain, "Bus not drained\n");
615        drainManager = dm;
616        return 1;
617    }
618    return 0;
619}
620
621template <typename SrcType, typename DstType>
622void
623BaseBus::Layer<SrcType,DstType>::regStats()
624{
625    using namespace Stats;
626
627    occupancy
628        .name(name() + ".occupancy")
629        .desc("Layer occupancy (ticks)")
630        .flags(nozero);
631
632    utilization
633        .name(name() + ".utilization")
634        .desc("Layer utilization (%)")
635        .precision(1)
636        .flags(nozero);
637
638    utilization = 100 * occupancy / simTicks;
639}
640
641/**
642 * Bus layer template instantiations. Could be removed with _impl.hh
643 * file, but since there are only two given options (MasterPort and
644 * SlavePort) it seems a bit excessive at this point.
645 */
646template class BaseBus::Layer<SlavePort,MasterPort>;
647template class BaseBus::Layer<MasterPort,SlavePort>;
648