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
15 #include "pw_assert/check.h"
16 #include "pw_clock_tree/clock_tree.h"
17 #include "pw_unit_test/framework.h"
18
19 namespace examples {
20
EnableClock()21 static pw::Status EnableClock() {
22 // Action to enable clock
23 return pw::OkStatus();
24 }
25
DisableClock()26 static pw::Status DisableClock() {
27 // Action to disable clock
28 return pw::OkStatus();
29 }
30
EnableClockDivider(uint32_t,uint32_t)31 static pw::Status EnableClockDivider(uint32_t, uint32_t) {
32 // Action to enable clock divider
33 return pw::OkStatus();
34 }
35
SetSelector(uint32_t)36 static pw::Status SetSelector(uint32_t) {
37 // Action to set selector
38 return pw::OkStatus();
39 }
40
41 // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef]
42 /// Class template implementing a clock source.
43 ///
44 /// Template argument `ElementType` can be of class `ElementBlocking`,
45 /// `ElementNonBlockingCannotFail` or
46 /// `ElementNonBlockingMightFail.`
47 template <typename ElementType>
48 class ClockSourceExample : public pw::clock_tree::ClockSource<ElementType> {
49 private:
DoEnable()50 pw::Status DoEnable() final { return EnableClock(); }
51
DoDisable()52 pw::Status DoDisable() final { return DisableClock(); }
53 };
54 using ClockSourceExampleNonBlocking =
55 ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>;
56 // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef]
57
58 // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef]
59 /// Class template implementing a clock divider.
60 ///
61 /// Template argument `ElementType` can be of class `ElementBlocking`,
62 /// `ElementNonBlockingCannotFail` or
63 /// `ElementNonBlockingMightFail.`
64 template <typename ElementType>
65 class ClockDividerExample
66 : public pw::clock_tree::ClockDividerElement<ElementType> {
67 public:
ClockDividerExample(ElementType & source,uint32_t divider_name,uint32_t divider)68 constexpr ClockDividerExample(ElementType& source,
69 uint32_t divider_name,
70 uint32_t divider)
71 : pw::clock_tree::ClockDividerElement<ElementType>(source, divider),
72 divider_name_(divider_name) {}
73
74 private:
DoEnable()75 pw::Status DoEnable() final {
76 return EnableClockDivider(divider_name_, this->divider());
77 }
78
79 uint32_t divider_name_;
80 };
81 using ClockDividerExampleNonBlocking =
82 ClockDividerExample<pw::clock_tree::ElementNonBlockingCannotFail>;
83 // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef]
84
85 // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef]
86 /// Class template implementing a clock selector.
87 ///
88 /// Template argument `ElementType` can be of class `ElementBlocking`,
89 /// `ElementNonBlockingCannotFail` or
90 /// `ElementNonBlockingMightFail.`
91 template <typename ElementType>
92 class ClockSelectorExample
93 : public pw::clock_tree::DependentElement<ElementType> {
94 public:
ClockSelectorExample(ElementType & source,uint32_t selector,uint32_t selector_enable,uint32_t selector_disable)95 constexpr ClockSelectorExample(ElementType& source,
96 uint32_t selector,
97 uint32_t selector_enable,
98 uint32_t selector_disable)
99 : pw::clock_tree::DependentElement<ElementType>(source),
100 selector_(selector),
101 selector_enable_(selector_enable),
102 selector_disable_(selector_disable) {}
103
SetSource(ElementType & new_source,uint32_t new_selector_enable)104 pw::Status SetSource(ElementType& new_source, uint32_t new_selector_enable) {
105 // Store a copy of the current `selector_enable_` variable in case
106 // that the update fails, since we need to update `selector_enable_`
107 // to its new value, since `UpdateSource` might call the `DoEnable`
108 // member function.
109 uint32_t old_selector_enable = selector_enable_;
110 selector_enable_ = new_selector_enable;
111 const bool kPermitChangeIfInUse = true;
112 pw::Status status = this->UpdateSource(new_source, kPermitChangeIfInUse);
113 if (!status.ok()) {
114 // Restore the old selector value.
115 selector_enable_ = old_selector_enable;
116 }
117
118 return status;
119 }
120
121 private:
DoEnable()122 pw::Status DoEnable() final { return SetSelector(selector_enable_); }
DoDisable()123 pw::Status DoDisable() final { return SetSelector(selector_disable_); }
124
125 uint32_t selector_;
126 uint32_t selector_enable_;
127 uint32_t selector_disable_;
128 template <typename U>
129 friend class ClockTreeSetSource;
130 };
131 using ClockSelectorExampleNonBlocking =
132 ClockSelectorExample<pw::clock_tree::ElementNonBlockingCannotFail>;
133 // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef]
134
135 // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef]
136 /// Class implementing a clock tree that also supports the `UpdateSource`
137 /// method of the `ClockSelectorExample` class template.
138 class ClockTreeSetSourceExample : public pw::clock_tree::ClockTree {
139 public:
140 /// SetSource could be implemented for the other clock tree element classes
141 /// as well.
SetSource(ClockSelectorExampleNonBlocking & element,pw::clock_tree::ElementNonBlockingCannotFail & new_source,uint32_t selector_enable)142 void SetSource(ClockSelectorExampleNonBlocking& element,
143 pw::clock_tree::ElementNonBlockingCannotFail& new_source,
144 uint32_t selector_enable) {
145 std::lock_guard lock(interrupt_spin_lock_);
146 element.SetSource(new_source, selector_enable).IgnoreError();
147 }
148 };
149 // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef]
150
TEST(ClockTree,ClockTreeElementExample)151 TEST(ClockTree, ClockTreeElementExample) {
152 // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec]
153 // Create the clock tree
154 ClockTreeSetSourceExample clock_tree;
155 // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec]
156
157 // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec]
158 // Define the clock tree
159 ClockSourceExampleNonBlocking clock_a;
160 ClockSourceExampleNonBlocking clock_b;
161
162 const uint32_t kSelectorId = 7;
163 const uint32_t kSelectorEnable1 = 2;
164 const uint32_t kSelectorEnable2 = 4;
165 const uint32_t kSelectorDisable = 7;
166 // clock_selector_c depends on clock_a.
167 ClockSelectorExampleNonBlocking clock_selector_c(
168 clock_a, kSelectorId, kSelectorEnable1, kSelectorDisable);
169
170 const uint32_t kDividerId = 12;
171 const uint32_t kDividerValue1 = 42;
172 // clock_divider_d depends on clock_b.
173 ClockDividerExampleNonBlocking clock_divider_d(
174 clock_b, kDividerId, kDividerValue1);
175 // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec]
176
177 // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC]
178 // Acquire a reference to clock_selector_c, which will enable clock_selector_c
179 // and its dependent clock_a.
180 clock_tree.Acquire(clock_selector_c);
181 // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC]
182
183 // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource]
184 // Change clock_selector_c to depend on clock_divider_d.
185 // This enables clock_b and clock_divider_d, and disables clock_a.
186 clock_tree.SetSource(clock_selector_c, clock_divider_d, kSelectorEnable2);
187 // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource]
188
189 // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue]
190 // Change the divider value for clock_divider_d.
191 const uint32_t kDividerValue2 = 21;
192 clock_tree.SetDividerValue(clock_divider_d, kDividerValue2);
193 // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue]
194
195 // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC]
196 // Release reference to clock_selector_c, which will disable clock_selector_c,
197 // clock_divider_d, and clock_b.
198 clock_tree.Release(clock_selector_c);
199 // All clock tree elements are disabled now.
200 // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC]
201 }
202
USART_RTOS_Init()203 static pw::Status USART_RTOS_Init() { return pw::OkStatus(); }
USART_RTOS_Deinit()204 static void USART_RTOS_Deinit() {}
205
206 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef]
207 #include "pw_clock_tree/clock_tree.h"
208
209 class UartStreamMcuxpresso {
210 public:
Init()211 pw::Status Init() {
212 // Acquire reference to clock before initializing device.
213 PW_TRY(clock_tree_element_controller_.Acquire());
214 pw::Status status = USART_RTOS_Init();
215 if (!status.ok()) {
216 // Failed to initialize device, release the acquired clock.
217 clock_tree_element_controller_.Release().IgnoreError();
218 }
219 return status;
220 }
221
Deinit()222 void Deinit() {
223 // Deinitialize the device before we can release the reference
224 // to the clock.
225 USART_RTOS_Deinit();
226 clock_tree_element_controller_.Release().IgnoreError();
227 }
228
229 // Device constructor that optionally accepts `clock_tree` and
230 // `clock_tree_element` to manage clock lifecycle.
UartStreamMcuxpresso(pw::clock_tree::ClockTree * clock_tree=nullptr,pw::clock_tree::ElementNonBlockingCannotFail * clock_tree_element=nullptr)231 constexpr UartStreamMcuxpresso(
232 pw::clock_tree::ClockTree* clock_tree = nullptr,
233 pw::clock_tree::ElementNonBlockingCannotFail* clock_tree_element =
234 nullptr)
235 : clock_tree_element_controller_(clock_tree, clock_tree_element) {}
236
237 private:
238 pw::clock_tree::ElementController clock_tree_element_controller_;
239 };
240 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef]
241
242 using ClockSourceUart =
243 ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>;
244
ClockTreeExample()245 pw::Status ClockTreeExample() {
246 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage]
247
248 // Declare the clock tree
249 pw::clock_tree::ClockTree clock_tree;
250 // Declare the uart clock source
251 ClockSourceUart uart_clock_source;
252 UartStreamMcuxpresso uart(&clock_tree, &uart_clock_source);
253
254 // Initialize the uart which enables the uart clock source.
255 PW_TRY(uart.Init());
256 PW_CHECK(uart_clock_source.ref_count() > 0);
257
258 // Do something with uart
259
260 // Deinitialize the uart which disable the uart clock source.
261 uart.Deinit();
262
263 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage]
264
265 return pw::OkStatus();
266 }
267
TEST(ClockTree,ClockTreeExample)268 TEST(ClockTree, ClockTreeExample) {
269 pw::Status status = ClockTreeExample();
270 EXPECT_EQ(status.code(), PW_STATUS_OK);
271 }
272 } // namespace examples
273