Examples#
pw_clock_tree: Clock tree management for embedded devices
Clock tree integration into device drivers#
The example below shows how the clock tree functionality can get integrated into a new device driver that requires that a clock tree abstraction is present in the system.
1#include "pw_clock_tree/clock_tree.h"
2
3class UartStreamMcuxpresso {
4 public:
5 pw::Status Init() {
6 // Acquire reference to clock before initializing device.
7 PW_TRY(clock_tree_element_controller_.Acquire());
8 pw::Status status = USART_RTOS_Init();
9 if (!status.ok()) {
10 // Failed to initialize device, release the acquired clock.
11 clock_tree_element_controller_.Release().IgnoreError();
12 }
13 return status;
14 }
15
16 void Deinit() {
17 // Deinitialize the device before we can release the reference
18 // to the clock.
19 USART_RTOS_Deinit();
20 clock_tree_element_controller_.Release().IgnoreError();
21 }
22
23 // Device constructor that optionally accepts `clock_tree` and
24 // `clock_tree_element` to manage clock lifecycle.
25 constexpr UartStreamMcuxpresso(
26 pw::clock_tree::ClockTree* clock_tree = nullptr,
27 pw::clock_tree::ElementNonBlockingCannotFail* clock_tree_element =
28 nullptr)
29 : clock_tree_element_controller_(clock_tree, clock_tree_element) {}
30
31 private:
32 pw::clock_tree::ElementController clock_tree_element_controller_;
33};
Definition and use of clock tree elements#
For the example below we use a clock tree with two clock sources clock_a
and clock_b
.
clock_a
can be selected as an input source by clock_selector_c
, and clock_b
is an input into
divider clock_divider_d
, which can be selected as an alternative input source by
clock_selector_c
.
Derived ClockSourceExample
class template that overrides
DoEnable()
and DoDisable()
methods.
1/// Class template implementing a clock source.
2///
3/// Template argument `ElementType` can be of class `ElementBlocking`,
4/// `ElementNonBlockingCannotFail` or
5/// `ElementNonBlockingMightFail.`
6template <typename ElementType>
7class ClockSourceExample : public pw::clock_tree::ClockSource<ElementType> {
8 private:
9 pw::Status DoEnable() final { return EnableClock(); }
10
11 pw::Status DoDisable() final { return DisableClock(); }
12};
13using ClockSourceExampleNonBlocking =
14 ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>;
Derived ClockDividerExample
class template that overrides
DoEnable()
method.
1/// Class template implementing a clock divider.
2///
3/// Template argument `ElementType` can be of class `ElementBlocking`,
4/// `ElementNonBlockingCannotFail` or
5/// `ElementNonBlockingMightFail.`
6template <typename ElementType>
7class ClockDividerExample
8 : public pw::clock_tree::ClockDividerElement<ElementType> {
9 public:
10 constexpr ClockDividerExample(ElementType& source,
11 uint32_t divider_name,
12 uint32_t divider)
13 : pw::clock_tree::ClockDividerElement<ElementType>(source, divider),
14 divider_name_(divider_name) {}
15
16 private:
17 pw::Status DoEnable() final {
18 return EnableClockDivider(divider_name_, this->divider());
19 }
20
21 uint32_t divider_name_;
22};
23using ClockDividerExampleNonBlocking =
24 ClockDividerExample<pw::clock_tree::ElementNonBlockingCannotFail>;
Derived ClockSelectorExample
class template that overrides
DoEnable()
and DoDisable()
methods,
and defines the SetSource
method to allow the clock selector to change from one dependent source to
another source. If the dependent source of a clock selector doesn’t change at any point, one doesn’t
need to implement a method like SetSource
.
1/// Class template implementing a clock selector.
2///
3/// Template argument `ElementType` can be of class `ElementBlocking`,
4/// `ElementNonBlockingCannotFail` or
5/// `ElementNonBlockingMightFail.`
6template <typename ElementType>
7class ClockSelectorExample
8 : public pw::clock_tree::DependentElement<ElementType> {
9 public:
10 constexpr ClockSelectorExample(ElementType& source,
11 uint32_t selector,
12 uint32_t selector_enable,
13 uint32_t selector_disable)
14 : pw::clock_tree::DependentElement<ElementType>(source),
15 selector_(selector),
16 selector_enable_(selector_enable),
17 selector_disable_(selector_disable) {}
18
19 pw::Status SetSource(ElementType& new_source, uint32_t new_selector_enable) {
20 // Store a copy of the current `selector_enable_` variable in case
21 // that the update fails, since we need to update `selector_enable_`
22 // to its new value, since `UpdateSource` might call the `DoEnable`
23 // member function.
24 uint32_t old_selector_enable = selector_enable_;
25 selector_enable_ = new_selector_enable;
26 const bool kPermitChangeIfInUse = true;
27 pw::Status status = this->UpdateSource(new_source, kPermitChangeIfInUse);
28 if (!status.ok()) {
29 // Restore the old selector value.
30 selector_enable_ = old_selector_enable;
31 }
32
33 return status;
34 }
35
36 private:
37 pw::Status DoEnable() final { return SetSelector(selector_enable_); }
38 pw::Status DoDisable() final { return SetSelector(selector_disable_); }
39
40 uint32_t selector_;
41 uint32_t selector_enable_;
42 uint32_t selector_disable_;
43 template <typename U>
44 friend class ClockTreeSetSource;
45};
46using ClockSelectorExampleNonBlocking =
47 ClockSelectorExample<pw::clock_tree::ElementNonBlockingCannotFail>;
Derived ClockTreeSetSource
class that provides SetSource
method to allow to change the
source a clock selector depends on. If ClockSelectorExample
wouldn’t provide the SetSource
method, one could use the ClockTree
class directly in the example below.
1/// Class implementing a clock tree that also supports the `UpdateSource`
2/// method of the `ClockSelectorExample` class template.
3class ClockTreeSetSourceExample : public pw::clock_tree::ClockTree {
4 public:
5 /// SetSource could be implemented for the other clock tree element classes
6 /// as well.
7 void SetSource(ClockSelectorExampleNonBlocking& element,
8 pw::clock_tree::ElementNonBlockingCannotFail& new_source,
9 uint32_t selector_enable) {
10 std::lock_guard lock(interrupt_spin_lock_);
11 element.SetSource(new_source, selector_enable).IgnoreError();
12 }
13};
Declare the ClockTree
class object.
1 // Create the clock tree
2 ClockTreeSetSourceExample clock_tree;
Declare the clock tree elements.
clock_selector_c
depends on clock_a
, and clock_divider_d
depends on clock_b
.
1 // Define the clock tree
2 ClockSourceExampleNonBlocking clock_a;
3 ClockSourceExampleNonBlocking clock_b;
4
5 const uint32_t kSelectorId = 7;
6 const uint32_t kSelectorEnable1 = 2;
7 const uint32_t kSelectorEnable2 = 4;
8 const uint32_t kSelectorDisable = 7;
9 // clock_selector_c depends on clock_a.
10 ClockSelectorExampleNonBlocking clock_selector_c(
11 clock_a, kSelectorId, kSelectorEnable1, kSelectorDisable);
12
13 const uint32_t kDividerId = 12;
14 const uint32_t kDividerValue1 = 42;
15 // clock_divider_d depends on clock_b.
16 ClockDividerExampleNonBlocking clock_divider_d(
17 clock_b, kDividerId, kDividerValue1);
Acquire a reference to clock_selector_c
, which will acquire a reference to the dependent source
clock_a
. When the reference to clock_a
gets acquired, clock_a
gets enabled. Once the
reference to clock_a
has been acquired and it is enabled, clock_selector_c
gets enabled.
1 // Acquire a reference to clock_selector_c, which will enable clock_selector_c
2 // and its dependent clock_a.
3 clock_tree.Acquire(clock_selector_c);
Change the dependent source of clock_selector_c
from clock_a
to clock_divider_d
while
the clock_selector_c
is enabled. Before clock_divider_d
can be configured as the new
dependent source, a reference to clock_divider_d
will need to get acquired, which will acquire
a reference to clock_b
and enable clock_b
before clock_divider_d
gets enabled.
Once the dependent source has been changed from clock_a
to clock_divider_d
, the reference to
clock_a
will get released, which will disable clock_a
.
1 // Change clock_selector_c to depend on clock_divider_d.
2 // This enables clock_b and clock_divider_d, and disables clock_a.
3 clock_tree.SetSource(clock_selector_c, clock_divider_d, kSelectorEnable2);
Set the clock divider value while the clock_divider_d
is enabled.
1 // Change the divider value for clock_divider_d.
2 const uint32_t kDividerValue2 = 21;
3 clock_tree.SetDividerValue(clock_divider_d, kDividerValue2);
Release the reference to the clock_selector_c
, which will disable clock_selector_c
, and
then release the reference to clock_divider_d
. Then clock_divider_d
will get disabled before
it releases its reference to clock_b
that gets disabled afterward. At this point all clock
tree elements are disabled.
1 // Release reference to clock_selector_c, which will disable clock_selector_c,
2 // clock_divider_d, and clock_b.
3 clock_tree.Release(clock_selector_c);
4 // All clock tree elements are disabled now.