1 /*
2 * Copyright (c) 2018 Piotr Mienkowski
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief Serial Wire Output (SWO) backend implementation.
9 *
10 * SWO/SWV has been developed by ARM. The following code works only on ARM
11 * architecture.
12 *
13 * An SWO viewer program will typically set-up the SWO port including its
14 * frequency when connected to the debug probe. Such configuration can persist
15 * only until the MCU reset. The SWO backend initialization function will
16 * re-configure the SWO port upon boot and set the frequency as specified by
17 * the LOG_BACKEND_SWO_FREQ_HZ Kconfig option. To ensure flawless operation
18 * this frequency should much the one set by the SWO viewer program.
19 *
20 * The initialization code assumes that SWO core frequency is equal to HCLK
21 * as defined by the clock-frequency property in the CPU node. This may require
22 * additional, vendor specific configuration.
23 */
24
25 #include <zephyr/logging/log_backend.h>
26 #include <zephyr/logging/log_core.h>
27 #include <zephyr/logging/log_output.h>
28 #include <zephyr/logging/log_backend_std.h>
29 #include <zephyr/drivers/pinctrl.h>
30 #include <soc.h>
31
32 /** The stimulus port from which SWO data is received and displayed */
33 #define ITM_PORT_LOGGER 0
34
35 /* If ITM has pin control properties, apply them for SWO pins */
36 #if DT_NODE_HAS_PROP(DT_NODELABEL(itm), pinctrl_0)
37 PINCTRL_DT_DEFINE(DT_NODELABEL(itm));
38 #endif
39
40 /* Set TPIU prescaler for the current debug trace clock frequency. */
41 #if CONFIG_LOG_BACKEND_SWO_FREQ_HZ == 0
42 #define SWO_FREQ_DIV 1
43 #else
44
45 #if CONFIG_LOG_BACKEND_SWO_REF_FREQ_HZ == 0
46 #error "SWO reference frequency is not configured"
47 #endif
48
49 #define SWO_FREQ_DIV \
50 ((CONFIG_LOG_BACKEND_SWO_REF_FREQ_HZ + (CONFIG_LOG_BACKEND_SWO_FREQ_HZ / 2)) / \
51 CONFIG_LOG_BACKEND_SWO_FREQ_HZ)
52
53 #if SWO_FREQ_DIV > 0xFFFF
54 #error CONFIG_LOG_BACKEND_SWO_FREQ_HZ is too low. SWO clock divider is 16-bit. \
55 Minimum supported SWO clock frequency is \
56 [Reference Clock Frequency]/2^16.
57 #endif
58
59 #endif
60
61 static uint8_t buf[1];
62 static uint32_t log_format_current = CONFIG_LOG_BACKEND_SWO_OUTPUT_DEFAULT;
63
char_out(uint8_t * data,size_t length,void * ctx)64 static int char_out(uint8_t *data, size_t length, void *ctx)
65 {
66 ARG_UNUSED(ctx);
67
68 for (size_t i = 0; i < length; i++) {
69 ITM_SendChar(data[i]);
70 }
71
72 return length;
73 }
74
75 LOG_OUTPUT_DEFINE(log_output_swo, char_out, buf, sizeof(buf));
76
log_backend_swo_process(const struct log_backend * const backend,union log_msg_generic * msg)77 static void log_backend_swo_process(const struct log_backend *const backend,
78 union log_msg_generic *msg)
79 {
80 uint32_t flags = log_backend_std_get_flags();
81
82 log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
83
84 log_output_func(&log_output_swo, &msg->log, flags);
85 }
86
format_set(const struct log_backend * const backend,uint32_t log_type)87 static int format_set(const struct log_backend *const backend, uint32_t log_type)
88 {
89 log_format_current = log_type;
90 return 0;
91 }
92
log_backend_swo_init(struct log_backend const * const backend)93 static void log_backend_swo_init(struct log_backend const *const backend)
94 {
95 /* Enable DWT and ITM units */
96 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
97 /* Enable access to ITM registers */
98 ITM->LAR = 0xC5ACCE55;
99 /* Disable stimulus ports ITM_STIM0-ITM_STIM31 */
100 ITM->TER = 0x0;
101 /* Disable ITM */
102 ITM->TCR = 0x0;
103 /* Select TPIU encoding protocol */
104 TPI->SPPR = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_PROTOCOL_NRZ) ? 2 : 1;
105 /* Set SWO baud rate prescaler value: SWO_clk = ref_clock/(ACPR + 1) */
106 TPI->ACPR = SWO_FREQ_DIV - 1;
107 /* Enable unprivileged access to ITM stimulus ports */
108 ITM->TPR = 0x0;
109 /* Configure Debug Watchpoint and Trace */
110 DWT->CTRL &= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk | DWT_CTRL_CYCCNTENA_Msk);
111 DWT->CTRL |= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk);
112 /* Configure Formatter and Flush Control Register */
113 TPI->FFCR = 0x00000100;
114 /* Enable ITM, set TraceBusID=1, no local timestamp generation */
115 ITM->TCR = 0x0001000D;
116 /* Enable stimulus port used by the logger */
117 ITM->TER = 1 << ITM_PORT_LOGGER;
118
119 /* Initialize pin control settings, if any are defined */
120 #if DT_NODE_HAS_PROP(DT_NODELABEL(itm), pinctrl_0)
121 const struct pinctrl_dev_config *pincfg =
122 PINCTRL_DT_DEV_CONFIG_GET(DT_NODELABEL(itm));
123
124 pinctrl_apply_state(pincfg, PINCTRL_STATE_DEFAULT);
125 #endif
126 }
127
log_backend_swo_panic(struct log_backend const * const backend)128 static void log_backend_swo_panic(struct log_backend const *const backend)
129 {
130 }
131
dropped(const struct log_backend * const backend,uint32_t cnt)132 static void dropped(const struct log_backend *const backend, uint32_t cnt)
133 {
134 ARG_UNUSED(backend);
135
136 log_backend_std_dropped(&log_output_swo, cnt);
137 }
138
139 const struct log_backend_api log_backend_swo_api = {
140 .process = log_backend_swo_process,
141 .panic = log_backend_swo_panic,
142 .init = log_backend_swo_init,
143 .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
144 .format_set = format_set,
145 };
146
147 LOG_BACKEND_DEFINE(log_backend_swo, log_backend_swo_api, true);
148