diff --git a/configs/example/gem5_library/arm-ubuntu-run.py b/configs/example/gem5_library/arm-ubuntu-run.py index 7f976f06db..78160c9976 100644 --- a/configs/example/gem5_library/arm-ubuntu-run.py +++ b/configs/example/gem5_library/arm-ubuntu-run.py @@ -43,7 +43,7 @@ scons build/ARM/gem5.opt -j from gem5.isas import ISA from m5.objects import ArmDefaultRelease from gem5.utils.requires import requires -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator from m5.objects import VExpress_GEM5_Foundation from gem5.coherence_protocol import CoherenceProtocol @@ -100,7 +100,7 @@ board = ArmBoard( # Here we set a full system workload. The "arm64-ubuntu-20.04-boot" boots # Ubuntu 20.04. -board.set_workload(Workload("arm64-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("arm64-ubuntu-20.04-boot")) # We define the system with the aforementioned system defined. diff --git a/configs/example/gem5_library/caches/octopi-cache-example.py b/configs/example/gem5_library/caches/octopi-cache-example.py index 1b39a8bee5..4a4926a174 100644 --- a/configs/example/gem5_library/caches/octopi-cache-example.py +++ b/configs/example/gem5_library/caches/octopi-cache-example.py @@ -51,7 +51,7 @@ from gem5.components.cachehierarchies.ruby.caches.mesi_three_level.octopi import from gem5.isas import ISA from gem5.coherence_protocol import CoherenceProtocol from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource num_ccds = 1 # CCDs num_cores_per_ccd = 8 # 8 cores/CCD @@ -94,7 +94,7 @@ board = ArmBoard( platform=platform, ) -board.set_workload(Workload("arm64-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("arm64-ubuntu-20.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py index abb15fb7f8..4a15da55ff 100644 --- a/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py +++ b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py @@ -56,7 +56,7 @@ from gem5.components.memory.single_channel import SingleChannelDDR3_1600 from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource from pathlib import Path from gem5.simulate.exit_event_generators import ( looppoint_save_checkpoint_generator, @@ -110,7 +110,9 @@ board = SimpleBoard( cache_hierarchy=cache_hierarchy, ) -board.set_workload(Workload("x86-matrix-multiply-omp-100-8-looppoint-csv")) +board.set_workload( + obtain_resource("x86-matrix-multiply-omp-100-8-looppoint-csv") +) dir = Path(args.checkpoint_path) dir.mkdir(exist_ok=True) diff --git a/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py index 21353a34a1..5224c6e6ea 100644 --- a/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py +++ b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py @@ -54,7 +54,6 @@ from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA from gem5.resources.resource import obtain_resource -from gem5.resources.workload import Workload from m5.stats import reset, dump requires(isa_required=ISA.X86) @@ -113,7 +112,7 @@ board = SimpleBoard( ) board.set_workload( - Workload( + obtain_resource( f"x86-matrix-multiply-omp-100-8-looppoint-region-{args.checkpoint_region}" ) ) diff --git a/configs/example/gem5_library/riscv-ubuntu-run.py b/configs/example/gem5_library/riscv-ubuntu-run.py index 87b98cc5ba..9b172fd501 100644 --- a/configs/example/gem5_library/riscv-ubuntu-run.py +++ b/configs/example/gem5_library/riscv-ubuntu-run.py @@ -50,7 +50,7 @@ from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource # This runs a check to ensure the gem5 binary is compiled for RISCV. @@ -88,7 +88,7 @@ board = RiscvBoard( # Ubuntu 20.04. Once the system successfully boots it encounters an `m5_exit` # instruction which stops the simulation. When the simulation has ended you may # inspect `m5out/system.pc.com_1.device` to see the stdout. -board.set_workload(Workload("riscv-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("riscv-ubuntu-20.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/configs/example/gem5_library/riscvmatched-fs.py b/configs/example/gem5_library/riscvmatched-fs.py index 3e84b8c1ea..29ec76e16b 100644 --- a/configs/example/gem5_library/riscvmatched-fs.py +++ b/configs/example/gem5_library/riscvmatched-fs.py @@ -42,7 +42,7 @@ from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard from gem5.utils.requires import requires from gem5.isas import ISA from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource import argparse @@ -76,7 +76,7 @@ board = RISCVMatchedBoard( # In the case where the `-i` flag is passed, we add the kernel argument # `init=/root/exit.sh`. This means the simulation will exit after the Linux # Kernel has booted. -workload = Workload("riscv-ubuntu-20.04-boot") +workload = obtain_resource("riscv-ubuntu-20.04-boot") kernel_args = board.get_default_kernel_args() if args.to_init: kernel_args.append("init=/root/exit.sh") diff --git a/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py b/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py index 1c65357921..f25ffb62f8 100644 --- a/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py +++ b/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py @@ -52,7 +52,7 @@ from gem5.isas import ISA from gem5.coherence_protocol import CoherenceProtocol from gem5.simulate.simulator import Simulator from gem5.simulate.exit_event import ExitEvent -from gem5.resources.workload import Workload +from gem5.resources.resoruce import obtain_resource # This simulation requires using KVM with gem5 compiled for X86 simulation # and with MESI_Two_Level cache coherence protocol. @@ -121,7 +121,7 @@ command = ( + "m5 exit;" ) -workload = Workload("x86-ubuntu-18.04-boot") +workload = obtain_resource("x86-ubuntu-18.04-boot") workload.set_parameter("readfile_contents", command) board.set_workload(workload) diff --git a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py index f55ec60f21..00c00d1459 100644 --- a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py +++ b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py @@ -51,7 +51,7 @@ from gem5.isas import ISA from gem5.coherence_protocol import CoherenceProtocol from gem5.simulate.simulator import Simulator from gem5.simulate.exit_event import ExitEvent -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource # This runs a check to ensure the gem5 binary is compiled to X86 and to the # MESI Two Level coherence protocol. @@ -117,7 +117,7 @@ command = ( + "m5 exit;" ) -workload = Workload("x86-ubuntu-18.04-boot") +workload = obtain_resource("x86-ubuntu-18.04-boot") workload.set_parameter("readfile_contents", command) board.set_workload(workload) diff --git a/configs/example/gem5_library/x86-ubuntu-run.py b/configs/example/gem5_library/x86-ubuntu-run.py index 50b52e6e3c..fe72d653f5 100644 --- a/configs/example/gem5_library/x86-ubuntu-run.py +++ b/configs/example/gem5_library/x86-ubuntu-run.py @@ -45,7 +45,7 @@ scons build/X86/gem5.opt """ from gem5.prebuilt.demo.x86_demo_board import X86DemoBoard -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator @@ -56,7 +56,7 @@ board = X86DemoBoard() # We then set the workload. Here we use the "x86-ubuntu-18.04-boot" workload. # This boots Ubuntu 18.04 with Linux 5.4.49. If the required resources are not # found locally, they will be downloaded. -board.set_workload(Workload("x86-ubuntu-18.04-boot")) +board.set_workload(obtain_resource("x86-ubuntu-18.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/src/python/gem5/components/boards/abstract_board.py b/src/python/gem5/components/boards/abstract_board.py index 4ea8866009..aba080e239 100644 --- a/src/python/gem5/components/boards/abstract_board.py +++ b/src/python/gem5/components/boards/abstract_board.py @@ -28,7 +28,7 @@ from abc import ABCMeta, abstractmethod import inspect from .mem_mode import MemMode, mem_mode_to_string -from ...resources.workload import AbstractWorkload +from ...resources.resource import WorkloadResource from m5.objects import ( AddrRange, @@ -198,7 +198,7 @@ class AbstractBoard: ) return self._is_fs - def set_workload(self, workload: AbstractWorkload) -> None: + def set_workload(self, workload: WorkloadResource) -> None: """ Set the workload for this board to run. diff --git a/src/python/gem5/resources/resource.py b/src/python/gem5/resources/resource.py index bc9f4480ba..9e6c79ab01 100644 --- a/src/python/gem5/resources/resource.py +++ b/src/python/gem5/resources/resource.py @@ -35,7 +35,7 @@ from .downloader import get_resource from .looppoint import LooppointCsvLoader, LooppointJsonLoader from ..isas import ISA, get_isa_from_str -from typing import Optional, Dict, Union, Type, Tuple, List +from typing import Optional, Dict, Union, Type, Tuple, List, Any from .client import get_resource_json_obj @@ -554,6 +554,66 @@ class SimpointDirectoryResource(SimpointResource): return simpoint_list, weight_list +class WorkloadResource(AbstractResource): + """A workload resource. This resource is used to specify a workload to run + on a board. It contains the function to call and the parameters to pass to + that function. + """ + + def __init__( + self, + function: str = None, + resource_version: Optional[str] = None, + description: Optional[str] = None, + source: Optional[str] = None, + local_path: Optional[str] = None, + parameters: Optional[Dict[str, Any]] = {}, + **kwargs, + ): + """ + :param function: The function to call on the board. + :param parameters: The parameters to pass to the function. + """ + + super().__init__( + local_path=local_path, + description=description, + source=source, + resource_version=resource_version, + ) + + self._func = function + self._params = parameters + + def get_function_str(self) -> str: + """ + Returns the name of the workload function to be run. + + This function is called via the AbstractBoard's `set_workload` + function. The parameters from the `get_parameters` function are passed + to this function. + """ + return self._func + + def get_parameters(self) -> Dict[str, Any]: + """ + Returns a dictionary mapping the workload parameters to their values. + + These parameters are passed to the function specified by + `get_function_str` via the AbstractBoard's `set_workload` function. + """ + return self._params + + def set_parameter(self, parameter: str, value: Any) -> None: + """ + Used to set or override a workload parameter + + :param parameter: The parameter of the function to set. + :param value: The value to set to the parameter. + """ + self._params[parameter] = value + + def obtain_resource( resource_id: str, resource_directory: Optional[str] = None, @@ -658,6 +718,26 @@ def obtain_resource( assert resources_category in _get_resource_json_type_map resource_class = _get_resource_json_type_map[resources_category] + if resources_category == "workload": + # This parses the "resources" and "additional_params" fields of the + # workload resource into a dictionary of AbstractResource objects and + # strings respectively. + params = {} + if "resources" in resource_json: + for key in resource_json["resources"].keys(): + assert isinstance(key, str) + value = resource_json["resources"][key] + assert isinstance(value, str) + params[key] = obtain_resource( + value, + ) + if "additional_params" in resource_json: + for key in resource_json["additional_params"].keys(): + assert isinstance(key, str) + value = resource_json["additional_params"][key] + assert isinstance(value, str) + params[key] = value + resource_json["parameters"] = params # Once we know what AbstractResource subclass we are using, we create it. # The fields in the JSON object are assumed to map like-for-like to the # subclass contructor, so we can pass the resource_json map directly. @@ -812,4 +892,5 @@ _get_resource_json_type_map = { "resource": Resource, "looppoint-pinpoint-csv": LooppointCsvResource, "looppoint-json": LooppointJsonResource, + "workload": WorkloadResource, } diff --git a/src/python/gem5/resources/workload.py b/src/python/gem5/resources/workload.py index 5b25bf879f..bdb596921f 100644 --- a/src/python/gem5/resources/workload.py +++ b/src/python/gem5/resources/workload.py @@ -24,219 +24,67 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .resource import obtain_resource +from .resource import obtain_resource, WorkloadResource from .client import get_resource_json_obj from _m5 import core +from m5.util import warn from typing import Dict, Any, List, Optional -class AbstractWorkload: +def CustomWorkload(function: str, parameters: Dict[str, Any]): """ - Workloads contain information needed to build a workload. + A custom workload gem5 resource. It can be used to specify a custom, + local workload. - A workload specifies a function and its parameters to run on a board to - set a workload. Workload's are passed to board via the `AbstractBoard`'s - `set_workload` function. + **Warning**: This `CustomWorkload` class is deprecated. It will be removed in a + future release of gem5. Please use the `gem5.resources.resource.WorkloadResource` + class instead. - The `AbstractBoard` has a `set_workload` function which accepts an - AbstractWorkload. The `set_workload` function uses the `get_function_str` - to determine which function should be called on the board and the - `get_parameters` function specifies the parameters to be passed. - - Example - ------- - - ```py - workload = CustomWorkload( - function = "set_se_binary_workload", - parameters = { - "binary" : obtain_resource("x86-print-this"), - "arguments" : ["hello", 6] - }, + The class has been stealthily converted to a function which wraps the + `WorkloadResource` class. + """ + warn( + "The `CustomWorkload` class is deprecated. Please use " + "the `gem5.resources.resource.WorkloadResource` class instead." ) + return WorkloadResource(function=function, parameters=parameters) - board.set_workload(workload) - ``` - The above is the equivalent of: +def Workload( + workload_name: str, + resource_directory: Optional[str] = None, + resource_version: Optional[str] = None, + clients: Optional[List] = None, + gem5_version: Optional[str] = core.gem5Version, +): + """ + **Warning**: The `Workload` class is deprecated. It will be removed in a + future release of gem5. Please use the `gem5.resources.resource.WorkloadResource` + class instead. - ```py - board.set_se_binary_workload( - binary = obtain_resource("x86-print-this"), - arguments = ["hello", 6], + The class has been stealthily converted to a function which wraps the + `WorkloadResource` class. + """ + warn( + "`Workload` has been deprecated. Please use the `obtain_resource` " + "function instead:\n\n" + "```\n" + "from gem5.resources.resource import obtain_resource\n" + "workload = obtain_resource(\n" + f' resource_id="{workload_name}",\n' + f' resource_directory="{resource_directory}",\n' + f' gem5_version="{gem5_version}",\n' + f" clients={clients},\n" + f" resource_version={resource_version},\n" + ")\n" + "```" ) - ``` - - Notes - ----- - This class should not be used directly. Please use `Workload` or - `CustomWorkload`. - """ - - def __init__(self, function: str, parameters: Dict[str, Any]) -> None: - self._func = function - self._params = parameters - - def get_function_str(self) -> str: - """ - Returns the name of the workload function to be run. - - This function is called via the AbstractBoard's `set_workload` - function. The parameters from the `get_parameters` function are passed - to this function. - """ - return self._func - - def get_parameters(self) -> Dict[str, Any]: - """ - Returns a dictionary mapping the workload parameters to their values. - - These parameters are passed to the function specified by - `get_function_str` via the AbstractBoard's `set_workload` function. - """ - return self._params - - def set_parameter(self, parameter: str, value: Any) -> None: - """ - Used to set or override a workload parameter - - :param parameter: The parameter of the function to set. - :param value: The value to set to the parameter. - """ - self._params[parameter] = value - - -class CustomWorkload(AbstractWorkload): - """ - A workload specified locally (i.e., not via gem5-resources as with the - `Workload` class). Here the user specifies the function and the parameters - to be passed. - - Usage - ----- - - ```py - workload = CustomWorkload( - function = "set_se_binary_workload", - parameters = { - "binary" : obtain_resource("x86-print-this"), - "arguments" : ["hello", 6] - }, + return obtain_resource( + workload_name, + resource_directory=resource_directory, + gem5_version=gem5_version, + clients=clients, + resource_version=resource_version, ) - - board.set_workload(workload) - ``` - """ - - def __init__(self, function: str, parameters: Dict[str, Any]) -> None: - super().__init__(function=function, parameters=parameters) - - -class Workload(AbstractWorkload): - """ - The `Workload` class loads a workload's information from gem5-resources - based on a name/id passed via the constructor. - - Usage - ----- - - ```py - # Determine what workload we want to run. - workload = Workload("example-workload-id") - - # Optionally we can override a parameter in the workload. In this example - # we are going to run this workload with a difference kernel. - workload.set_parameter("kernel", - obtain_resource("arm64-linux-kernel-4.14.134") - ) - - # We then set this workload to the board. - board.set_workload(workload) - ``` - - """ - - def __init__( - self, - workload_name: str, - resource_directory: Optional[str] = None, - resource_version: Optional[str] = None, - clients: Optional[List] = None, - gem5_version: Optional[str] = core.gem5Version, - ) -> None: - """ - This constructor will load the workload details from the workload with - the given name/id. - - This function assumes the dictionary returned by the downloader's - `get_workload_json_obj` is a dictionary. An example of the schema is - shown below: - - ```json - { - "category" : "workload", - "id" : "x86-ubuntu-18.04-echo-hello", - "description" : "Description of workload here", - "function" : "set_kernel_disk_workload", - "resources" : { - "kernel" : "x86-linux-kernel-5.4.49", - "disk-image" : "x86-ubuntu-18.04-img" - }, - "additional_params" : { - "readfile_contents" : "m5_exit; echo 'hello'; m5_exit" - } - } - ``` - - This resource will result in the equivalent of the following action - being taken: - - ```python - board.set_kernel_disk_workload( - kernel = obtain_resource("x86-linux-kernel-5.4.49"), - disk-image = obtain_resource("x86-ubuntu-18.04-img"), - readfile_contents = "m5_exit; echo 'hello'; m5_exit", - ) - ``` - - :param workload_name: The name of the workload in the resources.json - file to be loaded. - :param resource_directory: An optional parameter that specifies where - any resources should be download and accessed from. If None, a default - location will be used. None by default. - :param gem5_version: The gem5 version for the Workload to be loaded. - By default, the current gem5 version is used. This will filter - resources which are incompatible with the current gem5 version. If - None, no filtering will be done. - """ - - workload_json = get_resource_json_obj( - workload_name, - resource_version=resource_version, - clients=clients, - gem5_version=gem5_version, - ) - - func = workload_json["function"] - assert isinstance(func, str) - - params = {} - if "resources" in workload_json: - for key in workload_json["resources"].keys(): - assert isinstance(key, str) - value = workload_json["resources"][key] - assert isinstance(value, str) - params[key] = obtain_resource( - value, - resource_directory=resource_directory, - gem5_version=gem5_version, - ) - - if "additional_params" in workload_json: - for key in workload_json["additional_params"]: - assert isinstance(key, str) - params[key] = workload_json["additional_params"][key] - - super().__init__(function=func, parameters=params) diff --git a/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py b/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py index e9fc06b27b..3726d7de46 100644 --- a/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py +++ b/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py @@ -40,7 +40,7 @@ from gem5.components.processors.cpu_types import CPUTypes from gem5.components.boards.riscv_board import RiscvBoard from gem5.components.processors.simple_processor import SimpleProcessor from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource import argparse import importlib @@ -160,7 +160,7 @@ board = RiscvBoard( ) # Set the workload. -workload = Workload( +workload = obtain_resource( "riscv-ubuntu-20.04-boot", resource_directory=args.resource_directory ) board.set_workload(workload) diff --git a/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py b/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py index e9eeacefd8..63b6625479 100644 --- a/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py +++ b/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py @@ -41,7 +41,7 @@ from gem5.components.processors.cpu_types import ( ) from gem5.components.processors.simple_processor import SimpleProcessor from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource import argparse import importlib @@ -184,7 +184,7 @@ if args.boot_type == "init": kernal_args.append("init=/root/exit.sh") # Set the workload. -workload = Workload( +workload = obtain_resource( "x86-ubuntu-18.04-boot", resource_directory=args.resource_directory ) workload.set_parameter("kernel_args", kernal_args) diff --git a/tests/pyunit/stdlib/resources/pyunit_workload_checks.py b/tests/pyunit/stdlib/resources/pyunit_workload_checks.py index ef7c3caba1..a9fa5e28e2 100644 --- a/tests/pyunit/stdlib/resources/pyunit_workload_checks.py +++ b/tests/pyunit/stdlib/resources/pyunit_workload_checks.py @@ -32,6 +32,7 @@ from gem5.resources.resource import ( BinaryResource, DiskImageResource, obtain_resource, + WorkloadResource, ) from typing import Dict @@ -61,7 +62,7 @@ class CustomWorkloadTestSuite(unittest.TestCase): new=ClientWrapper(mock_config_json), ) def setUpClass(cls) -> None: - cls.custom_workload = CustomWorkload( + cls.custom_workload = WorkloadResource( function="set_se_binary_workload", parameters={ "binary": obtain_resource( @@ -135,7 +136,7 @@ class WorkloadTestSuite(unittest.TestCase): ClientWrapper(mock_config_json), ) def setUpClass(cls): - cls.workload = Workload("simple-boot", gem5_version="develop") + cls.workload = obtain_resource("simple-boot", gem5_version="develop") def test_get_function_str(self) -> None: # Tests `Resource.get_function_str`