log_backend_swo.c 5.3 KB

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