Skip to content

Commit 58ad079

Browse files
authored
RTC Failover to Monotonic Time Since Boot (#116)
* RTC Failover to Monotonic Time Since Boot * Update SDD
1 parent 5888fd9 commit 58ad079

File tree

5 files changed

+184
-75
lines changed

5 files changed

+184
-75
lines changed

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ void RtcManager ::configure(const struct device* dev) {
2828
// ----------------------------------------------------------------------
2929

3030
void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
31+
// Get microseconds from system clock cycles
32+
// Note: RV3028 does not provide sub-second precision, so this is
33+
// just an approximation based on system cycles.
34+
// FPrime expects microseconds in the range [0, 999999]
35+
uint32_t useconds = k_cyc_to_us_near32(k_cycle_get_32()) % 1000000;
36+
3137
// Check device readiness
3238
if (!device_is_ready(this->m_dev)) {
3339
// Use logger instead of events since this fn is in a critical path for FPrime
@@ -38,6 +44,10 @@ void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
3844
this->m_console_throttled = true;
3945
Fw::Logger::log("RTC not ready\n");
4046
}
47+
48+
// Use monotonic time as fallback
49+
uint32_t seconds = k_uptime_seconds();
50+
time.set(TimeBase::TB_PROC_TIME, 0, static_cast<U32>(seconds), static_cast<U32>(useconds));
4151
return;
4252
}
4353

@@ -56,12 +66,6 @@ void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
5666
return;
5767
}
5868

59-
// Get microseconds from system clock cycles
60-
// Note: RV3028 does not provide sub-second precision, so this is
61-
// just an approximation based on system cycles.
62-
// FPrime expects microseconds in the range [0, 999999]
63-
uint32_t useconds = k_cyc_to_us_near32(k_cycle_get_32()) % 1000000;
64-
6569
// Set FPrime time object
6670
time.set(TimeBase::TB_WORKSTATION_TIME, 0, static_cast<U32>(seconds), static_cast<U32>(useconds));
6771
}
@@ -127,6 +131,13 @@ void RtcManager ::TIME_SET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Drv::Time
127131
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
128132
}
129133

134+
void RtcManager ::TEST_UNCONFIGURE_DEVICE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
135+
// Unconfigure the RTC device by setting m_dev to nullptr
136+
this->m_dev = nullptr;
137+
138+
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
139+
}
140+
130141
bool RtcManager ::timeDataIsValid(Drv::TimeData t) {
131142
bool valid = true;
132143

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.fpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ module Drv {
2626
t: Drv.TimeData @< Set the time
2727
) opcode 0
2828

29+
@ TEST_UNCONFIGURE_DEVICE command to unconfigure the RTC device. Used for testing RTC failover to monotonic time since boot.
30+
sync command TEST_UNCONFIGURE_DEVICE() opcode 1
31+
2932
##############################################################################
3033
#### Uncomment the following examples to start customizing your component ####
3134
##############################################################################

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ class RtcManager final : public RtcManagerComponentBase {
6565
Drv::TimeData t //!< Set the time
6666
) override;
6767

68+
//! Handler implementation for command TEST_UNCONFIGURE_DEVICE
69+
//!
70+
//! TEST_UNCONFIGURE_DEVICE command to unconfigure the RTC device. Used for testing RTC failover to monotonic time
71+
//! since boot.
72+
void TEST_UNCONFIGURE_DEVICE_cmdHandler(FwOpcodeType opCode, //!< The opcode
73+
U32 cmdSeq //!< The command sequence number
74+
) override;
75+
6876
private:
6977
// ----------------------------------------------------------------------
7078
// Private helper methods
Lines changed: 111 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,45 @@
11
# Components::RtcManager
22

3-
The RTC Manager component interfaces with the RTC Real Time Clock (RTC) to provide time measurements.
3+
The RTC Manager component interfaces with the Real Time Clock (RTC) to provide time measurements. When the RTC is unavailable, the component automatically fails over to monotonic time (uptime since boot) to ensure continuous system operation.
44

55
### Typical Usage
66

77
#### `TIME_SET` Command Usage
88
1. The component is instantiated and initialized during system startup
99
2. A ground station sends a `TIME_SET` command with the desired time
1010
3. On each command, the component:
11-
- Sets the time on the RTC
12-
- Emits a `TimeSet` event if the time is set successfully
11+
- Validates the time data (year >= 1900, month [1-12], day [1-31], hour [0-23], minute [0-59], second [0-59])
12+
- Emits validation failure events if any field is invalid
13+
- Sets the time on the RTC if validation passes
14+
- Emits a `TimeSet` event with the previous time if the time is set successfully
1315
- Emits a `TimeNotSet` event if the time is not set successfully
1416
- Emits a `DeviceNotReady` event if the device is not ready
1517

1618
#### `timeGetPort` Port Usage
1719
1. The component is instantiated and initialized during system startup
18-
2. In a deployment topology, a `time connection` relation is made.
20+
2. In a deployment topology, a `time connection` relation is made to sync FPrime's internal clock
1921
3. On each call, the component:
20-
- Fetches and returns the time from the RTC
21-
- Emits a `DeviceNotReady` event if the device is not ready
22-
23-
#### `timeGet` Port Usage
24-
1. The component is instantiated and initialized during system startup
25-
2. A manager calls the `timeGet` ports
26-
3. On each call, the component:
27-
- Fetches and returns the time from the RTC
28-
- Emits a `DeviceNotReady` event if the device is not ready
22+
- Checks if the RTC device is ready
23+
- If the RTC is ready:
24+
- Fetches time from the RTC hardware
25+
- Returns time with `TB_WORKSTATION_TIME` time base
26+
- If the RTC is not ready (failover mode):
27+
- Logs a warning message (throttled to prevent console flooding)
28+
- Fetches monotonic uptime from the system
29+
- Returns time with `TB_PROC_TIME` time base (uptime since boot)
30+
- Calculates microseconds from system clock cycles for sub-second precision
2931

3032
## Requirements
3133
| Name | Description | Validation |
3234
|---|---|---|
3335
| RtcManager-001 | The RTC Manager has a command that sets the time on the RTC | Integration test |
34-
| RtcManager-002 | The RTC Manager has a port which, when called, set the time in FPrime | Integration test |
35-
| RtcManager-003 | A device not ready event is emitted if the RTC is not ready | Manual |
36-
| RtcManager-004 | A time set event is emitted if the time is set successfully | Integration test |
36+
| RtcManager-002 | The RTC Manager has a port which, when called, returns the time from the RTC or monotonic uptime | Integration test |
37+
| RtcManager-003 | The RTC Manager logs a warning when the RTC is not ready and falls back to monotonic time | Integration test |
38+
| RtcManager-004 | A time set event is emitted if the time is set successfully, including the previous time | Integration test |
3739
| RtcManager-005 | A time not set event is emitted if the time is not set successfully | Integration test |
40+
| RtcManager-006 | The RTC Manager validates time data and emits validation failure events for invalid fields | Integration test |
41+
| RtcManager-007 | The RTC Manager provides monotonic uptime when the RTC device is unavailable | Integration test |
42+
| RtcManager-008 | Time increments continuously regardless of RTC availability | Integration test |
3843

3944
## Port Descriptions
4045
| Name | Description |
@@ -44,14 +49,21 @@ The RTC Manager component interfaces with the RTC Real Time Clock (RTC) to provi
4449
## Commands
4550
| Name | Description |
4651
|---|---|
47-
| SET_TIME | Sets the time on the RTC |
52+
| TIME_SET | Sets the time on the RTC with validation of all time fields |
53+
| TEST_UNCONFIGURE_DEVICE | (Test only) Unconfigures the RTC device to test monotonic time failover |
4854

4955
## Events
5056
| Name | Description |
5157
|---|---|
52-
| DeviceNotReady | Emits on unsuccessful device connection |
53-
| TimeSet | Emits on successful time set |
54-
| TimeNotSet | Emits on unsuccessful time set |
58+
| DeviceNotReady | Emitted when the RTC device is not ready during TIME_SET command |
59+
| TimeSet | Emitted on successful time set, includes previous time (seconds and microseconds) |
60+
| TimeNotSet | Emitted on unsuccessful time set |
61+
| YearValidationFailed | Emitted when provided year is invalid (should be >= 1900) |
62+
| MonthValidationFailed | Emitted when provided month is invalid (should be [1-12]) |
63+
| DayValidationFailed | Emitted when provided day is invalid (should be [1-31]) |
64+
| HourValidationFailed | Emitted when provided hour is invalid (should be [0-23]) |
65+
| MinuteValidationFailed | Emitted when provided minute is invalid (should be [0-59]) |
66+
| SecondValidationFailed | Emitted when provided second is invalid (should be [0-59]) |
5567

5668
## Class Diagram
5769
```mermaid
@@ -61,12 +73,15 @@ classDiagram
6173
<<Auto-generated>>
6274
}
6375
class RtcManager {
64-
- dev: device*
76+
- m_dev: device*
77+
- m_console_throttled: atomic~bool~
6578
+ RtcManager(char* compName)
6679
+ ~RtcManager()
80+
+ void configure(const device* dev)
6781
- void timeGetPort_handler(FwIndexType portNum, Fw::Time& time)
68-
- Fw::CmdResponse timeSet_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, const Drv::TimeData& time)
69-
- Fw::Time timeGet(U32& posix_time, U32& u_secs)
82+
- void TIME_SET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Drv::TimeData& time)
83+
- void TEST_UNCONFIGURE_DEVICE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq)
84+
- bool timeDataIsValid(Drv::TimeData t)
7085
}
7186
}
7287
RtcManagerComponentBase <|-- RtcManager : inherits
@@ -76,58 +91,84 @@ classDiagram
7691

7792
### `timeGetPort` port
7893

79-
The `timeGetPort` port is called from a `time connection` in a deployment topology to sync the RTC's time with FPrime's internal clock.
94+
The `timeGetPort` port is called from a `time connection` in a deployment topology to sync the RTC's time with FPrime's internal clock. The component automatically falls back to monotonic uptime if the RTC is unavailable.
8095

81-
#### Success
96+
#### Success (RTC Available)
8297
```mermaid
8398
sequenceDiagram
8499
participant Deployment Time Connection
85100
participant RTC Manager
86-
participant Zephyr Time API
87-
participant RTC
88-
Deployment Time Connection-->>RTC Manager: Call timeGetPort time port
89-
RTC Manager->>Zephyr Time API: Read time
90-
Zephyr Time API->>RTC: Read time
91-
RTC->>Zephyr Time API: Return time
92-
Zephyr Time API->>RTC Manager: Return time
93-
RTC Manager-->>Deployment Time Connection: Return time
101+
participant System Clock
102+
participant Zephyr RTC API
103+
participant RTC Sensor
104+
Deployment Time Connection->>RTC Manager: Call timeGetPort time port
105+
RTC Manager->>RTC Manager: Check device_is_ready()
106+
RTC Manager->>System Clock: Get microseconds from k_cycle_get_32()
107+
System Clock-->>RTC Manager: Return cycle-based microseconds
108+
RTC Manager->>Zephyr RTC API: Read time via rtc_get_time()
109+
Zephyr RTC API->>RTC Sensor: Read time
110+
RTC Sensor-->>Zephyr RTC API: Return time
111+
Zephyr RTC API-->>RTC Manager: Return rtc_time struct
112+
RTC Manager->>RTC Manager: Convert to time_t via timeutil_timegm()
113+
RTC Manager-->>Deployment Time Connection: Return Fw::Time (TB_WORKSTATION_TIME)
94114
```
95115

96-
#### Device Not Ready
116+
#### Failover to Monotonic Time (RTC Unavailable)
97117
```mermaid
98118
sequenceDiagram
99-
participant Event Log
119+
participant Console Log
100120
participant Deployment Time Connection
101121
participant RTC Manager
102-
participant Zephyr Time API
103-
participant RTC
122+
participant System Clock
104123
Deployment Time Connection->>RTC Manager: Call timeGetPort time port
105-
RTC Manager->>Zephyr Time API: Read time
106-
Zephyr Time API->>RTC: Read time
107-
RTC->>Zephyr Time API: Return device not ready
108-
Zephyr Time API->>RTC Manager: Return device not ready
109-
RTC Manager->>Event Log: Emit DeviceNotReady event
110-
RTC Manager->>Deployment Time Connection: Return 0 time
124+
RTC Manager->>RTC Manager: Check device_is_ready()
125+
Note over RTC Manager: Device not ready
126+
RTC Manager->>Console Log: Log "RTC not ready" (throttled)
127+
RTC Manager->>System Clock: Get microseconds from k_cycle_get_32()
128+
System Clock-->>RTC Manager: Return cycle-based microseconds
129+
RTC Manager->>System Clock: Get uptime via k_uptime_seconds()
130+
System Clock-->>RTC Manager: Return seconds since boot
131+
RTC Manager-->>Deployment Time Connection: Return Fw::Time (TB_PROC_TIME)
111132
```
112133

113134
### `TIME_SET` Command
114135

115-
The `TIME_SET` command is called to set the current time on the RTC.
136+
The `TIME_SET` command is called to set the current time on the RTC. The component validates all time fields before attempting to set the time.
116137

117138
#### Success
118139
```mermaid
119140
sequenceDiagram
120141
participant Ground Station
121142
participant Event Log
122143
participant RTC Manager
123-
participant Zephyr Time API
124-
participant RTC
125-
Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct
126-
RTC Manager->>Zephyr Time API: Set time
127-
Zephyr Time API->>RTC: Set time
128-
RTC->>Zephyr Time API: Return set success
129-
Zephyr Time API->>RTC Manager: Return set success
130-
RTC Manager->>Event Log: Emit event TimeSet
144+
participant Zephyr RTC API
145+
participant RTC Sensor
146+
Ground Station->>RTC Manager: Command TIME_SET with Drv::TimeData struct
147+
RTC Manager->>RTC Manager: Check device_is_ready()
148+
RTC Manager->>RTC Manager: Validate time data (timeDataIsValid)
149+
Note over RTC Manager: Check year >= 1900<br/>month [1-12], day [1-31]<br/>hour [0-23], min [0-59], sec [0-59]
150+
RTC Manager->>RTC Manager: Store previous time via getTime()
151+
RTC Manager->>Zephyr RTC API: Set time via rtc_set_time()
152+
Zephyr RTC API->>RTC Sensor: Set time
153+
RTC Sensor-->>Zephyr RTC API: Return success
154+
Zephyr RTC API-->>RTC Manager: Return success (status = 0)
155+
RTC Manager->>Event Log: Emit TimeSet event (with previous time)
156+
RTC Manager-->>Ground Station: Command response OK
157+
```
158+
159+
#### Validation Failure
160+
```mermaid
161+
sequenceDiagram
162+
participant Ground Station
163+
participant Event Log
164+
participant RTC Manager
165+
Ground Station->>RTC Manager: Command TIME_SET with invalid Drv::TimeData
166+
RTC Manager->>RTC Manager: Check device_is_ready()
167+
RTC Manager->>RTC Manager: Validate time data (timeDataIsValid)
168+
Note over RTC Manager: Validation fails
169+
RTC Manager->>Event Log: Emit validation failure events<br/>(YearValidationFailed, etc.)
170+
RTC Manager->>Event Log: Emit TimeNotSet event
171+
RTC Manager-->>Ground Station: Command response VALIDATION_ERROR
131172
```
132173

133174
#### Device Not Ready
@@ -136,33 +177,34 @@ sequenceDiagram
136177
participant Ground Station
137178
participant Event Log
138179
participant RTC Manager
139-
participant Zephyr Time API
140-
participant RTC
141-
Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct
142-
RTC Manager->>Zephyr Time API: Set time
143-
Zephyr Time API->>RTC: Set time
144-
RTC->>Zephyr Time API: Return device not ready
145-
Zephyr Time API->>RTC Manager: Return device not ready
146-
RTC Manager->>Event Log: Emit event DeviceNotReady
180+
Ground Station->>RTC Manager: Command TIME_SET with Drv::TimeData struct
181+
RTC Manager->>RTC Manager: Check device_is_ready()
182+
Note over RTC Manager: Device not ready
183+
RTC Manager->>Event Log: Emit DeviceNotReady event
184+
RTC Manager-->>Ground Station: Command response EXECUTION_ERROR
147185
```
148186

149-
#### Time Not Set
187+
#### Time Not Set (RTC Failure)
150188
```mermaid
151189
sequenceDiagram
152190
participant Ground Station
153191
participant Event Log
154192
participant RTC Manager
155-
participant Zephyr Time API
156-
participant RTC
157-
Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct
158-
RTC Manager->>Zephyr Time API: Set time
159-
Zephyr Time API->>RTC: Set time
160-
RTC->>Zephyr Time API: Return set failure
161-
Zephyr Time API->>RTC Manager: Return set failure
162-
RTC Manager->>Event Log: Emit event TimeNotSet
193+
participant Zephyr RTC API
194+
participant RTC Sensor
195+
Ground Station->>RTC Manager: Command TIME_SET with Drv::TimeData struct
196+
RTC Manager->>RTC Manager: Check device_is_ready()
197+
RTC Manager->>RTC Manager: Validate time data (timeDataIsValid)
198+
RTC Manager->>Zephyr RTC API: Set time via rtc_set_time()
199+
Zephyr RTC API->>RTC Sensor: Set time
200+
RTC Sensor-->>Zephyr RTC API: Return failure
201+
Zephyr RTC API-->>RTC Manager: Return failure (status != 0)
202+
RTC Manager->>Event Log: Emit TimeNotSet event
203+
RTC Manager-->>Ground Station: Command response EXECUTION_ERROR
163204
```
164205

165206
## Change Log
166207
| Date | Description |
167208
|---|---|
168209
| 2025-9-18 | Initial RTC Manager component |
210+
| 2025-11-14 | Added monotonic time failover when RTC unavailable, input validation for TIME_SET command, TEST_UNCONFIGURE_DEVICE test command, and console logging for device not ready conditions |

0 commit comments

Comments
 (0)