stdlib: Update TestBoard to work with Simulator

This change makes minor updates to the TestBoard so that it can work
nicely with the Simulator module.

This change also makes the cache hierarchy optional for the TestBoard.

Change-Id: If46d53779164e87b6fc06176355be6b4ae05aa99
Signed-off-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/64017
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Mahyar Samani <msamani@ucdavis.edu>
diff --git a/src/python/gem5/components/boards/test_board.py b/src/python/gem5/components/boards/test_board.py
index 7267f2a..dea5ada 100644
--- a/src/python/gem5/components/boards/test_board.py
+++ b/src/python/gem5/components/boards/test_board.py
@@ -26,15 +26,15 @@
 
 from m5.objects import Port, IOXBar, AddrRange
 
-from .mem_mode import MemMode, mem_mode_to_string
 from ...utils.override import overrides
+from .abstract_board import AbstractBoard
 from .abstract_system_board import AbstractSystemBoard
-from ..processors.abstract_processor import AbstractProcessor
+from ..processors.abstract_generator import AbstractGenerator
 from ..memory.abstract_memory_system import AbstractMemorySystem
 from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy
 
 
-from typing import List
+from typing import List, Optional
 
 
 class TestBoard(AbstractSystemBoard):
@@ -43,21 +43,27 @@
     architecture.
 
     To work as a traffic generator board, pass a generator as a processor.
+
+    This board does not require a cache hierarchy (it can be none) in which
+    case the processor (generator) will be directly connected to the memory.
+    The clock frequency is only used if there is a cache hierarchy or when
+    using the GUPS generators.
     """
 
     def __init__(
         self,
         clk_freq: str,
-        processor: AbstractProcessor,
+        generator: AbstractGenerator,
         memory: AbstractMemorySystem,
-        cache_hierarchy: AbstractCacheHierarchy,
+        cache_hierarchy: Optional[AbstractCacheHierarchy],
     ):
         super().__init__(
-            clk_freq=clk_freq,
-            processor=processor,
+            clk_freq=clk_freq,  # Only used if cache hierarchy or GUPS-gen
+            processor=generator,
             memory=memory,
             cache_hierarchy=cache_hierarchy,
         )
+        self._set_fullsystem(False)
 
     @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
@@ -108,3 +114,16 @@
     @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return False
+
+    @overrides(AbstractBoard)
+    def _connect_things(self) -> None:
+        super()._connect_things()
+
+        if not self.get_cache_hierarchy():
+            # If we have no caches, then there must be a one-to-one
+            # connection between the generators and the memories.
+            assert len(self.get_processor().get_cores()) == 1
+            assert len(self.get_memory().get_mem_ports()) == 1
+            self.get_processor().get_cores()[0].connect_dcache(
+                self.get_memory().get_mem_ports()[0][1]
+            )
diff --git a/src/python/gem5/components/processors/abstract_generator.py b/src/python/gem5/components/processors/abstract_generator.py
index 41cbf5c..ff5387d 100644
--- a/src/python/gem5/components/processors/abstract_generator.py
+++ b/src/python/gem5/components/processors/abstract_generator.py
@@ -65,3 +65,6 @@
         this method needs to be implemented in detail or implmeneted as pass.
         """
         raise NotImplementedError
+
+    def _post_instantiate(self) -> None:
+        self.start_traffic()
diff --git a/src/python/gem5/simulate/exit_event.py b/src/python/gem5/simulate/exit_event.py
index 691e41a..0890178 100644
--- a/src/python/gem5/simulate/exit_event.py
+++ b/src/python/gem5/simulate/exit_event.py
@@ -87,6 +87,12 @@
             return ExitEvent.SIMPOINT_BEGIN
         elif exit_string == "a thread reached the max instruction count":
             return ExitEvent.MAX_INSTS
+        elif exit_string.endswith("will terminate the simulation.\n"):
+            # This is for the traffic generator exit event
+            return ExitEvent.EXIT
+        elif exit_string.endswith("is finished updating the memory.\n"):
+            # This is for the gups generator exit event
+            return ExitEvent.EXIT
         raise NotImplementedError(
             "Exit event '{}' not implemented".format(exit_string)
         )
diff --git a/tests/gem5/traffic_gen/simple_traffic_run.py b/tests/gem5/traffic_gen/simple_traffic_run.py
index dee20c9..b65163b 100644
--- a/tests/gem5/traffic_gen/simple_traffic_run.py
+++ b/tests/gem5/traffic_gen/simple_traffic_run.py
@@ -192,7 +192,7 @@
 # tasks
 motherboard = TestBoard(
     clk_freq="3GHz",
-    processor=generator,  # We pass the traffic generator as the processor.
+    generator=generator,
     memory=memory,
     cache_hierarchy=cache_hierarchy,
 )