Core Library

The core library (schedsim::core) provides the simulation engine, hardware model, and foundational types. It has no dependencies on scheduling algorithms or I/O formats.

Types

The core type system uses strong wrappers for physical quantities. Duration and TimePoint store nanosecond-precision values as int64_t; construction goes through named bridge functions rather than public constructors.

Duration

class Duration

Time interval represented as an integer nanosecond count.

Duration wraps an int64_t nanosecond value with a private constructor. All construction goes through named factories or bridge functions, ensuring explicit conversions between seconds (double) and nanoseconds (int64_t).

Arithmetic between two Durations yields a Duration; dividing Duration by Duration yields a double (via duration_ratio), not an integer.

See also

duration_from_seconds, duration_from_nanoseconds, duration_to_seconds

See also

TimePoint

Public Functions

inline constexpr Duration() noexcept

Default constructor: zero duration.

Needed for aggregate initialization and container value types.

inline double seconds() const noexcept

Convert to seconds (double).

See also

duration_to_seconds

Returns:

Duration value in seconds.

inline int64_t nanoseconds() const noexcept

Return the raw nanosecond count.

See also

duration_to_nanoseconds

Returns:

Duration value in nanoseconds.

inline Duration operator+(Duration rhs) const noexcept

Add two durations.

Parameters:

rhs – Duration to add.

Returns:

Sum of the two durations.

inline Duration operator-(Duration rhs) const noexcept

Subtract a duration.

Parameters:

rhs – Duration to subtract.

Returns:

Difference of the two durations.

inline Duration &operator+=(Duration rhs) noexcept

Add a duration in place.

Parameters:

rhs – Duration to add.

Returns:

Reference to this.

inline Duration &operator-=(Duration rhs) noexcept

Subtract a duration in place.

Parameters:

rhs – Duration to subtract.

Returns:

Reference to this.

inline Duration operator-() const noexcept

Negate the duration.

Returns:

Duration with opposite sign.

Public Static Functions

static inline Duration zero() noexcept

Named factory returning a zero-length duration.

Returns:

Duration with value 0 ns.

TimePoint

class TimePoint

Absolute simulation time as a Duration offset from epoch (time zero).

TimePoint supports arithmetic with Duration (TimePoint +/- Duration yields TimePoint) and differencing (TimePoint - TimePoint yields Duration). Two TimePoints cannot be added.

See also

time_from_seconds, time_to_seconds, Duration

Public Functions

inline constexpr TimePoint() noexcept

Default constructor: epoch (time zero).

Needed for containers like std::map and std::unordered_map.

inline Duration time_since_epoch() const noexcept

Return the duration elapsed since epoch.

Returns:

Duration from epoch to this time point.

inline TimePoint operator+(Duration d) const noexcept

Advance a time point by a duration.

Parameters:

d – Duration to add.

Returns:

New time point offset by d.

inline TimePoint operator-(Duration d) const noexcept

Retreat a time point by a duration.

Parameters:

d – Duration to subtract.

Returns:

New time point offset backward by d.

inline TimePoint &operator+=(Duration d) noexcept

Advance this time point in place.

Parameters:

d – Duration to add.

Returns:

Reference to this.

inline TimePoint &operator-=(Duration d) noexcept

Retreat this time point in place.

Parameters:

d – Duration to subtract.

Returns:

Reference to this.

inline Duration operator-(TimePoint rhs) const noexcept

Compute the duration between two time points.

Parameters:

rhs – Time point to subtract.

Returns:

Duration from rhs to this time point.

Public Static Functions

static inline TimePoint epoch() noexcept

Named factory returning the epoch (time zero).

Returns:

TimePoint at simulation start.

Bridge Functions

Duration schedsim::core::duration_from_seconds(double s)

Create a Duration from a value in seconds (round to nearest ns).

See also

duration_from_seconds_ceil, duration_to_seconds

Parameters:

s – Time interval in seconds.

Returns:

Duration rounded to the nearest nanosecond.

Duration schedsim::core::duration_from_seconds_ceil(double s)

Create a Duration from a value in seconds (round up to next ns).

Ceiling rounding is used for completion timers where truncation could cause a timer to fire one nanosecond too early.

See also

duration_from_seconds

Parameters:

s – Time interval in seconds.

Returns:

Duration rounded up to the next nanosecond.

double schedsim::core::duration_to_seconds(Duration d)

Convert a Duration to seconds (double).

See also

duration_from_seconds

Parameters:

d – Duration to convert.

Returns:

Value in seconds.

Duration schedsim::core::duration_from_nanoseconds(int64_t ns)

Create a Duration from a raw nanosecond count.

See also

duration_to_nanoseconds

Parameters:

ns – Nanosecond count.

Returns:

Duration wrapping ns.

int64_t schedsim::core::duration_to_nanoseconds(Duration d)

Extract the raw nanosecond count from a Duration.

See also

duration_from_nanoseconds

Parameters:

d – Duration to convert.

Returns:

Nanosecond count.

TimePoint schedsim::core::time_from_seconds(double s)

Create a TimePoint from a value in seconds since epoch.

See also

time_to_seconds

Parameters:

s – Absolute time in seconds.

Returns:

TimePoint at s seconds from epoch.

double schedsim::core::time_to_seconds(TimePoint tp)

Convert a TimePoint to seconds since epoch (double).

See also

time_from_seconds

Parameters:

tp – TimePoint to convert.

Returns:

Absolute time in seconds.

Duration schedsim::core::scale_duration(Duration d, double factor)

Scale a Duration by a floating-point factor (round to nearest ns).

Useful for frequency/performance scaling of execution times.

See also

divide_duration, duration_ratio

Parameters:
  • d – Duration to scale.

  • factor – Multiplicative factor.

Returns:

Scaled duration, rounded to nearest nanosecond.

Duration schedsim::core::divide_duration(Duration d, double divisor)

Divide a Duration by a floating-point divisor (round to nearest ns).

Useful for computing remaining execution time at a different speed.

See also

scale_duration, duration_ratio

Parameters:
  • d – Duration to divide.

  • divisor – Divisor (must not be zero).

Returns:

Divided duration, rounded to nearest nanosecond.

double schedsim::core::duration_ratio(Duration a, Duration b)

Compute the ratio of two Durations as a double.

Returns a / b as a floating-point value, not an integer truncation. This is the canonical way to compute utilization ratios (WCET/period).

See also

scale_duration, divide_duration

Parameters:
  • a – Numerator duration.

  • b – Denominator duration (must not be zero).

Returns:

Floating-point ratio a/b.

Frequency

struct Frequency

Strong type for processor clock frequency in MHz.

Public Members

double mhz

Frequency value in megahertz.

Power

struct Power

Strong type for power consumption in milliwatts.

Public Members

double mw

Power value in milliwatts.

Energy

struct Energy

Strong type for energy consumption in millijoules.

Public Functions

inline Energy &operator+=(const Energy &other)

Accumulate energy.

Parameters:

other – Energy to add.

Returns:

Reference to this.

Public Members

double mj

Energy value in millijoules.

Engine

The Engine is the central event-driven simulation loop. It owns the event priority queue and the Platform, advancing simulation time by dispatching events in chronological order.

class Engine

Event-driven simulation engine.

The Engine is the central simulation loop. It owns an event priority queue and a Platform, and advances simulation time by dispatching events in chronological order. Within a single timestep, events are processed by priority, followed by all requested deferred callbacks.

The Engine is non-copyable and non-movable, designed for stack allocation. A typical usage pattern is:

core::Engine engine;
auto& plat = engine.platform();
// ... add hardware and tasks to platform ...
engine.finalize();
// ... set up scheduler ...
engine.run();

See also

Platform, TraceWriter

Public Types

using JobArrivalHandler = std::function<void(Task&, Job)>

Callback type for handling job arrivals.

Public Functions

Engine()
~Engine()
Engine(const Engine&) = delete
Engine &operator=(const Engine&) = delete
Engine(Engine&&) = delete
Engine &operator=(Engine&&) = delete
inline TimePoint time() const noexcept

Returns the current simulation time.

Returns:

Current simulation time.

void run()

Run simulation until the event queue is empty.

void run(TimePoint until)

Run simulation until the given time point.

Parameters:

until – Simulation stops after processing all events at this time.

void run(std::function<bool()> stop_condition)

Run simulation until the stop condition returns true.

Parameters:

stop_condition – Evaluated between timesteps; simulation stops when it returns true.

void request_stop() noexcept

Request the engine to stop after the current timestep completes.

The current timestep finishes atomically; the flag is checked between timesteps. Auto-resets at the start of each run() call.

bool stop_requested() const noexcept

Returns true if a stop has been requested.

Returns:

True if request_stop() has been called.

TimerId add_timer(TimePoint when, int priority, std::function<void()> callback)

Schedule a one-shot timer.

Parameters:
  • when – Absolute simulation time to fire (must be > time()).

  • priority – Lower values fire first within the same timestep.

  • callback – Invoked when the timer fires.

Throws:

InvalidStateError – if when <= time().

Returns:

Identifier that can be passed to cancel_timer().

TimerId add_timer(TimePoint when, std::function<void()> callback)

Schedule a one-shot timer with default priority.

Parameters:
  • when – Absolute simulation time to fire (must be > time()).

  • callback – Invoked when the timer fires.

Returns:

Identifier that can be passed to cancel_timer().

void cancel_timer(TimerId &timer_id)

Cancel a pending timer.

Parameters:

timer_id – Timer to cancel; reset to invalid on return.

DeferredId register_deferred(std::function<void()> callback)

Register a deferred callback.

Deferred callbacks execute at the end of the current timestep, after all events at that time have been processed. They are useful for batching rescheduling decisions.

Parameters:

callback – Function to invoke when requested.

Returns:

Identifier used with request_deferred().

void request_deferred(DeferredId deferred_id)

Request that a registered deferred callback fires this timestep.

Parameters:

deferred_id – Identifier returned by register_deferred().

void set_trace_writer(TraceWriter *writer) noexcept

Set the trace writer for simulation event logging.

The Engine does not own the writer. Pass nullptr to disable tracing.

Parameters:

writer – Pointer to a TraceWriter, or nullptr.

template<typename F>
void trace(F &&func)

Invoke a tracing callback only if a trace writer is set.

Zero overhead when tracing is disabled: the callback is not invoked and no TraceRecord is allocated.

Template Parameters:

F – Callable with signature void(TraceWriter&).

Parameters:

func – Callback that writes trace data.

void finalize()

Finalize the engine and its platform.

Must be called after all hardware and tasks have been added to the Platform and before run(). Locks the platform collections and schedules initial job arrivals.

inline bool is_finalized() const noexcept

Returns true if the engine has been finalized.

Returns:

True after finalize() has been called.

void schedule_event(TimePoint when, int priority, Event event)

Insert a raw event into the event queue.

Parameters:
  • when – Simulation time for the event.

  • priority – Priority within the timestep (lower = earlier).

  • event – The event payload.

Platform &platform() noexcept

Access the simulation platform.

Returns:

Reference to the platform.

const Platform &platform() const noexcept

Access the simulation platform (const).

Returns:

Const reference to the platform.

void schedule_job_arrival(Task &task, TimePoint arrival_time, Duration exec_time)

Schedule a job arrival event for a task.

Parameters:
  • task – The task that will receive the job.

  • arrival_time – When the job arrives.

  • exec_time – Actual execution time for this job instance.

void set_job_arrival_handler(JobArrivalHandler handler)

Set the global job arrival handler.

Exactly one handler may be set per engine. Typically set by the scheduler to receive new jobs for admission and dispatch.

Parameters:

handler – Called on every job arrival event.

Throws:

HandlerAlreadySetError – if called more than once.

inline void enable_context_switch(bool enabled) noexcept

Enable or disable context switch overhead modeling.

Parameters:

enabled – True to enable, false to disable (default: disabled).

inline bool context_switch_enabled() const noexcept

Returns true if context switch overhead is enabled.

Returns:

True if context switching is modelled.

void enable_energy_tracking(bool enabled)

Enable or disable energy tracking.

Must be called before finalize(). When enabled, the engine tracks energy consumption per processor, clock domain, and power domain.

Parameters:

enabled – True to enable energy tracking.

bool energy_tracking_enabled() const noexcept

Returns true if energy tracking is enabled.

Returns:

True if energy tracking was enabled before finalization.

Energy processor_energy(std::size_t proc_id) const

Returns cumulative energy consumed by a processor.

Parameters:

proc_id – Processor index (0-based).

Throws:

InvalidStateError – if energy tracking is disabled.

Returns:

Cumulative energy for the processor.

Energy clock_domain_energy(std::size_t cd_id) const

Returns cumulative energy consumed by a clock domain.

Parameters:

cd_id – Clock domain index (0-based).

Throws:

InvalidStateError – if energy tracking is disabled.

Returns:

Cumulative energy for the clock domain.

Energy power_domain_energy(std::size_t pd_id) const

Returns cumulative energy consumed by a power domain.

Parameters:

pd_id – Power domain index (0-based).

Throws:

InvalidStateError – if energy tracking is disabled.

Returns:

Cumulative energy for the power domain.

Energy total_energy() const

Returns total energy consumed across all domains.

Throws:

InvalidStateError – if energy tracking is disabled.

Returns:

Sum of all domain energies.

Platform

The Platform is the container for all hardware resources: processor types, clock domains, power domains, processors, and tasks. Factory methods must be called before Engine::finalize().

class Platform

Container for all hardware resources in the simulation.

The Platform owns processor types, clock domains, power domains, processors, and tasks. Factory methods (add_*) must be called before Engine::finalize(). After finalization, collections are locked and indexed access methods become valid.

Internally uses vector<unique_ptr<T>> for contiguous pointer storage (good cache locality for iteration) with stable references (objects do not move when the vector grows).

Factory Methods

Add hardware components. Must be called before finalize().

ProcessorType &add_processor_type(std::string_view name, double performance, Duration context_switch_delay = duration_from_seconds(0.0))

Add a processor type.

Parameters:
  • name – Display name (e.g. “Cortex-A15”).

  • performance – Relative performance factor (1.0 = reference).

  • context_switch_delay – Overhead per context switch.

Returns:

Reference to the created ProcessorType.

ClockDomain &add_clock_domain(Frequency freq_min, Frequency freq_max, Duration transition_delay = duration_from_seconds(0.0))

Add a clock domain with frequency range.

Parameters:
  • freq_min – Minimum operating frequency.

  • freq_max – Maximum operating frequency.

  • transition_delay – Time to complete a frequency change.

Returns:

Reference to the created ClockDomain.

PowerDomain &add_power_domain(std::vector<CStateLevel> c_states)

Add a power domain with C-state levels.

Parameters:

c_states – Vector of available C-state levels (C0, C1, …).

Returns:

Reference to the created PowerDomain.

Processor &add_processor(ProcessorType &type, ClockDomain &clock_domain, PowerDomain &power_domain)

Add a processor assigned to a type, clock domain, and power domain.

Parameters:
  • type – Processor type describing the micro-architecture.

  • clock_domain – Clock domain for frequency scaling.

  • power_domain – Power domain for C-state management.

Returns:

Reference to the created Processor.

Task &add_task(Duration period, Duration relative_deadline, Duration wcet)

Add a task with auto-assigned ID.

Parameters:
  • period – Task period.

  • relative_deadline – Relative deadline (from arrival).

  • wcet – Worst-case execution time.

Returns:

Reference to the created Task.

Task &add_task(std::size_t id, Duration period, Duration relative_deadline, Duration wcet)

Add a task with an explicit ID (for matching scenario JSON).

Parameters:
  • id – Task identifier (typically 1-based from JSON).

  • period – Task period.

  • relative_deadline – Relative deadline (from arrival).

  • wcet – Worst-case execution time.

Returns:

Reference to the created Task.

Collection Sizes

inline std::size_t processor_type_count() const noexcept

Return the number of processor types.

Returns:

Processor type count.

inline std::size_t processor_count() const noexcept

Return the number of processors.

Returns:

Processor count.

inline std::size_t clock_domain_count() const noexcept

Return the number of clock domains.

Returns:

Clock domain count.

inline std::size_t power_domain_count() const noexcept

Return the number of power domains.

Returns:

Power domain count.

inline std::size_t task_count() const noexcept

Return the number of tasks.

Returns:

Task count.

Indexed Access

inline ProcessorType &processor_type(std::size_t idx)

Access a processor type by index.

Parameters:

idx – Zero-based index.

Returns:

Reference to the ProcessorType.

inline Processor &processor(std::size_t idx)

Access a processor by index.

Parameters:

idx – Zero-based index.

Returns:

Reference to the Processor.

inline ClockDomain &clock_domain(std::size_t idx)

Access a clock domain by index.

Parameters:

idx – Zero-based index.

Returns:

Reference to the ClockDomain.

inline PowerDomain &power_domain(std::size_t idx)

Access a power domain by index.

Parameters:

idx – Zero-based index.

Returns:

Reference to the PowerDomain.

inline Task &task(std::size_t idx)

Access a task by index.

Parameters:

idx – Zero-based index.

Returns:

Reference to the Task.

inline const ProcessorType &processor_type(std::size_t idx) const

Access a processor type by index (const).

Parameters:

idx – Zero-based index.

Returns:

Const reference to the ProcessorType.

inline const Processor &processor(std::size_t idx) const

Access a processor by index (const).

Parameters:

idx – Zero-based index.

Returns:

Const reference to the Processor.

inline const ClockDomain &clock_domain(std::size_t idx) const

Access a clock domain by index (const).

Parameters:

idx – Zero-based index.

Returns:

Const reference to the ClockDomain.

inline const PowerDomain &power_domain(std::size_t idx) const

Access a power domain by index (const).

Parameters:

idx – Zero-based index.

Returns:

Const reference to the PowerDomain.

inline const Task &task(std::size_t idx) const

Access a task by index (const).

Parameters:

idx – Zero-based index.

Returns:

Const reference to the Task.

Public Functions

explicit Platform(Engine &engine)

Construct a Platform owned by the given engine.

Parameters:

engine – The simulation engine that owns this platform.

inline double reference_performance() const noexcept

Returns the reference performance used for heterogeneous scaling.

Returns:

Reference performance factor.

void finalize()

Finalize the platform, locking all collections.

inline bool is_finalized() const noexcept

Returns true if the platform has been finalized.

Returns:

True after finalize() has been called.

Platform(const Platform&) = delete
Platform &operator=(const Platform&) = delete
Platform(Platform&&) = delete
Platform &operator=(Platform&&) = delete

Hardware Model

ProcessorType

class ProcessorType

Represents a category of processors sharing the same performance characteristics.

Performance is a relative value expressing how fast processors of this type execute work compared to other types. Normalization to the reference (highest-performance) type is performed by Platform.

ProcessorTypes are non-copyable but movable, following Decision 61.

See also

Processor, Platform, Task::wcet(const ProcessorType&, double)

Public Functions

ProcessorType(std::size_t id, std::string_view name, double performance, Duration context_switch_delay = duration_from_seconds(0.0))

Construct a new ProcessorType.

Parameters:
  • id – Unique identifier for this processor type.

  • name – Human-readable name (e.g., “A15”, “A7”).

  • performance – Relative performance factor (higher is faster).

  • context_switch_delay – Overhead duration for a context switch on this type. Defaults to zero.

inline std::size_t id() const noexcept

Get the unique processor type identifier.

Returns:

Processor type ID.

inline std::string_view name() const noexcept

Get the human-readable name of this processor type.

Returns:

Name as a string_view.

inline double performance() const noexcept

Get the relative performance factor.

See also

Task::wcet(const ProcessorType&, double)

Returns:

Performance value (higher means faster execution).

inline Duration context_switch_delay() const noexcept

Get the context switch delay for processors of this type.

See also

Processor

Returns:

Context switch overhead as a Duration.

ProcessorType(const ProcessorType&) = delete
ProcessorType &operator=(const ProcessorType&) = delete

Processor

enum class schedsim::core::ProcessorState

Enumeration of the possible states of a Processor.

Each processor is in exactly one state at any given simulation time. State transitions are managed internally by Processor and triggered by scheduling decisions, DVFS operations, and C-state requests.

Values:

enumerator Idle

No task assigned; the processor is available for scheduling.

enumerator ContextSwitching

Transitioning to run a job (optional overhead modelling).

enumerator Running

Actively executing a job.

enumerator Sleep

In a low-power C-state (C1 or deeper).

enumerator Changing

A DVFS frequency transition is in progress.

class Processor

Models a single CPU core in the simulated platform.

A Processor tracks its execution state and the job currently running on it. It belongs to exactly one ClockDomain (shared frequency) and one PowerDomain (shared C-state management). Scheduling decisions are communicated through ISR-style callback handlers that the algo layer registers.

Processor is non-copyable but movable (Decision 61).

Public Types

using JobCompletionHandler = std::function<void(Processor&, Job&)>

Callback type invoked when a job completes execution.

using DeadlineMissHandler = std::function<void(Processor&, Job&)>

Callback type invoked when a job misses its absolute deadline.

using ProcessorAvailableHandler = std::function<void(Processor&)>

Callback type invoked when a processor becomes available.

This fires after a DVFS transition completes and the processor has no pending work, or after a wake-up completes without a pending job.

Public Functions

Processor(std::size_t id, ProcessorType &type, ClockDomain &clock_domain, PowerDomain &power_domain)

Construct a Processor with its hardware affiliations.

Parameters:
  • id – Unique processor identifier within the platform.

  • type – Reference to the ProcessorType describing this core’s micro-architecture (performance factor, context-switch overhead, etc.).

  • clock_domain – The ClockDomain this processor belongs to.

  • power_domain – The PowerDomain this processor belongs to.

inline std::size_t id() const noexcept

Return the unique identifier of this processor.

Returns:

Processor ID (zero-based index within the platform).

inline ProcessorType &type() const noexcept

Return the ProcessorType describing this core’s micro-architecture.

See also

ProcessorType

Returns:

Reference to the associated ProcessorType.

inline ProcessorState state() const noexcept

Return the current execution state of the processor.

See also

ProcessorState

Returns:

The current ProcessorState.

inline ClockDomain &clock_domain() const noexcept

Return the ClockDomain this processor belongs to.

See also

ClockDomain

Returns:

Reference to the associated ClockDomain.

inline PowerDomain &power_domain() const noexcept

Return the PowerDomain this processor belongs to.

See also

PowerDomain

Returns:

Reference to the associated PowerDomain.

inline Job *current_job() const noexcept

Return the job currently assigned to this processor, if any.

See also

Job

Returns:

Pointer to the current Job, or nullptr if no job is assigned.

Task *current_task() const noexcept

Return the task that owns the current job, if any.

See also

Task

Returns:

Pointer to the current Task, or nullptr if no job is assigned.

double speed(double reference_performance) const noexcept

Compute the normalised execution speed of this processor.

Speed is defined as (freq / freq_max) * (perf / reference_performance), where freq and freq_max come from the ClockDomain, perf from the ProcessorType, and reference_performance from the Platform.

Parameters:

reference_performance – The platform-wide reference performance factor used for normalisation.

Returns:

Normalised speed in the range (0, 1] under typical configurations.

inline int current_cstate_level() const noexcept

Return the current C-state level of this processor.

Returns:

C-state level: 0 means active (C0); higher values indicate deeper sleep states.

void assign(Job &job)

Assign a job to this processor for execution.

If the processor is Idle, the job starts immediately (or after an optional context-switch delay). If the processor is in Sleep, a wake-up sequence is initiated; the job starts once the processor has woken up.

See also

clear, Job

Parameters:

job – The Job to execute on this processor.

Throws:

InvalidStateError – if the processor is not in Idle or Sleep state (Decision 72).

void clear()

Remove the current job from this processor.

The exact behaviour depends on the current state:

  • Running: stops execution, returns to Idle.

  • ContextSwitching: cancels the transition; the job never runs.

  • Changing (DVFS in progress): sets a pending-clear flag; the processor returns to Idle once the DVFS transition completes.

See also

assign

Throws:

InvalidStateError – if the processor is already Idle (Decision 53).

void request_cstate(int level)

Request a transition to a low-power C-state.

The processor enters the specified C-state level. Level 0 represents the fully active state (C0).

Parameters:

level – The target C-state level (0 = C0/active, higher = deeper).

Throws:

InvalidStateError – if the processor is Running, Changing, or ContextSwitching.

void set_job_completion_handler(JobCompletionHandler handler)

Register a handler called when a job finishes its work.

Parameters:

handler – The callback to invoke on job completion.

void set_deadline_miss_handler(DeadlineMissHandler handler)

Register a handler called when a job’s absolute deadline is missed.

Parameters:

handler – The callback to invoke on deadline miss.

void set_processor_available_handler(ProcessorAvailableHandler handler)

Register a handler called when the processor becomes available.

Parameters:

handler – The callback to invoke when the processor is ready.

Processor(const Processor&) = delete
Processor &operator=(const Processor&) = delete

ClockDomain

class ClockDomain

Groups processors that share a common frequency setting.

A ClockDomain models a voltage/frequency island: all processors within it operate at the same frequency. DVFS operations (set_frequency()) affect every processor in the domain simultaneously.

Optionally, a ClockDomain can be configured with discrete Operating Performance Points (OPPs) via set_frequency_modes(), and with a polynomial power model via set_power_coefficients() for energy tracking.

ClockDomain is non-copyable but movable (Decision 61).

Public Functions

ClockDomain(std::size_t id, Frequency freq_min, Frequency freq_max, Duration transition_delay = duration_from_seconds(0.0))

Construct a ClockDomain with its frequency bounds.

Parameters:
  • id – Unique identifier within the platform.

  • freq_min – Minimum allowed frequency for this domain.

  • freq_max – Maximum allowed frequency for this domain. The domain is initialised to this frequency.

  • transition_delay – Duration of an asynchronous DVFS transition. A zero duration means frequency changes are applied instantaneously.

inline std::size_t id() const noexcept

Return the unique identifier of this clock domain.

Returns:

Clock domain ID.

inline Frequency frequency() const noexcept

Return the current operating frequency.

Returns:

The active frequency of all processors in this domain.

inline Frequency freq_min() const noexcept

Return the minimum allowed frequency.

Returns:

Lower bound of the frequency range.

inline Frequency freq_max() const noexcept

Return the maximum allowed frequency.

Returns:

Upper bound of the frequency range.

inline Duration transition_delay() const noexcept

Return the DVFS transition delay.

Returns:

Duration of an asynchronous frequency change. Zero means transitions are instantaneous.

inline bool is_locked() const noexcept

Check whether DVFS is permanently disabled for this domain.

See also

lock_frequency

Returns:

True if lock_frequency() has been called.

inline bool is_transitioning() const noexcept

Check whether an asynchronous DVFS transition is in progress.

Returns:

True if a frequency change has been initiated but not yet completed.

std::span<Processor*const> processors() const noexcept

Return the processors belonging to this clock domain.

See also

Processor

Returns:

A read-only span of processor pointers.

void set_frequency(Frequency freq)

Set the operating frequency of this clock domain.

If transition_delay is zero the change takes effect immediately and all processors are notified synchronously. Otherwise, an asynchronous DVFS transition is started: each processor enters the Changing state for the duration of the transition.

Parameters:

freq – The target frequency.

Throws:
inline void lock_frequency() noexcept

Permanently disable DVFS for this domain.

After this call, set_frequency() will throw InvalidStateError.

See also

is_locked

void set_frequency_modes(std::vector<Frequency> modes)

Define the set of discrete Operating Performance Points (OPPs).

When OPPs are set, set_frequency() only accepts values that exactly match one of the modes, and ceil_to_mode() can be used to round up.

Parameters:

modes – A list of valid frequencies, sorted ascending.

std::span<const Frequency> frequency_modes() const noexcept

Return the discrete OPP frequency list.

Returns:

A span of the configured frequency modes, or an empty span if continuous DVFS is in use.

bool has_frequency_modes() const noexcept

Check whether discrete OPPs have been configured.

Returns:

True if set_frequency_modes() has been called with a non-empty list.

Frequency ceil_to_mode(Frequency freq) const

Round a frequency up to the nearest configured OPP.

Returns the smallest frequency mode that is greater than or equal to freq. If freq exceeds all modes, returns the highest mode.

Parameters:

freq – The frequency to round up.

Returns:

The ceiling OPP frequency.

inline void set_freq_eff(Frequency freq) noexcept

Set the efficient frequency threshold for DPM decisions.

Below this frequency it may be more energy-efficient to use C-states (race-to-idle) rather than running at a very low frequency.

See also

freq_eff

Parameters:

freq – The efficient frequency threshold.

inline Frequency freq_eff() const noexcept

Return the efficient frequency threshold.

See also

set_freq_eff

Returns:

The frequency below which DPM sleep may be preferable to DVFS.

void set_power_coefficients(std::array<double, 4> coeffs) noexcept

Set the polynomial power-model coefficients.

The power model is: P(f) = a0 + a1*f + a2*f^2 + a3*f^3, where power is in milliwatts and frequency is in GHz.

Parameters:

coeffs – Array of four coefficients {a0, a1, a2, a3}.

Power power_at_frequency(Frequency freq) const noexcept

Evaluate the power model at a given frequency.

Parameters:

freq – The frequency at which to compute power.

Returns:

Estimated power consumption (mW) at the specified frequency.

ClockDomain(const ClockDomain&) = delete
ClockDomain &operator=(const ClockDomain&) = delete

PowerDomain

enum class schedsim::core::CStateScope

Determines whether a C-state applies per-processor or domain-wide.

Shallow C-states are typically per-processor (each core can independently enter or exit them), while deeper C-states may require all cores in a power domain to coordinate.

Values:

enumerator PerProcessor

Each processor can enter and exit this C-state independently.

enumerator DomainWide

All processors in the power domain must coordinate to enter this C-state.

struct CStateLevel

Describes a single C-state level and its hardware characteristics.

C-states form an ordered hierarchy: level 0 is the fully active state (C0), and increasing levels represent progressively deeper sleep states with lower power consumption but higher wake-up latency.

See also

PowerDomain, CStateScope

Public Members

int level

C-state number (0 = active/C0, higher = deeper sleep).

CStateScope scope

Whether this C-state is per-processor or domain-wide.

Duration wake_latency

Time required to transition from this C-state back to C0.

Power power

Power consumption while in this C-state (mW).

class PowerDomain

Groups processors that share C-state management.

A PowerDomain models a shared power rail or power-gating region. All processors in a power domain share the same set of available C-state levels. For domain-wide C-states, all processors must agree before the domain can enter a deeper sleep state.

PowerDomain is non-copyable but movable (Decision 61).

See also

Processor, CStateLevel, CStateScope, ClockDomain

Public Functions

PowerDomain(std::size_t id, std::vector<CStateLevel> c_states)

Construct a PowerDomain with its available C-state levels.

Parameters:
  • id – Unique identifier within the platform.

  • c_states – Ordered list of C-state levels supported by this domain. Level 0 (C0/active) should typically be included.

inline std::size_t id() const noexcept

Return the unique identifier of this power domain.

Returns:

Power domain ID.

inline std::span<const CStateLevel> c_states() const noexcept

Return the available C-state levels for this domain.

See also

CStateLevel

Returns:

A read-only span of CStateLevel descriptors.

inline std::span<Processor*const> processors() const noexcept

Return the processors belonging to this power domain.

See also

Processor

Returns:

A read-only span of processor pointers.

int achieved_cstate_for_processor(const Processor &proc) const

Compute the effective C-state for a given processor.

For per-processor C-states, this returns the processor’s own requested level. For domain-wide C-states, the achieved level is the minimum of all processors’ requested levels, since every processor must agree before the domain can enter a deeper state.

See also

Processor::current_cstate_level, CStateScope

Parameters:

proc – The processor to query.

Returns:

The effective C-state level that the processor can achieve.

Duration wake_latency(int level) const

Return the wake-up latency for a given C-state level.

This is the time required to transition from the specified C-state back to the fully active state (C0).

Parameters:

level – The C-state level to query.

Returns:

Wake-up latency duration. Returns a zero duration if level is 0 (C0) or if the level is not found.

Power cstate_power(int level) const

Return the power consumption for a given C-state level.

Parameters:

level – The C-state level to query.

Returns:

Power consumption (mW). Returns zero if level is not found.

PowerDomain(const PowerDomain&) = delete
PowerDomain &operator=(const PowerDomain&) = delete

Tasks and Jobs

Task

class Task

Represents a periodic or sporadic real-time task in the system.

WCET is specified in reference units, normalized to the highest-performance processor type in the platform. Actual wall-clock execution time depends on the processor speed and is obtained via the overloaded wcet(ProcessorType&, double) method.

Tasks are non-copyable but movable, following Decision 61.

Public Functions

Task(std::size_t id, Duration period, Duration relative_deadline, Duration wcet)

Construct a new Task.

Parameters:
  • id – Unique task identifier (1-based in scenario JSON).

  • period – Minimum inter-arrival time between consecutive jobs.

  • relative_deadline – Relative deadline from job arrival to completion.

  • wcet – Worst-case execution time in reference (normalized) units.

inline std::size_t id() const noexcept

Get the unique task identifier.

Returns:

Task ID.

inline Duration period() const noexcept

Get the task period (minimum inter-arrival time).

Returns:

Task period as a Duration.

inline Duration relative_deadline() const noexcept

Get the relative deadline.

Returns:

Relative deadline as a Duration from job arrival.

inline Duration wcet() const noexcept

Get the worst-case execution time in reference units.

See also

wcet(const ProcessorType&, double) for wall-clock WCET on a specific processor.

Returns:

WCET as a Duration in normalized reference units.

Duration wcet(const ProcessorType &type, double reference_performance) const noexcept

Compute the wall-clock WCET for a specific processor type.

Scales the reference WCET by the ratio of reference performance to the target processor type’s performance: wall_clock_wcet = wcet / (type.performance() / reference_performance)

Parameters:
  • type – The target processor type.

  • reference_performance – The reference (highest) performance value in the platform.

Returns:

Wall-clock WCET as a Duration for the given processor type.

Task(const Task&) = delete
Task &operator=(const Task&) = delete

Job

class Job

Represents a single instance (release) of a Task.

A Job is created by the Engine at job arrival time and is owned by the allocator (Library 2). Work is tracked in reference units (normalized execution time), independent of the processor on which the job executes.

Jobs are non-copyable but movable, following Decision 73.

See also

Task, Processor, Engine

Public Functions

Job(Task &task, Duration total_work, TimePoint absolute_deadline)

Construct a new Job.

Parameters:
  • task – Reference to the parent Task that spawned this job.

  • total_work – Total amount of work for this job in reference units.

  • absolute_deadline – Absolute deadline by which the job must complete.

inline Task &task() const noexcept

Get the parent task.

See also

Task

Returns:

Reference to the Task that this job belongs to.

inline Duration remaining_work() const noexcept

Get the remaining work for this job.

Returns:

Remaining work as a Duration in reference units.

inline Duration total_work() const noexcept

Get the total work for this job.

Returns:

Total work as a Duration in reference units.

inline TimePoint absolute_deadline() const noexcept

Get the absolute deadline.

Returns:

Absolute deadline as a TimePoint.

inline bool is_complete() const noexcept

Check whether the job has completed all of its work.

Returns:

True if remaining work is zero or less, false otherwise.

void consume_work(Duration amount)

Consume a given amount of work from this job.

Called by Processor during execution tracking. The amount is specified in reference units and is subtracted from the remaining work. The remaining work is clamped so it does not go below zero.

See also

Processor

Parameters:

amount – Amount of work to consume, in reference units.

inline bool deadline_miss_reported() const noexcept

Check whether a deadline miss has already been reported for this job.

Returns:

True after this job’s first deadline miss has been reported.

inline void set_deadline_miss_reported() noexcept

Mark that a deadline miss has been reported for this job.

Job(const Job&) = delete
Job &operator=(const Job&) = delete

Events and Timers

EventKey

struct EventKey

Deterministic ordering key for events in the priority queue.

Events are ordered first by simulation time, then by priority (lower values fire first), then by insertion sequence number to guarantee determinism when time and priority are equal.

Public Members

TimePoint time

Primary: simulation time at which the event fires.

int priority

Secondary: lower values fire first within a timestep.

uint64_t sequence

Tertiary: insertion order for determinism.

EventPriority

struct EventPriority

Named constants for event dispatch priority.

Lower numeric values indicate higher priority (fire first within the same simulation timestep). The ordering ensures that completions are processed before deadline checks, which are processed before new arrivals, matching standard real-time scheduling semantics.

Public Static Attributes

static int JOB_COMPLETION = -400

Highest: job finishes executing.

static int DEADLINE_MISS = -300

Deadline miss detection.

static int PROCESSOR_AVAILABLE = -200

Processor becomes idle or wakes.

static int JOB_ARRIVAL = -100

New job arrives for scheduling.

static int TIMER_DEFAULT = 0

Default priority for user timers.

Event Types

struct JobArrivalEvent

A new job instance arrives for a task.

Carries the task that generated the job and the actual execution time for this particular instance (may be less than the WCET).

Public Members

Task *task

Task that generated this job.

Duration exec_time

Actual execution time for this job instance.

struct JobCompletionEvent

A job has finished executing on its processor.

Fired when the processor completes all remaining work for the job. The scheduler should release the processor and update its ready queue.

See also

Processor, Job

Public Members

Processor *proc

Processor that completed the job.

Job *job

Job that finished execution.

struct DeadlineMissEvent

A job’s absolute deadline has been reached.

Fired when a job’s deadline expires. The scheduler’s deadline miss policy determines whether the simulation continues or aborts.

See also

Job, Processor

Public Members

Processor *proc

Processor running the job (may be nullptr if queued).

Job *job

Job whose deadline has been missed.

struct ProcessorAvailableEvent

A processor has become available for scheduling.

Fired when a processor transitions to an idle state (e.g., after completing a job or waking from a low-power C-state). The scheduler should check its ready queue for pending work.

See also

Processor

Public Members

Processor *proc

Processor that became available.

struct TimerEvent

A one-shot timer callback fires.

Created by Engine::add_timer(). The callback is invoked during event dispatch at the scheduled time and priority.

Public Members

std::function<void()> callback

User-provided callback to invoke.

TimerId

class TimerId

Provides O(1) timer cancellation by wrapping a map iterator.

A TimerId is returned by Engine::set_timer() and stores an iterator into the engine’s ordered event map, enabling constant-time cancellation. Default-constructed instances are invalid; only the Engine may create valid identifiers.

Timer callbacks should call clear() at their entry point to prevent dangling-iterator issues when the callback itself cancels other timers.

See also

Engine::set_timer()

See also

DeferredId

Public Functions

TimerId() = default

Default-construct an invalid timer identifier.

inline bool valid() const noexcept

Check whether this timer is still valid (not fired, not cancelled).

Returns:

true if the timer has not yet fired or been cancelled.

inline explicit operator bool() const noexcept

Allow contextual conversion to bool for if (timer_id) checks.

Returns:

true if valid.

inline void clear() noexcept

Mark the timer as no longer valid.

Call this at the start of timer callbacks to prevent dangling-iterator issues if the callback later tries to cancel this or another timer.

DeferredId

class DeferredId

Identifies a registered deferred callback.

A DeferredId is returned by Engine::defer() and wraps an index into the engine’s internal deferred-callback vector. Default-constructed instances are invalid; only the Engine may create valid identifiers.

See also

Engine::defer()

See also

Engine::cancel_deferred()

Public Functions

DeferredId() = default

Default-construct an invalid deferred callback identifier.

inline bool valid() const noexcept

Check whether this identifier refers to a valid deferred callback.

Returns:

true if the identifier was created by the Engine and has not been invalidated, false otherwise.

inline explicit operator bool() const noexcept

Allow contextual conversion to bool for if (deferred_id) checks.

Returns:

true if valid.

Trace Writer

class TraceWriter

Abstract interface for recording simulation trace events.

Implementations of TraceWriter serialise simulation events to a specific format (JSON, binary, socket, memory buffer, etc.). Each trace record is built incrementally:

  1. begin() &#8212; opens a new record at a given simulation time

  2. type() &#8212; sets the event type name

  3. field() &#8212; (repeated) adds key/value data fields

  4. end() &#8212; closes and optionally flushes the record

The Engine holds an optional pointer to a TraceWriter. When no writer is installed the overhead is a single null-pointer check (zero-cost).

Subclassed by schedsim::io::JsonTraceWriter, schedsim::io::MemoryTraceWriter, schedsim::io::NullTraceWriter, schedsim::io::TextualTraceWriter

Public Functions

virtual ~TraceWriter() = default

Virtual destructor for safe polymorphic deletion.

virtual void begin(TimePoint time) = 0

Begin a new trace record at the given simulation time.

Parameters:

time – The simulation time-point at which the event occurs.

virtual void type(std::string_view name) = 0

Set the event type name for the current record.

Parameters:

name – A short identifier for the event category (e.g. "dispatch", "preempt", "freq_change").

virtual void field(std::string_view key, double value) = 0

Add a floating-point field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, uint64_t value) = 0

Add an unsigned integer field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, std::string_view value) = 0

Add a string field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void end() = 0

End the current record and flush if needed.

After this call the writer is ready for a new begin()/end() cycle.

Energy Tracker

class EnergyTracker

Monitors power consumption and accumulates energy over time.

EnergyTracker listens for state-change notifications from Processor and ClockDomain objects and uses the configured power models to integrate instantaneous power into cumulative energy. It maintains per-processor accumulators that can be queried individually or aggregated by clock domain, power domain, or across the entire platform.

Typical usage:

  1. Construct with a Platform reference and the simulation start time.

  2. Wire up the on_* hooks so that Processor and ClockDomain call them on every state change.

  3. At the end of the simulation (or at any point), call update_to_time() and then read energy via the query methods.

Public Functions

explicit EnergyTracker(Platform &platform, TimePoint start_time)

Construct an EnergyTracker bound to a platform.

Parameters:
  • platform – The Platform whose processors and domains are tracked.

  • start_time – The simulation time at which tracking begins. Energy is accumulated from this point forward.

void on_processor_state_change(Processor &proc, ProcessorState old_state, ProcessorState new_state, TimePoint now)

Notify the tracker that a processor has changed execution state.

The tracker closes the previous power interval for the processor and opens a new one based on the new state.

See also

ProcessorState

Parameters:
  • proc – The processor whose state changed.

  • old_state – The state the processor was in before the change.

  • new_state – The state the processor is now in.

  • now – The simulation time at which the change occurred.

void on_frequency_change(ClockDomain &cd, Frequency old_freq, Frequency new_freq, TimePoint now)

Notify the tracker that a clock domain has changed frequency.

The tracker closes the previous power interval for every processor in the clock domain and opens new intervals at the new frequency.

Parameters:
  • cd – The clock domain whose frequency changed.

  • old_freq – The frequency before the change.

  • new_freq – The frequency after the change.

  • now – The simulation time at which the change occurred.

void on_cstate_change(Processor &proc, int old_level, int new_level, TimePoint now)

Notify the tracker that a processor’s C-state level has changed.

The tracker adjusts the power model for the processor based on the new C-state’s power consumption.

Parameters:
  • proc – The processor whose C-state changed.

  • old_level – The C-state level before the change (0 = C0/active).

  • new_level – The C-state level after the change.

  • now – The simulation time at which the change occurred.

void update_to_time(TimePoint now)

Flush all accumulators up to the specified simulation time.

Computes the energy consumed between each processor’s last-update time and now, then advances all last-update timestamps. Call this before reading energy values to ensure they reflect the latest state.

Parameters:

now – The simulation time to advance to.

Energy processor_energy(std::size_t proc_id) const

Return the accumulated energy for a single processor.

See also

update_to_time

Parameters:

proc_id – The processor’s unique identifier.

Returns:

Accumulated energy (mJ) from start_time to the last update.

Energy clock_domain_energy(std::size_t cd_id) const

Return the accumulated energy for all processors in a clock domain.

See also

ClockDomain

Parameters:

cd_id – The clock domain’s unique identifier.

Returns:

Sum of accumulated energy (mJ) across all processors in the domain.

Energy power_domain_energy(std::size_t pd_id) const

Return the accumulated energy for all processors in a power domain.

See also

PowerDomain

Parameters:

pd_id – The power domain’s unique identifier.

Returns:

Sum of accumulated energy (mJ) across all processors in the domain.

Energy total_energy() const

Return the total accumulated energy across the entire platform.

Returns:

Sum of accumulated energy (mJ) for every processor.

Errors

class SimulationError : public std::runtime_error

Base exception for all simulation errors.

All exceptions thrown by the core library derive from this class, allowing callers to catch simulation-specific errors separately from other std::runtime_error exceptions.

Subclassed by schedsim::core::AlreadyFinalizedError, schedsim::core::HandlerAlreadySetError, schedsim::core::InvalidStateError, schedsim::core::OutOfRangeError

class InvalidStateError : public schedsim::core::SimulationError

Thrown when an operation is invalid for the current object state.

For example, calling a method that requires finalization before the engine has been finalized, or attempting to execute a job on a processor that is already busy.

See also

SimulationError

class OutOfRangeError : public schedsim::core::SimulationError

Thrown when a value is outside its valid range.

For example, requesting a processor by index when the index exceeds the number of processors in the platform.

See also

SimulationError

class AlreadyFinalizedError : public schedsim::core::SimulationError

Thrown when attempting to modify the platform after finalize().

Once Engine::finalize() has been called, hardware and task collections are locked. Any add_* call after that point raises this exception.

class HandlerAlreadySetError : public schedsim::core::SimulationError

Thrown when setting a callback handler that has already been set.

Handlers such as Engine::set_job_arrival_handler() may only be registered once. A second call raises this exception.