Examples#

pw_clock_tree: Clock tree management for embedded devices

Integration into device driver

Example that shows how to integrate the clock tree functionality into a device driver.

Clock tree usage

Example that shows how to define platform specific clock tree elements and how to interact with them to manage clocks of an embedded system.

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.

flowchart LR A(clock_a) -..-> C(clock_selector_c) B(clock_b)--> D(clock_divider_d) D -..-> 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.

flowchart LR A(clock_A) -->C(clock_selector_c) B(clock_B)--> D(clock_divider_d) D -..-> C style A fill:#0f0,stroke:#333,stroke-width:4px style C fill:#0f0,stroke:#333,stroke-width:4px style B fill:#f00,stroke:#333,stroke-width:4px style D fill:#f00,stroke:#333,stroke-width:4px
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.

flowchart LR A(clock_A) -..->C(clock_selector_c) B(clock_B)--> D(clock_divider_d) D --> C style A fill:#f00,stroke:#333,stroke-width:4px style C fill:#0f0,stroke:#333,stroke-width:4px style B fill:#0f0,stroke:#333,stroke-width:4px style D fill:#0f0,stroke:#333,stroke-width:4px
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.