| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_assert/check.h" |
| #include "pw_clock_tree/clock_tree.h" |
| #include "pw_unit_test/framework.h" |
| |
| namespace examples { |
| |
| static pw::Status EnableClock() { |
| // Action to enable clock |
| return pw::OkStatus(); |
| } |
| |
| static pw::Status DisableClock() { |
| // Action to disable clock |
| return pw::OkStatus(); |
| } |
| |
| static pw::Status EnableClockDivider(uint32_t, uint32_t) { |
| // Action to enable clock divider |
| return pw::OkStatus(); |
| } |
| |
| static pw::Status SetSelector(uint32_t) { |
| // Action to set selector |
| return pw::OkStatus(); |
| } |
| |
| // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef] |
| /// Class template implementing a clock source. |
| /// |
| /// Template argument `ElementType` can be of class `ElementBlocking`, |
| /// `ElementNonBlockingCannotFail` or |
| /// `ElementNonBlockingMightFail.` |
| template <typename ElementType> |
| class ClockSourceExample : public pw::clock_tree::ClockSource<ElementType> { |
| private: |
| pw::Status DoEnable() final { return EnableClock(); } |
| |
| pw::Status DoDisable() final { return DisableClock(); } |
| }; |
| using ClockSourceExampleNonBlocking = |
| ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>; |
| // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef] |
| /// Class template implementing a clock divider. |
| /// |
| /// Template argument `ElementType` can be of class `ElementBlocking`, |
| /// `ElementNonBlockingCannotFail` or |
| /// `ElementNonBlockingMightFail.` |
| template <typename ElementType> |
| class ClockDividerExample |
| : public pw::clock_tree::ClockDividerElement<ElementType> { |
| public: |
| constexpr ClockDividerExample(ElementType& source, |
| uint32_t divider_name, |
| uint32_t divider) |
| : pw::clock_tree::ClockDividerElement<ElementType>(source, divider), |
| divider_name_(divider_name) {} |
| |
| private: |
| pw::Status DoEnable() final { |
| return EnableClockDivider(divider_name_, this->divider()); |
| } |
| |
| uint32_t divider_name_; |
| }; |
| using ClockDividerExampleNonBlocking = |
| ClockDividerExample<pw::clock_tree::ElementNonBlockingCannotFail>; |
| // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef] |
| /// Class template implementing a clock selector. |
| /// |
| /// Template argument `ElementType` can be of class `ElementBlocking`, |
| /// `ElementNonBlockingCannotFail` or |
| /// `ElementNonBlockingMightFail.` |
| template <typename ElementType> |
| class ClockSelectorExample |
| : public pw::clock_tree::DependentElement<ElementType> { |
| public: |
| constexpr ClockSelectorExample(ElementType& source, |
| uint32_t selector, |
| uint32_t selector_enable, |
| uint32_t selector_disable) |
| : pw::clock_tree::DependentElement<ElementType>(source), |
| selector_(selector), |
| selector_enable_(selector_enable), |
| selector_disable_(selector_disable) {} |
| |
| pw::Status SetSource(ElementType& new_source, uint32_t new_selector_enable) { |
| // Store a copy of the current `selector_enable_` variable in case |
| // that the update fails, since we need to update `selector_enable_` |
| // to its new value, since `UpdateSource` might call the `DoEnable` |
| // member function. |
| uint32_t old_selector_enable = selector_enable_; |
| selector_enable_ = new_selector_enable; |
| const bool kPermitChangeIfInUse = true; |
| pw::Status status = this->UpdateSource(new_source, kPermitChangeIfInUse); |
| if (!status.ok()) { |
| // Restore the old selector value. |
| selector_enable_ = old_selector_enable; |
| } |
| |
| return status; |
| } |
| |
| private: |
| pw::Status DoEnable() final { return SetSelector(selector_enable_); } |
| pw::Status DoDisable() final { return SetSelector(selector_disable_); } |
| |
| uint32_t selector_; |
| uint32_t selector_enable_; |
| uint32_t selector_disable_; |
| template <typename U> |
| friend class ClockTreeSetSource; |
| }; |
| using ClockSelectorExampleNonBlocking = |
| ClockSelectorExample<pw::clock_tree::ElementNonBlockingCannotFail>; |
| // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef] |
| /// Class implementing a clock tree that also supports the `UpdateSource` |
| /// method of the `ClockSelectorExample` class template. |
| class ClockTreeSetSourceExample : public pw::clock_tree::ClockTree { |
| public: |
| /// SetSource could be implemented for the other clock tree element classes |
| /// as well. |
| void SetSource(ClockSelectorExampleNonBlocking& element, |
| pw::clock_tree::ElementNonBlockingCannotFail& new_source, |
| uint32_t selector_enable) { |
| std::lock_guard lock(interrupt_spin_lock_); |
| element.SetSource(new_source, selector_enable).IgnoreError(); |
| } |
| }; |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef] |
| |
| TEST(ClockTree, ClockTreeElementExample) { |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec] |
| // Create the clock tree |
| ClockTreeSetSourceExample clock_tree; |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec] |
| // Define the clock tree |
| ClockSourceExampleNonBlocking clock_a; |
| ClockSourceExampleNonBlocking clock_b; |
| |
| const uint32_t kSelectorId = 7; |
| const uint32_t kSelectorEnable1 = 2; |
| const uint32_t kSelectorEnable2 = 4; |
| const uint32_t kSelectorDisable = 7; |
| // clock_selector_c depends on clock_a. |
| ClockSelectorExampleNonBlocking clock_selector_c( |
| clock_a, kSelectorId, kSelectorEnable1, kSelectorDisable); |
| |
| const uint32_t kDividerId = 12; |
| const uint32_t kDividerValue1 = 42; |
| // clock_divider_d depends on clock_b. |
| ClockDividerExampleNonBlocking clock_divider_d( |
| clock_b, kDividerId, kDividerValue1); |
| // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec] |
| |
| // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC] |
| // Acquire a reference to clock_selector_c, which will enable clock_selector_c |
| // and its dependent clock_a. |
| clock_tree.Acquire(clock_selector_c); |
| // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource] |
| // Change clock_selector_c to depend on clock_divider_d. |
| // This enables clock_b and clock_divider_d, and disables clock_a. |
| clock_tree.SetSource(clock_selector_c, clock_divider_d, kSelectorEnable2); |
| // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource] |
| |
| // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue] |
| // Change the divider value for clock_divider_d. |
| const uint32_t kDividerValue2 = 21; |
| clock_tree.SetDividerValue(clock_divider_d, kDividerValue2); |
| // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue] |
| |
| // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC] |
| // Release reference to clock_selector_c, which will disable clock_selector_c, |
| // clock_divider_d, and clock_b. |
| clock_tree.Release(clock_selector_c); |
| // All clock tree elements are disabled now. |
| // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC] |
| } |
| |
| static pw::Status USART_RTOS_Init() { return pw::OkStatus(); } |
| static void USART_RTOS_Deinit() {} |
| |
| // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef] |
| #include "pw_clock_tree/clock_tree.h" |
| |
| class UartStreamMcuxpresso { |
| public: |
| pw::Status Init() { |
| // Acquire reference to clock before initializing device. |
| PW_TRY(clock_tree_element_controller_.Acquire()); |
| pw::Status status = USART_RTOS_Init(); |
| if (!status.ok()) { |
| // Failed to initialize device, release the acquired clock. |
| clock_tree_element_controller_.Release().IgnoreError(); |
| } |
| return status; |
| } |
| |
| void Deinit() { |
| // Deinitialize the device before we can release the reference |
| // to the clock. |
| USART_RTOS_Deinit(); |
| clock_tree_element_controller_.Release().IgnoreError(); |
| } |
| |
| // Device constructor that optionally accepts `clock_tree` and |
| // `clock_tree_element` to manage clock lifecycle. |
| constexpr UartStreamMcuxpresso( |
| pw::clock_tree::ClockTree* clock_tree = nullptr, |
| pw::clock_tree::ElementNonBlockingCannotFail* clock_tree_element = |
| nullptr) |
| : clock_tree_element_controller_(clock_tree, clock_tree_element) {} |
| |
| private: |
| pw::clock_tree::ElementController clock_tree_element_controller_; |
| }; |
| // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef] |
| |
| using ClockSourceUart = |
| ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>; |
| |
| pw::Status ClockTreeExample() { |
| // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage] |
| |
| // Declare the clock tree |
| pw::clock_tree::ClockTree clock_tree; |
| // Declare the uart clock source |
| ClockSourceUart uart_clock_source; |
| UartStreamMcuxpresso uart(&clock_tree, &uart_clock_source); |
| |
| // Initialize the uart which enables the uart clock source. |
| PW_TRY(uart.Init()); |
| PW_CHECK(uart_clock_source.ref_count() > 0); |
| |
| // Do something with uart |
| |
| // Deinitialize the uart which disable the uart clock source. |
| uart.Deinit(); |
| |
| // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage] |
| |
| return pw::OkStatus(); |
| } |
| |
| TEST(ClockTree, ClockTreeExample) { |
| pw::Status status = ClockTreeExample(); |
| EXPECT_EQ(status.code(), PW_STATUS_OK); |
| } |
| } // namespace examples |