configs,ext: Updated the gem5 SST Bridge to use SST 13.0.0 (#396)

This change updates the gem5 SST Bridge to use SST 13.0.0. Changes are
made to replace SimpleMem class to StandardMem class as SimpleMem will
be deprecated in SST 14 and above. In addition, the translator.hh is
updated to translate more types of gem5 packets. A new parameter `ports`
was added on SST's side when invoking the gem5 component which does not
require recompiling the gem5 component whenever a new outgoing bridge is
added in a gem5 config.
This commit is contained in:
Bobby R. Bruce
2023-10-11 13:34:48 -07:00
committed by GitHub
12 changed files with 354 additions and 158 deletions

View File

@@ -1,8 +1,8 @@
# Installing SST
The links to download SST source code are available here
[http://sst-simulator.org/SSTPages/SSTMainDownloads/].
This guide is using the most recent SST version (11.0.0) as of September 2021.
The links to download SST source code are available at
<http://sst-simulator.org/SSTPages/SSTMainDownloads/>.
This guide is using the most recent SST version (13.0.0) as of September 2023.
The following guide assumes `$SST_CORE_HOME` as the location where SST will be
installed.
@@ -11,14 +11,14 @@ installed.
### Downloading the SST-Core Source Code
```sh
wget https://github.com/sstsimulator/sst-core/releases/download/v11.1.0_Final/sstcore-11.1.0.tar.gz
tar xf sstcore-11.1.0.tar.gz
wget https://github.com/sstsimulator/sst-core/releases/download/v13.0.0_Final/sstcore-13.0.0.tar.gz
tar xzf sstcore-13.0.0.tar.gz
```
### Installing SST-Core
```sh
cd sstcore-11.1.0
cd sstcore-13.0.0
./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \
--disable-mpi # optional, used when MPI is not available.
make all -j$(nproc)
@@ -36,14 +36,14 @@ export PATH=$SST_CORE_HOME/bin:$PATH
### Downloading the SST-Elements Source Code
```sh
wget https://github.com/sstsimulator/sst-elements/releases/download/v11.1.0_Final/sstelements-11.1.0.tar.gz
tar xf sstelements-11.1.0.tar.gz
wget https://github.com/sstsimulator/sst-elements/releases/download/v13.0.0_Final/sstelements-13.0.0.tar.gz
tar xzf sstelements-13.0.0.tar.gz
```
### Installing SST-Elements
```sh
cd sst-elements-library-11.1.0
cd sst-elements-library-13.0.0
./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \
--with-sst-core=$SST_CORE_HOME
make all -j$(nproc)
@@ -58,24 +58,36 @@ echo "export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$SST_CORE_HOME/lib/pkgconfig/" >>
### Building gem5 library
At the root of gem5 folder,
At the root of the gem5 folder, you need to compile gem5 as a library. This
varies dependent on which OS you are using. If you're using Linux, then
execute the following:
```sh
scons build/RISCV/libgem5_opt.so -j $(nproc) --without-tcmalloc --duplicate-sources
```
In case you're using Mac, then type the following:
```sh
scons build/RISCV/libgem5_opt.dylib -j $(nproc) --without-tcmalloc --duplicate-sources
```
**Note:** `--without-tcmalloc` is required to avoid a conflict with SST's malloc.
`--duplicate-sources` is required as the compilation of SST depends on sources to be present in the "build" directory.
**Note:**
* `--without-tcmalloc` is required to avoid a conflict with SST's malloc.
* `--duplicate-sources` is required as the compilation of SST depends on sources to be present in the "build" directory.
* The Mac version was tested on a Macbook Air with M2 processor.
### Compiling the SST integration
At the root of gem5 folder,
Go to the SST directory in the gem5 repo.
```sh
cd ext/sst
make
```
Depending on your OS, you need to copy the correct `Makefile.xxx` file to
`Makefile`.
```sh
cp Makefile.xxx Makefile # linux or mac
make -j4
```
The make file is hardcoded to RISC-V. IN the case you wish to compile to ARM,
edit the Makefile or pass `ARCH=RISCV` to `ARCH=ARM` while compiling.
### Running an example simulation
See `README.md`

21
ext/sst/Makefile.linux Normal file
View File

@@ -0,0 +1,21 @@
SST_VERSION=SST-13.0.0 # Name of the .pc file in lib/pkgconfig where SST is installed
GEM5_LIB=gem5_opt
ARCH=RISCV
OFLAG=3
LDFLAGS=-shared -fno-common ${shell pkg-config ${SST_VERSION} --libs} -L../../build/${ARCH}/ -Wl,-rpath ../../build/${ARCH}
CXXFLAGS=-std=c++17 -g -O${OFLAG} -fPIC ${shell pkg-config ${SST_VERSION} --cflags} ${shell python3-config --includes} -I../../build/${ARCH}/ -I../../ext/pybind11/include/ -I../../build/softfloat/ -I../../ext
CPPFLAGS+=-MMD -MP
SRC=$(wildcard *.cc)
.PHONY: clean all
all: libgem5.so
libgem5.so: $(SRC:%.cc=%.o)
${CXX} ${CPPFLAGS} ${LDFLAGS} $? -o $@ -l${GEM5_LIB}
-include $(SRC:%.cc=%.d)
clean:
${RM} *.[do] libgem5.so

21
ext/sst/Makefile.mac Normal file
View File

@@ -0,0 +1,21 @@
SST_VERSION=SST-13.0.0 # Name of the .pc file in lib/pkgconfig where SST is installed
GEM5_LIB=gem5_opt
ARCH=RISCV
OFLAG=3
LDFLAGS=-shared -fno-common ${shell pkg-config ${SST_VERSION} --libs} -L../../build/${ARCH}/ -Wl,-rpath ../../build/${ARCH}
CXXFLAGS=-std=c++17 -g -O${OFLAG} -fPIC ${shell pkg-config ${SST_VERSION} --cflags} ${shell python3-config --includes} -I../../build/${ARCH}/ -I../../ext/pybind11/include/ -I../../build/softfloat/ -I../../ext
CPPFLAGS+=-MMD -MP
SRC=$(wildcard *.cc)
.PHONY: clean all
all: libgem5.dylib
libgem5.dylib: $(SRC:%.cc=%.o)
${CXX} ${CPPFLAGS} ${LDFLAGS} $? -o $@ -l${GEM5_LIB}
-include $(SRC:%.cc=%.d)
clean:
${RM} *.[do] libgem5.dylib

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -70,7 +70,6 @@
#include <sst/core/sst_config.h>
#include <sst/core/componentInfo.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/elements/memHierarchy/memEvent.h>
#include <sst/elements/memHierarchy/memTypes.h>
#include <sst/elements/memHierarchy/util.h>
@@ -169,16 +168,29 @@ gem5Component::gem5Component(SST::ComponentId_t id, SST::Params& params):
registerAsPrimaryComponent();
primaryComponentDoNotEndSim();
systemPort = \
loadUserSubComponent<SSTResponderSubComponent>("system_port",0);
cachePort = \
loadUserSubComponent<SSTResponderSubComponent>("cache_port", 0);
systemPort->setTimeConverter(timeConverter);
systemPort->setOutputStream(&(output));
cachePort->setTimeConverter(timeConverter);
cachePort->setOutputStream(&(output));
// We need to add another parameter when invoking gem5 scripts from SST to
// keep a track of all the OutgoingBridges. This will allow to add or
// remove OutgoingBridges from gem5 configs without the need to recompile
// the ext/sst source everytime.
std::string ports = params.find<std::string>("ports", "");
if (ports.empty()) {
output.fatal(
CALL_INFO, -1, "Component %s must have a 'ports' parameter.\n",
getName().c_str()
);
}
// Split the port names using the util method defined.
splitPortNames(ports);
for (int i = 0 ; i < sstPortCount ; i++) {
std::cout << sstPortNames[i] << std::endl;
sstPorts.push_back(
loadUserSubComponent<SSTResponderSubComponent>(sstPortNames[i], 0)
);
// If the name defined in the `ports` is incorrect, then the program
// will crash when calling `setTimeConverter`.
sstPorts[i]->setTimeConverter(timeConverter);
sstPorts[i]->setOutputStream(&(output));
}
}
gem5Component::~gem5Component()
@@ -216,8 +228,9 @@ gem5Component::init(unsigned phase)
// find the corresponding SimObject for each SSTResponderSubComponent
gem5::Root* gem5_root = gem5::Root::root();
systemPort->findCorrespondingSimObject(gem5_root);
cachePort->findCorrespondingSimObject(gem5_root);
for (auto &port : sstPorts) {
port->findCorrespondingSimObject(gem5_root);
}
// initialize the gem5 event queue
if (!(threadInitialized)) {
@@ -230,17 +243,18 @@ gem5Component::init(unsigned phase)
}
}
systemPort->init(phase);
cachePort->init(phase);
for (auto &port : sstPorts) {
port->init(phase);
}
}
void
gem5Component::setup()
{
output.verbose(CALL_INFO, 1, 0, "Component is being setup.\n");
systemPort->setup();
cachePort->setup();
for (auto &port : sstPorts) {
port->setup();
}
}
void
@@ -427,3 +441,16 @@ gem5Component::splitCommandArgs(std::string &cmd, std::vector<char*> &args)
for (auto part: parsed_args)
args.push_back(strdup(part.c_str()));
}
void
gem5Component::splitPortNames(std::string port_names)
{
std::vector<std::string> parsed_args = tokenizeString(
port_names, {'\\', ' ', '\'', '\"'}
);
sstPortCount = 0;
for (auto part: parsed_args) {
sstPortNames.push_back(strdup(part.c_str()));
sstPortCount++;
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,6 @@
#include <sst/core/sst_config.h>
#include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h>
@@ -108,15 +107,20 @@ class gem5Component: public SST::Component
private:
SST::Output output;
SSTResponderSubComponent* systemPort;
SSTResponderSubComponent* cachePort;
uint64_t clocksProcessed;
SST::TimeConverter* timeConverter;
gem5::GlobalSimLoopExitEvent *simulateLimitEvent;
std::vector<char*> args;
// We need a list of incoming port names so that we don't need to recompile
// everytime when we add a new OutgoingBridge from python.
std::vector<SSTResponderSubComponent*> sstPorts;
std::vector<std::string> sstPortNames;
int sstPortCount;
void initPython(int argc, char **argv);
void splitCommandArgs(std::string &cmd, std::vector<char*> &args);
void splitPortNames(std::string port_names);
bool threadInitialized;
@@ -139,6 +143,7 @@ class gem5Component: public SST::Component
)
SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS(
// These are the generally expected ports.
{"system_port", "Connection to gem5 system_port", "gem5.gem5Bridge"},
{"cache_port", "Connection to gem5 CPU", "gem5.gem5Bridge"}
)

View File

@@ -10,7 +10,7 @@
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Copyright (c) 2021 The Regents of the University of California
# Copyright (c) 2021-2023 The Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -46,9 +46,10 @@ cache_link_latency = "1ps"
kernel = "vmlinux_exit.arm64"
cpu_clock_rate = "3GHz"
# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory
# currently, we do not subtract 0x80000000 from the request's address to get the "real" address
# so, the mem_size would always be 2GiB larger than the desired memory size
# gem5 will send requests to physical addresses of range [0x80000000, inf) to
# memory currently, we do not subtract 0x80000000 from the request's address to
# get the "real" address so, the mem_size would always be 2GiB larger than the
# desired memory size
memory_size_gem5 = "4GiB"
memory_size_sst = "16GiB"
addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue()
@@ -69,9 +70,22 @@ gem5_command = f" ../../configs/example/sst/arm_fs.py \
--cpu-clock-rate {cpu_clock_rate} \
--memory-size {memory_size_gem5}"
# We keep a track of all the memory ports that we have.
sst_ports = {
"system_port" : "system.system_outgoing_bridge",
"cache_port" : "system.memory_outgoing_bridge"
}
# We need a list of ports.
port_list = []
for port in sst_ports:
port_list.append(port)
cpu_params = {
"frequency": cpu_clock_rate,
"cmd": gem5_command,
"ports" : " ".join(port_list),
"debug_flags" : ""
}
gem5_node = sst.Component("gem5_node", "gem5.gem5Component")
@@ -79,16 +93,16 @@ gem5_node.addParams(cpu_params)
cache_bus = sst.Component("cache_bus", "memHierarchy.Bus")
cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } )
system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization
# for initialization
system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0)
system_port.addParams({
"response_receiver_name": "system.system_outgoing_bridge",
"response_receiver_name": sst_ports["system_port"],
"mem_size": memory_size_sst
})
cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5
# SST -> gem5
cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0)
cache_port.addParams({
"response_receiver_name": "system.memory_outgoing_bridge",
"response_receiver_name": sst_ports["cache_port"],
"mem_size": memory_size_sst
})
@@ -98,11 +112,12 @@ l1_cache.addParams(l1_params)
# Memory
memctrl = sst.Component("memory", "memHierarchy.MemController")
# `addr_range_end` should be changed accordingly to memory_size_sst
memctrl.addParams({
"debug" : "0",
"clock" : "1GHz",
"request_width" : "64",
"addr_range_end" : addr_range_end, # should be changed accordingly to memory_size_sst
"addr_range_end" : addr_range_end,
})
memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
memory.addParams({

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2021 The Regents of the University of California
# Copyright (c) 2021-2023 The Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -34,9 +34,10 @@ cache_link_latency = "1ps"
bbl = "riscv-boot-exit-nodisk"
cpu_clock_rate = "3GHz"
# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory
# currently, we do not subtract 0x80000000 from the request's address to get the "real" address
# so, the mem_size would always be 2GiB larger than the desired memory size
# gem5 will send requests to physical addresses of range [0x80000000, inf) to
# memory currently, we do not subtract 0x80000000 from the request's address to
# get the "real" address so, the mem_size would always be 2GiB larger than the
# desired memory size
memory_size_gem5 = "4GiB"
memory_size_sst = "6GiB"
addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue()
@@ -52,10 +53,24 @@ l1_params = {
"L1" : "1",
}
# We keep a track of all the memory ports that we have.
sst_ports = {
"system_port" : "system.system_outgoing_bridge",
"cache_port" : "system.memory_outgoing_bridge"
}
# We need a list of ports.
port_list = []
for port in sst_ports:
port_list.append(port)
cpu_params = {
"frequency": cpu_clock_rate,
"cmd": " ../../configs/example/sst/riscv_fs.py --cpu-clock-rate {} --memory-size {}".format(cpu_clock_rate, memory_size_gem5),
"debug_flags": ""
"cmd": " ../../configs/example/sst/riscv_fs.py"
+ f" --cpu-clock-rate {cpu_clock_rate}"
+ f" --memory-size {memory_size_gem5}",
"debug_flags": "",
"ports" : " ".join(port_list)
}
gem5_node = sst.Component("gem5_node", "gem5.gem5Component")
@@ -64,11 +79,14 @@ gem5_node.addParams(cpu_params)
cache_bus = sst.Component("cache_bus", "memHierarchy.Bus")
cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } )
system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization
system_port.addParams({ "response_receiver_name": "system.system_outgoing_bridge"}) # tell the SubComponent the name of the corresponding SimObject
# for initialization
system_port = gem5_node.setSubComponent(port_list[0], "gem5.gem5Bridge", 0)
# tell the SubComponent the name of the corresponding SimObject
system_port.addParams({ "response_receiver_name": sst_ports["system_port"]})
cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5
cache_port.addParams({ "response_receiver_name": "system.memory_outgoing_bridge"})
# SST -> gem5
cache_port = gem5_node.setSubComponent(port_list[1], "gem5.gem5Bridge", 0)
cache_port.addParams({ "response_receiver_name": sst_ports["cache_port"]})
# L1 cache
l1_cache = sst.Component("l1_cache", "memHierarchy.Cache")
@@ -76,11 +94,12 @@ l1_cache.addParams(l1_params)
# Memory
memctrl = sst.Component("memory", "memHierarchy.MemController")
# `addr_range_end` should be changed accordingly to memory_size_sst
memctrl.addParams({
"debug" : "0",
"clock" : "1GHz",
"request_width" : "64",
"addr_range_end" : addr_range_end, # should be changed accordingly to memory_size_sst
"addr_range_end" : addr_range_end,
})
memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
memory.addParams({

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -35,9 +35,8 @@
#include <sst/core/sst_config.h>
#include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/core/interfaces/stdMem.h>
#include <sst/core/eli/elementinfo.h>
#include <sst/core/link.h>

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -64,13 +64,12 @@ SSTResponderSubComponent::setTimeConverter(SST::TimeConverter* tc)
// 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 interfaces 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)
memoryInterface = loadAnonymousSubComponent<SST::Interfaces::StandardMem>(
"memHierarchy.standardInterface", "memory", 0,
SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS,
interface_params, timeConverter,
new SST::Interfaces::StandardMem::Handler<SSTResponderSubComponent>(
this, &SSTResponderSubComponent::portEventHandler)
);
assert(memoryInterface != NULL);
}
@@ -91,9 +90,9 @@ SSTResponderSubComponent::setResponseReceiver(
bool
SSTResponderSubComponent::handleTimingReq(
SST::Interfaces::SimpleMem::Request* request)
SST::Interfaces::StandardMem::Request* request)
{
memoryInterface->sendRequest(request);
memoryInterface->send(request);
return true;
}
@@ -104,12 +103,10 @@ SSTResponderSubComponent::init(unsigned phase)
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);
SST::Interfaces::StandardMem::Request* request = \
new SST::Interfaces::StandardMem::Write(
addr, data.size(), data);
memoryInterface->sendUntimedData(request);
}
}
memoryInterface->init(phase);
@@ -132,20 +129,24 @@ SSTResponderSubComponent::findCorrespondingSimObject(gem5::Root* gem5_root)
void
SSTResponderSubComponent::handleSwapReqResponse(
SST::Interfaces::SimpleMem::Request* request)
SST::Interfaces::StandardMem::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;
SST::Interfaces::StandardMem::Request::id_t request_id = request->getID();
TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
assert(it != sstRequestIdToPacketMap.end());
std::vector<uint8_t> data = request->data;
std::vector<uint8_t> data = \
dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(request)->data;
// step 1
gem5::PacketPtr pkt = it->second;
pkt->setData(request->data.data());
pkt->setData(
dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(
request)->data.data()
);
pkt->makeAtomicResponse();
pkt->headerDelay = pkt->payloadDelay = 0;
if (blocked() || !responseReceiver->sendTimingResp(pkt))
@@ -153,27 +154,29 @@ SSTResponderSubComponent::handleSwapReqResponse(
// 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;
// This is a Write. Need to use the Write visitor class. But the original
// request is a read response. Therefore, we need to find the address and
// the data size and then call Write.
SST::Interfaces::StandardMem::Addr addr = \
dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(request)->pAddr;
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);
// Create the Write request here.
SST::Interfaces::StandardMem::Request* write_request = \
new SST::Interfaces::StandardMem::Write(addr, data_size, data);
// F_LOCKED flag in SimpleMem was changed to ReadLock and WriteUnlock
// visitor classes. This has to be addressed in the future. The boot test
// works without using ReadLock and WriteUnlock classes.
memoryInterface->send(write_request);
delete request;
}
void
SSTResponderSubComponent::portEventHandler(
SST::Interfaces::SimpleMem::Request* request)
SST::Interfaces::StandardMem::Request* request)
{
// Expect to handle an SST response
SST::Interfaces::SimpleMem::Request::id_t request_id = request->id;
SST::Interfaces::StandardMem::Request::id_t request_id = request->getID();
TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
@@ -193,19 +196,27 @@ SSTResponderSubComponent::portEventHandler(
Translator::inplaceSSTRequestToGem5PacketPtr(pkt, request);
if (blocked() || !(responseReceiver->sendTimingResp(pkt)))
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)
}
} else {
// we can handle unexpected invalidates, but nothing else.
if (SST::Interfaces::StandardMem::Read* test =
dynamic_cast<SST::Interfaces::StandardMem::Read*>(request)) {
return;
assert(cmd == SST::Interfaces::SimpleMem::Request::Command::Inv);
// make Req/Pkt for Snoop/no response needed
}
else if (SST::Interfaces::StandardMem::WriteResp* test =
dynamic_cast<SST::Interfaces::StandardMem::WriteResp*>(
request)) {
return;
}
// 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
);
dynamic_cast<SST::Interfaces::StandardMem::FlushAddr*>(
request)->pAddr,
dynamic_cast<SST::Interfaces::StandardMem::FlushAddr*>(
request)->size, 0, 0);
gem5::PacketPtr pkt = new gem5::Packet(
req, gem5::MemCmd::InvalidateReq);

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -36,10 +36,8 @@
#include <sst/core/sst_config.h>
#include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/core/interfaces/stdMem.h>
#include <sst/core/eli/elementinfo.h>
#include <sst/core/link.h>
@@ -59,12 +57,12 @@ class SSTResponderSubComponent: public SST::SubComponent
gem5::OutgoingRequestBridge* responseReceiver;
gem5::SSTResponderInterface* sstResponder;
SST::Interfaces::SimpleMem* memoryInterface;
SST::Interfaces::StandardMem* memoryInterface;
SST::TimeConverter* timeConverter;
SST::Output* output;
std::queue<gem5::PacketPtr> responseQueue;
std::vector<SST::Interfaces::SimpleMem::Request*> initRequests;
std::vector<SST::Interfaces::StandardMem::Request*> initRequests;
std::string gem5SimObjectName;
std::string memSize;
@@ -78,7 +76,7 @@ class SSTResponderSubComponent: public SST::SubComponent
void setOutputStream(SST::Output* output_);
void setResponseReceiver(gem5::OutgoingRequestBridge* gem5_bridge);
void portEventHandler(SST::Interfaces::SimpleMem::Request* request);
void portEventHandler(SST::Interfaces::StandardMem::Request* request);
bool blocked();
void setup();
@@ -86,18 +84,18 @@ class SSTResponderSubComponent: public SST::SubComponent
// return true if the SimObject could be found
bool findCorrespondingSimObject(gem5::Root* gem5_root);
bool handleTimingReq(SST::Interfaces::SimpleMem::Request* request);
bool handleTimingReq(SST::Interfaces::StandardMem::Request* request);
void handleRecvRespRetry();
void handleRecvFunctional(gem5::PacketPtr pkt);
void handleSwapReqResponse(SST::Interfaces::SimpleMem::Request* request);
void handleSwapReqResponse(SST::Interfaces::StandardMem::Request* request);
TPacketMap sstRequestIdToPacketMap;
public: // register the component to SST
SST_ELI_REGISTER_SUBCOMPONENT_API(SSTResponderSubComponent);
SST_ELI_REGISTER_SUBCOMPONENT_DERIVED(
SST_ELI_REGISTER_SUBCOMPONENT(
SSTResponderSubComponent,
"gem5", // SST will look for libgem5.so
"gem5", // SST will look for libgem5.so or libgem5.dylib
"gem5Bridge",
SST_ELI_ELEMENT_VERSION(1, 0, 0),
"Initialize gem5 and link SST's ports to gem5's ports",
@@ -106,7 +104,7 @@ class SSTResponderSubComponent: public SST::SubComponent
SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS(
{"memory", "Interface to the memory subsystem", \
"SST::Interfaces::SimpleMem"}
"SST::Interfaces::StandardMem"}
)
SST_ELI_DOCUMENT_PORTS(

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 The Regents of the University of California
// Copyright (c) 2021-2023 The Regents of the University of California
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,87 +27,143 @@
#ifndef __TRANSLATOR_H__
#define __TRANSLATOR_H__
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stdMem.h>
#include <sst/core/interfaces/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/elements/memHierarchy/memEvent.h>
#include <sst/elements/memHierarchy/memTypes.h>
#include <sst/elements/memHierarchy/util.h>
typedef std::unordered_map<SST::Interfaces::SimpleMem::Request::id_t,
typedef std::unordered_map<SST::Interfaces::StandardMem::Request::id_t,
gem5::PacketPtr> TPacketMap;
namespace Translator
{
inline SST::Interfaces::SimpleMem::Request*
inline SST::Interfaces::StandardMem::Request*
gem5RequestToSSTRequest(gem5::PacketPtr pkt,
TPacketMap& sst_request_id_to_packet_map)
{
SST::Interfaces::SimpleMem::Request::Command cmd;
// Listing all the different SST Memory commands.
enum sst_standard_mem_commands
{
Read,
ReadResp,
Write,
WriteResp,
FlushAddr,
FlushResp,
ReadLock,
WriteUnlock,
LoadLink,
StoreConditional,
MoveData,
CustomReq,
CustomResp,
InvNotify
};
// SST's standard memory class has visitor classes for all the different
// types of memory commands. Request class now does not have a command
// variable. Instead for different types of request, we now need to
// dynamically cast the class object. I'm using an extra variable to map
// the type of command for SST.
int sst_command_type = -1;
// StandardMem only has one cache flush class with an option to flush or
// flush and invalidate an address. By default, this is set to true so that
// it corresponds to ge,::MemCmd::InvalidateReq
bool flush_addr_flag = true;
switch ((gem5::MemCmd::Command)pkt->cmd.toInt()) {
case gem5::MemCmd::HardPFReq:
case gem5::MemCmd::SoftPFReq:
case gem5::MemCmd::SoftPFExReq:
case gem5::MemCmd::LoadLockedReq:
case gem5::MemCmd::ReadExReq:
case gem5::MemCmd::ReadCleanReq:
case gem5::MemCmd::ReadSharedReq:
case gem5::MemCmd::ReadReq:
case gem5::MemCmd::SwapReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::Read;
sst_command_type = Read;
break;
case gem5::MemCmd::StoreCondReq:
case gem5::MemCmd::WritebackDirty:
case gem5::MemCmd::WritebackClean:
case gem5::MemCmd::WriteReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::Write;
sst_command_type = Write;
break;
case gem5::MemCmd::CleanInvalidReq:
case gem5::MemCmd::InvalidateReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLineInv;
sst_command_type = FlushAddr;
break;
case gem5::MemCmd::CleanSharedReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLine;
sst_command_type = FlushAddr;
flush_addr_flag = false;
break;
default:
panic("Unable to convert gem5 packet: %s\n", pkt->cmd.toString());
}
SST::Interfaces::SimpleMem::Addr addr = pkt->getAddr();
uint8_t* data_ptr = pkt->getPtr<uint8_t>();
SST::Interfaces::StandardMem::Addr addr = pkt->getAddr();
auto data_size = pkt->getSize();
std::vector<uint8_t> data = std::vector<uint8_t>(
data_ptr, data_ptr + data_size
);
std::vector<uint8_t> data;
// Need to make sure that the command type is a Write to retrive the data
// data_ptr.
if (sst_command_type == Write) {
uint8_t* data_ptr = pkt->getPtr<uint8_t>();
data = std::vector<uint8_t>(data_ptr, data_ptr + data_size);
SST::Interfaces::SimpleMem::Request* request = \
new SST::Interfaces::SimpleMem::Request(
cmd, addr, data_size, data
);
}
// Now convert a sst StandardMem request.
SST::Interfaces::StandardMem::Request* request = nullptr;
// find the corresponding memory command type.
switch(sst_command_type) {
case Read:
request = new SST::Interfaces::StandardMem::Read(addr, data_size);
break;
case Write:
request =
new SST::Interfaces::StandardMem::Write(addr, data_size, data);
break;
case FlushAddr: {
// StandardMem::FlushAddr has a invoking variable called `depth`
// which defines the number of cache levels to invalidate. Ideally
// this has to be input from the SST config, however in
// implementation I'm hardcoding this value to 2.
int cache_depth = 2;
request =
new SST::Interfaces::StandardMem::FlushAddr(
addr, data_size, flush_addr_flag, cache_depth);
break;
}
default:
panic("Unable to translate command %d to Request class!",
sst_command_type);
}
if ((gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::LoadLockedReq
|| (gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::SwapReq
|| pkt->req->isLockedRMW()) {
request->setMemFlags(
SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED);
} else if ((gem5::MemCmd::Command)pkt->cmd.toInt() == \
// F_LOCKED is deprecated. Therefore I'm skipping this flag for the
// StandardMem request.
} else if ((gem5::MemCmd::Command)pkt->cmd.toInt() ==
gem5::MemCmd::StoreCondReq) {
request->setMemFlags(
SST::Interfaces::SimpleMem::Request::Flags::F_LLSC);
// F_LLSC is deprecated. Therefore I'm skipping this flag for the
// StandardMem request.
}
if (pkt->req->isUncacheable()) {
request->setFlags(
SST::Interfaces::SimpleMem::Request::Flags::F_NONCACHEABLE);
request->setFlag(
SST::Interfaces::StandardMem::Request::Flag::F_NONCACHEABLE);
}
if (pkt->needsResponse())
sst_request_id_to_packet_map[request->id] = pkt;
sst_request_id_to_packet_map[request->getID()] = pkt;
return request;
}
inline void
inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt,
SST::Interfaces::SimpleMem::Request* request)
SST::Interfaces::StandardMem::Request* request)
{
pkt->makeResponse();
@@ -116,8 +172,18 @@ inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt,
// SC interprets ExtraData == 1 as the store was successful
pkt->req->setExtraData(1);
}
pkt->setData(request->data.data());
// If there is data in the request, send it back. Only ReadResp requests
// have data associated with it. Other packets does not need to be casted.
if (!pkt->isWrite()) {
// Need to verify whether the packet is a ReadResp, otherwise the
// program will try to incorrectly cast the request object.
if (SST::Interfaces::StandardMem::ReadResp* test =
dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(request)) {
pkt->setData(dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(
request)->data.data()
);
}
}
// Clear out bus delay notifications
pkt->headerDelay = pkt->payloadDelay = 0;