// 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 class ClockSourceExample : public pw::clock_tree::ClockSource { private: pw::Status DoEnable() final { return EnableClock(); } pw::Status DoDisable() final { return DisableClock(); } }; using ClockSourceExampleNonBlocking = ClockSourceExample; // 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 class ClockDividerExample : public pw::clock_tree::ClockDividerElement { public: constexpr ClockDividerExample(ElementType& source, uint32_t divider_name, uint32_t divider) : pw::clock_tree::ClockDividerElement(source, divider), divider_name_(divider_name) {} private: pw::Status DoEnable() final { return EnableClockDivider(divider_name_, this->divider()); } uint32_t divider_name_; }; using ClockDividerExampleNonBlocking = ClockDividerExample; // 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 class ClockSelectorExample : public pw::clock_tree::DependentElement { public: constexpr ClockSelectorExample(ElementType& source, uint32_t selector, uint32_t selector_enable, uint32_t selector_disable) : pw::clock_tree::DependentElement(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 friend class ClockTreeSetSource; }; using ClockSelectorExampleNonBlocking = ClockSelectorExample; // 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::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