1 //*****************************************************************************
2 //
3 //! @file am_hal_wdt.c
4 //!
5 //! @brief Hardware abstraction layer for the Watchdog Timer module.
6 //!
7 //! @addtogroup wdt3p WDT - Watchdog Timer
8 //! @ingroup apollo3p_hal
9 //! @{
10 //
11 //*****************************************************************************
12
13 //*****************************************************************************
14 //
15 // Copyright (c) 2024, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision release_sdk_3_2_0-dd5f40c14b of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47
48 #include <stdint.h>
49 #include <stdbool.h>
50 #include "am_mcu_apollo.h"
51
52 //*****************************************************************************
53 //
54 // Adjacency check
55 //
56 // This is related to the timer read workaround. This macro checks to see if
57 // the two supplied count values are within one "tick" of eachother. It should
58 // still pass in the event of a timer rollover. The "B" read is assumed to
59 // follow the "A" read. The macro returns "TRUE" when the adjacent timer reads
60 // can be used.
61 //
62 //*****************************************************************************
63 #define adjacent(A, B) (((A) == (B)) || (((A) + 1) == (B)) || ((B) == 0))
64
65 //*****************************************************************************
66 //
67 // @brief Configure the watchdog timer.
68 //
69 // @param psConfig - pointer to a configuration structure containing the
70 // desired watchdog settings.
71 //
72 // This function will set the watchdog configuration register based on the
73 // user's desired settings listed in the structure referenced by psConfig. If
74 // the structure indicates that watchdog interrupts are desired, this function
75 // will also set the interrupt enable bit in the configuration register.
76 //
77 // @note In order to actually receive watchdog interrupt and/or watchdog reset
78 // events, the caller will also need to make sure that the watchdog interrupt
79 // vector is enabled in the ARM NVIC, and that watchdog resets are enabled in
80 // the reset generator module. Otherwise, the watchdog-generated interrupt and
81 // reset events will have no effect.
82 //
83 //*****************************************************************************
84 void
am_hal_wdt_init(const am_hal_wdt_config_t * psConfig)85 am_hal_wdt_init(const am_hal_wdt_config_t *psConfig)
86 {
87 uint32_t ui32ConfigVal;
88 uint16_t ui16IntCount, ui16ResetCount;
89 bool bResetEnabled = psConfig->ui32Config & AM_HAL_WDT_ENABLE_RESET;
90 bool bInterruptEnabled = psConfig->ui32Config & AM_HAL_WDT_ENABLE_INTERRUPT;
91
92 //
93 // Read the desired settings from the psConfig structure.
94 //
95 ui16IntCount = psConfig->ui16InterruptCount;
96 ui16ResetCount = psConfig->ui16ResetCount;
97
98 //
99 // Write the interrupt and reset count values to a temporary variable.
100 //
101 // Accept the passed Config value, but clear the Counts that we are about to set.
102 ui32ConfigVal = psConfig->ui32Config & ~(WDT_CFG_INTVAL_Msk | WDT_CFG_RESVAL_Msk);
103 ui32ConfigVal |= _VAL2FLD(WDT_CFG_INTVAL, ui16IntCount);
104 ui32ConfigVal |= _VAL2FLD(WDT_CFG_RESVAL, ui16ResetCount);
105
106 //
107 // If interrupts should be enabled, set the appropriate bit in the
108 // temporary variable. Also, enable the interrupt in INTEN register in the
109 // watchdog module.
110 //
111 if ( bInterruptEnabled )
112 {
113 //
114 // Enable the watchdog interrupt if the configuration calls for them.
115 //
116 WDT->INTEN |= WDT_INTEN_WDTINT_Msk;
117 }
118 else
119 {
120 //
121 // Disable the watchdog interrupt if the configuration doesn't call for
122 // watchdog interrupts.
123 //
124 WDT->INTEN &= ~WDT_INTEN_WDTINT_Msk;
125 }
126
127 //
128 // If resets should be enabled, set the appropriate bit in the temporary
129 // variable.
130 //
131 if ( bResetEnabled )
132 {
133 //
134 // Also enable watchdog resets in the reset module.
135 //
136 RSTGEN->CFG |= RSTGEN_CFG_WDREN_Msk;
137 }
138 else
139 {
140 //
141 // Disable watchdog resets in the reset module.
142 //
143 RSTGEN->CFG &= ~RSTGEN_CFG_WDREN_Msk;
144 }
145
146 //
147 // Check for a user specified clock select. If none specified then
148 // set 128Hz.
149 //
150 if ( !(psConfig->ui32Config & WDT_CFG_CLKSEL_Msk) )
151 {
152 ui32ConfigVal |= _VAL2FLD(WDT_CFG_CLKSEL, WDT_CFG_CLKSEL_128HZ);
153 }
154
155 //
156 // Write the saved value to the watchdog configuration register.
157 //
158 WDT->CFG = ui32ConfigVal;
159 } // am_hal_wdt_init()
160
161 //*****************************************************************************
162 //
163 // @brief Starts the watchdog timer.
164 //
165 // Enables the watchdog timer tick using the 'enable' bit in the watchdog
166 // configuration register. This function does not perform any locking of the
167 // watchdog timer, so it can be disabled or reconfigured later.
168 //
169 //*****************************************************************************
170 void
am_hal_wdt_start(void)171 am_hal_wdt_start(void)
172 {
173 //
174 // Make sure the watchdog timer is in the "reset" state, and then set the
175 // enable bit to start counting.
176 //
177 WDT->CFG |= WDT_CFG_WDTEN_Msk;
178 WDT->RSTRT = WDT_RSTRT_RSTRT_KEYVALUE;
179 } // am_hal_wdt_start()
180
181 //*****************************************************************************
182 //
183 // @brief Stops the watchdog timer.
184 //
185 // Disables the watchdog timer tick by clearing the 'enable' bit in the
186 // watchdog configuration register.
187 //
188 //*****************************************************************************
189 void
am_hal_wdt_halt(void)190 am_hal_wdt_halt(void)
191 {
192 //
193 // Clear the watchdog enable bit.
194 //
195 WDT->CFG &= ~WDT_CFG_WDTEN_Msk;
196 } // am_hal_wdt_halt()
197
198 //*****************************************************************************
199 //
200 // @brief Locks the watchdog configuration and starts the watchdog timer.
201 //
202 // This function sets the watchdog "lock" register, which prevents software
203 // from re-configuring the watchdog. This action will also set the enable bit
204 // for the watchdog timer, so it will start counting immediately.
205 //
206 //*****************************************************************************
207 void
am_hal_wdt_lock_and_start(void)208 am_hal_wdt_lock_and_start(void)
209 {
210 //
211 // Write the 'key' value to the watchdog lock register.
212 //
213 WDT->LOCK = WDT_LOCK_LOCK_KEYVALUE;
214 } // am_hal_wdt_lock_and_start()
215
216 //*****************************************************************************
217 //
218 // @brief Read the state of the wdt interrupt status.
219 //
220 // @param bEnabledOnly - return the status of only the enabled interrupts.
221 //
222 // This function extracts the interrupt status bits and returns the enabled or
223 // raw based on bEnabledOnly.
224 //
225 // @return WDT interrupt status.
226 //
227 //*****************************************************************************
228 uint32_t
am_hal_wdt_int_status_get(bool bEnabledOnly)229 am_hal_wdt_int_status_get(bool bEnabledOnly)
230 {
231 if ( bEnabledOnly )
232 {
233 uint32_t ui32RetVal;
234 AM_CRITICAL_BEGIN
235 ui32RetVal = WDT->INTSTAT;
236 ui32RetVal &= WDT->INTEN;
237 AM_CRITICAL_END
238 return ui32RetVal;
239 }
240 else
241 {
242 return WDT->INTSTAT;
243 }
244 } // am_hal_wdt_int_status_get()
245
246 //*****************************************************************************
247 //
248 // @brief Set the state of the wdt interrupt status bit.
249 //
250 // This function sets the interrupt bit.
251 //
252 //*****************************************************************************
253 void
am_hal_wdt_int_set(void)254 am_hal_wdt_int_set(void)
255 {
256 WDT->INTSET = WDT_INTSET_WDTINT_Msk;
257 } // am_hal_wdt_int_set()
258
259 //*****************************************************************************
260 //
261 // @brief Clear the state of the wdt interrupt status bit.
262 //
263 // This function clear the interrupt bit.
264 //
265 //*****************************************************************************
266 void
am_hal_wdt_int_clear(void)267 am_hal_wdt_int_clear(void)
268 {
269 WDT->INTCLR = WDT_INTCLR_WDTINT_Msk;
270 } // am_hal_wdt_int_clear()
271
272 //*****************************************************************************
273 //
274 // @brief Enable the wdt interrupt.
275 //
276 // This function enable the interrupt.
277 //
278 //*****************************************************************************
279 void
am_hal_wdt_int_enable(void)280 am_hal_wdt_int_enable(void)
281 {
282 WDT->INTEN |= WDT_INTEN_WDTINT_Msk;
283 } // am_hal_wdt_int_enable()
284
285 //*****************************************************************************
286 //
287 // @brief Return the enabled WDT interrupts.
288 //
289 // This function returns the enabled WDT interrupts.
290 //
291 // @return enabled WDT interrupts.
292 //
293 //*****************************************************************************
294 uint32_t
am_hal_wdt_int_enable_get(void)295 am_hal_wdt_int_enable_get(void)
296 {
297 return WDT->INTEN;
298 } // am_hal_wdt_int_enable_get()
299
300 //*****************************************************************************
301 //
302 // @brief Disable the wdt interrupt.
303 //
304 // This function disablee the interrupt.
305 //
306 //*****************************************************************************
307 void
am_hal_wdt_int_disable(void)308 am_hal_wdt_int_disable(void)
309 {
310 WDT->INTEN &= ~WDT_INTEN_WDTINT_Msk;
311 } // am_hal_wdt_int_disable()
312
313 //*****************************************************************************
314 //
315 // @brief Get the wdt counter value.
316 //
317 // This function reads the current value of watch dog timer counter register.
318 //
319 // @note WARNING caller is responsible for masking interrupts before calling this
320 // function.
321 //
322 //*****************************************************************************
323 uint32_t
am_hal_wdt_counter_get(void)324 am_hal_wdt_counter_get(void)
325 {
326 uint32_t ui32Values[3] = {0};
327 uint32_t ui32Value;
328
329 //
330 // First, go read the value from the counter register 3 times
331 // back to back in assembly language.
332 //
333 am_hal_triple_read( AM_REGADDR(WDT, COUNT), ui32Values );
334
335 //
336 // Mask out the COUNT field from the 3 read values.
337 //
338 ui32Values[0] = _VAL2FLD(WDT_COUNT_COUNT, ui32Values[0]);
339 ui32Values[1] = _VAL2FLD(WDT_COUNT_COUNT, ui32Values[1]);
340 ui32Values[2] = _VAL2FLD(WDT_COUNT_COUNT, ui32Values[2]);
341
342 //
343 // Now, we'll figure out which of the three values is the correct time.
344 //
345 if (ui32Values[0] == ui32Values[1])
346 {
347 //
348 // If the first two values match, then neither one was a bad read.
349 // We'll take this as the current time.
350 //
351 ui32Value = ui32Values[1];
352 }
353 else
354 {
355 //
356 // If the first two values didn't match, then one of them might be bad.
357 // If one of the first two values is bad, then the third one should
358 // always be correct. We'll take the third value as the correct count.
359 //
360 ui32Value = ui32Values[2];
361
362 //
363 // If all of the statements about the architecture are true, the third
364 // value should be correct, and it should always be within one count of
365 // either the first or the second value.
366 //
367 // Just in case, we'll check against the previous two values to make
368 // sure that our final answer was reasonable. If it isn't, we will
369 // flag it as a "bad read", and fail this assert statement.
370 //
371 // This shouldn't ever happen, and it hasn't ever happened in any of
372 // our tests so far.
373 //
374 am_hal_debug_assert_msg((adjacent(ui32Values[1], ui32Values[2]) ||
375 adjacent(ui32Values[0], ui32Values[2])),
376 "Bad CDT read");
377 }
378
379 return ui32Value;
380 } // am_hal_wdt_counter_get()
381
382 //*****************************************************************************
383 //
384 // End Doxygen group.
385 //! @}
386 //
387 //*****************************************************************************
388