diff --git a/include/circt/Dialect/Sim/EventQueue.h b/include/circt/Dialect/Sim/EventQueue.h index 36ad43af68..1a1a8d8c45 100644 --- a/include/circt/Dialect/Sim/EventQueue.h +++ b/include/circt/Dialect/Sim/EventQueue.h @@ -390,6 +390,14 @@ class TimeWheel { /// Returns the number of events processed. size_t processCurrentDelta(); + /// Process all events in the current delta up to (inclusive) limitRegion. + /// Returns the number of events processed. + size_t processDeltaUpTo(SchedulingRegion limitRegion); + + /// Peek at the next non-empty region in the current delta without advancing. + /// Returns NumRegions if no events remain in the current delta. + SchedulingRegion peekCurrentRegion() const; + /// Process all events at the current real time (all deltas and regions). /// Returns the number of events processed. size_t processCurrentTime(); @@ -566,6 +574,14 @@ class EventScheduler { /// Returns false if simulation is complete. bool stepDelta(); + /// Step through the current delta up to (inclusive) the given region limit. + /// Returns true if any events were processed. + bool stepDeltaUpTo(SchedulingRegion limitRegion); + + /// Peek at the next non-empty region in the current delta without advancing. + /// Returns NumRegions if no events remain in the current delta. + SchedulingRegion peekCurrentRegion() const; + /// Advance time to the next scheduled event without processing it. /// This allows the caller to control when events are executed. /// Returns true if time was advanced, false if no events or already at next event. diff --git a/lib/Dialect/Sim/EventQueue.cpp b/lib/Dialect/Sim/EventQueue.cpp index 991f89410f..cf08458ac9 100644 --- a/lib/Dialect/Sim/EventQueue.cpp +++ b/lib/Dialect/Sim/EventQueue.cpp @@ -497,6 +497,81 @@ size_t TimeWheel::processCurrentDelta() { return total; } +SchedulingRegion TimeWheel::peekCurrentRegion() const { + size_t slotIdx = getSlotIndex(currentTime.realTime, 0); + const auto &slot = levels[0].slots[slotIdx]; + const DeltaCycleQueue *deltaQueuePtr = nullptr; + if (currentTime.deltaStep < Slot::kInlineDeltaSlots) { + if (slot.deltaQueues[currentTime.deltaStep].hasAnyEvents()) + deltaQueuePtr = &slot.deltaQueues[currentTime.deltaStep]; + } else { + auto it = slot.extraDeltaQueues.find(currentTime.deltaStep); + if (it != slot.extraDeltaQueues.end()) + deltaQueuePtr = &it->second; + } + if (!deltaQueuePtr) + return SchedulingRegion::NumRegions; + return deltaQueuePtr->getNextNonEmptyRegion( + static_cast(currentTime.region)); +} + +size_t TimeWheel::processDeltaUpTo(SchedulingRegion limitRegion) { + size_t total = 0; + size_t slotIdx = getSlotIndex(currentTime.realTime, 0); + auto &slot = levels[0].slots[slotIdx]; + + DeltaCycleQueue *deltaQueue = nullptr; + bool isInline = (currentTime.deltaStep < Slot::kInlineDeltaSlots); + if (isInline) { + if (slot.deltaQueues[currentTime.deltaStep].hasAnyEvents()) + deltaQueue = &slot.deltaQueues[currentTime.deltaStep]; + } else { + auto it = slot.extraDeltaQueues.find(currentTime.deltaStep); + if (it != slot.extraDeltaQueues.end()) + deltaQueue = &it->second; + } + + if (deltaQueue) { + bool madeProgress = true; + while (madeProgress) { + madeProgress = false; + auto region = deltaQueue->getNextNonEmptyRegion( + static_cast(currentTime.region)); + while (region != SchedulingRegion::NumRegions && + static_cast(region) <= + static_cast(limitRegion)) { + size_t count = deltaQueue->executeAndClearRegion(region); + totalEvents -= count; + total += count; + madeProgress = true; + + unsigned nextIdx = static_cast(region) + 1; + if (nextIdx > static_cast(limitRegion)) + break; + region = deltaQueue->getNextNonEmptyRegion( + static_cast(nextIdx)); + } + } + + // Advance region cursor to just past the limit so subsequent stepRegion() + // calls start from the correct position. + unsigned nextIdx = static_cast(limitRegion) + 1; + if (nextIdx < static_cast(SchedulingRegion::NumRegions)) { + auto nextRegion = deltaQueue->getNextNonEmptyRegion( + static_cast(nextIdx)); + if (nextRegion != SchedulingRegion::NumRegions) + currentTime.region = static_cast(nextRegion); + } + + if (!isInline && !deltaQueue->hasAnyEvents()) + slot.extraDeltaQueues.erase(currentTime.deltaStep); + } + + slot.hasEvents = slot.hasAnyEvents(); + updateSlotBit(0, slotIdx, slot.hasEvents); + return total; +} + size_t TimeWheel::processCurrentTime() { size_t total = 0; @@ -656,6 +731,21 @@ bool EventScheduler::stepDelta() { return processed > 0; } +bool EventScheduler::stepDeltaUpTo(SchedulingRegion limitRegion) { + if (!wheel->hasEvents()) + return false; + + size_t processed = wheel->processDeltaUpTo(limitRegion); + stats.eventsProcessed += processed; + if (processed > 0) + ++stats.deltaCycles; + return processed > 0; +} + +SchedulingRegion EventScheduler::peekCurrentRegion() const { + return wheel->peekCurrentRegion(); +} + bool EventScheduler::advanceToNextTime() { if (!wheel->hasEvents()) return false;