From 937b829e8fc45f4ee94f884375f2822799d41506 Mon Sep 17 00:00:00 2001 From: Kaustav Goswami Date: Wed, 4 Oct 2023 17:11:05 -0700 Subject: [PATCH] configs,ext: Updated the gem5 SST Bridge to use SST 13.0.0 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. Change-Id: I45f0013bc35d088df0aa5a71951422cabab4d7f7 Signed-off-by: Kaustav Goswami --- configs/example/sst/riscv_fs.py | 20 ++-- ext/sst/INSTALL.md | 46 +++++---- ext/sst/Makefile.linux | 21 +++++ ext/sst/Makefile.mac | 21 +++++ ext/sst/gem5.cc | 65 +++++++++---- ext/sst/gem5.hh | 13 ++- ext/sst/sst/arm_example.py | 37 +++++--- ext/sst/sst/example.py | 41 ++++++--- ext/sst/sst_responder.hh | 5 +- ext/sst/sst_responder_subcomponent.cc | 93 ++++++++++--------- ext/sst/sst_responder_subcomponent.hh | 22 ++--- ext/sst/translator.hh | 128 +++++++++++++++++++------- 12 files changed, 354 insertions(+), 158 deletions(-) create mode 100644 ext/sst/Makefile.linux create mode 100644 ext/sst/Makefile.mac diff --git a/configs/example/sst/riscv_fs.py b/configs/example/sst/riscv_fs.py index 77db9e4dbe..46754debf5 100644 --- a/configs/example/sst/riscv_fs.py +++ b/configs/example/sst/riscv_fs.py @@ -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 @@ -104,16 +104,9 @@ def createHiFivePlatform(system): 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.pma_checker = PMAChecker( - uncacheable=[ - *system.platform._on_chip_ranges(), - *system.platform._off_chip_ranges(), - ] - ) - system.iobus = IOXBar() system.bridge = Bridge(delay="50ns") system.bridge.mem_side_port = system.iobus.cpu_side_ports @@ -122,6 +115,15 @@ def createHiFivePlatform(system): 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.attachOffChipIO(system.iobus) diff --git a/ext/sst/INSTALL.md b/ext/sst/INSTALL.md index 91f92eb7ff..ba61996b32 100644 --- a/ext/sst/INSTALL.md +++ b/ext/sst/INSTALL.md @@ -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 +. +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` diff --git a/ext/sst/Makefile.linux b/ext/sst/Makefile.linux new file mode 100644 index 0000000000..f44ecd46d9 --- /dev/null +++ b/ext/sst/Makefile.linux @@ -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 diff --git a/ext/sst/Makefile.mac b/ext/sst/Makefile.mac new file mode 100644 index 0000000000..4a67570a44 --- /dev/null +++ b/ext/sst/Makefile.mac @@ -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 diff --git a/ext/sst/gem5.cc b/ext/sst/gem5.cc index 7af0eed7b7..8c845f329e 100644 --- a/ext/sst/gem5.cc +++ b/ext/sst/gem5.cc @@ -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 #include -#include #include #include #include @@ -169,16 +168,29 @@ gem5Component::gem5Component(SST::ComponentId_t id, SST::Params& params): registerAsPrimaryComponent(); primaryComponentDoNotEndSim(); - systemPort = \ - loadUserSubComponent("system_port",0); - cachePort = \ - loadUserSubComponent("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("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(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 &args) for (auto part: parsed_args) args.push_back(strdup(part.c_str())); } + +void +gem5Component::splitPortNames(std::string port_names) +{ + std::vector parsed_args = tokenizeString( + port_names, {'\\', ' ', '\'', '\"'} + ); + sstPortCount = 0; + for (auto part: parsed_args) { + sstPortNames.push_back(strdup(part.c_str())); + sstPortCount++; + } +} diff --git a/ext/sst/gem5.hh b/ext/sst/gem5.hh index 447c68c3b2..fd00b1504e 100644 --- a/ext/sst/gem5.hh +++ b/ext/sst/gem5.hh @@ -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 #include -#include #include #include @@ -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 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 sstPorts; + std::vector sstPortNames; + int sstPortCount; + void initPython(int argc, char **argv); void splitCommandArgs(std::string &cmd, std::vector &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"} ) diff --git a/ext/sst/sst/arm_example.py b/ext/sst/sst/arm_example.py index cdee3ca40a..4bc111cb86 100644 --- a/ext/sst/sst/arm_example.py +++ b/ext/sst/sst/arm_example.py @@ -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({ diff --git a/ext/sst/sst/example.py b/ext/sst/sst/example.py index 76cf8ad24e..1c35bc3f83 100644 --- a/ext/sst/sst/example.py +++ b/ext/sst/sst/example.py @@ -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({ diff --git a/ext/sst/sst_responder.hh b/ext/sst/sst_responder.hh index a89d311064..5f483be845 100644 --- a/ext/sst/sst_responder.hh +++ b/ext/sst/sst_responder.hh @@ -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 #include -#include #include -#include +#include #include #include diff --git a/ext/sst/sst_responder_subcomponent.cc b/ext/sst/sst_responder_subcomponent.cc index 366f99aecf..8cd2c04628 100644 --- a/ext/sst/sst_responder_subcomponent.cc +++ b/ext/sst/sst_responder_subcomponent.cc @@ -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 interface’s will be too) - memoryInterface = \ - loadAnonymousSubComponent( - "memHierarchy.memInterface", "memory", 0, - SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS, - interface_params, timeConverter, - new SST::Interfaces::SimpleMem::Handler( - this, &SSTResponderSubComponent::portEventHandler) + memoryInterface = loadAnonymousSubComponent( + "memHierarchy.standardInterface", "memory", 0, + SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS, + interface_params, timeConverter, + new SST::Interfaces::StandardMem::Handler( + 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 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 data = request->data; + std::vector data = \ + dynamic_cast(request)->data; // step 1 gem5::PacketPtr pkt = it->second; - pkt->setData(request->data.data()); + pkt->setData( + dynamic_cast( + 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(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(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( + request)) { + return; + } + // for Snoop/no response needed // presently no consideration for masterId, packet type, flags... gem5::RequestPtr req = std::make_shared( - request->addr, request->size, 0, 0 - ); + dynamic_cast( + request)->pAddr, + dynamic_cast( + request)->size, 0, 0); gem5::PacketPtr pkt = new gem5::Packet( req, gem5::MemCmd::InvalidateReq); diff --git a/ext/sst/sst_responder_subcomponent.hh b/ext/sst/sst_responder_subcomponent.hh index 51bc4f9318..ed9f09d6b8 100644 --- a/ext/sst/sst_responder_subcomponent.hh +++ b/ext/sst/sst_responder_subcomponent.hh @@ -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 #include - -#include #include -#include +#include #include #include @@ -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 responseQueue; - std::vector initRequests; + std::vector 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( diff --git a/ext/sst/translator.hh b/ext/sst/translator.hh index 2d8c8b782a..bf6a168d9a 100644 --- a/ext/sst/translator.hh +++ b/ext/sst/translator.hh @@ -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 +#include #include -#include #include #include #include -typedef std::unordered_map 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(); + SST::Interfaces::StandardMem::Addr addr = pkt->getAddr(); auto data_size = pkt->getSize(); - std::vector data = std::vector( - data_ptr, data_ptr + data_size - ); + std::vector 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(); + data = std::vector(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(request)) { + pkt->setData(dynamic_cast( + request)->data.data() + ); + } + } // Clear out bus delay notifications pkt->headerDelay = pkt->payloadDelay = 0;