The memory interface param was hardcoded to 8GiB therefore not matching any python changes in the memory size Change-Id: I180f57f662886010a38a9b7ebbdbb73e0ae48276 Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/53625 Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Reviewed-by: Hoa Nguyen <hoanguyen@ucdavis.edu> Maintainer: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
240 lines
8.1 KiB
C++
240 lines
8.1 KiB
C++
// 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());
|
||
}
|