1 //*****************************************************************************
2 //
3 //! @file am_hal_itm.c
4 //!
5 //! @brief Functions for Operating the Instrumentation Trace Macrocell
6 //!
7 //! @addtogroup itm3p ITM - Instrumentation Trace Macrocell
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 // Global Variables
55 //
56 //*****************************************************************************
57
58 //*****************************************************************************
59 //
60 // @brief Enables the ITM
61 //
62 // This function enables the ARM ITM by setting the TRCENA bit in the DEMCR
63 // register.
64 //
65 //*****************************************************************************
66 void
am_hal_itm_enable(void)67 am_hal_itm_enable(void)
68 {
69 //
70 // To be able to access ITM registers, set the Trace Enable bit
71 // in the Debug Exception and Monitor Control Register (DEMCR).
72 //
73 CoreDebug->DEMCR |= _VAL2FLD(CoreDebug_DEMCR_TRCENA, 1);
74 while ( !(CoreDebug->DEMCR & _VAL2FLD(CoreDebug_DEMCR_TRCENA, 1)) );
75
76 //
77 // Write the key to the ITM Lock Access register to unlock the ITM_TCR.
78 //
79 ITM->LAR = ITM_LAR_KEYVAL;
80
81 //
82 // Set the enable bits in the ITM trace enable register, and the ITM
83 // control registers to enable trace data output.
84 //
85 ITM->TPR = 0x0000000F;
86 ITM->TER = 0xFFFFFFFF;
87
88 //
89 // Write to the ITM control and status register.
90 //
91 ITM->TCR =
92 _VAL2FLD(ITM_TCR_TraceBusID, 0x15) |
93 _VAL2FLD(ITM_TCR_GTSFREQ, 1) |
94 _VAL2FLD(ITM_TCR_TSPrescale, 1) |
95 _VAL2FLD(ITM_TCR_SWOENA, 1) |
96 _VAL2FLD(ITM_TCR_DWTENA, 0) |
97 _VAL2FLD(ITM_TCR_SYNCENA, 0) |
98 _VAL2FLD(ITM_TCR_TSENA, 0) |
99 _VAL2FLD(ITM_TCR_ITMENA, 1);
100
101
102
103 }
104
105 //*****************************************************************************
106 //
107 // @brief Disables the ITM
108 //
109 // This function completely disables the ARM ITM by resetting the TRCENA bit
110 // in the DEMCR register.
111 //
112 //*****************************************************************************
113 void
am_hal_itm_disable(void)114 am_hal_itm_disable(void)
115 {
116
117 if ( MCUCTRL->TPIUCTRL == 0 )
118 {
119 //
120 // This is a disable without enable, which could be the case with some
121 // earlier versions of SBL. To avoid a hang, ITM (particularly TPIU
122 // clock) must first be enabled.
123 //
124 am_hal_itm_enable();
125 }
126
127 //
128 // Make sure the ITM/TPIU is not busy.
129 //
130 am_hal_itm_not_busy();
131
132 //
133 // Make sure the ITM_TCR is unlocked.
134 //
135 ITM->LAR = ITM_LAR_KEYVAL;
136
137 //
138 // Disable the ITM.
139 //
140 for (int ix = 0; ix < 100; ix++)
141 {
142 ITM->TCR &= ~_VAL2FLD(ITM_TCR_ITMENA, 1);
143 while ( ITM->TCR & (_VAL2FLD(ITM_TCR_ITMENA, 1) | _VAL2FLD(ITM_TCR_BUSY, 1)) );
144 }
145
146 //
147 // Reset the TRCENA bit in the DEMCR register, which should disable the ITM
148 // for operation.
149 //
150 CoreDebug->DEMCR &= ~_VAL2FLD(CoreDebug_DEMCR_TRCENA, 1);
151 while ( CoreDebug->DEMCR & _VAL2FLD(CoreDebug_DEMCR_TRCENA, 1) );
152
153 //
154 // Disable the TPIU clock source in MCU control.
155 //
156 MCUCTRL->TPIUCTRL =
157 _VAL2FLD(MCUCTRL_TPIUCTRL_CLKSEL, MCUCTRL_TPIUCTRL_CLKSEL_LOWPWR) |
158 _VAL2FLD(MCUCTRL_TPIUCTRL_ENABLE, MCUCTRL_TPIUCTRL_ENABLE_DIS);
159 while (MCUCTRL->TPIUCTRL);
160
161 }
162
163 //*****************************************************************************
164 //
165 // @brief Checks if itm is busy and provides a delay to flush the fifo
166 //
167 // This function disables the ARM ITM by resetting the TRCENA bit in the DEMCR
168 // register.
169 //
170 //*****************************************************************************
171 void
am_hal_itm_not_busy(void)172 am_hal_itm_not_busy(void)
173 {
174 //
175 // Make sure the ITM/TPIU is not busy.
176 //
177 while (ITM->TCR & _VAL2FLD(ITM_TCR_BUSY, 1));
178
179 //
180 // wait for 50us for the data to flush out
181 //
182 am_hal_flash_delay(FLASH_CYCLES_US(50));
183 }
184
185 //*****************************************************************************
186 //
187 // @brief Enables tracing on a given set of ITM ports
188 //
189 // @param ui8portNum - Set ports to be enabled
190 //
191 // Enables tracing on the ports referred to by \e ui8portNum by writing the
192 // associated bit in the Trace Privilege Register in the ITM. The value for
193 // ui8portNum should be the logical OR one or more of the following values:
194 //
195 // \e ITM_PRIVMASK_0_7 - enable ports 0 through 7
196 // \e ITM_PRIVMASK_8_15 - enable ports 8 through 15
197 // \e ITM_PRIVMASK_16_23 - enable ports 16 through 23
198 // \e ITM_PRIVMASK_24_31 - enable ports 24 through 31
199 //
200 //*****************************************************************************
201 void
am_hal_itm_trace_port_enable(uint8_t ui8portNum)202 am_hal_itm_trace_port_enable(uint8_t ui8portNum)
203 {
204 ITM->TPR |= (0x00000001 << (ui8portNum>>3));
205 }
206
207 //*****************************************************************************
208 //
209 // @brief Disable tracing on the given ITM stimulus port.
210 //
211 // @param ui8portNum
212 //
213 // Disables tracing on the ports referred to by \e ui8portNum by writing the
214 // associated bit in the Trace Privilege Register in the ITM. The value for
215 // ui8portNum should be the logical OR one or more of the following values:
216 //
217 // \e ITM_PRIVMASK_0_7 - disable ports 0 through 7
218 // \e ITM_PRIVMASK_8_15 - disable ports 8 through 15
219 // \e ITM_PRIVMASK_16_23 - disable ports 16 through 23
220 // \e ITM_PRIVMASK_24_31 - disable ports 24 through 31
221 //
222 //*****************************************************************************
223 void
am_hal_itm_trace_port_disable(uint8_t ui8portNum)224 am_hal_itm_trace_port_disable(uint8_t ui8portNum)
225 {
226 ITM->TPR &= ~(0x00000001 << (ui8portNum >> 3));
227 }
228
229 //*****************************************************************************
230 //
231 // @brief Poll the given ITM stimulus register until not busy.
232 //
233 // @param ui32StimReg - stimulus register
234 //
235 // @return true if not busy, false if busy (timed out or other error).
236 //
237 //*****************************************************************************
238 bool
am_hal_itm_stimulus_not_busy(uint32_t ui32StimReg)239 am_hal_itm_stimulus_not_busy(uint32_t ui32StimReg)
240 {
241 uint32_t ui32StimAddr = (uint32_t)&ITM->PORT[0] + (4 * ui32StimReg);
242
243 //
244 // Busy waiting until it is available, non-zero means ready.
245 //
246 while ( !AM_REGVAL(ui32StimAddr) );
247
248 return true;
249 }
250
251 //*****************************************************************************
252 //
253 // @brief Writes a 32-bit value to the given ITM stimulus register.
254 //
255 // @param ui32StimReg - stimulus register
256 // @param ui32Value - value to be written.
257 //
258 // Write a word to the desired stimulus register.
259 //
260 //*****************************************************************************
261 void
am_hal_itm_stimulus_reg_word_write(uint32_t ui32StimReg,uint32_t ui32Value)262 am_hal_itm_stimulus_reg_word_write(uint32_t ui32StimReg, uint32_t ui32Value)
263 {
264 uint32_t ui32StimAddr = (uint32_t)&ITM->PORT[0] + (4 * ui32StimReg);
265
266
267 //
268 // Busy waiting until it is available, non-zero means ready
269 //
270 while (!AM_REGVAL(ui32StimAddr));
271
272 //
273 // Write the register.
274 //
275 AM_REGVAL(ui32StimAddr) = ui32Value;
276 }
277
278 //*****************************************************************************
279 //
280 // @brief Writes a short to the given ITM stimulus register.
281 //
282 // @param ui32StimReg - stimulus register
283 // @param ui16Value - short to be written.
284 //
285 // Write a short to the desired stimulus register.
286 //
287 //*****************************************************************************
288 void
am_hal_itm_stimulus_reg_short_write(uint32_t ui32StimReg,uint16_t ui16Value)289 am_hal_itm_stimulus_reg_short_write(uint32_t ui32StimReg, uint16_t ui16Value)
290 {
291 uint32_t ui32StimAddr = (uint32_t)&ITM->PORT[0] + (4 * ui32StimReg);
292
293 //
294 // Busy waiting until it is available non-zero means ready
295 //
296 while ( !AM_REGVAL(ui32StimAddr) );
297
298 //
299 // Write the register.
300 //
301 *((volatile uint16_t *) ui32StimAddr) = ui16Value;
302 }
303
304 //*****************************************************************************
305 //
306 // @brief Writes a byte to the given ITM stimulus register.
307 //
308 // @param ui32StimReg - stimulus register
309 // @param ui8Value - byte to be written.
310 //
311 // Write a byte to the desired stimulus register.
312 //
313 //*****************************************************************************
314 void
am_hal_itm_stimulus_reg_byte_write(uint32_t ui32StimReg,uint8_t ui8Value)315 am_hal_itm_stimulus_reg_byte_write(uint32_t ui32StimReg, uint8_t ui8Value)
316 {
317 uint32_t ui32StimAddr = (uint32_t)&ITM->PORT[0] + (4 * ui32StimReg);
318
319 //
320 // Busy waiting until it is available (non-zero means ready)
321 //
322 while (!AM_REGVAL(ui32StimAddr));
323
324 //
325 // Write the register.
326 //
327 *((volatile uint8_t *) ui32StimAddr) = ui8Value;
328 }
329
330 //*****************************************************************************
331 //
332 // @brief Sends a Sync Packet.
333 //
334 // Sends a sync packet. This can be useful for external software should it
335 // become out of sync with the ITM stream.
336 //
337 //*****************************************************************************
338 void
am_hal_itm_sync_send(void)339 am_hal_itm_sync_send(void)
340 {
341 //
342 // Write the register.
343 //
344 am_hal_itm_stimulus_reg_word_write(AM_HAL_ITM_SYNC_REG,
345 AM_HAL_ITM_SYNC_VAL);
346 }
347
348 //*****************************************************************************
349 //
350 // @brief Poll the print stimulus registers until not busy.
351 //
352 // @return true if not busy, false if busy (timed out or other error).
353 //
354 //*****************************************************************************
355 bool
am_hal_itm_print_not_busy(void)356 am_hal_itm_print_not_busy(void)
357 {
358 //
359 // Poll stimulus register allocated for printing.
360 //
361 am_hal_itm_stimulus_not_busy(0);
362
363
364 return true;
365 }
366
367 //*****************************************************************************
368 //
369 // @brief Prints a char string out of the ITM.
370 //
371 // @param pcString pointer to the character sting
372 //
373 // This function prints a sting out of the ITM.
374 //
375 //*****************************************************************************
376 void
am_hal_itm_print(char * pcString)377 am_hal_itm_print(char *pcString)
378 {
379 uint32_t ui32Length = 0;
380
381 //
382 // Determine the length of the string.
383 //
384 while (*(pcString + ui32Length))
385 {
386 ui32Length++;
387 }
388
389 //
390 // If there is no longer a word left, empty out the remaining characters.
391 //
392 while (ui32Length)
393 {
394 //
395 // Print string out the ITM.
396 //
397 am_hal_itm_stimulus_reg_byte_write(0, (uint8_t)*pcString++);
398
399 //
400 // Subtract from length.
401 //
402 ui32Length--;
403 }
404 }
405 //*****************************************************************************
406 //
407 // End Doxygen group.
408 //! @}
409 //
410 //*****************************************************************************
411