mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-11-04 00:16:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			119 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
//  TimedEventLoop.hpp
 | 
						|
//  Clock Signal
 | 
						|
//
 | 
						|
//  Created by Thomas Harte on 29/07/2016.
 | 
						|
//  Copyright 2016 Thomas Harte. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include "Storage.hpp"
 | 
						|
#include "ClockReceiver/ClockReceiver.hpp"
 | 
						|
#include "SignalProcessing/Stepper.hpp"
 | 
						|
 | 
						|
#include <memory>
 | 
						|
 | 
						|
namespace Storage {
 | 
						|
 | 
						|
/*!
 | 
						|
	Provides a mechanism for arbitrarily timed events to be processed according to a fixed-base
 | 
						|
	discrete clock signal, ensuring correct timing.
 | 
						|
 | 
						|
	Subclasses are responsible for calling @c set_next_event_time_interval to establish the time
 | 
						|
	until a next event; @c process_next_event will be called when that event occurs, with progression
 | 
						|
	determined via @c run_for.
 | 
						|
 | 
						|
	Due to the aggregation of total timing information between events, e.g. if an event loop has
 | 
						|
	a clock rate of 1000 ticks per second and a steady stream of events that occur 10,000 times a second,
 | 
						|
	bookkeeping is necessary to ensure that 10 events are triggered per tick. Subclasses should call
 | 
						|
	@c reset_timer if there is a discontinuity in events.
 | 
						|
 | 
						|
	Subclasses may also call @c jump_to_next_event to cause the next event to be communicated instantly.
 | 
						|
 | 
						|
	Subclasses are therefore expected to call @c set_next_event_time_interval upon obtaining an event stream,
 | 
						|
	and again in response to each call to @c process_next_event while events are ongoing. They may use
 | 
						|
	@c reset_timer to initiate a distinctly-timed stream or @c jump_to_next_event to short-circuit the timing
 | 
						|
	loop and fast forward immediately to the next event.
 | 
						|
*/
 | 
						|
class TimedEventLoop {
 | 
						|
public:
 | 
						|
	/*!
 | 
						|
		Constructs a timed event loop that will be clocked at @c input_clock_rate.
 | 
						|
	*/
 | 
						|
	TimedEventLoop(Cycles::IntType input_clock_rate);
 | 
						|
 | 
						|
	/*!
 | 
						|
		Advances the event loop by @c number_of_cycles cycles.
 | 
						|
	*/
 | 
						|
	void run_for(const Cycles cycles);
 | 
						|
 | 
						|
	/*!
 | 
						|
		@returns the number of whole cycles remaining until the next event is triggered.
 | 
						|
	*/
 | 
						|
	Cycles::IntType get_cycles_until_next_event() const;
 | 
						|
 | 
						|
	/*!
 | 
						|
		@returns the input clock rate.
 | 
						|
	*/
 | 
						|
	Cycles::IntType get_input_clock_rate() const;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	/*!
 | 
						|
		For debugging purposes only, returns the number of events that have so far passed.
 | 
						|
	*/
 | 
						|
	int event_count() { return event_count_; }
 | 
						|
#endif
 | 
						|
 | 
						|
protected:
 | 
						|
	/*!
 | 
						|
		Sets the time interval, as a proportion of a second, until the next event should be triggered.
 | 
						|
	*/
 | 
						|
	void set_next_event_time_interval(Time interval);
 | 
						|
	void set_next_event_time_interval(float interval);
 | 
						|
 | 
						|
	/*!
 | 
						|
		Communicates that the next event is triggered. A subclass will idiomatically process that event
 | 
						|
		and make a fresh call to @c set_next_event_time_interval to keep the event loop running.
 | 
						|
	*/
 | 
						|
	virtual void process_next_event() = 0;
 | 
						|
 | 
						|
	/*!
 | 
						|
		Optionally allows a subclass to track time within run_for periods; if a subclass implements
 | 
						|
		advnace then it will receive advance increments that add up to the number of cycles supplied
 | 
						|
		to run_for, but calls to process_next_event will be precisely interspersed. No time will carry
 | 
						|
		forward between calls into run_for; a subclass can receive arbitrarily many instructions to
 | 
						|
		advance before receiving a process_next_event.
 | 
						|
	*/
 | 
						|
	virtual void advance([[maybe_unused]] const Cycles cycles) {};
 | 
						|
 | 
						|
	/*!
 | 
						|
		Resets timing, throwing away any current internal state. So clears any fractional ticks
 | 
						|
		that the event loop is currently tracking.
 | 
						|
	*/
 | 
						|
	void reset_timer();
 | 
						|
 | 
						|
	/*!
 | 
						|
		Causes an immediate call to @c process_next_event and a call to @c reset_timer with the
 | 
						|
		net effect of processing the current event immediately and fast forwarding exactly to the
 | 
						|
		start of the interval prior to the next event.
 | 
						|
	*/
 | 
						|
	void jump_to_next_event();
 | 
						|
 | 
						|
	/*!
 | 
						|
		@returns the amount of time that has passed since the last call to @c set_next_time_interval,
 | 
						|
		which will always be less than or equal to the time that was supplied to @c set_next_time_interval.
 | 
						|
	*/
 | 
						|
	Time get_time_into_next_event();
 | 
						|
 | 
						|
private:
 | 
						|
	Cycles::IntType input_clock_rate_ = 0;
 | 
						|
	Cycles::IntType cycles_until_event_ = 0;
 | 
						|
	float subcycles_until_event_ = 0.0f;
 | 
						|
#ifndef NDEBUG
 | 
						|
	int event_count_ = 0;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
}
 |