1 /*
2  * Trace Recorder for Tracealyzer v4.9.2
3  * Copyright 2023 Percepio AB
4  * www.percepio.com
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  *
8  * Supporting functions for trace streaming, used by the "stream ports"
9  * for reading and writing data to the interface.
10  * Existing ports can easily be modified to fit another setup, e.g., a
11  * different TCP/IP stack, or to define your own stream port.
12  *
13  * This stream port is for ITM streaming on Arm Cortex-M devices.
14  *
15  * To setup Keil uVision for ITM tracing with a Keil ULINKpro (or ULINKplus),
16  * see Percepio Application Note PA-021, available at
17  * https://percepio.com/2018/05/04/keil-itm-support/
18  *
19  * To setup IAR Embedded Workbench for ITM tracing with an IAR I-Jet,
20  * see Percepio Application Note PA-023, https://percepio.com/iar
21  *
22  * NOTE: This stream port may block the application in case the ITM port
23  * is not ready for more data (the TPIU FIFO has become full). This is
24  * necessary to avoid data loss, as the TPIU FIFO is often quite small.
25  *
26  * --- Direct vs. Indirect ITM streaming ---
27  * Direct streaming: By default, this stream port writes directly to the ITM
28  * register mode without any RAM buffer. This assumes you have a fast debug
29  * probe, like aKeil ULINKpro or IAR I-Jet, to avoid excessive blocking.
30  * In case the ITM blocking appears to disturb your application, make sure your
31  * debugger is configured for maximum performance, as described in the above
32  * Application Nodes.
33  *
34  * Indirect streaming: If direct streaming gives too much overhead, you may
35  * instead try indirect ITM streaming. This is done by enabling the internal
36  * RAM buffer, like below. This reconfigures the recorder to store the events
37  * in the internal RAM buffer instead of writing them directly to the ITM port.
38  *
39  * Set TRC_STREAM_PORT_USE_INTERNAL_BUFFER to 1 to use the indirect mode.
40  *
41  * This increases RAM usage but eliminates peaks in the trace data rate.
42  * Moreover, the ITM writes are then performed in a separate task (TzCtrl).
43  * You find relevant settings (buffer size etc.) in trcStreamingConfig.h.
44  *
45  * See also https://percepio.com/2018/10/11/tuning-your-custom-trace-streaming
46  *
47  * --- One-way vs. Two-way Communication ---
48  * The ITM port only provides one-way communication, from target to host.
49  * This is sufficient if you start the tracing from the target application,
50  * using vTraceEnable(TRC_START). Just make sure to start the Tracealyzer
51  * recording before you start the target system.
52  *
53  * In case you prefer to interactively start and stop the tracing from the host
54  * computer, you need two-way communication to send commands to the recorder.
55  * This is possible by writing such "start" and "stop" commands to a special
56  * buffer, monitored by the recorder library, using the debugger IDE.
57  * See trcStreamingPort.c and also the example macro for Keil uVision
58  * (Keil-uVision-Tracealyzer-ITM-Exporter.ini).
59  */
60 
61 #include <trcRecorder.h>
62 
63 #if (TRC_USE_TRACEALYZER_RECORDER == 1)
64 
65 #if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)
66 
67 static TraceStreamPortBuffer_t* pxStreamPortITM TRC_CFG_RECORDER_DATA_ATTRIBUTE;
68 
69 /* This will be set by the debugger when there is data to be read */
70 volatile int32_t tz_host_command_bytes_to_read = 0;
71 
72 /* This will be filled with data from the debugger */
73 volatile char tz_host_command_data[32];
74 
75 /* These variables are used for reading commands from the host, using read_from_host().
76  * This is not required if using vTraceEnable(TRC_START).
77  * A debugger IDE may write to these functions using a macro.
78  * An example for Keil is included (Keil-uVision-Tracealyzer-ITM-Exporter.ini). */
79 
80 #define itm_write_32(__data) \
81 {\
82 		while (ITM->PORT[TRC_CFG_STREAM_PORT_ITM_PORT].u32 == 0) { /* Do nothing */ }	/* Block until room in ITM FIFO - This stream port is always in "blocking mode", since intended for high-speed ITM! */ \
83 		ITM->PORT[TRC_CFG_STREAM_PORT_ITM_PORT].u32 = __data;							/* Write the data */ \
84 }
85 
86 /* This is assumed to execute from within the recorder, with interrupts disabled */
prvTraceItmWrite(void * ptrData,uint32_t size,int32_t * ptrBytesWritten)87 traceResult prvTraceItmWrite(void* ptrData, uint32_t size, int32_t* ptrBytesWritten)
88 {
89 	uint32_t* ptr32 = (uint32_t*)ptrData;
90 
91 	TRC_ASSERT(size % 4 == 0);
92 	TRC_ASSERT(ptrBytesWritten != 0);
93 
94 	*ptrBytesWritten = 0;
95 
96 	if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk) &&					/* Trace enabled? */ \
97 		(ITM->TCR & ITM_TCR_ITMENA_Msk) &&									/* ITM enabled? */ \
98 		(ITM->TER & (1UL << (TRC_CFG_STREAM_PORT_ITM_PORT))))				/* ITM port enabled? */
99 	{
100 		while (*ptrBytesWritten < (int32_t)size)
101 		{
102 			itm_write_32(*ptr32);
103 			ptr32++;
104 			*ptrBytesWritten += 4;
105 		}
106 	}
107 
108 	return TRC_SUCCESS;
109 }
110 
111 /* This reads "command" data from a RAM buffer, written by a host macro in the debugger */
prvTraceItmRead(void * ptrData,uint32_t uiSize,int32_t * piBytesRead)112 traceResult prvTraceItmRead(void* ptrData, uint32_t uiSize, int32_t* piBytesRead)
113 {
114 	int32_t i;
115 	uint8_t* bytesBuffer = (uint8_t*)ptrData;
116 
117 	TRC_ASSERT(piBytesRead != 0);
118 
119 	/* Check if the debugger has updated tz_host_command_bytes_to_read */
120 	if (tz_host_command_bytes_to_read > 0)
121 	{
122 		if (tz_host_command_bytes_to_read != (int32_t)uiSize)
123 		{
124 			/* Sanity check. */
125 			return TRC_FAIL;
126 		}
127 
128 		*piBytesRead = (int32_t)tz_host_command_bytes_to_read;
129 
130 		/* Read the bytes */
131 		for (i = 0; i < tz_host_command_bytes_to_read; i++)
132 		{
133 			bytesBuffer[i] = tz_host_command_data[i];
134 		}
135 
136 		/* Reset */
137 		tz_host_command_bytes_to_read = 0;
138 	}
139 
140 	return TRC_SUCCESS;
141 }
142 
xTraceStreamPortInitialize(TraceStreamPortBuffer_t * pxBuffer)143 traceResult xTraceStreamPortInitialize(TraceStreamPortBuffer_t* pxBuffer)
144 {
145 	TRC_ASSERT_EQUAL_SIZE(TraceStreamPortBuffer_t, TraceStreamPortITM_t);
146 
147 	TRC_ASSERT(pxBuffer != 0);
148 
149 	pxStreamPortITM = (TraceStreamPortBuffer_t*)pxBuffer;
150 
151 #if (TRC_USE_INTERNAL_BUFFER == 1)
152 	return xTraceInternalEventBufferInitialize(pxStreamPortITM->bufferInternal, sizeof(pxStreamPortITM->bufferInternal));
153 #else
154         /* Prevents a warning for set but not used when internal buffer
155          * isn't used. */
156         (void)pxStreamPortITM;
157 
158 	return TRC_SUCCESS;
159 #endif
160 }
161 
162 #endif
163 
164 #endif
165