/* * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file Definition of a simple bus bridge without buffering. */ #include #include "base/trace.hh" #include "mem/bridge.hh" #include "sim/builder.hh" Bridge::BridgePort::BridgePort(const std::string &_name, Bridge *_bridge, BridgePort *_otherPort, int _delay, int _queueLimit) : Port(_name), bridge(_bridge), otherPort(_otherPort), delay(_delay), outstandingResponses(0), queueLimit(_queueLimit), sendEvent(this) { } Bridge::Bridge(const std::string &n, int qsa, int qsb, Tick _delay, int write_ack) : MemObject(n), portA(n + "-portA", this, &portB, _delay, qsa), portB(n + "-portB", this, &portA, _delay, qsa), ackWrites(write_ack) { } Port * Bridge::getPort(const std::string &if_name) { BridgePort *port; if (if_name == "side_a") port = &portA; else if (if_name == "side_b") port = &portB; else return NULL; if (port->getPeer() != NULL) panic("bridge side %s already connected to.", if_name); return port; } void Bridge::init() { // Make sure that both sides are connected to. if (portA.getPeer() == NULL || portB.getPeer() == NULL) panic("Both ports of bus bridge are not connected to a bus.\n"); } /** Function called by the port when the bus is receiving a Timing * transaction.*/ bool Bridge::BridgePort::recvTiming(Packet *pkt) { DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr()); if (pkt->isResponse()) { // This is a response for a request we forwarded earlier. The // corresponding PacketBuffer should be stored in the packet's // senderState field. PacketBuffer *buf = dynamic_cast(pkt->senderState); assert(buf != NULL); // set up new packet dest & senderState based on values saved // from original request buf->fixResponse(pkt); DPRINTF(BusBridge, " is response, new dest %d\n", pkt->getDest()); delete buf; } return otherPort->queueForSendTiming(pkt); } bool Bridge::BridgePort::queueForSendTiming(Packet *pkt) { if (queueFull()) return false; Tick readyTime = curTick + delay; PacketBuffer *buf = new PacketBuffer(pkt, readyTime); // If we're about to put this packet at the head of the queue, we // need to schedule an event to do the transmit. Otherwise there // should already be an event scheduled for sending the head // packet. if (sendQueue.empty()) { sendEvent.schedule(readyTime); } sendQueue.push_back(buf); // Did we just become blocked? If yes, let other side know. if (queueFull()) otherPort->sendStatusChange(Port::Blocked); return true; } void Bridge::BridgePort::finishSend(PacketBuffer *buf) { if (buf->expectResponse) { // Must wait for response. We just need to count outstanding // responses (in case we want to cap them); PacketBuffer // pointer will be recovered on response. ++outstandingResponses; DPRINTF(BusBridge, " successful: awaiting response (%d)\n", outstandingResponses); } else { // no response expected... deallocate packet buffer now. DPRINTF(BusBridge, " successful: no response expected\n"); delete buf; } // If there are more packets to send, schedule event to try again. if (!sendQueue.empty()) { buf = sendQueue.front(); sendEvent.schedule(std::max(buf->ready, curTick + 1)); } } void Bridge::BridgePort::trySend() { assert(!sendQueue.empty()); PacketBuffer *buf = sendQueue.front(); assert(buf->ready <= curTick); Packet *pkt = buf->pkt; DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", buf->origSrc, pkt->getDest(), pkt->getAddr()); if (sendTiming(pkt)) { // send successful sendQueue.pop_front(); buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it finishSend(buf); } else { DPRINTF(BusBridge, " unsuccessful\n"); } } Packet * Bridge::BridgePort::recvRetry() { PacketBuffer *buf = sendQueue.front(); Packet *pkt = buf->pkt; finishSend(buf); return pkt; } /** Function called by the port when the bus is receiving a Atomic * transaction.*/ Tick Bridge::BridgePort::recvAtomic(Packet *pkt) { return otherPort->sendAtomic(pkt) + delay; } /** Function called by the port when the bus is receiving a Functional * transaction.*/ void Bridge::BridgePort::recvFunctional(Packet *pkt) { std::list::iterator i; bool pktContinue = true; for (i = sendQueue.begin(); i != sendQueue.end(); ++i) { if (pkt->intersect((*i)->pkt)) { pktContinue &= fixPacket(pkt, (*i)->pkt); } } if (pktContinue) { otherPort->sendFunctional(pkt); } } /** Function called by the port when the bus is receiving a status change.*/ void Bridge::BridgePort::recvStatusChange(Port::Status status) { if (status == Port::Blocked || status == Port::Unblocked) return; otherPort->sendStatusChange(status); } void Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) { otherPort->getPeerAddressRanges(resp, snoop); } BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bridge) Param queue_size_a; Param queue_size_b; Param delay; Param write_ack; END_DECLARE_SIM_OBJECT_PARAMS(Bridge) BEGIN_INIT_SIM_OBJECT_PARAMS(Bridge) INIT_PARAM(queue_size_a, "The size of the queue for data coming into side a"), INIT_PARAM(queue_size_b, "The size of the queue for data coming into side b"), INIT_PARAM(delay, "The miminum delay to cross this bridge"), INIT_PARAM(write_ack, "Acknowledge any writes that are received.") END_INIT_SIM_OBJECT_PARAMS(Bridge) CREATE_SIM_OBJECT(Bridge) { return new Bridge(getInstanceName(), queue_size_a, queue_size_b, delay, write_ack); } REGISTER_SIM_OBJECT("Bridge", Bridge)