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