diff options
Diffstat (limited to 'src/cpu/testers/traffic_gen/generators.cc')
-rw-r--r-- | src/cpu/testers/traffic_gen/generators.cc | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/cpu/testers/traffic_gen/generators.cc b/src/cpu/testers/traffic_gen/generators.cc new file mode 100644 index 000000000..5cf40b2e6 --- /dev/null +++ b/src/cpu/testers/traffic_gen/generators.cc @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2012-2013 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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. + * + * Authors: Thomas Grass + * Andreas Hansson + * Sascha Bischoff + */ + +#include "base/random.hh" +#include "cpu/testers/traffic_gen/generators.hh" +#include "debug/TrafficGen.hh" +#include "proto/packet.pb.h" + +BaseGen::BaseGen(QueuedMasterPort& _port, MasterID master_id, Tick _duration) + : port(_port), masterID(master_id), duration(_duration) +{ +} + +void +BaseGen::send(Addr addr, unsigned size, const MemCmd& cmd) +{ + // Create new request + Request::Flags flags; + Request *req = new Request(addr, size, flags, masterID); + + // Embed it in a packet + PacketPtr pkt = new Packet(req, cmd); + + uint8_t* pkt_data = new uint8_t[req->getSize()]; + pkt->dataDynamicArray(pkt_data); + + if (cmd.isWrite()) { + memset(pkt_data, 0xA, req->getSize()); + } + + port.schedTimingReq(pkt, curTick()); +} + +void +LinearGen::enter() +{ + // reset the address and the data counter + nextAddr = startAddr; + dataManipulated = 0; + + // this test only needs to happen once, but cannot be performed + // before init() is called and the ports are connected + if (port.deviceBlockSize() && blocksize > port.deviceBlockSize()) + fatal("TrafficGen %s block size (%d) is larger than port" + " block size (%d)\n", blocksize, port.deviceBlockSize()); +} + +void +LinearGen::execute() +{ + // choose if we generate a read or a write here + bool isRead = readPercent != 0 && + (readPercent == 100 || random_mt.random<uint8_t>(0, 100) < readPercent); + + assert((readPercent == 0 && !isRead) || (readPercent == 100 && isRead) || + readPercent != 100); + + DPRINTF(TrafficGen, "LinearGen::execute: %c to addr %x, size %d\n", + isRead ? 'r' : 'w', nextAddr, blocksize); + + send(nextAddr, blocksize, isRead ? MemCmd::ReadReq : MemCmd::WriteReq); + + // increment the address + nextAddr += blocksize; + + // Add the amount of data manipulated to the total + dataManipulated += blocksize; +} + +Tick +LinearGen::nextExecuteTick() +{ + // If we have reached the end of the address space, reset the + // address to the start of the range + if (nextAddr + blocksize > endAddr) { + DPRINTF(TrafficGen, "Wrapping address to the start of " + "the range\n"); + nextAddr = startAddr; + } + + // Check to see if we have reached the data limit. If dataLimit is + // zero we do not have a data limit and therefore we will keep + // generating requests for the entire residency in this state. + if (dataLimit && dataManipulated >= dataLimit) { + DPRINTF(TrafficGen, "Data limit for LinearGen reached.\n"); + // there are no more requests, therefore return MaxTick + return MaxTick; + } else { + // return the time when the next request should take place + return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); + } +} + +void +RandomGen::enter() +{ + // reset the counter to zero + dataManipulated = 0; + + // this test only needs to happen once, but cannot be performed + // before init() is called and the ports are connected + if (port.deviceBlockSize() && blocksize > port.deviceBlockSize()) + fatal("TrafficGen %s block size (%d) is larger than port" + " block size (%d)\n", blocksize, port.deviceBlockSize()); +} + +void +RandomGen::execute() +{ + // choose if we generate a read or a write here + bool isRead = readPercent != 0 && + (readPercent == 100 || random_mt.random<uint8_t>(0, 100) < readPercent); + + assert((readPercent == 0 && !isRead) || (readPercent == 100 && isRead) || + readPercent != 100); + + // address of the request + Addr addr = random_mt.random<Addr>(startAddr, endAddr - 1); + + // round down to start address of block + addr -= addr % blocksize; + + DPRINTF(TrafficGen, "RandomGen::execute: %c to addr %x, size %d\n", + isRead ? 'r' : 'w', addr, blocksize); + + // send a new request packet + send(addr, blocksize, isRead ? MemCmd::ReadReq : MemCmd::WriteReq); + + // Add the amount of data manipulated to the total + dataManipulated += blocksize; +} + +Tick +RandomGen::nextExecuteTick() +{ + // Check to see if we have reached the data limit. If dataLimit is + // zero we do not have a data limit and therefore we will keep + // generating requests for the entire residency in this state. + if (dataLimit && dataManipulated >= dataLimit) + { + DPRINTF(TrafficGen, "Data limit for RandomGen reached.\n"); + // No more requests. Return MaxTick. + return MaxTick; + } else { + // Return the time when the next request should take place. + return curTick() + random_mt.random<Tick>(minPeriod, maxPeriod); + } +} + +TraceGen::InputStream::InputStream(const std::string& filename) + : trace(filename) +{ + // Create a protobuf message for the header and read it from the stream + Message::PacketHeader header_msg; + if (!trace.read(header_msg)) { + panic("Failed to read packet header from %s\n", filename); + + if (header_msg.tick_freq() != SimClock::Frequency) { + panic("Trace %s was recorded with a different tick frequency %d\n", + header_msg.tick_freq()); + } + } +} + +void +TraceGen::InputStream::reset() +{ + trace.reset(); +} + +bool +TraceGen::InputStream::read(TraceElement& element) +{ + Message::Packet pkt_msg; + if (trace.read(pkt_msg)) { + element.cmd = pkt_msg.cmd(); + element.addr = pkt_msg.addr(); + element.blocksize = pkt_msg.size(); + element.tick = pkt_msg.tick(); + return true; + } + + // We have reached the end of the file + return false; +} + +Tick +TraceGen::nextExecuteTick() { + if (traceComplete) + // We are at the end of the file, thus we have no more data in + // the trace Return MaxTick to signal that there will be no + // more transactions in this active period for the state. + return MaxTick; + + + //Reset the nextElement to the default values + currElement = nextElement; + nextElement.clear(); + + // We need to look at the next line to calculate the next time an + // event occurs, or potentially return MaxTick to signal that + // nothing has to be done. + if (!trace.read(nextElement)) { + traceComplete = true; + return MaxTick; + } + + DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n", + currElement.cmd.isRead() ? 'r' : 'w', + currElement.addr, + currElement.blocksize, + currElement.tick + tickOffset, + currElement.tick); + + DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n", + nextElement.cmd.isRead() ? 'r' : 'w', + nextElement.addr, + nextElement.blocksize, + nextElement.tick + tickOffset, + nextElement.tick); + + return tickOffset + nextElement.tick; +} + +void +TraceGen::enter() +{ + // update the trace offset to the time where the state was entered. + tickOffset = curTick(); + + // clear everything + nextElement.clear(); + currElement.clear(); + + traceComplete = false; +} + +void +TraceGen::execute() +{ + // it is the responsibility of nextExecuteTick to prevent the + // state graph from executing the state if it should not + assert(currElement.isValid()); + + DPRINTF(TrafficGen, "TraceGen::execute: %c %d %d %d\n", + currElement.cmd.isRead() ? 'r' : 'w', + currElement.addr, + currElement.blocksize, + currElement.tick); + + send(currElement.addr + addrOffset, currElement.blocksize, + currElement.cmd); +} + +void +TraceGen::exit() +{ + // Check if we reached the end of the trace file. If we did not + // then we want to generate a warning stating that not the entire + // trace was played. + if (!traceComplete) { + warn("Trace player %s was unable to replay the entire trace!\n", + name()); + } + + // Clear any flags and start over again from the beginning of the + // file + trace.reset(); +} |