indexpreviousnext

file name: clockifc.hpp, clockifc.cpp
classification: simulation
contents: class CClockInterface
derived from: class CApplControlledObject, class CNeedInterrupt, class CCrashingModule
friends: class ACGps, void CallWobbleISR()
use: provide a high-level interface to the UTCSU
 

global items used by CClockInterface:


global items defined by CClockInterface:

void CallWobbleISR (CNeedInterrupt *obj):  Assumes that obj is of type CClockInterface and calls obj->wobble_isr().

enum EInterruptSource: IntSnapshot, IntGps1PPS1-3, IntDutyA1-6, IntDutyB1-6
All possible interrupt sources. No other interrupts can be enabled through CClockInterface.

struct SWobbleDriftData: contains all data needed to "wobble" the clock drift, that is, continually move it through a certain range. The structure is accessed via parameter WobbleDrift and contains the following members:


local items used by CClockInterface:

const double GpsAmort: the new value of the amortization time of a clock if it switches from S- to P-Node and the current amortization is greater or equal to one.

const SINT32 LowerAccuracyBound: the lower bound of the accuracy. Needed in DragAccuracy().

const double TwoPow38: is needed by DragAccuracy() to compute the accuracy values of the clock at some time in the future.

const double TwoPow51: is needed for computing the value of the STEP and STEPLOW registers.

const SINT32 UpperAccuracyBound: the upper bound of the accuracy. Needed in DragAccuracy().
 

local functions defined in the implementation file:

INT32 SourceToMask (EInterruptSource source): Returns the interrupt bit number for the given interrupt source.
 

member variables:

double amorttime_: The maximum duration of the amortization phase. Used by function PreloadAmort().
default and reset value: DefaultAmortizationDuration

int area_: The area of the application. It is needed for disabling GPS interrupts (if the clock has GPS receivers).
default value: parameter area of the ctor

CClock *clock_: A pointer to the low-level clock interface (either hardware or simulated). The object is obtained through the global variable clockTemplate.

double deltaRho_: The current faulty drift. Members deltaSteplow and deltaSteppure are set according to this value.
default and reset value: 0

INT16 deltaSteplow_: The first eight bits are added to the STEPLOW register to simulate a faulty clock drift. If bit 8 is 1, then the value is subtracted instead of added. If bit 9 is 1, then the deltaSteppure value is subtracted instead of added.
default and reset value: 0

INT32 deltaSteppure_: Is added to the STEPPURE register to simulate a faulty clock drift.
default and reset value: 0

SRandomData distDataFast_: Distribution data for choosing a fast clock drift.
default and reset value: dist=Uniform, all other members are 0

SRandomData distDataregular_: Distribution data for choosing a normal clock drift.
default and reset value: dist=Uniform, all other members are 0

SRandomData distDataSlow_: Distribution data for choosing a slow clock drift.
default and reset value: dist=Uniform, all other members are 0

ACGps *gps_: If the clock has GPS receivers connected to it (P-Node), this variable stores a pointer to the GPS object. Otherwise, it is set to NULL.
default and reset value: NULL

EBool gpsStatus_: The status of the GPS receivers. True if all GPS receivers connected to the clock are healthy, False if at least one is not okay (the status is set by gps_). If gps_ is NULL, the value of this member is ignored.
default and reset value: False

INT16 lambdaN_: The current value of clock register LAMBDANPURE, which is used for deteriorating the negative clock accuracy.
default and reset value: 0

INT16 lambdaP_: The current value of clock register LAMBDAPPURE, which is used for deteriorating the positive clock accuracy.
default and reset value: 0

double pDriftChange_: The probability that the clock drift is being changed in the course of fault injection.
default and reset value: 0

double pFast_: The probability for a faulty clock drift being taken from the interval (rhoPlus_, WcRhoPlus).
default and reset value: 0

double pReadFault_: The probability that a read access to an NTPTIME clock register returns an arbitrary value.
default and reset value: 0

double pSlow_: The probability for a faulty clock drift being taken from the interval -(rhoMinus_, WcRhoMinus).
default and reset value: 0

double pWriteFault_: The probability that a write access to an NTPTIME clock register sets an arbitrary value.
default and reset value: 0

CRandom random_: the random class used to obtain all kinds of random numbers.

double rhoMinus_: The worst case lower bound on the clock rate drift.
default and reset value: WcRhoMinus

double rhoPlus_: The worst case upper bound on the clock rate drift.
default and reset value: WcRhoPlus

INT32 steplow_: The STEPLOW value of the clock. It must be set in the uppermost byte of MSSET.
default and reset value: (2^59/f) mod 256 (f is the ideal clock frequency)

INT32 steppure_: The step value of STEPPURE.
default and reset value: 2^51/f (f is the ideal clock frequency)

SWobbleDriftData wobbleData_: Contains all data necessary to wobble the drift.
default and reset value: distrib={Uniform, all other members are 0}, period=0, addDrift=True, rhoMax=rhoMin=0
 

member functions:

CClockInterface (CControllingObject *ctrl, double clockFrequency, int area): First, the constructor obtains a new clock object from the clockTemplate. Then it sets its default ISR to the clock interrupt vector of type INTA (hardware snapshot) and the wobble_isr() to INTT (duty timer B6) through a call to SetISR(). Before setting the ISRs, all interrupts are disabled via DisableInterrupts() and afterwards they are enabled again using EnableInterrupts(). All members are set to their default values and Init() is called.

virtual ~CClockInterface (): Calls Dispose() to delete members that are also deleted during a reset and to disable the interrupts and duty timers used by the module. Then, the snapshot and wobble ISRs are removed from the vector table using RemoveISR() and the clock is deleted. During the calls to RemoveISR(), interrupts are disabled.

void AddGps (EGpsUnit gpu): If gps is NULL, a new object of the GPS class is created using gpsTemplate. In that case, if amorttime is greater or equal to one, it is set to gpsAmort instead. Anyway, SetISR() is called to install the interrupt service routine of the GPS class as ISR for the GPS receiver interrupt, and interrupts of the receiver are enabled through calling gps::EnableGps(). During the call to SetISR(), interrupts are disabled.
If gpu is greater or equal to EGpsUnitCount, an assert fails.

virtual void Amortize (double diff, INT32 diffPlus, INT32 diffMinus): The function calls PreloadAmort() to load all data into the clock, then writes to register STARTAMORT to start the amortization phase. The function is also called by the associated GPS class (if the clock has GPS functionality) if the clock value diverges from the GPS time.
Note: right now, the amortization duration is fixed (but configurable). Future versions will use a variable delay that is chosen to fulfill some requirements.

virtual void ClearInterrupt (EInterruptSource source): Clears the IP bit of the given interrupt source by setting the corresponding bit in UTCINTCLEAR.

virtual void ClearInterrupt (EClockInterrupt inttype): Clears the IP bits of all interrupts of the given interrupt type through UTCINTCLEAR.

void ComputeAccuracyCorrection (INT32 acc, double afterTime, INT32& diffPlus, INT32& diffMinus): Computes the accuracy difference that should be amortized after afterTime (clock) seconds have passed and returns them in diffPlus and diffMinus. The following amortization is typically done  through a call to Amortize() or PreloadAmort().
The function obtains the current clock accuracies, computes their values in afterTime seconds through a call to DragAccuracy() and then computes the differences between these future accuracies and the desired values, which are specified in parameter acc. Parameter acc contains both accuracies: the lower two bytes represent the positive accuracy, the upper two bytes the negative accuracy, both in the unsigned format [-8,-23]. The computed differences are stored in the parameters diffPlus and diffMinus in format S:[-8,-38], so they can directly be used to load the STATEP(N)SET registers of the clock.

virtual void Crash(): Calls SetStepRegisters() to set the STEPPURE(ACT) and STEPLOW registers of the UTCSU to 0. This stops the clock. Then, CCrashingModule::Crash() is called.

virtual void DisableDutyTimer (EInterruptSource source): If source is a duty timer (A1-6, B1-6), the function disables it by setting its DUTYL register to zero. If source is no duty timer, the function has no effect.

virtual void DisableInterrupt (EInterruptSource source): The function disables the interrupt specified by source by calling clock->DisableInterrupt() with the appropriate interrupt masks. If this function returns False, an assert fails.

virtual void Dispose(): If gps is not NULL, it is deleted (that disables all GPS receivers, so the CSA has then the type S-Node) and set to NULL. Additionally, member random is deleted. Then, the function disables the snapshot and duty timer B6 interrupts and disables duty timers B1 and B6.

SINT32 DragAccuracy (SINT32 currAlpha, SINT16 lambda, double afterTime): Computes the accuracy after afterTime clock seconds have passed if the current accuracy is currAlpha (in S:[-8,-38] format) and the deterioration value of the accuracy is lambda (in S:[-37,-51] format). The value returned is in S:[-8,-38] format. If afterTime is less than 0, an assert fails.

virtual void EnableInterrupt (EInterruptSource source): The function enables the interrupt specified by source by calling clock->EnableInterrupt() with the appropriate interrupt masks. If the clock function returns False, an assert fails.

virtual EBool ExecuteCommand (const SCommand& command): Returns True if the command could be executed. If not specified otherwise, the parameters assume that param is of type SGpsConnectCommand.

All other commands are passed to the ExecuteCommand() function of CApplControlledObject, and if that returns True, this is also the return value of this function. If it returns False, the command is passed on to CCrashingModule and the return value of this call is returned.

INT32 GetAccuracy () const: Returns the current accuracy of the clock (reads and returns register STATEPGETL).

CClock *GetClock(): Returns the low-level clock interface itself. Use this function only if direct access to the clock registers is absolutely necessary!

double GetFaultyDrift(): The function returns the current value of the faulty clock drift, which is taken from member deltaRho.

CNtpTime GetCurrentClockTime(): The function reads TSGETL and MSGET (the checksum is masked out) into a variable of type CNtpTime and returns this temporary variable.

double GetFrequency () const: Returns the ideal clock frequency.

double GetGranularity () const: Returns the external clock granularity.

double GetGranularityAcc () const: Returns the accuracy transmission loss of the clock.

double GetGranularityDT () const: Returns the duty timer granularity.

double GetGranularityOsc () const: Returns the oscillator granularity of the clock.

double GetGranularityS () const: Returns the internal clock granularity.

virtual EBool GetParameter (SParameter& param) const: Returns True if the parameter could be read. If not specified otherwise, the parameters assume that param is of type SSingleParameter.

All other parameters are first passed to the GetParameter() function of CApplControlledObject, and if the call returns True, this value is returned. Otherwise, the GetParameter() function of clock is called and its value is returned.

double GetRhoMinus () const: Returns the lower bound on the rate drift.

double GetRhoPlus () const: Returns the upper bound on the rate drift.

INT32 GetSteppure (): Returns the value of member steppure.

virtual EBool GetTimestamp (STimestamp& time): First calls InjectFaults() to inject faults. Then, the function determines according to pReadFault, if it should simulate a read fault. If yes, then it reports an event of type ClockReadFault, sets the members of time to arbitrary values and returns True. Otherweise, it stores the NTP time (the checksum is masked out from the macrostamp) and the accuracy of the clock in time. If gps is NULL, the function returns True, otherwise, it returns the value of gpsStatus.

EBool GpsConnected(): Returns True if at least one GPS receiver is connected to the clock (ie, if the node type is P-Node), otherwise False.

virtual void Init(): Sets the drift bounds, probabilities, distributions, drift wobbling data and the amortization duration to their reset values, creates member random and at last calls function InitializeClock() to initialize the clock and all members belonging to it. After the clock registers have been initialized, the function enables the hardware snapshot and duty timer B6 interrupts before it returns.

void InitializeClock(): The function first resets some registers:

Afterwards, the necessary initializations are carried out: Note: currently, the clock time value is set to the current global time to avoid problems with the initial synchronization. This will change in future versions (then, the clock time will be set to zero).

void InjectFaults(): The function first calls ModifyCrashStatus() to change the crash status of the module. Then, it uses pDriftChange to check if the clock drift should be changed. If not, then the function returns. If it should be changed, then pSlow and pFast are used to determine in which interval the new drift should be and the function uses random to obtain a random drift according to the corresponding distribution function. At last, function SetFaultyDrift() is called with this value to take over the faulty clock drift.

virtual void InterruptAfter (EInterruptSource source, double at): If source is a duty timer, that timer is programmed to raise an interrupt at clock time at. If source is not one of the duty timers, an assert fails.

virtual void InterruptAt (EInterruptSource source, double after): If source is a duty timer, that timer is programmed to raise an interrupt after (clock) seconds from the current clock time. If source is not one of the duty timers, an assert fails.

virtual double InterruptAtMult (EInterruptSource source, double multTime): If source is a duty timer, this timer is programmed to produce an interrupt at the next occasion where the clock time is a multiple of multTime (seconds). If source is not one of the duty timers, an assert fails. If the timer value is too large (overflow), then Report() is called to notify the user and the timer value is truncated. The function returns the clock time in seconds at which the interrupt is triggered.
Example: if multTime is 100 sec, and the clock time is 185, the clock will raise an interrupt at clock time 200.
Note: if in the example above the current clock time were 100, still the interrupt would occur at clock time 200, never at once.

virtual EBool InterruptPending (EInterruptSource source): The function checks the appropriate register UTCINTSTAT and returns True if the IP bit of the source is set. If it is not set, False is returned.

virtual EBool InterruptPending (EClockInterrupt inttype): The function checks registers UTCINTSTAT and returns True if an interrupt of the given type is pending. If none is pending, False is returned.

virtual void isr(): The ISR is called whenever an interrupt of type INTA occurs. If no hardware snapshot interrupt is pending, the function returns at once. Otherwise, the interrupt is cleared calling ClearInterrupt(). The snapshot data (MSSNU, TSSNU and ACCPSNU) is read and sent to the evaluation in an event with event type Snapshot.

virtual void PreloadAmort (double diff, INT32 diffPlus, INT32 diffMinus): The function tries to amortize the given time difference diff (seconds). Amortization lasts for amorttime seconds. If the difference cannot be fully amortized, as much as possible is amortized. The accuracy differences diffPlus and diffMinus, which have to be specified in the UTCSU format S:[-8,-38], are not amortized, but the state correction of the UTCSU is used instead. The function preloads all necessary data, but does not start the amortization. If amorttime is less or equal to zero, an assert fails.
Note: right now, the amortization duration is fixed (but configurable). Future versions will use a variable delay that is chosen to fulfill some requirements.

virtual void PreloadTimestamp (double loadAt, const STimestamp& time):
virtual void PreloadTimestamp (double loadAt, const SExtendedTimestamp& time): If the clock has GPS, a call to the function has no effect. Else, InjectFaults() is called to inject faults. Then, the crash status of the module is checked, and if it is crashed, the function sends event IgnoreSetting and returns at once. If the module is not crashed, then with probability pWriteFault the values specified in time are replaced with arbitrary values and event ClockWriteFault is sent to the evaluation. In any case, if amorttime is zero, the function calls SetTimestamp() to set the new clock value at once. Otherwise, event ClockPreload is sent to the evaluation and function PreloadAmort() is executed to load all amortization data. The necessary accuracy differences are computed by ComputeAccuracyCorrection() with parameter afterTime set to the difference between loadAt and the current time. Then, InterruptAt() is called with loadAt to arm the duty timer B of SSU1, which automatically starts the amortization phase upon activation.

virtual void Recover(): Calls SetStepRegisters() to set the UTCSU registers STEPPURE(ACT) and STEPLOW to the original step values, which are stored in the members steppure and steplow. Then, CCrashingModule::Recover() is called.

void RemoveGps (EGpsUnit gpu): if gps is NULL, the function returns at once. Otherwise, interrupts from the GPS receiver are disabled through calling gps::DisableGps(). Then, RemoveISR() is called to remove the ISR of this GPU from the vector table. If there are no other receivers enabled, gps is deleted and set to NULL. During the call to RemoveISR(), interrupts are disabled.

virtual void ReportCrashChange(): Creates an event of type SApplCrashEvent, fills in all parameters (module, crashed, type = CrashStatusChange) and calls ReportEvent() to send it to the evaluation.

virtual void ReportError(): Calls the global ReportError() function with module = Clock, nodeID = GetNodeID() and area = area_.

virtual void Reset(): Calls the Reset() functions of the base classes CCrashingModule and CApplControlledObject.

EBool SetAmortizationDuration (double amorttime): If amorttime is greater or equal to zero, and if the clock does not have GPS, internal member amorttime_ is set to the argument. If it is set to zero, the clock value is set instantaneously in SetTimestamp(). If the clock has GPS, amorttime must be in the interval (0,1). If amorttime is not in the correct range, False is returned and the value of the internal member is not changed.

void SetBounds (INT32 bounds): If the module is crashed, event IgnoreSetting is sent to the evaluation and the function returns at once. Otherwise, clock_->SetBOUNDP() is called with the parameter.

void SetDutyTimer (EInterruptSource source, const CNtpTime& time): If source is a duty timer (A1-6, B1-6), that timer is set to the value specified in time (since the duty timer corresponds to bits [+31,-16] of the NTP time, the value of time is rounded to the next multiple of the duty timer granularity) and enabled. If source is not one of the duty timers, an assert fails.

void SetFaultyDrift (double rho): The function converts rho into additive (or subtractive) values that are added to or subtracted from the step values steppure and steplow when setting registers STEPPURE and STEPLOW of the UTCSU. These values are stored in the members deltaSteppure and deltaSteplow. Since the values can also be subtracted, the sign bits have to be stored, so bit 8 of deltaSteplow is set to 1 if deltaSteplow should be subtracted from steplow, and bit 9 is set to 1 if deltaSteppure should be subtracted from steppure. At last, SetStepRegisters() is called with steppure and steplow to take over the changed drift and the value of rho is stored in member deltaRho. Note that rho is interpreted as the drift from the current value of steppure and steplow. So adding, e.g., deltaSteplow to steplow will result in steplow*(1+rho).

void SetLambdapure (INT16 lambdaP, INT16 lambdaN): If the module is crashed, event IgnoreSetting is sent to the evaluation and the function returns at once. Otherwise, clock_->SetLAMBDAPPURE() is called with lambdaP, and clock_->SetLAMBDANPURE() is called with lambdaN. In this case, the values of the registers are stored in members lambdaP_ and lambdaN_.

virtual EBool SetParameter (const SParameter& param): Returns True if the parameter could be set. If not specified otherwise, the parameters assume that param is of type SSingleParameter.

All other parameters are first passed to the SetParameter() function of CApplControlledObject, and if the call returns True, this value is returned. Otherwise, the SetParameter() function of clock is called and its value is returned.

void SetSteplow (INT8 steplow): If the module is crashed, event IgnoreSetting is sent to the evaluation and the function returns at once. Otherwise, member steplow_ is set to the parameter (which is shifted left by 24 bits). If deltaRho is not zero, then SetFaultyDrift() is called to adapt the delta step values and take over the new step values. Otherwise, SetStepRegisters() is called with the members steppure_ and steplow_.

void SetSteppure (INT32 steppure): If the module is crashed, event IgnoreSetting is sent to the evaluation and the function returns at once. Otherwise, member steppure_ is set to the parameter. If deltaRho is not zero, then SetFaultyDrift() is called to adapt the delta step values and take over the new step values. Otherwise, SetStepRegisters() is called with the members steppure_ and steplow_.

void SetStepRegisters (INT32 steppure, INT32 steplow): Sets the UTCSU registers STEPPURE(ACT) and STEPLOW. Parameter steplow must already be in the correct format for accessing MSSET (that is, the STEPLOW bits must be in the high byte). First, the lower 24 bits of steplow are set to zero (the macrostamp is not important anyway). Then, if steppure and steplow are both 0, the step registers are set to that value and the clock value (NTPTIME) freezes. If one of the parameters is not 0, then the drift values specified by the members deltaSteppure and deltaSteplow are added to simulate a faulty drift. Afterwards, the resulting values are again stored in STEPPURE(ACT) and STEPLOW.

The following steps are necessary to set the STEPPURE(ACT) and STEPLOW registers:

virtual void SetTimestamp (const STimestamp& time):
virtual void SetTimestamp (const SExtendedTimestamp& time): Sets the clock to the NTP time and the accuracy (through amortization). If the clock has GPS, a call to the function has no effect. Else, InjectFaults() is called to inject faults. Then, the crash status of the module is checked, and if it is crashed, the function sends event IgnoreSetting and returns at once. If the module is not crashed, then with probability pWriteFault the values specified in time are replaced with arbitrary values and event ClockWriteFault is sent to the evaluation. In any case, an event is sent containing the current and the desired value of the clock (event type ClockUpdate), and if amorttime is greater than zero, Amortize() is called with the time and accuracy differences. The accuracy differences are computed by ComputeAccuracyCorrection() with parameter afterTime set to zero. If amorttime is zero, the clock is set to the new timestamp and accuracy at once (since the accuracies can only be set symetrically, both the upper and lower accuracy are set to the higher value in time.acc).

virtual void wobble_isr (): The function is called for INTT interrupts. If no interrupt from duty timer B6 is pending, the function returns and has no effect. Otherwise, it clears this interrupt and sends event TimerEvent. Then, it obtains an incremental value for the clock drift from wobbleData.distrib and gets the current drift via GetFaultyDrift(). With this data, the function computes the modifier for the current drift and the direction into which the modification should go:
Starting with the current direction, it checks the appropriate bound for the wobble drift and if the new faulty drift would be beyond this bound, then it toggles addDrift to reverse the direction of the sweep. If the new drift would be beyond the other bound as well, then addDrift is toggled again.
If the direction was changed at most once, than everything is okay and the incremental value can be used without any modifications. However, if the direction was changed twice, the situation requires further analysis: If the current drift is within the wobble bounds, then obviously we cannot modify it in any direction without overstepping the bounds and should leave it be. Therefore, in this case the incremental value is set to zero.
If the current drift is already beyond the bounds of the wobble drift, then addDrift is set so that the new faulty drift goes into the direction of the nearest bound. If this modification would go past both bounds and make the new faulty drift invalid again, then the incremental value is modified so that the new faulty drift is set to the bound which is nearest to the current drift.
After all these modifications and checks, the function adds/subtracts (according to addDrift) the incremental value to/from the current faulty drift and calls SetFaultyDrift() to take over the new drift.

At last, the wobble period is checked and if it is 0, duty timer B6 is disabled.
The timer is also disabled if the values of the wobble drift bounds are both set to zero (the user can use this feature to turn off wobbling while setting the drift to zero at the same time).
If the conditions for turning off wobbling are not fulfilled, then the duty timer is set to the next multiple of the wobble period. Here, the implementation for pure simulation is different from that for hardware-based simulation: If PSOS_SIM is defined, the function simply calls InterruptAtMult(). If it is not defined, then we have to avoid a problem that is most likely due to the fact that the scheduling time of the interrupt process cannot be represented by the double format C++SIM uses and that a slightly smaller value is used instead. This makes it necessary to check if InterruptAtMult() has really scheduled the interrupt for the next multiple, or if it has scheduled it again for the current multiple. In the latter case, the interrupt is scheduled again using InterruptAt(). As an alternative, the interrupt could be scheduled with a simple call to InterruptAfter(), avoiding the problem of the call to InterruptAtMult(). Both implementations are given, and you can use a define to choose the version you want. The differences between them are explained in the implementation file, but they will only become apparent if the simulation is executed for a long time.
 


last modified: Fri Feb 5 18:56:05 1999