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,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. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@@ -104,16 +104,9 @@ def createHiFivePlatform(system):
system.platform.pci_host.pio = system.membus.mem_side_ports system.platform.pci_host.pio = system.membus.mem_side_ports
system.platform.rtc = RiscvRTC(frequency=Frequency("100MHz")) system.platform.rtc = RiscvRTC(frequency=Frequency("10MHz"))
system.platform.clint.int_pin = system.platform.rtc.int_pin system.platform.clint.int_pin = system.platform.rtc.int_pin
system.pma_checker = PMAChecker(
uncacheable=[
*system.platform._on_chip_ranges(),
*system.platform._off_chip_ranges(),
]
)
system.iobus = IOXBar() system.iobus = IOXBar()
system.bridge = Bridge(delay="50ns") system.bridge = Bridge(delay="50ns")
system.bridge.mem_side_port = system.iobus.cpu_side_ports system.bridge.mem_side_port = system.iobus.cpu_side_ports
@@ -122,6 +115,15 @@ def createHiFivePlatform(system):
system.platform.setNumCores(1) system.platform.setNumCores(1)
for cpu in system.cpu:
# pma_checker has to be added for each of the system cpus.
cpu.mmu.pma_checker = PMAChecker(
uncacheable=[
*system.platform._on_chip_ranges(),
*system.platform._off_chip_ranges(),
]
)
system.platform.attachOnChipIO(system.membus) system.platform.attachOnChipIO(system.membus)
system.platform.attachOffChipIO(system.iobus) system.platform.attachOffChipIO(system.iobus)

View File

@@ -1,8 +1,8 @@
# Installing SST # Installing SST
The links to download SST source code are available here The links to download SST source code are available at
[http://sst-simulator.org/SSTPages/SSTMainDownloads/]. <http://sst-simulator.org/SSTPages/SSTMainDownloads/>.
This guide is using the most recent SST version (11.0.0) as of September 2021. 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 The following guide assumes `$SST_CORE_HOME` as the location where SST will be
installed. installed.
@@ -11,14 +11,14 @@ installed.
### Downloading the SST-Core Source Code ### Downloading the SST-Core Source Code
```sh ```sh
wget https://github.com/sstsimulator/sst-core/releases/download/v11.1.0_Final/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 xf sstcore-11.1.0.tar.gz tar xzf sstcore-13.0.0.tar.gz
``` ```
### Installing SST-Core ### Installing SST-Core
```sh ```sh
cd sstcore-11.1.0 cd sstcore-13.0.0
./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \
--disable-mpi # optional, used when MPI is not available. --disable-mpi # optional, used when MPI is not available.
make all -j$(nproc) make all -j$(nproc)
@@ -36,14 +36,14 @@ export PATH=$SST_CORE_HOME/bin:$PATH
### Downloading the SST-Elements Source Code ### Downloading the SST-Elements Source Code
```sh ```sh
wget https://github.com/sstsimulator/sst-elements/releases/download/v11.1.0_Final/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 xf sstelements-11.1.0.tar.gz tar xzf sstelements-13.0.0.tar.gz
``` ```
### Installing SST-Elements ### Installing SST-Elements
```sh ```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 \ ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \
--with-sst-core=$SST_CORE_HOME --with-sst-core=$SST_CORE_HOME
make all -j$(nproc) make all -j$(nproc)
@@ -58,24 +58,36 @@ echo "export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$SST_CORE_HOME/lib/pkgconfig/" >>
### Building gem5 library ### 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 ```sh
scons build/RISCV/libgem5_opt.so -j $(nproc) --without-tcmalloc --duplicate-sources 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. **Note:**
`--duplicate-sources` is required as the compilation of SST depends on sources to be present in the "build" directory. * `--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 ### Compiling the SST integration
At the root of gem5 folder, Go to the SST directory in the gem5 repo.
```sh ```sh
cd ext/sst 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 ### Running an example simulation
See `README.md` 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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@@ -70,7 +70,6 @@
#include <sst/core/sst_config.h> #include <sst/core/sst_config.h>
#include <sst/core/componentInfo.h> #include <sst/core/componentInfo.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/elements/memHierarchy/memEvent.h> #include <sst/elements/memHierarchy/memEvent.h>
#include <sst/elements/memHierarchy/memTypes.h> #include <sst/elements/memHierarchy/memTypes.h>
#include <sst/elements/memHierarchy/util.h> #include <sst/elements/memHierarchy/util.h>
@@ -169,16 +168,29 @@ gem5Component::gem5Component(SST::ComponentId_t id, SST::Params& params):
registerAsPrimaryComponent(); registerAsPrimaryComponent();
primaryComponentDoNotEndSim(); primaryComponentDoNotEndSim();
systemPort = \ // We need to add another parameter when invoking gem5 scripts from SST to
loadUserSubComponent<SSTResponderSubComponent>("system_port",0); // keep a track of all the OutgoingBridges. This will allow to add or
cachePort = \ // remove OutgoingBridges from gem5 configs without the need to recompile
loadUserSubComponent<SSTResponderSubComponent>("cache_port", 0); // the ext/sst source everytime.
std::string ports = params.find<std::string>("ports", "");
systemPort->setTimeConverter(timeConverter); if (ports.empty()) {
systemPort->setOutputStream(&(output)); output.fatal(
cachePort->setTimeConverter(timeConverter); CALL_INFO, -1, "Component %s must have a 'ports' parameter.\n",
cachePort->setOutputStream(&(output)); 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() gem5Component::~gem5Component()
@@ -216,8 +228,9 @@ gem5Component::init(unsigned phase)
// find the corresponding SimObject for each SSTResponderSubComponent // find the corresponding SimObject for each SSTResponderSubComponent
gem5::Root* gem5_root = gem5::Root::root(); gem5::Root* gem5_root = gem5::Root::root();
systemPort->findCorrespondingSimObject(gem5_root); for (auto &port : sstPorts) {
cachePort->findCorrespondingSimObject(gem5_root); port->findCorrespondingSimObject(gem5_root);
}
// initialize the gem5 event queue // initialize the gem5 event queue
if (!(threadInitialized)) { if (!(threadInitialized)) {
@@ -230,17 +243,18 @@ gem5Component::init(unsigned phase)
} }
} }
for (auto &port : sstPorts) {
systemPort->init(phase); port->init(phase);
cachePort->init(phase); }
} }
void void
gem5Component::setup() gem5Component::setup()
{ {
output.verbose(CALL_INFO, 1, 0, "Component is being setup.\n"); output.verbose(CALL_INFO, 1, 0, "Component is being setup.\n");
systemPort->setup(); for (auto &port : sstPorts) {
cachePort->setup(); port->setup();
}
} }
void void
@@ -427,3 +441,16 @@ gem5Component::splitCommandArgs(std::string &cmd, std::vector<char*> &args)
for (auto part: parsed_args) for (auto part: parsed_args)
args.push_back(strdup(part.c_str())); 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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,6 @@
#include <sst/core/sst_config.h> #include <sst/core/sst_config.h>
#include <sst/core/component.h> #include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.h> #include <sst/core/interfaces/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h> #include <sst/core/interfaces/simpleMem.h>
@@ -108,15 +107,20 @@ class gem5Component: public SST::Component
private: private:
SST::Output output; SST::Output output;
SSTResponderSubComponent* systemPort;
SSTResponderSubComponent* cachePort;
uint64_t clocksProcessed; uint64_t clocksProcessed;
SST::TimeConverter* timeConverter; SST::TimeConverter* timeConverter;
gem5::GlobalSimLoopExitEvent *simulateLimitEvent; gem5::GlobalSimLoopExitEvent *simulateLimitEvent;
std::vector<char*> args; 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 initPython(int argc, char **argv);
void splitCommandArgs(std::string &cmd, std::vector<char*> &args); void splitCommandArgs(std::string &cmd, std::vector<char*> &args);
void splitPortNames(std::string port_names);
bool threadInitialized; bool threadInitialized;
@@ -139,6 +143,7 @@ class gem5Component: public SST::Component
) )
SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS( SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS(
// These are the generally expected ports.
{"system_port", "Connection to gem5 system_port", "gem5.gem5Bridge"}, {"system_port", "Connection to gem5 system_port", "gem5.gem5Bridge"},
{"cache_port", "Connection to gem5 CPU", "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, # unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form. # 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. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@@ -46,9 +46,10 @@ cache_link_latency = "1ps"
kernel = "vmlinux_exit.arm64" kernel = "vmlinux_exit.arm64"
cpu_clock_rate = "3GHz" cpu_clock_rate = "3GHz"
# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory # gem5 will send requests to physical addresses of range [0x80000000, inf) to
# currently, we do not subtract 0x80000000 from the request's address to get the "real" address # memory currently, we do not subtract 0x80000000 from the request's address to
# so, the mem_size would always be 2GiB larger than the desired memory size # get the "real" address so, the mem_size would always be 2GiB larger than the
# desired memory size
memory_size_gem5 = "4GiB" memory_size_gem5 = "4GiB"
memory_size_sst = "16GiB" memory_size_sst = "16GiB"
addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue() 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} \ --cpu-clock-rate {cpu_clock_rate} \
--memory-size {memory_size_gem5}" --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 = { cpu_params = {
"frequency": cpu_clock_rate, "frequency": cpu_clock_rate,
"cmd": gem5_command, "cmd": gem5_command,
"ports" : " ".join(port_list),
"debug_flags" : ""
} }
gem5_node = sst.Component("gem5_node", "gem5.gem5Component") 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 = sst.Component("cache_bus", "memHierarchy.Bus")
cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } ) cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } )
# for initialization
system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0)
system_port.addParams({ system_port.addParams({
"response_receiver_name": "system.system_outgoing_bridge", "response_receiver_name": sst_ports["system_port"],
"mem_size": memory_size_sst "mem_size": memory_size_sst
}) })
# SST -> gem5
cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5 cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0)
cache_port.addParams({ cache_port.addParams({
"response_receiver_name": "system.memory_outgoing_bridge", "response_receiver_name": sst_ports["cache_port"],
"mem_size": memory_size_sst "mem_size": memory_size_sst
}) })
@@ -98,11 +112,12 @@ l1_cache.addParams(l1_params)
# Memory # Memory
memctrl = sst.Component("memory", "memHierarchy.MemController") memctrl = sst.Component("memory", "memHierarchy.MemController")
# `addr_range_end` should be changed accordingly to memory_size_sst
memctrl.addParams({ memctrl.addParams({
"debug" : "0", "debug" : "0",
"clock" : "1GHz", "clock" : "1GHz",
"request_width" : "64", "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 = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
memory.addParams({ 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. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@@ -34,9 +34,10 @@ cache_link_latency = "1ps"
bbl = "riscv-boot-exit-nodisk" bbl = "riscv-boot-exit-nodisk"
cpu_clock_rate = "3GHz" cpu_clock_rate = "3GHz"
# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory # gem5 will send requests to physical addresses of range [0x80000000, inf) to
# currently, we do not subtract 0x80000000 from the request's address to get the "real" address # memory currently, we do not subtract 0x80000000 from the request's address to
# so, the mem_size would always be 2GiB larger than the desired memory size # get the "real" address so, the mem_size would always be 2GiB larger than the
# desired memory size
memory_size_gem5 = "4GiB" memory_size_gem5 = "4GiB"
memory_size_sst = "6GiB" memory_size_sst = "6GiB"
addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue() addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue()
@@ -52,10 +53,24 @@ l1_params = {
"L1" : "1", "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 = { cpu_params = {
"frequency": cpu_clock_rate, "frequency": cpu_clock_rate,
"cmd": " ../../configs/example/sst/riscv_fs.py --cpu-clock-rate {} --memory-size {}".format(cpu_clock_rate, memory_size_gem5), "cmd": " ../../configs/example/sst/riscv_fs.py"
"debug_flags": "" + 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") 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 = sst.Component("cache_bus", "memHierarchy.Bus")
cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } ) cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } )
system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization # for initialization
system_port.addParams({ "response_receiver_name": "system.system_outgoing_bridge"}) # tell the SubComponent the name of the corresponding SimObject 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 # SST -> gem5
cache_port.addParams({ "response_receiver_name": "system.memory_outgoing_bridge"}) 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
l1_cache = sst.Component("l1_cache", "memHierarchy.Cache") l1_cache = sst.Component("l1_cache", "memHierarchy.Cache")
@@ -76,11 +94,12 @@ l1_cache.addParams(l1_params)
# Memory # Memory
memctrl = sst.Component("memory", "memHierarchy.MemController") memctrl = sst.Component("memory", "memHierarchy.MemController")
# `addr_range_end` should be changed accordingly to memory_size_sst
memctrl.addParams({ memctrl.addParams({
"debug" : "0", "debug" : "0",
"clock" : "1GHz", "clock" : "1GHz",
"request_width" : "64", "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 = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
memory.addParams({ 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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@@ -35,9 +35,8 @@
#include <sst/core/sst_config.h> #include <sst/core/sst_config.h>
#include <sst/core/component.h> #include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.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/eli/elementinfo.h>
#include <sst/core/link.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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // 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 // SHARE_PORTS means the interface can use our port as if it were its own
// INSERT_STATS means the interface will inherit our statistic // INSERT_STATS means the interface will inherit our statistic
// configuration (e.g., if ours are enabled, the interfaces will be too) // configuration (e.g., if ours are enabled, the interfaces will be too)
memoryInterface = \ memoryInterface = loadAnonymousSubComponent<SST::Interfaces::StandardMem>(
loadAnonymousSubComponent<SST::Interfaces::SimpleMem>( "memHierarchy.standardInterface", "memory", 0,
"memHierarchy.memInterface", "memory", 0, SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS,
SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS, interface_params, timeConverter,
interface_params, timeConverter, new SST::Interfaces::StandardMem::Handler<SSTResponderSubComponent>(
new SST::Interfaces::SimpleMem::Handler<SSTResponderSubComponent>( this, &SSTResponderSubComponent::portEventHandler)
this, &SSTResponderSubComponent::portEventHandler)
); );
assert(memoryInterface != NULL); assert(memoryInterface != NULL);
} }
@@ -91,9 +90,9 @@ SSTResponderSubComponent::setResponseReceiver(
bool bool
SSTResponderSubComponent::handleTimingReq( SSTResponderSubComponent::handleTimingReq(
SST::Interfaces::SimpleMem::Request* request) SST::Interfaces::StandardMem::Request* request)
{ {
memoryInterface->sendRequest(request); memoryInterface->send(request);
return true; return true;
} }
@@ -104,12 +103,10 @@ SSTResponderSubComponent::init(unsigned phase)
for (auto p: responseReceiver->getInitData()) { for (auto p: responseReceiver->getInitData()) {
gem5::Addr addr = p.first; gem5::Addr addr = p.first;
std::vector<uint8_t> data = p.second; std::vector<uint8_t> data = p.second;
SST::Interfaces::SimpleMem::Request* request = \ SST::Interfaces::StandardMem::Request* request = \
new SST::Interfaces::SimpleMem::Request( new SST::Interfaces::StandardMem::Write(
SST::Interfaces::SimpleMem::Request::Command::Write, addr, addr, data.size(), data);
data.size(), data memoryInterface->sendUntimedData(request);
);
memoryInterface->sendInitData(request);
} }
} }
memoryInterface->init(phase); memoryInterface->init(phase);
@@ -132,20 +129,24 @@ SSTResponderSubComponent::findCorrespondingSimObject(gem5::Root* gem5_root)
void void
SSTResponderSubComponent::handleSwapReqResponse( SSTResponderSubComponent::handleSwapReqResponse(
SST::Interfaces::SimpleMem::Request* request) SST::Interfaces::StandardMem::Request* request)
{ {
// get the data, then, // get the data, then,
// 1. send a response to gem5 with the original data // 1. send a response to gem5 with the original data
// 2. send a write to memory with atomic op applied // 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); TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
assert(it != sstRequestIdToPacketMap.end()); 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 // step 1
gem5::PacketPtr pkt = it->second; gem5::PacketPtr pkt = it->second;
pkt->setData(request->data.data()); pkt->setData(
dynamic_cast<SST::Interfaces::StandardMem::ReadResp*>(
request)->data.data()
);
pkt->makeAtomicResponse(); pkt->makeAtomicResponse();
pkt->headerDelay = pkt->payloadDelay = 0; pkt->headerDelay = pkt->payloadDelay = 0;
if (blocked() || !responseReceiver->sendTimingResp(pkt)) if (blocked() || !responseReceiver->sendTimingResp(pkt))
@@ -153,27 +154,29 @@ SSTResponderSubComponent::handleSwapReqResponse(
// step 2 // step 2
(*(pkt->getAtomicOp()))(data.data()); // apply the atomic op (*(pkt->getAtomicOp()))(data.data()); // apply the atomic op
SST::Interfaces::SimpleMem::Request::Command cmd = \ // This is a Write. Need to use the Write visitor class. But the original
SST::Interfaces::SimpleMem::Request::Command::Write; // request is a read response. Therefore, we need to find the address and
SST::Interfaces::SimpleMem::Addr addr = request->addr; // 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(); auto data_size = data.size();
SST::Interfaces::SimpleMem::Request* write_request = \ // Create the Write request here.
new SST::Interfaces::SimpleMem::Request( SST::Interfaces::StandardMem::Request* write_request = \
cmd, addr, data_size, data new SST::Interfaces::StandardMem::Write(addr, data_size, data);
); // F_LOCKED flag in SimpleMem was changed to ReadLock and WriteUnlock
write_request->setMemFlags( // visitor classes. This has to be addressed in the future. The boot test
SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED); // works without using ReadLock and WriteUnlock classes.
memoryInterface->sendRequest(write_request); memoryInterface->send(write_request);
delete request; delete request;
} }
void void
SSTResponderSubComponent::portEventHandler( SSTResponderSubComponent::portEventHandler(
SST::Interfaces::SimpleMem::Request* request) SST::Interfaces::StandardMem::Request* request)
{ {
// Expect to handle an SST response // 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); TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id);
@@ -193,19 +196,27 @@ SSTResponderSubComponent::portEventHandler(
Translator::inplaceSSTRequestToGem5PacketPtr(pkt, request); Translator::inplaceSSTRequestToGem5PacketPtr(pkt, request);
if (blocked() || !(responseReceiver->sendTimingResp(pkt))) if (blocked() || !(responseReceiver->sendTimingResp(pkt))) {
responseQueue.push(pkt); responseQueue.push(pkt);
} else { // we can handle unexpected invalidates, but nothing else. }
SST::Interfaces::SimpleMem::Request::Command cmd = request->cmd; } else {
if (cmd == SST::Interfaces::SimpleMem::Request::Command::WriteResp) // we can handle unexpected invalidates, but nothing else.
if (SST::Interfaces::StandardMem::Read* test =
dynamic_cast<SST::Interfaces::StandardMem::Read*>(request)) {
return; return;
assert(cmd == SST::Interfaces::SimpleMem::Request::Command::Inv); }
else if (SST::Interfaces::StandardMem::WriteResp* test =
// make Req/Pkt for Snoop/no response needed dynamic_cast<SST::Interfaces::StandardMem::WriteResp*>(
request)) {
return;
}
// for Snoop/no response needed
// presently no consideration for masterId, packet type, flags... // presently no consideration for masterId, packet type, flags...
gem5::RequestPtr req = std::make_shared<gem5::Request>( 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( gem5::PacketPtr pkt = new gem5::Packet(
req, gem5::MemCmd::InvalidateReq); 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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@@ -36,10 +36,8 @@
#include <sst/core/sst_config.h> #include <sst/core/sst_config.h>
#include <sst/core/component.h> #include <sst/core/component.h>
#include <sst/core/simulation.h>
#include <sst/core/interfaces/stringEvent.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/eli/elementinfo.h>
#include <sst/core/link.h> #include <sst/core/link.h>
@@ -59,12 +57,12 @@ class SSTResponderSubComponent: public SST::SubComponent
gem5::OutgoingRequestBridge* responseReceiver; gem5::OutgoingRequestBridge* responseReceiver;
gem5::SSTResponderInterface* sstResponder; gem5::SSTResponderInterface* sstResponder;
SST::Interfaces::SimpleMem* memoryInterface; SST::Interfaces::StandardMem* memoryInterface;
SST::TimeConverter* timeConverter; SST::TimeConverter* timeConverter;
SST::Output* output; SST::Output* output;
std::queue<gem5::PacketPtr> responseQueue; std::queue<gem5::PacketPtr> responseQueue;
std::vector<SST::Interfaces::SimpleMem::Request*> initRequests; std::vector<SST::Interfaces::StandardMem::Request*> initRequests;
std::string gem5SimObjectName; std::string gem5SimObjectName;
std::string memSize; std::string memSize;
@@ -78,7 +76,7 @@ class SSTResponderSubComponent: public SST::SubComponent
void setOutputStream(SST::Output* output_); void setOutputStream(SST::Output* output_);
void setResponseReceiver(gem5::OutgoingRequestBridge* gem5_bridge); void setResponseReceiver(gem5::OutgoingRequestBridge* gem5_bridge);
void portEventHandler(SST::Interfaces::SimpleMem::Request* request); void portEventHandler(SST::Interfaces::StandardMem::Request* request);
bool blocked(); bool blocked();
void setup(); void setup();
@@ -86,18 +84,18 @@ class SSTResponderSubComponent: public SST::SubComponent
// return true if the SimObject could be found // return true if the SimObject could be found
bool findCorrespondingSimObject(gem5::Root* gem5_root); bool findCorrespondingSimObject(gem5::Root* gem5_root);
bool handleTimingReq(SST::Interfaces::SimpleMem::Request* request); bool handleTimingReq(SST::Interfaces::StandardMem::Request* request);
void handleRecvRespRetry(); void handleRecvRespRetry();
void handleRecvFunctional(gem5::PacketPtr pkt); void handleRecvFunctional(gem5::PacketPtr pkt);
void handleSwapReqResponse(SST::Interfaces::SimpleMem::Request* request); void handleSwapReqResponse(SST::Interfaces::StandardMem::Request* request);
TPacketMap sstRequestIdToPacketMap; TPacketMap sstRequestIdToPacketMap;
public: // register the component to SST public: // register the component to SST
SST_ELI_REGISTER_SUBCOMPONENT_API(SSTResponderSubComponent); SST_ELI_REGISTER_SUBCOMPONENT_API(SSTResponderSubComponent);
SST_ELI_REGISTER_SUBCOMPONENT_DERIVED( SST_ELI_REGISTER_SUBCOMPONENT(
SSTResponderSubComponent, SSTResponderSubComponent,
"gem5", // SST will look for libgem5.so "gem5", // SST will look for libgem5.so or libgem5.dylib
"gem5Bridge", "gem5Bridge",
SST_ELI_ELEMENT_VERSION(1, 0, 0), SST_ELI_ELEMENT_VERSION(1, 0, 0),
"Initialize gem5 and link SST's ports to gem5's ports", "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( SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS(
{"memory", "Interface to the memory subsystem", \ {"memory", "Interface to the memory subsystem", \
"SST::Interfaces::SimpleMem"} "SST::Interfaces::StandardMem"}
) )
SST_ELI_DOCUMENT_PORTS( 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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@@ -27,87 +27,143 @@
#ifndef __TRANSLATOR_H__ #ifndef __TRANSLATOR_H__
#define __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/stringEvent.h>
#include <sst/core/interfaces/simpleMem.h>
#include <sst/elements/memHierarchy/memEvent.h> #include <sst/elements/memHierarchy/memEvent.h>
#include <sst/elements/memHierarchy/memTypes.h> #include <sst/elements/memHierarchy/memTypes.h>
#include <sst/elements/memHierarchy/util.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; gem5::PacketPtr> TPacketMap;
namespace Translator namespace Translator
{ {
inline SST::Interfaces::SimpleMem::Request* inline SST::Interfaces::StandardMem::Request*
gem5RequestToSSTRequest(gem5::PacketPtr pkt, gem5RequestToSSTRequest(gem5::PacketPtr pkt,
TPacketMap& sst_request_id_to_packet_map) 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()) { switch ((gem5::MemCmd::Command)pkt->cmd.toInt()) {
case gem5::MemCmd::HardPFReq: case gem5::MemCmd::HardPFReq:
case gem5::MemCmd::SoftPFReq: case gem5::MemCmd::SoftPFReq:
case gem5::MemCmd::SoftPFExReq: case gem5::MemCmd::SoftPFExReq:
case gem5::MemCmd::LoadLockedReq: case gem5::MemCmd::LoadLockedReq:
case gem5::MemCmd::ReadExReq: case gem5::MemCmd::ReadExReq:
case gem5::MemCmd::ReadCleanReq:
case gem5::MemCmd::ReadSharedReq:
case gem5::MemCmd::ReadReq: case gem5::MemCmd::ReadReq:
case gem5::MemCmd::SwapReq: case gem5::MemCmd::SwapReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::Read; sst_command_type = Read;
break; break;
case gem5::MemCmd::StoreCondReq: case gem5::MemCmd::StoreCondReq:
case gem5::MemCmd::WritebackDirty:
case gem5::MemCmd::WritebackClean:
case gem5::MemCmd::WriteReq: case gem5::MemCmd::WriteReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::Write; sst_command_type = Write;
break; break;
case gem5::MemCmd::CleanInvalidReq: case gem5::MemCmd::CleanInvalidReq:
case gem5::MemCmd::InvalidateReq: case gem5::MemCmd::InvalidateReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLineInv; sst_command_type = FlushAddr;
break; break;
case gem5::MemCmd::CleanSharedReq: case gem5::MemCmd::CleanSharedReq:
cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLine; sst_command_type = FlushAddr;
flush_addr_flag = false;
break; break;
default: default:
panic("Unable to convert gem5 packet: %s\n", pkt->cmd.toString()); panic("Unable to convert gem5 packet: %s\n", pkt->cmd.toString());
} }
SST::Interfaces::SimpleMem::Addr addr = pkt->getAddr(); SST::Interfaces::StandardMem::Addr addr = pkt->getAddr();
uint8_t* data_ptr = pkt->getPtr<uint8_t>();
auto data_size = pkt->getSize(); auto data_size = pkt->getSize();
std::vector<uint8_t> data = std::vector<uint8_t>( std::vector<uint8_t> data;
data_ptr, data_ptr + data_size // 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( // Now convert a sst StandardMem request.
cmd, addr, data_size, data 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 if ((gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::LoadLockedReq
|| (gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::SwapReq || (gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::SwapReq
|| pkt->req->isLockedRMW()) { || pkt->req->isLockedRMW()) {
request->setMemFlags( // F_LOCKED is deprecated. Therefore I'm skipping this flag for the
SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED); // StandardMem request.
} else if ((gem5::MemCmd::Command)pkt->cmd.toInt() == \ } else if ((gem5::MemCmd::Command)pkt->cmd.toInt() ==
gem5::MemCmd::StoreCondReq) { gem5::MemCmd::StoreCondReq) {
request->setMemFlags( // F_LLSC is deprecated. Therefore I'm skipping this flag for the
SST::Interfaces::SimpleMem::Request::Flags::F_LLSC); // StandardMem request.
} }
if (pkt->req->isUncacheable()) { if (pkt->req->isUncacheable()) {
request->setFlags( request->setFlag(
SST::Interfaces::SimpleMem::Request::Flags::F_NONCACHEABLE); SST::Interfaces::StandardMem::Request::Flag::F_NONCACHEABLE);
} }
if (pkt->needsResponse()) if (pkt->needsResponse())
sst_request_id_to_packet_map[request->id] = pkt; sst_request_id_to_packet_map[request->getID()] = pkt;
return request; return request;
} }
inline void inline void
inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt, inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt,
SST::Interfaces::SimpleMem::Request* request) SST::Interfaces::StandardMem::Request* request)
{ {
pkt->makeResponse(); pkt->makeResponse();
@@ -116,8 +172,18 @@ inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt,
// SC interprets ExtraData == 1 as the store was successful // SC interprets ExtraData == 1 as the store was successful
pkt->req->setExtraData(1); pkt->req->setExtraData(1);
} }
// If there is data in the request, send it back. Only ReadResp requests
pkt->setData(request->data.data()); // 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 // Clear out bus delay notifications
pkt->headerDelay = pkt->payloadDelay = 0; pkt->headerDelay = pkt->payloadDelay = 0;