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.
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.
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:
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.
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 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.