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;