LogSinkUART.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023-2024 Texas Instruments Incorporated - https://www.ti.com
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of Texas Instruments Incorporated nor the names of
17  * its contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*!*****************************************************************************
34  * @file LogSinkUART.h
35  * @brief <b>PRELIMINARY</b> LogSinkUART interface
36  *
37  * @warning These APIs are <b>PRELIMINARY</b>
38  *
39  * The LogSinkUART module is a sink that can be used in conjunction with the
40  * Log.h API in source/ti/log/ and the logging tools available in
41  * tools/log/tiutils. The API defined in this file is made available to the
42  * Logging framework and used as a transport layer for Log.h. For more
43  * information about the Log API, see the @ref log "Log documentation".
44  *
45  * To use the UART sink, ensure that the correct library for your
46  * device is linked in and include this header file as follows:
47  * @code
48  * #include <ti/log/LogSinkUART.h>
49  * @endcode
50  *
51  * This module implements two functions that are required by the Log API:
52  * - printf(const Log_Module *handle, uint32_t header, uint32_t headerPtr,
53  * uint32_t numArgs, ...);
54  * - buf(const Log_Module *handle, uint32_t header, uint32_t headerPtr,
55  * uint8_t *data, size_t size);
56  *
57  * Whenever a log statement that uses LogSinkUART as its sink is called, the
58  * log module delegates to one of the functions above.
59  *
60  * @anchor ti_log_LogSinkUART_Overview
61  * # Overview
62  * LogSinkUART is a sink/transport layer that asynchronously outputs encoded
63  * log statements over UART. It uses the UART2 driver to stream data out onto a
64  * user-selectable pin, which can be received and processed by a host-side
65  * tool. For more information about the host-side tool, see tools/log/tiutils.
66  *
67  * At the log-site the sink separates the generation of the log record from the
68  * transportation of the record off the device. Deferring the transportation to
69  * a later point of the program execution is done to minimize the runtime
70  * intrusion that would be caused by synchronously outputting the log
71  * statements through the relatively slow UART. The first part generates and
72  * stores the log statements into an intermediate storage synchronously. The
73  * second part uses the idle task of the OS, executed when no other tasks or
74  * interrupts are running, to move the data stored in the intermediate storage
75  * off the device.
76  *
77  * By transmitting messages asynchronously the readout at the host-side is
78  * also asynchronous. As a consequence, the receiving of log statements at the
79  * host-side can be deferred from their execution in the program.
80  *
81  * This sink requires no special hardware to capture and decode the logs beyond
82  * a basic UART-to-USB bridge.
83  *
84  * The data flow at a high level is:
85  * 1. Log statement captured at the log site with timestamp
86  * 2. Log data marshalled into a log packet
87  * 3. Log packet moved to ring buffer working as intermediate storage
88  * 4. Intermediate storage flushed via the UART2 driver when nothing else
89  * is happening through a function installed in the Idle-loop/task
90  * 5. Data sent out on the UART line
91  * 6. Data received by listening COM port on host
92  * 7. Data decoded by host and fed through remainder of host logging
93  * infrastructure
94  * 8. Logs visualised in Wireshark or dumped to console / log file
95  *
96  * @note Throughout this documentation and API the term "packet" is used
97  * instead of the term "record". Both terms are equivalent and can be used
98  * interchangeably.
99  *
100  * <hr>
101  * @anchor ti_log_LogSinkUART_Considerations
102  * # Considerations
103  *
104  * When using this sink consider the following:
105  * - The number of sinks is limited by the number of UART peripherals in
106  * the device.
107  * - When a UART instance is consumed for a sink it can not be used for
108  * other operations.
109  * - When a single UART LogSink instance is used, an optimised printf is
110  * used that reduces the footprint of each #Log_printf statement.
111  * - Each UART LogSink will create its own Ring Buffer.
112  * - If any part of the transmission is lost/not received by the
113  * logging-tool, the rest of the data might not be interpreted correctly.
114  * - 9.54 hours of logging timestamps are available before overflowing in a
115  * system where 1 bit = 8 us. The tick period for each device can be read
116  * from the multiplier variable in TimestamP<device>.c under
117  * kernel/freertos/dpl. If the timestamp overflows it will go back to 0.
118  * - The LogSinkUART sinks are flushed in the order they are instantiated. 
119  * - If performance issues are observed either increase the size of the
120  * UART2 ring buffer to increase throughput or increase the size of the
121  * intermediate ring buffer size for more log statements. Also note that
122  * the device needs to have idle-time to automatically flush data. If the
123  * software is always doing something then nothing will ever be output.
124  * - SRAM requirements scale with the number of log records to store in
125  * between flushing the buffer.
126  * - A #Log_printf call has an execution time of 20 us to 27.8 us depending
127  * on the number of arguments.
128  * - A Log_buf call has a minimum execution time of 32.8 us and an
129  * approximate increase of 0.183 us per byte in the buffer.
130  *
131  * <hr>
132  * @anchor ti_log_LogSinkUART_DesignArchitecture
133  * # Design Architecture
134  *
135  * The LogSinkUART implementation is based on the following architecture.
136  *
137  * ## Packet Transmission Format
138  * All log packets begin with a 32-bit metadata pointer followed by a 32-bit
139  * timestamp. The next fields depend on the type of log statement:
140  * - Log_printf: Variable number of 32-bit arguments that range from 0 to
141  * 8.
142  * - Log_buf: 32-bit field with the size of the buffer being sent followed
143  * by the buffer data.
144  *
145  * @startuml
146  * skinparam useBetaStyle true
147  *
148  * <style>
149  * timingDiagram {
150  * LineColor #065959
151  * }
152  * </style>
153  *
154  * concise "Log_printf packet" as LP
155  * concise "Log_buf packet" as LB
156  * hide time-axis
157  *
158  * scale 1 as 50 pixels
159  *
160  * @LP
161  * 0 is Metadata_Pointer #FFCC99: 0:31
162  * +4 is Timestamp #CC99FF: 32:63
163  * +4 is VA_Arg_0 #99CCFF: 64:95
164  * +4 is {-} #99CCFF: ...
165  * +1 is VA_Arg_n #99CCFF: 32-bits
166  * +4 is {-}
167  *
168  * @LB
169  * 0 is Metadata_Pointer #FFCC99: 0:31
170  * +4 is Timestamp #CC99FF: 32:63
171  * +4 is Buffer_Size #97D077: 64:95
172  * +4 is Buffer_Data #99CCFF
173  * +5 is {-}
174  * @enduml
175  *
176  * If a packet would overflow the ring buffer, a 32-bit overflow packet is
177  * placed instead. It is the original metadata pointer modified to be
178  * identified as an overflow packet. The host-side tool decodes it and displays
179  * an overflow message, indicating that at least that message would have
180  * overflowed. When this is observed, it is recommended to either resize the
181  * ring buffer or disable some log statements.
182  * @note If the intermediate ring buffer is full, no new overflow or log
183  * packets will be stored.
184  *
185  * Each log statement used will occupy the following amount of SRAM:
186  *
187  * Log statement type | Log statement size (bytes) |
188  * ------------------ | --------------------------- |
189  * Log_printf | 8 + 4 * number_of_arguments |
190  * Log_buf | 12 + buffer_size |
191  * Overflow | 4 |
192  *
193  * ## Packet Framing
194  * The host-side must receive and properly handle a continuous stream of packets.
195  * It is able to decode and synchronize packets. If the first 32 bits is not a
196  * valid metadata-pointer address, it will left-shift byte-by-byte until it detects
197  * a valid one. Once a metadata-pointer address is verified, the host-side tool
198  * knows that it is followed by a timestamp. The number of arguments for each
199  * frame is extracted from the .out file. This determines the length of the
200  * current packet and when the metadata-pointer address from the next packet is
201  * expected.
202  *
203  * ## Flushing the data
204  * A hook function installed in the Idle-loop/task is run when no other tasks
205  * or interrupts are running. It flushes as many log packets as possible from
206  * the intermediate storage via the UART2 driver set in nonblocking mode. In
207  * this mode, UART2_write() will copy as much data into the transmit buffer as
208  * space allows and return immediately. The maximum space allowed, and
209  * therefore the amount of data sent out every time that the hook function is
210  * called, is determined by the size of the TX Ring Buffer.
211  *
212  * The Idle-loop/task will always be run before the power management loop.
213  *
214  * Since each OS has a different implementation of the Idle-loop/task, the
215  * installation of the hook function will also be different for each OS. The
216  * automatic installation is currently supported for FreeRTOS and TI-RTOS 7
217  * when using SysConfig.
218  *
219  * <hr>
220  * @anchor ti_log_LogSinkUART_Usage
221  * # Usage
222  *
223  * To use the UART LogSink the application calls the following APIs:
224  * - LogSinkUART_init(): Initialize a UART sink. This function takes as
225  * argument an index that describes the sink that has to be initialized.
226  * - LogSinkUART_flush(): Function to flush all the LogSinkUART sinks in the
227  * order they are added. For each sink it will read data from the ring
228  * buffer and put as much as possible on the UART interface.
229  * - LogSinkUART_finalize(): Finalize a UART sink. This function takes as
230  * argument an index that describes the sink that has to be finalized.
231  *
232  * Details on usage are provided in the following subsections.
233  *
234  * @anchor ti_log_LogSinkUART_Examples
235  * ## Examples #
236  * * @ref ti_log_LogSinkUART_initialize "Initializing a UART LogSink"
237  * * @ref ti_log_LogSinkUART_flush "Flushing UART sinks"
238  * * @ref ti_log_LogSinkUART_finalize "Finalizing a UART LogSink"
239  * * @ref ti_log_LogSinkUART_protect "Protect log statements"
240  *
241  * @anchor ti_log_LogSinkUART_initialize
242  * ### Initializing a UART LogSink
243  * If LogSinkUART is enabled through SysConfig, then LogSinkUART_init() will be
244  * automatically called from Board_init(). If SysConfig is not used, the user
245  * must initialize the log sink. LogSinkUART_init() can also be called after a
246  * sink has been finalized with LogSinkUART_finalize(). To initialize a sink,
247  * first include the ti_log_config.h library containing the expansion of the
248  * sink name. Afterwards, call the initialize function passing as an argument
249  * the name of the sink to be initialized.
250  *
251  * @code
252  * #include "ti_log_config.h"
253  *
254  * LogSinkUART_init(sink_name);
255  * @endcode
256  *
257  * @anchor ti_log_LogSinkUART_flush
258  * ### Flushing UART sinks
259  * LogSinkUART_flush() will be called automatically in the Idle-loop/task for
260  * FreeRTOS and TI-RTOS 7. Despite this, the user can still manually call the
261  * function to send out as much data as possible from all the existing ring
262  * buffers when desired. The flush function can be called from either a task or
263  * interrupt context.
264  *
265  * @anchor ti_log_LogSinkUART_finalize
266  * ### Finalizing a UART LogSink
267  * LogSinkUART_finalize() will cancel all ongoing UART writes and call
268  * UART2_close(). All log packets remaining in the intermediate storage will be
269  * lost when calling LogSinkUART_init() because the ring buffer will be reset.
270  * All log statements after a finalize call will also be lost due to the reset
271  * of the ring buffer when initializing the sink. To finalize a sink first
272  * include the ti_log_config.h library containing the expansion of the sink
273  * name. Afterwards, call the finalize function passing as an argument the
274  * name of the sink to be finalized.
275  *
276  * @code
277  * #include "ti_log_config.h"
278  *
279  * LogSinkUART_finalize(sink_name);
280  * @endcode
281  *
282  * @anchor ti_log_LogSinkUART_protect
283  * ### Protect log statements
284  * To ensure that logs are correctly ordered when put back together on the
285  * host, log statements can be called from a context where HWI are disabled.
286  * This also ensures that the recorded timestamp is faithful to when the log
287  * statement was executed.
288  *
289  * Logs can end up ordered out as a consequence of a log call being preempted
290  * by a higher priority task with other log statements. The following example
291  * shows how a log statement can be protected to ensure that the execution
292  * sequence is maintained in the ordering on the host-side.
293  *
294  * @code
295  * #include <ti/drivers/dpl/HwiP.h>
296  * uint32_t key;
297  *
298  * key = HwiP_disable();
299  * Log_printf(MyModule, Log_DEBUG, "The answer is %d", 42);
300  * HwiP_restore(key);
301  * @endcode
302  *
303  * <hr>
304  * @anchor ti_log_LogSinkUART_Configuration
305  * # Configuration
306  *
307  * In order to use the LogSinkUART APIs, the application is required to provide
308  * sink-specific configuration in the ti_log_config.c file. The LogSinkUART
309  * interface defines a configuration data structure:
310  *
311  * @code
312  * typedef struct {
313  * void *object;
314  * void const *hwAttrs;
315  * } LogSinkUART_Config;
316  * @endcode
317  *
318  * The application must declare an array of #LogSinkUART_Config elements, named
319  * @p LogSinkUART_config[]. Each element of @p LogSinkUART_config[] must be
320  * populated with pointers to a sink specific object, and hardware attributes.
321  * The hardware attributes define properties such as the UART peripheral's
322  * attributes, and a pointer to the intermediate ring buffer and its size.
323  * These are automatically assigned through SysConfig. Each element in
324  * @p LogSinkUART_config[] corresponds to a UART sink instance, and none of the
325  * elements should have NULL pointers.
326  *
327  *
328  * The configuration for the UART LogSink is based on the driver's
329  * configuration. Refer to the @ref driver_configuration
330  * "Driver's Configuration" section for driver configuration information.
331  *
332  * To automatically initialize a UART sink when initializing the board,
333  * LogSinkUART_init() is called inside Board_init() in ti_drivers_config.c. To
334  * have access to the function and get the expansion of the sink name, include
335  * the following libraries:
336  *
337  * @code
338  * #include "ti_log_config.h"
339  * #include <ti/log/LogSinkUART.h>
340  * @endcode
341  *
342  * ============================================================================
343  */
344 
345 #ifndef ti_log_LogSinkUART__include
346 #define ti_log_LogSinkUART__include
347 
348 #include <stdint.h>
349 #include <stddef.h>
350 #include <ti/log/Log.h>
351 #include <ti/drivers/UART2.h>
353 
354 #if defined(__cplusplus)
355 extern "C" {
356 #endif
357 
361 #define Log_TI_LOG_SINK_UART_VERSION 0.1.0
362 
366 typedef struct
367 {
368  unsigned char *bufPtr;
369  size_t bufSize;
370  uint32_t baudRate;
372  uint32_t uartIndex;
374 
380 typedef struct
381 {
385 
393 typedef struct
394 {
396  void *object;
398  void const *hwAttrs;
400 
405 
412 typedef struct
413 {
416  uint_least8_t index;
418 
423 
431 extern void LogSinkUART_flush(void);
432 
454 extern void LogSinkUART_init(uint_least8_t index);
455 
466 extern void LogSinkUART_finalize(uint_least8_t index);
467 
496 extern void LogSinkUART_printfSingleton(const Log_Module *handle,
497  uint32_t header,
498  uint32_t headerPtr,
499  uint32_t numArgs,
500  ...);
501 
502 extern void LogSinkUART_printfSingleton0(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
503 
504 extern void LogSinkUART_printfSingleton1(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
505 
506 extern void LogSinkUART_printfSingleton2(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
507 
508 extern void LogSinkUART_printfSingleton3(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
537 extern void LogSinkUART_printfDepInjection(const Log_Module *handle,
538  uint32_t header,
539  uint32_t headerPtr,
540  uint32_t numArgs,
541  ...);
542 
543 extern void LogSinkUART_printfDepInjection0(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
544 
545 extern void LogSinkUART_printfDepInjection1(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
546 
547 extern void LogSinkUART_printfDepInjection2(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
548 
549 extern void LogSinkUART_printfDepInjection3(const Log_Module *handle, uint32_t header, uint32_t headerPtr, ...);
577 extern void LogSinkUART_bufDepInjection(const Log_Module *handle,
578  uint32_t header,
579  uint32_t headerPtr,
580  uint8_t *data,
581  size_t size);
587 #define Log_SINK_UART_DEFINE(name) LogSinkUART_Instance LogSinkUART_##name##_Config = {.index = name}
588 
593 #define Log_SINK_UART_USE(name) extern LogSinkUART_Instance LogSinkUART_##name##_Config
594 
600 #define Log_MODULE_INIT_SINK_UART(name, _levels, printfDelegate, bufDelegate, _dynamicLevelsPtr) \
601  { \
602  .sinkConfig = &LogSinkUART_##name##_Config, .printf = printfDelegate, .printf0 = printfDelegate##0, \
603  .printf1 = printfDelegate##1, .printf2 = printfDelegate##2, .printf3 = printfDelegate##3, .buf = bufDelegate, \
604  .levels = _levels, .dynamicLevelsPtr = _dynamicLevelsPtr, \
605  }
606 
617 #if defined(__cplusplus)
618 }
619 #endif
620 
621 #endif /* ti_log_LogSinkUART__include */
UART2 Global configuration.
Definition: UART2.h:721
void LogSinkUART_init(uint_least8_t index)
Initialize a given LogSinkUART sink.
LogSinkUART_Instance * LogSinkUART_Handle
A handle for the LogSinkUART_Instance structure.
Definition: LogSinkUART.h:422
LogSinkUART Sink parameters.
Definition: LogSinkUART.h:412
void LogSinkUART_flush(void)
Flush all the LogSinkUART sinks.
void const * hwAttrs
Definition: LogSinkUART.h:398
UART2_Parity
UART2 parity type settings.
Definition: UART2.h:571
size_t bufSize
Definition: LogSinkUART.h:369
PRELIMINARY UART driver interface
unsigned char * bufPtr
Definition: LogSinkUART.h:368
RingBuf_Object ringObj
Definition: LogSinkUART.h:383
uint_least8_t index
Definition: LogSinkUART.h:416
LogSinkUART Hardware attributes.
Definition: LogSinkUART.h:366
const LogSinkUART_Config LogSinkUART_config[]
Array with the configuration of each sink.
uint32_t baudRate
Definition: LogSinkUART.h:370
void * object
Definition: LogSinkUART.h:396
_Log_DEFINE_LOG_VERSION(LogSinkBuf, 0.2.0)
LogSinkUART Object.
Definition: LogSinkUART.h:380
#define Log_TI_LOG_SINK_UART_VERSION
LogSinkUART version.
Definition: LogSinkUART.h:361
UART2_Handle uartHandle
Definition: LogSinkUART.h:382
Definition: RingBuf.h:44
UART2_Parity parity
Definition: LogSinkUART.h:371
LogSinkUART Global configuration.
Definition: LogSinkUART.h:393
uint32_t uartIndex
Definition: LogSinkUART.h:372
void LogSinkUART_finalize(uint_least8_t index)
Finalize a given LogSinkUART sink.
© Copyright 1995-2025, Texas Instruments Incorporated. All rights reserved.
Trademarks | Privacy policy | Terms of use | Terms of sale