diff --git a/src/python/gem5/simulate/simulator.py b/src/python/gem5/simulate/simulator.py index 4947b46ebb..e355d200ad 100644 --- a/src/python/gem5/simulate/simulator.py +++ b/src/python/gem5/simulate/simulator.py @@ -85,7 +85,11 @@ class Simulator: on_exit_event: Optional[ Dict[ ExitEvent, - Union[Generator[Optional[bool], None, None], List[Callable]], + Union[ + Generator[Optional[bool], None, None], + List[Callable], + Callable, + ], ] ] = None, expected_execution_order: Optional[List[ExitEvent]] = None, @@ -97,16 +101,21 @@ class Simulator: This is optional and used to override default behavior. If not set, whether or not to run in FS mode will be determined via the board's `is_fullsystem()` function. - :param on_exit_event: An optional map to specify the generator to - execute on each exit event. The generator may yield a boolean which, - if True, will have the Simulator exit the run loop. A List of functions - may also be used. Each function must be callable with no arguments and - return a boolean specifying if the Simulation should exit the run loop. - :param expected_execution_order: May be specified to check the exit - events come in a specified order. If the order specified is not - encountered (e.g., 'Workbegin', 'Workend', then 'Exit'), an Exception - is thrown. If this parameter is not specified, any ordering of exit - events is valid. + :param on_exit_event: An optional map to specify what to execute on + each exit event. There are three possibilities here: a generator, a + list of functions, or a single function.: + 1. Generator: The generator may yield a boolean each time the + associated exit event is encountered. If True the simulator will exit + the simulation loop. + 2. List of functions: Each function must be callable with no mandatory + arguments and return a boolean specifying if the Simulation should exit + the simulation loop. Upon each exit event the list will pop the start + of the list and execute it. If the list is empty the default behavior + for that exit event will be executed. + 3. Single function: The function must be callable with no mandatory + arguments and return a boolean specifying if the Simulation should exit + or not. This function is executed each time the associated exit event + is encountered. :param checkpoint_path: An optional parameter specifying the directory of the checkpoint to instantiate from. When the path is None, no checkpoint will be loaded. By default, the path is None. **This @@ -195,6 +204,29 @@ class Simulator: again before finally, on the forth exit event will call `stop_simulation` which will stop the simulation as it returns False. + With a function + =============== + A single function can be passed. In this case every exit event of that + type will execute that function every time. The function should not + accept any mandatory parameters and return a boolean specifying if the + simulation loop should end after it is executed. + An example: + ``` + def print_hello() -> bool: + print("Hello") + return False + simulator = Simulator( + board=board, + on_exit_event = { + ExitEvent.Exit : print_hello + }, + ) + ``` + The above will print "Hello" on every `Exit` type Exit Event. As the + function returns False, the simulation loop will not end on these + events. + + Exit Event defaults =================== @@ -266,6 +298,15 @@ class Simulator: # In instances where we have a list of functions, we # convert this to a generator. self._on_exit_event[key] = (func() for func in value) + elif isinstance(value, Callable): + # In instances where the user passes a lone function, the + # function is called on every exit event of that type. Here + # we convert the function into an infinite generator. + def function_generator(func: Callable): + while True: + yield func() + + self._on_exit_event[key] = function_generator(func=value) else: raise Exception( f"`on_exit_event` for '{key.value}' event is " diff --git a/tests/gem5/stdlib/configs/simulator_exit_event_run.py b/tests/gem5/stdlib/configs/simulator_exit_event_run.py index c34cfe3ec9..56c99359f2 100644 --- a/tests/gem5/stdlib/configs/simulator_exit_event_run.py +++ b/tests/gem5/stdlib/configs/simulator_exit_event_run.py @@ -41,8 +41,9 @@ About to exit the simulation for the 3 st/nd/rd/th time Handling the final exit event. We'll exit now. ``` -By default a generator is passed to define the evit_event. A list of functions -can also be passed. This is enabled by passing the `--list-format` flag. +By default a generator is passed to define the exit_event behavior. A list of +functions or a lone function can also be passed. This can be specified by the +`--exit-event-type` parameter. """ from gem5.resources.resource import obtain_resource @@ -63,11 +64,12 @@ parser = argparse.ArgumentParser( ) parser.add_argument( - "-l", - "--list-format", - action="store_true", - help="Use a list of functions, instead of a generator, for the exit event " - "handler", + "-e", + "--exit-event-type", + type=str, + choices=("generator", "function-list", "function"), + default="generator", + help="Used to specify what exit event format is to be passed.", ) parser.add_argument( @@ -106,9 +108,9 @@ binary = obtain_resource( ) motherboard.set_se_binary_workload(binary) -# Create the exit event handler. Here there are two kinds: either pass a -# generator or a list of functions. In this script they both do the same things -# for testing purposes. +# Create the exit event handler. Here there are three kinds: either pass a +# generator, a list of functions, or a lone function. In this script they all +# do the same thing for testing purposes. def event_handle() -> bool: @@ -129,11 +131,24 @@ def generator(): func_list = [event_handle, event_handle, event_handle_final] +i = 0 + + +def lone_function() -> bool: + global i + i += 1 + if i < 3: + return event_handle() + return event_handle_final() + + exit_event_handler = None -if args.list_format: +if args.exit_event_type == "function-list": exit_event_handler = func_list -else: +elif args.exit_event_type == "generator": exit_event_handler = generator() +elif args.exit_event_type == "function": + exit_event_handler = lone_function assert exit_event_handler is not None diff --git a/tests/gem5/stdlib/simulator/test_event_event.py b/tests/gem5/stdlib/simulator/test_event_event.py index c71f0b2fb9..ed755c2e36 100644 --- a/tests/gem5/stdlib/simulator/test_event_event.py +++ b/tests/gem5/stdlib/simulator/test_event_event.py @@ -47,7 +47,7 @@ gem5_verify_config( "configs", "simulator_exit_event_run.py", ), - config_args=["-l"], + config_args=["-e", "function-list"], valid_isas=(constants.all_compiled_tag,), length=constants.quick_tag, ) @@ -64,7 +64,24 @@ gem5_verify_config( "configs", "simulator_exit_event_run.py", ), - config_args=[], + config_args=["-e", "generator"], + valid_isas=(constants.all_compiled_tag,), + length=constants.quick_tag, +) + +gem5_verify_config( + name="simulator-exit-event-handler-with-lone-function", + verifiers=verifiers, + fixtures=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simulator_exit_event_run.py", + ), + config_args=["-e", "function"], valid_isas=(constants.all_compiled_tag,), length=constants.quick_tag, )