| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title RtcDriver.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief cpp file for RtcDriver component implementation class | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #include "fprime-samd/Drv/RtcDriver/RtcDriver.hpp" | ||
| 8 | #include "Fw/Types/Assert.hpp" | ||
| 9 | #include "Os/RawTime.hpp" | ||
| 10 | #include "config/FwAssertArgTypeAliasAc.h" | ||
| 11 | #include "fprime-samd/Drv/RtcDriver/RawTime.hpp" | ||
| 12 | #include "fprime-samd/Drv/RtcDriver/RtcDriverHardware.hpp" | ||
| 13 | #include "samd-config/RtcDriverConfig.hpp" | ||
| 14 | |||
| 15 | namespace Os { | ||
| 16 | namespace Samd21 { | ||
| 17 | |||
| 18 | 9 | Os::Samd21::Samd21RawTimeHandle timeNow() { | |
| 19 | 9 | Os::Samd21::Samd21RawTimeHandle time; | |
| 20 | |||
| 21 |
1/1✓ Branch 1 taken 9 times.
|
9 | ::Samd21::RtcHardware::RtcState& state = ::Samd21::RtcHardware::getRtcState(); |
| 22 | |||
| 23 |
3/4✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 1 times.
|
9 | if (state.configured) { |
| 24 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
|
8 | U32 seconds_offset = state.n_ticks / static_cast<U32>(state.rate_hz); |
| 25 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
|
8 | U32 subsecond_ticks = state.n_ticks % static_cast<U32>(state.rate_hz); |
| 26 | |||
| 27 | 8 | constexpr U32 USEC_PER_SEC = 1000000; | |
| 28 |
1/1✓ Branch 1 taken 8 times.
|
8 | U32 subtick_counter = ::Samd21::RtcHardware::RtcHal::readRtcCounter(); |
| 29 | |||
| 30 | // Use 64-bit arithmetic to avoid overflow, then cast result back to U32 | ||
| 31 | // Maximum intermediate value: 32768 * 1000000 = 32,768,000,000 (fits in U64) | ||
| 32 | U32 usecond_offset = | ||
| 33 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
8 | static_cast<U32>((static_cast<U64>(subsecond_ticks) * USEC_PER_SEC) / static_cast<U32>(state.rate_hz)); |
| 34 | U32 useconds_counter = | ||
| 35 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
8 | static_cast<U32>((static_cast<U64>(subtick_counter) * USEC_PER_SEC) / state.clock_rate_hz); |
| 36 | |||
| 37 | // Compute the final time | ||
| 38 | 8 | U32 useconds = usecond_offset + useconds_counter; | |
| 39 | 8 | U32 seconds = seconds_offset + (useconds / USEC_PER_SEC); | |
| 40 | 8 | useconds %= USEC_PER_SEC; | |
| 41 | |||
| 42 | 8 | time.m_seconds = seconds; | |
| 43 | 8 | time.m_micros = useconds; | |
| 44 | } else { | ||
| 45 | // RTC is not configured, we have no way of determining the processor time | ||
| 46 | 1 | time.m_seconds = 0; | |
| 47 | 1 | time.m_micros = 0; | |
| 48 | } | ||
| 49 | |||
| 50 | 9 | return time; | |
| 51 | } | ||
| 52 | |||
| 53 | 5 | Fw::TimeInterval timeInterval(const Samd21RawTimeHandle& a, const Samd21RawTimeHandle& b) { | |
| 54 |
6/6✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
|
5 | const bool a_is_newer = (a.m_seconds > b.m_seconds) || ((a.m_seconds == b.m_seconds) && (a.m_micros > b.m_micros)); |
| 55 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | const Samd21RawTimeHandle& newer = a_is_newer ? a : b; |
| 56 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | const Samd21RawTimeHandle& older = a_is_newer ? b : a; |
| 57 | |||
| 58 |
2/2✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
|
5 | if (newer.m_micros < older.m_micros) { |
| 59 | 1 | return Fw::TimeInterval(newer.m_seconds - older.m_seconds - 1, 1000000 + newer.m_micros - older.m_micros); | |
| 60 | } | ||
| 61 | 4 | return Fw::TimeInterval(newer.m_seconds - older.m_seconds, newer.m_micros - older.m_micros); | |
| 62 | } | ||
| 63 | |||
| 64 | } // namespace Samd21 | ||
| 65 | } // namespace Os | ||
| 66 | |||
| 67 | namespace Samd21 { | ||
| 68 | |||
| 69 | // ---------------------------------------------------------------------- | ||
| 70 | // Component construction and destruction | ||
| 71 | // ---------------------------------------------------------------------- | ||
| 72 | |||
| 73 | 15 | RtcDriver ::RtcDriver(const char* const compName) : RtcDriverComponentBase(compName) {} | |
| 74 | |||
| 75 | 34 | void RtcDriver ::configure(ClockSource clk_source, TickRate rate) { | |
| 76 | 34 | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 77 | 34 | FW_ASSERT(!state.configured); | |
| 78 | |||
| 79 | // Validate the rate is one of our predefined rates | ||
| 80 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | switch (rate) { |
| 81 | 34 | case TickRate::TICK_1_HZ: | |
| 82 | case TickRate::TICK_2_HZ: | ||
| 83 | case TickRate::TICK_4_HZ: | ||
| 84 | case TickRate::TICK_8_HZ: | ||
| 85 | case TickRate::TICK_16_HZ: | ||
| 86 | case TickRate::TICK_32_HZ: | ||
| 87 | case TickRate::TICK_64_HZ: | ||
| 88 | case TickRate::TICK_128_HZ: | ||
| 89 | 34 | break; | |
| 90 | ✗ | default: | |
| 91 | ✗ | FW_ASSERT(0, static_cast<FwAssertArgType>(rate)); | |
| 92 | break; | ||
| 93 | } | ||
| 94 | |||
| 95 | // Select the clock source | ||
| 96 |
3/4✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
34 | switch (clk_source) { |
| 97 | 18 | case RtcDriver::ClockSource::InternalOscillator: | |
| 98 | 18 | state.clock_rate_hz = RtcDriver::INTERNAL_CLOCK_FREQ_HZ; | |
| 99 | 18 | break; | |
| 100 | 8 | case RtcDriver::ClockSource::ExternalOscillator: | |
| 101 | 8 | state.clock_rate_hz = ::Samd21::RtcDriverConfig::EXTERNAL_CLOCK_FREQ_HZ; | |
| 102 | 8 | break; | |
| 103 | 8 | case RtcDriver::ClockSource::UltraLowPowerOscillator: | |
| 104 | 8 | state.clock_rate_hz = RtcDriver::INTERNAL_CLOCK_FREQ_HZ; | |
| 105 | 8 | break; | |
| 106 | ✗ | default: | |
| 107 | ✗ | FW_ASSERT(0, static_cast<FwAssertArgType>(clk_source)); | |
| 108 | break; | ||
| 109 | } | ||
| 110 | |||
| 111 | // Configure GCLK and get the clock rate | ||
| 112 | 34 | RtcHardware::RtcHal::configureGclk(clk_source); | |
| 113 | |||
| 114 | // Configure RTC peripheral | ||
| 115 | 34 | RtcHardware::RtcHal::configureRtc(state.clock_rate_hz, rate); | |
| 116 | |||
| 117 | // Initialize state | ||
| 118 | 34 | state.n_ticks = 0; | |
| 119 | 34 | state.rate_hz = rate; | |
| 120 | 34 | state.configured = true; | |
| 121 | 34 | } | |
| 122 | |||
| 123 | 9 | void RtcDriver ::enable() { | |
| 124 | 9 | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 125 | 9 | FW_ASSERT(state.configured); | |
| 126 | |||
| 127 | // Enable the RTC peripheral | ||
| 128 | 9 | RtcHardware::RtcHal::enableRtc(); | |
| 129 | 9 | } | |
| 130 | |||
| 131 | 11 | bool RtcDriver ::activeIn_handler(FwIndexType portNum, U32 context) { | |
| 132 | // Acknowledge the ISR on every cycle | ||
| 133 | 11 | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 134 | 11 | state.deadline_reached = true; | |
| 135 | |||
| 136 |
3/4✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 1 times.
|
11 | if (state.wakeup_interrupt) { |
| 137 | // Acknowledge the interrupt | ||
| 138 | 10 | state.wakeup_interrupt = false; | |
| 139 | |||
| 140 | // The RTC woke us up, emit the signal | ||
| 141 |
1/1✓ Branch 2 taken 10 times.
|
10 | Os::RawTime cycle_start; |
| 142 |
1/1✓ Branch 2 taken 10 times.
|
10 | auto status = cycle_start.now(); |
| 143 | 10 | FW_ASSERT(status == Os::RawTimeInterface::Status::OP_OK, status); | |
| 144 | |||
| 145 |
2/2✓ Branch 6 taken 10 times.
✓ Branch 11 taken 10 times.
|
10 | this->tlmWrite_CycleOverrun(state.deadline_exceeded); |
| 146 |
1/1✓ Branch 5 taken 10 times.
|
10 | this->CycleOut_out(0, cycle_start); |
| 147 | |||
| 148 | // A cycle was emitted this tick, so work was done | ||
| 149 | 10 | return true; | |
| 150 | 10 | } | |
| 151 | |||
| 152 | // No pending wakeup interrupt: nothing to do this tick | ||
| 153 | 1 | return false; | |
| 154 | } | ||
| 155 | |||
| 156 | 30 | RtcDriver ::~RtcDriver() {} | |
| 157 | |||
| 158 | } // namespace Samd21 | ||
| 159 |