blob: 366f99aecf96cf3899477e13dcec8eb0de63132d [file] [log] [blame]
// Copyright (c) 2021 The Regents of the University of California
// 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.
#include "sst_responder_subcomponent.hh"
#include <cassert>
#include <sstream>
#include <iomanip>
#ifdef fatal // gem5 sets this
#undef fatal
#endif
SSTResponderSubComponent::SSTResponderSubComponent(SST::ComponentId_t id,
SST::Params& params)
: SubComponent(id)
{
sstResponder = new SSTResponder(this);
gem5SimObjectName = params.find<std::string>("response_receiver_name", "");
memSize = params.find<std::string>("mem_size", "8GiB");
if (gem5SimObjectName == "")
assert(false && "The response_receiver_name must be specified");
}
SSTResponderSubComponent::~SSTResponderSubComponent()
{
delete sstResponder;
}
void
SSTResponderSubComponent::setTimeConverter(SST::TimeConverter* tc)
{
timeConverter = tc;
// Get the memory interface
SST::Params interface_params;
// This is how you tell the interface the name of the port it should use
interface_params.insert("port", "port");
interface_params.insert("mem_size", memSize.c_str());
// Loads a “memHierarchy.memInterface” into index 0 of the “memory” slot
// SHARE_PORTS means the interface can use our port as if it were its own
// INSERT_STATS means the interface will inherit our statistic
// configuration (e.g., if ours are enabled, the interface’s will be too)
memoryInterface = \
loadAnonymousSubComponent<SST::Interfaces::SimpleMem>(
"memHierarchy.memInterface", "memory", 0,
SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS,
interface_params, timeConverter,
new SST::Interfaces::SimpleMem::Handler<SSTResponderSubComponent>(
this, &SSTResponderSubComponent::portEventHandler)
);
assert(memoryInterface != NULL);
}
void
SSTResponderSubComponent::setOutputStream(SST::Output* output_)
{
output = output_;
}
void
SSTResponderSubComponent::setResponseReceiver(
gem5::OutgoingRequestBridge* gem5_bridge)
{
responseReceiver = gem5_bridge;
responseReceiver->setResponder(sstResponder);
}
bool
SSTResponderSubComponent::handleTimingReq(
SST::Interfaces::SimpleMem::Request* request)
{
memoryInterface->sendRequest(request);
return true;
}
void
SSTResponderSubComponent::init(unsigned phase)
{
if (phase == 1) {
for (auto p: responseReceiver->getInitData()) {
gem5::Addr addr = p.first;
std::vector<uint8_t> data = p.second;
SST::Interfaces::SimpleMem::Request* request = \
new SST::Interfaces::SimpleMem::Request(
SST::Interfaces::SimpleMem::Request::Command::Write, addr,
data.size(), data
);
memoryInterface->sendInitData(request);
}
}
memoryInterface->init(phase);
}
void
SSTResponderSubComponent::setup()
{
}
bool
SSTResponderSubComponent::findCorrespondingSimObject(gem5::Root* gem5_root)
{
gem5::OutgoingRequestBridge* receiver = \
dynamic_cast<gem5::OutgoingRequestBridge*>(
gem5_root->find(gem5SimObjectName.c_str()));
setResponseReceiver(receiver);
return receiver != NULL;
}
void
SSTResponderSubComponent::handleSwapReqResponse(
SST::Interfaces::SimpleMem::Request* request)
{
// get the data, then,
// 1. send a response to gem5 with the original data
// 2. send a write to memory with atomic op applied
SST::Interfaces::SimpleMem::Request::id_t request_id = request->id;
TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
assert(it != sstRequestIdToPacketMap.end());
std::vector<uint8_t> data = request->data;
// step 1
gem5::PacketPtr pkt = it->second;
pkt->setData(request->data.data());
pkt->makeAtomicResponse();
pkt->headerDelay = pkt->payloadDelay = 0;
if (blocked() || !responseReceiver->sendTimingResp(pkt))
responseQueue.push(pkt);
// step 2
(*(pkt->getAtomicOp()))(data.data()); // apply the atomic op
SST::Interfaces::SimpleMem::Request::Command cmd = \
SST::Interfaces::SimpleMem::Request::Command::Write;
SST::Interfaces::SimpleMem::Addr addr = request->addr;
auto data_size = data.size();
SST::Interfaces::SimpleMem::Request* write_request = \
new SST::Interfaces::SimpleMem::Request(
cmd, addr, data_size, data
);
write_request->setMemFlags(
SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED);
memoryInterface->sendRequest(write_request);
delete request;
}
void
SSTResponderSubComponent::portEventHandler(
SST::Interfaces::SimpleMem::Request* request)
{
// Expect to handle an SST response
SST::Interfaces::SimpleMem::Request::id_t request_id = request->id;
TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
// replying to a prior request
if (it != sstRequestIdToPacketMap.end()) {
gem5::PacketPtr pkt = it->second; // the packet that needs response
// Responding to a SwapReq requires a special handler
// 1. send a response to gem5 with the original data
// 2. send a write to memory with atomic op applied
if ((gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::SwapReq) {
handleSwapReqResponse(request);
return;
}
sstRequestIdToPacketMap.erase(it);
Translator::inplaceSSTRequestToGem5PacketPtr(pkt, request);
if (blocked() || !(responseReceiver->sendTimingResp(pkt)))
responseQueue.push(pkt);
} else { // we can handle unexpected invalidates, but nothing else.
SST::Interfaces::SimpleMem::Request::Command cmd = request->cmd;
if (cmd == SST::Interfaces::SimpleMem::Request::Command::WriteResp)
return;
assert(cmd == SST::Interfaces::SimpleMem::Request::Command::Inv);
// make Req/Pkt for Snoop/no response needed
// presently no consideration for masterId, packet type, flags...
gem5::RequestPtr req = std::make_shared<gem5::Request>(
request->addr, request->size, 0, 0
);
gem5::PacketPtr pkt = new gem5::Packet(
req, gem5::MemCmd::InvalidateReq);
// Clear out bus delay notifications
pkt->headerDelay = pkt->payloadDelay = 0;
responseReceiver->sendTimingSnoopReq(pkt);
}
delete request;
}
void
SSTResponderSubComponent::handleRecvRespRetry()
{
while (blocked() &&
responseReceiver->sendTimingResp(responseQueue.front()))
responseQueue.pop();
}
void
SSTResponderSubComponent::handleRecvFunctional(gem5::PacketPtr pkt)
{
}
bool
SSTResponderSubComponent::blocked()
{
return !(responseQueue.empty());
}