fastmodel: Implement inst count events in the IRIS thread contexts.

These use the IRIS stepping API.

Change-Id: Ib45744cb0928fece664187e4df6b25b064b19f0e
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/22115
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/arch/arm/fastmodel/iris/thread_context.cc b/src/arch/arm/fastmodel/iris/thread_context.cc
index 43be171..1380cf3 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.cc
+++ b/src/arch/arm/fastmodel/iris/thread_context.cc
@@ -68,6 +68,35 @@
     }
 }
 
+void
+ThreadContext::maintainStepping()
+{
+    Tick now = 0;
+
+    while (true) {
+        if (comInstEventQueue.empty()) {
+            // Set to 0 to deactivate stepping.
+            call().step_setup(_instId, 0, "instruction");
+            break;
+        }
+
+        Tick next = comInstEventQueue.nextTick();
+        if (!now)
+            now = getCurrentInstCount();
+
+        if (next <= now) {
+            comInstEventQueue.serviceEvents(now);
+            // Start over now that comInstEventQueue has likely changed.
+            continue;
+        }
+
+        // Set to the number of instructions still to step through.
+        Tick remaining = next - now;
+        call().step_setup(_instId, remaining, "instruction");
+        break;
+    }
+}
+
 iris::IrisErrorCode
 ThreadContext::instanceRegistryChanged(
         uint64_t esId, const iris::IrisValueMap &fields, uint64_t time,
@@ -107,11 +136,34 @@
     return iris::E_ok;
 }
 
+iris::IrisErrorCode
+ThreadContext::simulationTimeEvent(
+        uint64_t esId, const iris::IrisValueMap &fields, uint64_t time,
+        uint64_t sInstId, bool syncEc, std::string &error_message_out)
+{
+    if (fields.at("RUNNING").getAsBool()) {
+        // If this is just simulation time starting up, don't do anything.
+        return iris::E_ok;
+    }
+
+    // If simulation time has stopped for any reason, IRIS helpfully clears
+    // all stepping counters and we need to set them back. We might also need
+    // to service events based on the current number of executed instructions.
+    maintainStepping();
+
+    // Restart simulation time to make sure things progress once we give
+    // control back.
+    call().simulationTime_run(iris::IrisInstIdSimulationEngine);
+
+    return iris::E_ok;
+}
+
 ThreadContext::ThreadContext(
         BaseCPU *cpu, int id, System *system,
         iris::IrisConnectionInterface *iris_if, const std::string &iris_path) :
     _cpu(cpu), _threadId(id), _system(system), _irisPath(iris_path),
     _instId(iris::IRIS_UINT64_MAX), _status(Active),
+    comInstEventQueue("instruction-based event queue"),
     client(iris_if, "client." + iris_path)
 {
     iris::InstanceInfo info;
@@ -148,6 +200,17 @@
     call().eventStream_create(
             iris::IrisInstIdSimulationEngine, initEventStreamId,
             evSrcInfo.evSrcId, client.getInstId());
+
+    client.registerEventCallback<Self, &Self::simulationTimeEvent>(
+            this, "ec_IRIS_SIMULATION_TIME_EVENT",
+            "Handle simulation time stopping for breakpoints or stepping",
+            "Iris::ThreadContext");
+    call().event_getEventSource(iris::IrisInstIdSimulationEngine, evSrcInfo,
+            "IRIS_SIMULATION_TIME_EVENT");
+    timeEventStreamId = iris::IRIS_UINT64_MAX;
+    call().eventStream_create(
+            iris::IrisInstIdSimulationEngine, timeEventStreamId,
+            evSrcInfo.evSrcId, client.getInstId());
 }
 
 ThreadContext::~ThreadContext()
@@ -161,6 +224,29 @@
             iris::IrisInstIdGlobalInstance, regEventStreamId);
     regEventStreamId = iris::IRIS_UINT64_MAX;
     client.unregisterEventCallback("ec_IRIS_INSTANCE_REGISTRY_CHANGED");
+
+    call().eventStream_destroy(
+            iris::IrisInstIdGlobalInstance, timeEventStreamId);
+    timeEventStreamId = iris::IRIS_UINT64_MAX;
+    client.unregisterEventCallback("ec_IRIS_SIMULATION_TIME_EVENT");
+}
+
+void
+ThreadContext::scheduleInstCountEvent(Event *event, Tick count)
+{
+    Tick now = getCurrentInstCount();
+    comInstEventQueue.schedule(event, count);
+    if (count <= now)
+        call().simulationTime_stop(iris::IrisInstIdSimulationEngine);
+    else
+        maintainStepping();
+}
+
+void
+ThreadContext::descheduleInstCountEvent(Event *event)
+{
+    comInstEventQueue.deschedule(event);
+    maintainStepping();
 }
 
 Tick
diff --git a/src/arch/arm/fastmodel/iris/thread_context.hh b/src/arch/arm/fastmodel/iris/thread_context.hh
index acb325c..bdf12ef 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.hh
+++ b/src/arch/arm/fastmodel/iris/thread_context.hh
@@ -73,15 +73,27 @@
     ResourceIds intRegIds;
 
 
+    // A queue to keep track of instruction count based events.
+    EventQueue comInstEventQueue;
+    // A helper function to maintain the IRIS step count. This makes sure the
+    // step count is correct even after IRIS resets it for us, and also handles
+    // events which are supposed to happen at the current instruction count.
+    void maintainStepping();
+
+
     iris::IrisErrorCode instanceRegistryChanged(
             uint64_t esId, const iris::IrisValueMap &fields, uint64_t time,
             uint64_t sInstId, bool syncEc, std::string &error_message_out);
     iris::IrisErrorCode phaseInitLeave(
             uint64_t esId, const iris::IrisValueMap &fields, uint64_t time,
             uint64_t sInstId, bool syncEc, std::string &error_message_out);
+    iris::IrisErrorCode simulationTimeEvent(
+            uint64_t esId, const iris::IrisValueMap &fields, uint64_t time,
+            uint64_t sInstId, bool syncEc, std::string &error_message_out);
 
     iris::EventStreamId regEventStreamId;
     iris::EventStreamId initEventStreamId;
+    iris::EventStreamId timeEventStreamId;
 
     mutable iris::IrisInstance client;
     iris::IrisCppAdapter &call() const { return client.irisCall(); }
@@ -96,8 +108,8 @@
     bool schedule(PCEvent *e) override { return false; }
     bool remove(PCEvent *e) override { return false; }
 
-    void scheduleInstCountEvent(Event *event, Tick count) override {}
-    void descheduleInstCountEvent(Event *event) override {}
+    void scheduleInstCountEvent(Event *event, Tick count) override;
+    void descheduleInstCountEvent(Event *event) override;
     Tick getCurrentInstCount() override;
 
     ::BaseCPU *getCpuPtr() override { return _cpu; }