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 <logging/log_backend.h>
26 #include <logging/log_core.h>
27 #include <logging/log_msg.h>
28 #include <logging/log_output.h>
29 #include <logging/log_backend_std.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 /* Set TPIU prescaler for the current debug trace clock frequency. */
36 #if CONFIG_LOG_BACKEND_SWO_FREQ_HZ == 0
37 #define SWO_FREQ_DIV 1
38 #else
39
40 /* Set reference frequency which can be custom or cpu frequency. */
41 #if DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
42 #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
43 #elif DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
44 #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
45 #else
46 #error "Missing DT 'clock-frequency' property on cpu@0 node"
47 #endif
48
49 #define SWO_FREQ_DIV \
50 ((SWO_REF_FREQ + (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
char_out(uint8_t * data,size_t length,void * ctx)63 static int char_out(uint8_t *data, size_t length, void *ctx)
64 {
65 ARG_UNUSED(ctx);
66
67 for (size_t i = 0; i < length; i++) {
68 ITM_SendChar(data[i]);
69 }
70
71 return length;
72 }
73
74 LOG_OUTPUT_DEFINE(log_output_swo, char_out, buf, sizeof(buf));
75
log_backend_swo_put(const struct log_backend * const backend,struct log_msg * msg)76 static void log_backend_swo_put(const struct log_backend *const backend,
77 struct log_msg *msg)
78 {
79 uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
80 LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
81
82 log_backend_std_put(&log_output_swo, flag, msg);
83 }
84
log_backend_swo_process(const struct log_backend * const backend,union log_msg2_generic * msg)85 static void log_backend_swo_process(const struct log_backend *const backend,
86 union log_msg2_generic *msg)
87 {
88 uint32_t flags = log_backend_std_get_flags();
89
90 log_output_msg2_process(&log_output_swo, &msg->log, flags);
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 NRZ (UART) encoding protocol */
104 TPI->SPPR = 2;
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
log_backend_swo_panic(struct log_backend const * const backend)120 static void log_backend_swo_panic(struct log_backend const *const backend)
121 {
122 }
123
dropped(const struct log_backend * const backend,uint32_t cnt)124 static void dropped(const struct log_backend *const backend, uint32_t cnt)
125 {
126 ARG_UNUSED(backend);
127
128 log_backend_std_dropped(&log_output_swo, cnt);
129 }
130
log_backend_swo_sync_string(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * fmt,va_list ap)131 static void log_backend_swo_sync_string(const struct log_backend *const backend,
132 struct log_msg_ids src_level, uint32_t timestamp,
133 const char *fmt, va_list ap)
134 {
135 uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
136 LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
137
138 log_backend_std_sync_string(&log_output_swo, flag, src_level,
139 timestamp, fmt, ap);
140 }
141
log_backend_swo_sync_hexdump(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * metadata,const uint8_t * data,uint32_t length)142 static void log_backend_swo_sync_hexdump(
143 const struct log_backend *const backend,
144 struct log_msg_ids src_level, uint32_t timestamp,
145 const char *metadata, const uint8_t *data, uint32_t length)
146 {
147 uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
148 LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
149
150 log_backend_std_sync_hexdump(&log_output_swo, flag, src_level,
151 timestamp, metadata, data, length);
152 }
153
154 const struct log_backend_api log_backend_swo_api = {
155 .process = IS_ENABLED(CONFIG_LOG2) ? log_backend_swo_process : NULL,
156 .put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? log_backend_swo_put : NULL,
157 .put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
158 log_backend_swo_sync_string : NULL,
159 .put_sync_hexdump = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
160 log_backend_swo_sync_hexdump : NULL,
161 .panic = log_backend_swo_panic,
162 .init = log_backend_swo_init,
163 .dropped = IS_ENABLED(CONFIG_LOG_IMMEDIATE) ? NULL : dropped,
164 };
165
166 LOG_BACKEND_DEFINE(log_backend_swo, log_backend_swo_api, true);
167