123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- /*
- * Copyright (c) 2018 Piotr Mienkowski
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /** @file
- * @brief Serial Wire Output (SWO) backend implementation.
- *
- * SWO/SWV has been developed by ARM. The following code works only on ARM
- * architecture.
- *
- * An SWO viewer program will typically set-up the SWO port including its
- * frequency when connected to the debug probe. Such configuration can persist
- * only until the MCU reset. The SWO backend initialization function will
- * re-configure the SWO port upon boot and set the frequency as specified by
- * the LOG_BACKEND_SWO_FREQ_HZ Kconfig option. To ensure flawless operation
- * this frequency should much the one set by the SWO viewer program.
- *
- * The initialization code assumes that SWO core frequency is equal to HCLK
- * as defined by the clock-frequency property in the CPU node. This may require
- * additional, vendor specific configuration.
- */
- #include <logging/log_backend.h>
- #include <logging/log_core.h>
- #include <logging/log_msg.h>
- #include <logging/log_output.h>
- #include <logging/log_backend_std.h>
- #include <soc.h>
- /** The stimulus port from which SWO data is received and displayed */
- #define ITM_PORT_LOGGER 0
- /* Set TPIU prescaler for the current debug trace clock frequency. */
- #if CONFIG_LOG_BACKEND_SWO_FREQ_HZ == 0
- #define SWO_FREQ_DIV 1
- #else
- /* Set reference frequency which can be custom or cpu frequency. */
- #if DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
- #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
- #elif DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
- #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
- #else
- #error "Missing DT 'clock-frequency' property on cpu@0 node"
- #endif
- #define SWO_FREQ_DIV \
- ((SWO_REF_FREQ + (CONFIG_LOG_BACKEND_SWO_FREQ_HZ / 2)) / \
- CONFIG_LOG_BACKEND_SWO_FREQ_HZ)
- #if SWO_FREQ_DIV > 0xFFFF
- #error CONFIG_LOG_BACKEND_SWO_FREQ_HZ is too low. SWO clock divider is 16-bit. \
- Minimum supported SWO clock frequency is \
- [Reference Clock Frequency]/2^16.
- #endif
- #endif
- static uint8_t buf[1];
- static int char_out(uint8_t *data, size_t length, void *ctx)
- {
- ARG_UNUSED(ctx);
- for (size_t i = 0; i < length; i++) {
- ITM_SendChar(data[i]);
- }
- return length;
- }
- LOG_OUTPUT_DEFINE(log_output_swo, char_out, buf, sizeof(buf));
- static void log_backend_swo_put(const struct log_backend *const backend,
- struct log_msg *msg)
- {
- uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
- LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
- log_backend_std_put(&log_output_swo, flag, msg);
- }
- static void log_backend_swo_process(const struct log_backend *const backend,
- union log_msg2_generic *msg)
- {
- uint32_t flags = log_backend_std_get_flags();
- log_output_msg2_process(&log_output_swo, &msg->log, flags);
- }
- static void log_backend_swo_init(struct log_backend const *const backend)
- {
- /* Enable DWT and ITM units */
- CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
- /* Enable access to ITM registers */
- ITM->LAR = 0xC5ACCE55;
- /* Disable stimulus ports ITM_STIM0-ITM_STIM31 */
- ITM->TER = 0x0;
- /* Disable ITM */
- ITM->TCR = 0x0;
- /* Select NRZ (UART) encoding protocol */
- TPI->SPPR = 2;
- /* Set SWO baud rate prescaler value: SWO_clk = ref_clock/(ACPR + 1) */
- TPI->ACPR = SWO_FREQ_DIV - 1;
- /* Enable unprivileged access to ITM stimulus ports */
- ITM->TPR = 0x0;
- /* Configure Debug Watchpoint and Trace */
- DWT->CTRL &= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk | DWT_CTRL_CYCCNTENA_Msk);
- DWT->CTRL |= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk);
- /* Configure Formatter and Flush Control Register */
- TPI->FFCR = 0x00000100;
- /* Enable ITM, set TraceBusID=1, no local timestamp generation */
- ITM->TCR = 0x0001000D;
- /* Enable stimulus port used by the logger */
- ITM->TER = 1 << ITM_PORT_LOGGER;
- }
- static void log_backend_swo_panic(struct log_backend const *const backend)
- {
- }
- static void dropped(const struct log_backend *const backend, uint32_t cnt)
- {
- ARG_UNUSED(backend);
- log_backend_std_dropped(&log_output_swo, cnt);
- }
- static void 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)
- {
- uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
- LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
- log_backend_std_sync_string(&log_output_swo, flag, src_level,
- timestamp, fmt, ap);
- }
- static void 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)
- {
- uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
- LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
- log_backend_std_sync_hexdump(&log_output_swo, flag, src_level,
- timestamp, metadata, data, length);
- }
- const struct log_backend_api log_backend_swo_api = {
- .process = IS_ENABLED(CONFIG_LOG2) ? log_backend_swo_process : NULL,
- .put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? log_backend_swo_put : NULL,
- .put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
- log_backend_swo_sync_string : NULL,
- .put_sync_hexdump = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
- log_backend_swo_sync_hexdump : NULL,
- .panic = log_backend_swo_panic,
- .init = log_backend_swo_init,
- .dropped = IS_ENABLED(CONFIG_LOG_IMMEDIATE) ? NULL : dropped,
- };
- LOG_BACKEND_DEFINE(log_backend_swo, log_backend_swo_api, true);
|