Pigweed
 
Loading...
Searching...
No Matches
clock_tree.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <cstdint>
17#include <memory>
18#include <mutex>
19
20#include "pw_assert/assert.h"
21#include "pw_status/status.h"
22#include "pw_status/try.h"
23#include "pw_sync/interrupt_spin_lock.h"
24#include "pw_sync/mutex.h"
25
26namespace pw::clock_tree {
27
39class Element {
40 public:
41 constexpr Element(bool may_block = false) : may_block_(may_block) {}
42 virtual ~Element() = default;
43
45 uint32_t ref_count() const { return ref_count_; }
46
48 bool may_block() const { return may_block_; }
49
50 // Not copyable or movable
51 Element(const Element&) = delete;
52 Element(const Element&&) = delete;
53 Element& operator=(const Element&) = delete;
54 Element& operator=(const Element&&) = delete;
55
56 protected:
68 virtual Status Acquire() = 0;
69
85 virtual Status Release() = 0;
86
88 uint32_t IncRef() { return ++ref_count_; }
89
91 uint32_t DecRef() { return --ref_count_; }
92
94 virtual Status DoEnable() = 0;
95
100 virtual Status DoDisable() { return OkStatus(); }
101
102 private:
104 uint32_t ref_count_ = 0;
105
107 const bool may_block_;
108
109 friend class ClockTree;
110 template <typename ElementType>
111 friend class DependentElement;
112 friend class ClockDivider;
113};
114
117class ElementBlocking : public Element {
118 public:
119 constexpr ElementBlocking() : Element(/*may_block=*/true) {}
120};
121
125
129
146template <typename ElementType>
147class ClockSource : public ElementType {
148 private:
152 Status Acquire() final {
153 if (this->IncRef() > 1) {
154 // This clock tree element is already enabled.
155 return OkStatus();
156 }
157
158 // Enable clock source.
159 Status status = this->DoEnable();
160 if (!status.ok()) {
161 this->DecRef();
162 }
163 return status;
164 }
165
169 Status Release() final {
170 if (this->DecRef() > 0) {
171 // The clock tree element remains enabled.
172 return OkStatus();
173 }
174
175 // Disable the clock source.
176 Status status = this->DoDisable();
177 if (!status.ok()) {
178 this->IncRef();
179 }
180 return status;
181 }
182};
183
187class ClockSourceNoOp : public ClockSource<ElementNonBlockingCannotFail> {
188 private:
189 pw::Status DoEnable() final { return pw::OkStatus(); }
190
191 pw::Status DoDisable() final { return pw::OkStatus(); }
192};
193
207template <typename ElementType>
208class DependentElement : public ElementType {
209 public:
211 constexpr DependentElement(ElementType& source) : source_(&source) {}
212
213 protected:
228 Status UpdateSource(ElementType& new_source, bool permit_change_if_in_use) {
229 // If the element is not enabled, we can update the `source_` directly.
230 if (this->ref_count() == 0) {
231 source_ = &new_source;
232 return OkStatus();
233 }
234
235 // The element is active, check whether we are allowed to change the source.
236 if (!permit_change_if_in_use) {
237 return Status::FailedPrecondition();
238 }
239
240 ElementType* old_source = source_;
241
242 // Acquire the dependent sources for the new_source element.
243 PW_TRY(new_source.Acquire());
244
245 // Disable this current element configuration.
246 if (Status status = this->DoDisable(); !status.ok()) {
247 new_source.Release().IgnoreError();
248 return status;
249 }
250
251 // Enable the new source element configuration.
252 Status status = this->DoEnable();
253
254 // Release the reference to the old dependent source regardless whether
255 // we have enabled the new source, since we have successfully disabled it.
256 old_source->Release().IgnoreError();
257
258 // Check whether the `DoEnable` method succeeded for the new source.
259 if (!status.ok()) {
260 new_source.Release().IgnoreError();
261 this->DecRef();
262 return status;
263 }
264
265 // Everything has succeeded, change the source_ element.
266 source_ = &new_source;
267 return OkStatus();
268 }
269
270 private:
276 Status Acquire() final {
277 if (this->IncRef() > 1) {
278 // This clock tree element is already enabled.
279 return OkStatus();
280 }
281
282 // Acquire a reference to the dependent clock tree element before
283 // enabling this clock tree element.
284 if (Status status = source_->Acquire(); !status.ok()) {
285 this->DecRef();
286 return status;
287 }
288
289 Status status = this->DoEnable();
290 if (!status.ok()) {
291 source_->Release().IgnoreError();
292 this->DecRef();
293 }
294 return status;
295 }
296
302 Status Release() final {
303 if (this->DecRef() > 0) {
304 // The clock tree element remains enabled.
305 return OkStatus();
306 }
307
308 // Disable the clock tree element.
309 if (Status status = this->DoDisable(); !status.ok()) {
310 this->IncRef();
311 return status;
312 }
313 // Even if releasing the dependent source references fails,
314 // we won't re-enable the clock source, and instead just return
315 // the error code to the caller.
316 return source_->Release();
317 }
318
321 ElementType* source_;
322};
323
331 public:
332 constexpr ClockDivider(Element& element) : element_(element) {}
333
334 virtual ~ClockDivider() = default;
335
341 virtual Status Set(uint32_t divider) = 0;
342
344 Element& element() const { return element_; }
345
346 private:
348 Element& element_;
349};
350
363template <typename ElementType>
364class ClockDividerElement : public DependentElement<ElementType>,
365 public ClockDivider {
366 public:
369 constexpr ClockDividerElement(ElementType& source, uint32_t divider)
370 : DependentElement<ElementType>(source),
371 ClockDivider(static_cast<Element&>(*this)),
372 divider_(divider) {}
373
379 Status Set(uint32_t divider) override {
380 uint32_t old_divider = divider_;
381 divider_ = divider;
382 if (this->ref_count() == 0) {
383 return OkStatus();
384 }
385
386 Status status = this->DoEnable();
387 if (!status.ok()) {
388 // Restore old divider value.
389 divider_ = old_divider;
390 }
391 return status;
392 }
393
394 protected:
396 uint32_t divider() const { return divider_; }
397
398 private:
400 uint32_t divider_;
401};
402
404using ClockDividerBlocking = ClockDividerElement<ElementBlocking>;
405
408using ClockDividerNonBlockingCannotFail =
409 ClockDividerElement<ElementNonBlockingCannotFail>;
410
413using ClockDividerNonBlockingMightFail =
414 ClockDividerElement<ElementNonBlockingMightFail>;
415
429 public:
433 std::lock_guard lock(interrupt_spin_lock_);
434 Status status = element.Acquire();
435 PW_DASSERT(status.ok());
436 }
437
441 std::lock_guard lock(interrupt_spin_lock_);
442 return element.Acquire();
443 }
444
448 std::lock_guard lock(mutex_);
449 return element.Acquire();
450 }
451
458 if (element.may_block()) {
459 std::lock_guard lock(mutex_);
460 return element.Acquire();
461 }
462 std::lock_guard lock(interrupt_spin_lock_);
463 return element.Acquire();
464 }
465
477 Status AcquireWith(Element& element, Element& element_with) {
478 PW_TRY(Acquire(element_with));
479 Status status = Acquire(element);
480 Release(element_with).IgnoreError();
481 return status;
482 }
483
487 std::lock_guard lock(interrupt_spin_lock_);
488 Status status = element.Release();
489 PW_DASSERT(status.ok());
490 }
491
495 std::lock_guard lock(interrupt_spin_lock_);
496 return element.Release();
497 }
498
502 std::lock_guard lock(mutex_);
503 return element.Release();
504 }
505
512 if (element.may_block()) {
513 std::lock_guard lock(mutex_);
514 return element.Release();
515 }
516 std::lock_guard lock(interrupt_spin_lock_);
517 return element.Release();
518 }
519
523 uint32_t divider_value) {
524 std::lock_guard lock(interrupt_spin_lock_);
525 Status status = clock_divider.Set(divider_value);
526 PW_DASSERT(status.ok());
527 }
528
532 uint32_t divider_value) {
533 std::lock_guard lock(interrupt_spin_lock_);
534 return clock_divider.Set(divider_value);
535 }
536
540 uint32_t divider_value) {
541 std::lock_guard lock(mutex_);
542 return clock_divider.Set(divider_value);
543 }
544
550 Status SetDividerValue(ClockDivider& clock_divider, uint32_t divider_value) {
551 if (clock_divider.element().may_block()) {
552 std::lock_guard lock(mutex_);
553 return clock_divider.Set(divider_value);
554 }
555 std::lock_guard lock(interrupt_spin_lock_);
556 return clock_divider.Set(divider_value);
557 }
558
559 protected:
562
566};
567
571 public:
574 constexpr ElementController(ClockTree* clock_tree = nullptr,
575 Element* element = nullptr)
576 : clock_tree_(clock_tree), element_(element) {}
577
583 if (clock_tree_ != nullptr && element_ != nullptr) {
584 return clock_tree_->Acquire(*element_);
585 }
586 return OkStatus();
587 }
588
594 if (clock_tree_ != nullptr && element_ != nullptr) {
595 return clock_tree_->Release(*element_);
596 }
597 return OkStatus();
598 }
599
602
604 Element* element_ = nullptr;
605};
606} // namespace pw::clock_tree
Definition: status.h:85
constexpr bool ok() const
Definition: status.h:157
Definition: clock_tree.h:365
Status Set(uint32_t divider) override
Definition: clock_tree.h:379
uint32_t divider() const
Get current divider value.
Definition: clock_tree.h:396
constexpr ClockDividerElement(ElementType &source, uint32_t divider)
Definition: clock_tree.h:369
Definition: clock_tree.h:330
Element & element() const
Return the element implementing this interface.
Definition: clock_tree.h:344
virtual Status Set(uint32_t divider)=0
Definition: clock_tree.h:147
Status Release() final
Definition: clock_tree.h:169
Status Acquire() final
Definition: clock_tree.h:152
Definition: clock_tree.h:187
pw::Status DoEnable() final
Function called when the clock tree element needs to get enabled.
Definition: clock_tree.h:189
pw::Status DoDisable() final
Definition: clock_tree.h:191
Definition: clock_tree.h:428
Status Release(Element &element)
Definition: clock_tree.h:511
void Acquire(ElementNonBlockingCannotFail &element)
Definition: clock_tree.h:432
Status SetDividerValue(ClockDivider &clock_divider, uint32_t divider_value)
Definition: clock_tree.h:550
Status AcquireWith(Element &element, Element &element_with)
Definition: clock_tree.h:477
Status SetDividerValue(ClockDividerNonBlockingMightFail &clock_divider, uint32_t divider_value)
Definition: clock_tree.h:531
Status Acquire(Element &element)
Definition: clock_tree.h:457
Status SetDividerValue(ClockDividerBlocking &clock_divider, uint32_t divider_value)
Definition: clock_tree.h:539
Status Release(ElementNonBlockingMightFail &element)
Definition: clock_tree.h:494
Status Acquire(ElementNonBlockingMightFail &element)
Definition: clock_tree.h:440
Status Acquire(ElementBlocking &element)
Definition: clock_tree.h:447
Status Release(ElementBlocking &element)
Definition: clock_tree.h:501
sync::InterruptSpinLock interrupt_spin_lock_
Definition: clock_tree.h:565
void SetDividerValue(ClockDividerNonBlockingCannotFail &clock_divider, uint32_t divider_value)
Definition: clock_tree.h:522
void Release(ElementNonBlockingCannotFail &element)
Definition: clock_tree.h:486
sync::Mutex mutex_
mutex_ protects ElementBlocking clock tree elements.
Definition: clock_tree.h:561
Definition: clock_tree.h:208
Status Acquire() final
Definition: clock_tree.h:276
constexpr DependentElement(ElementType &source)
Create a dependent clock tree element that depends on source.
Definition: clock_tree.h:211
Status UpdateSource(ElementType &new_source, bool permit_change_if_in_use)
Definition: clock_tree.h:228
Status Release() final
Definition: clock_tree.h:302
Definition: clock_tree.h:117
Definition: clock_tree.h:570
Element * element_
Pointer to optional Element object.
Definition: clock_tree.h:604
Status Acquire()
Definition: clock_tree.h:582
ClockTree * clock_tree_
Pointer to optional ClockTree object.
Definition: clock_tree.h:601
constexpr ElementController(ClockTree *clock_tree=nullptr, Element *element=nullptr)
Definition: clock_tree.h:574
Status Release()
Definition: clock_tree.h:593
Definition: clock_tree.h:39
virtual Status Release()=0
virtual Status Acquire()=0
virtual Status DoEnable()=0
Function called when the clock tree element needs to get enabled.
uint32_t IncRef()
Increment reference count and return incremented value.
Definition: clock_tree.h:88
bool may_block() const
Check whether acquiring or releasing the element may block.
Definition: clock_tree.h:48
uint32_t ref_count() const
Get reference count for this clock tree element.
Definition: clock_tree.h:45
uint32_t DecRef()
Decrement reference count and return decremented value.
Definition: clock_tree.h:91
virtual Status DoDisable()
Definition: clock_tree.h:100
Definition: interrupt_spin_lock.h:48
Definition: mutex.h:42
constexpr Status OkStatus()
Definition: status.h:234