LogSinkUART.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023 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 index,
53  * uint32_t numArgs, ...);
54  * - buf(const Log_Module *handle, uint32_t header, uint32_t index,
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 minimze the runtime
70  * intrusion that would be caused by synchronously outputing the log statements
71  * through the relatively slow UART. The first part generates and stores the
72  * log statements into an intermediate storage synchronously. The second part
73  * uses the idle task of the OS, executed when no other tasks or interrupts are
74  * running, to move the data stored in the intermediate storage off the device.
75  *
76  * By transmitting messages asynchronously the readout at the host-side is
77  * also asynchronous. As a consequence, the receiving of log statements at the
78  * host-side can be deferred from their execution in the program.
79  *
80  * This sink requires no special hardware to capture and decode the logs beyond
81  * a basic UART-to-USB bridge.
82  *
83  * The data flow at a high level is:
84  * 1. Log statement captured at the log site with timestamp
85  * 2. Log data marshalled into a log packet
86  * 3. Log packet moved to ring buffer working as intermediate storage
87  * 4. Intermediate storage flushed via the UART2 driver when nothing else
88  * is happening through a function installed in the Idle-loop/task
89  * 5. Data sent out on the UART line
90  * 6. Data received by listening COM port on host
91  * 7. Data decoded by host and fed through remainder of host logging
92  * infrastructure
93  * 8. Logs visualised in Wireshark or dumped to console / log file
94  *
95  * @note Throughout this documentation and API the term "packet" is used
96  * instead of the term "record". Both terms are equivalent and can be used
97  * interchangeably.
98  *
99  * <hr>
100  * @anchor ti_log_LogSinkUART_Considerations
101  * # Considerations
102  *
103  * When using this sink consider the following:
104  * - The number of sinks is limited by the number of UART peripherals in
105  * the device.
106  * - When a UART instance is consumed for a sink it can not be used for
107  * other operations.
108  * - Each UART LogSink will create its own Ring Buffer.
109  * - If any part of the transmission is lost/not received by the
110  * logging-tool, the rest of the data might not be interpreted correctly.
111  * - 9.54 hours of logging timestamps are available before overflowing in a
112  * system where 1 bit = 8 us. The tick period for each device can be read
113  * from the multiplier variable in TimestamP<device>.c under
114  * kernel/freertos/dpl. If the timestamp overflows it will go back to 0.
115  * - The LogSinkUART sinks are flushed in the order they are instantiated. 
116  * - If performance issues are observed either increase the size of the
117  * UART2 ring buffer to increase throughput or increase the size of the
118  * intermediate ring buffer size for more log statements. Also note that
119  * the device needs to have idle-time to automatically flush data. If the
120  * software is always doing something then nothing will ever be output.
121  * - SRAM requirements scale with the number of log records to store in
122  * between flushing the buffer.
123  * - A Log_printf call has an execution time of 20 us to 27.8 us depending
124  * on the number of arguments.
125  * - A Log_buf call has a minimum execution time of 32.8 us and an
126  * approximate increase of 0.183 us per byte in the buffer.
127  *
128  * <hr>
129  * @anchor ti_log_LogSinkUART_DesignArchitecture
130  * # Design Architecture
131  *
132  * The LogSinkUART implementation is based on the following architecture.
133  *
134  * ## Packet Transmission Format
135  * All log packets begin with a 32-bit metadata pointer followed by a 32-bit
136  * timestamp. The next fields depend on the type of log statement:
137  * - Log_printf: Variable number of 32-bit arguments that range from 0 to
138  * 8.
139  * - Log_buf: 32-bit field with the size of the buffer being sent followed
140  * by the buffer data.
141  *
142  * @startuml
143  * skinparam useBetaStyle true
144  *
145  * <style>
146  * timingDiagram {
147  * LineColor #065959
148  * }
149  * </style>
150  *
151  * concise "Log_printf packet" as LP
152  * concise "Log_buf packet" as LB
153  * hide time-axis
154  *
155  * scale 1 as 50 pixels
156  *
157  * @LP
158  * 0 is Metadata_Pointer #FFCC99: 0:31
159  * +4 is Timestamp #CC99FF: 32:63
160  * +4 is VA_Arg_0 #99CCFF: 64:95
161  * +4 is {-} #99CCFF: ...
162  * +1 is VA_Arg_n #99CCFF: 32-bits
163  * +4 is {-}
164  *
165  * @LB
166  * 0 is Metadata_Pointer #FFCC99: 0:31
167  * +4 is Timestamp #CC99FF: 32:63
168  * +4 is Buffer_Size #97D077: 64:95
169  * +4 is Buffer_Data #99CCFF
170  * +5 is {-}
171  * @enduml
172  *
173  * If a packet would overflow the ring buffer, a 32-bit overflow packet is
174  * placed instead. It is the original metadata pointer modified to be
175  * identified as an overflow packet. The host-side tool decodes it and displays
176  * an overflow message, indicating that at least that message would have
177  * overflowed. When this is observed, it is recommended to either resize the
178  * ring buffer or disable some log statements.
179  * @note If the intermediate ring buffer is full no new oveflow or log packets
180  * will be stored.
181  *
182  * Each log statement used will occupy the following amount of SRAM:
183  *
184  * Log statement type | Log statement size (bytes) |
185  * ------------------ | --------------------------- |
186  * Log_printf | 8 + 4 * number_of_arguments |
187  * Log_buf | 12 + buffer_size |
188  * Overflow | 4 |
189  *
190  * ## Packet Framing
191  * The host-side must receive and properly handle a continuous stream of
192  * in-phase, uncorrupted packets. Starting decoding in the middle of a packet
193  * would lead to incorrect synchronisation and decoding. This is solved by
194  * sending an initial 64-bit reset-frame. The reset-frame consists of a
195  * pre-determined 32-bit sequence followed by 32 bits indicating how the
196  * timestamp is formatted. After receiving the reset-frame, the host-side tool
197  * knows that the next 32 bits will be a metadata-pointer followed by a
198  * timestamp. The number of arguments for each frame is extracted from the
199  * .out file. This determines the length of the current packet and when the
200  * metadata pointer from the next packet is expected.
201  *
202  * @startuml
203  * skinparam useBetaStyle true
204  *
205  * <style>
206  * timingDiagram {
207  * LineColor #065959
208  * }
209  * </style>
210  *
211  * concise "Reset-frame packet" as RP
212  * hide time-axis
213  *
214  * scale 1 as 50 pixels
215  *
216  * @RP
217  * 0 is 0xBBBBBBBB: 0:31
218  * +4 is Timestamp_format: 32:63
219  * +4 is {-}
220  * @enduml
221  *
222  * ## Flushing the data
223  * A hook function installed in the Idle-loop/task is run when no other tasks
224  * or interrupts are running. It flushes as many log packets as possible from
225  * the intermediate storage via the UART2 driver set in nonblocking mode. In
226  * this mode, UART2_write() will copy as much data into the transmit buffer as
227  * space allows and return immediately. The maximum space allowed, and
228  * therefore the amount of data sent out every time that the hook function is
229  * called, is determined by the size of the TX Ring Buffer.
230  *
231  * The Idle-loop/task will always be run before the power management loop.
232  *
233  * Since each OS has a different implementation of the Idle-loop/task, the
234  * installation of the hook function will also be different for each OS. The
235  * automatic installation is currently supported for FreeRTOS and TI-RTOS 7
236  * when using SysConfig.
237  *
238  * <hr>
239  * @anchor ti_log_LogSinkUART_Usage
240  * # Usage
241  *
242  * To use the UART LogSink the application calls the following APIs:
243  * - LogSinkUART_init(): Initialize a UART sink. This function takes as
244  * argument an index that describes the sink that has to be initialized.
245  * - LogSinkUART_flush(): Function to flush all the LogSinkUART sinks in the
246  * order they are added. For each sink it will read data from the ring
247  * buffer and put as much as possible on the UART interface.
248  * - LogSinkUART_finalize(): Finalize a UART sink. This function takes as
249  * argument an index that describes the sink that has to be finalized.
250  *
251  * Details on usage are provided in the following subsections.
252  *
253  * @anchor ti_log_LogSinkUART_Examples
254  * ## Examples #
255  * * @ref ti_log_LogSinkUART_initialize "Initializing a UART LogSink"
256  * * @ref ti_log_LogSinkUART_flush "Flushing UART sinks"
257  * * @ref ti_log_LogSinkUART_finalize "Finalizing a UART LogSink"
258  * * @ref ti_log_LogSinkUART_protect "Protect log statements"
259  *
260  * @anchor ti_log_LogSinkUART_initialize
261  * ### Initializing a UART LogSink
262  * If LogSinkUART is enabled through SysConfig, then LogSinkUART_init() will be
263  * automatically called from Board_init(). If SysConfig is not used, the user
264  * must initialize the log sink. LogSinkUART_init() can also be called after a
265  * sink has been finalized with LogSinkUART_finalize(). To initialize a sink,
266  * first include the ti_log_config.h library containing the expansion of the
267  * sink name. Afterwards, call the initialize function passing as an argument
268  * the name of the sink to be initialized.
269  *
270  * @code
271  * #include "ti_log_config.h"
272  *
273  * LogSinkUART_init(sink_name);
274  * @endcode
275  *
276  * @anchor ti_log_LogSinkUART_flush
277  * ### Flushing UART sinks
278  * LogSinkUART_flush() will be called automatically in the Idle-loop/task for
279  * FreeRTOS and TI-RTOS 7. Despite this, the user can still manually call the
280  * function to send out as much data as possible from all the existing ring
281  * buffers when desired. The flush function can be called from either a task or
282  * interrupt context.
283  *
284  * @anchor ti_log_LogSinkUART_finalize
285  * ### Finalizing a UART LogSink
286  * LogSinkUART_finalize() will cancel all ongoing UART writes and call
287  * UART2_close(). All log packets remaining in the intermediate storage will be
288  * lost when calling LogSinkUART_init() because the ring buffer will be reset.
289  * All log statements after a finalize call will also be lost due to the reset
290  * of the ring buffer when initializing the sink. To finalize a sink first
291  * include the ti_log_config.h library containing the expansion of the sink
292  * name. Afterwards, call the finalize function passing as an argument the
293  * name of the sink to be finalized.
294  *
295  * @code
296  * #include "ti_log_config.h"
297  *
298  * LogSinkUART_finalize(sink_name);
299  * @endcode
300  *
301  * @anchor ti_log_LogSinkUART_protect
302  * ### Protect log statements
303  * To ensure that logs are correctly ordered when put back together on the
304  * host, log statements can be called from a context where HWI are disabled.
305  * This also ensures that the recorded timestamp is faithful to when the log
306  * statement was executed.
307  *
308  * Logs can end up ordered out as a consequence of a log call being preempted
309  * by a higher priority task with other log statements. The following example
310  * shows how a log statement can be protected to ensure that the execution
311  * sequence is mantained in the ordering on the host-side.
312  *
313  * @code
314  * #include <ti/drivers/dpl/HwiP.h>
315  * uint32_t key;
316  *
317  * key = HwiP_disable();
318  * Log_printf(MyModule, Log_DEBUG, "The answer is %d", 42);
319  * HwiP_restore(key);
320  * @endcode
321  *
322  * <hr>
323  * @anchor ti_log_LogSinkUART_Configuration
324  * # Configuration
325  *
326  * In order to use the LogSinkUART APIs, the application is required to provide
327  * sink-specific configuration in the ti_log_config.c file. The LogSinkUART
328  * interface defines a configuration data structure:
329  *
330  * @code
331  * typedef struct {
332  * void *object;
333  * void const *hwAttrs;
334  * } LogSinkUART_Config;
335  * @endcode
336  *
337  * The application must declare an array of #LogSinkUART_Config elements, named
338  * @p LogSinkUART_config[]. Each element of @p LogSinkUART_config[] must be
339  * populated with pointers to a sink specific object, and hardware attributes.
340  * The hardware attributes define properties such as the UART peripheral's
341  * attributes, and a pointer to the intermediate ring buffer and its size.
342  * These are automatically assigned through SysConfig. Each element in
343  * @p LogSinkUART_config[] corresponds to a UART sink instance, and none of the
344  * elements should have NULL pointers.
345  *
346  *
347  * The configuration for the UART LogSink is based on the driver's
348  * configuration. Refer to the @ref driver_configuration
349  * "Driver's Configuration" section for driver configuration information.
350  *
351  * To automatically initialize a UART sink when initializing the board,
352  * LogSinkUART_init() is called inside Board_init() in ti_drivers_config.c. To
353  * have acces to the function and get the expansion of the sink name, include
354  * the following libraries:
355  *
356  * @code
357  * #include "ti_log_config.h"
358  * #include <ti/log/LogSinkUART.h>
359  * @endcode
360  *
361  * ============================================================================
362  */
363 
364 #ifndef ti_log_LogSinkUART__include
365 #define ti_log_LogSinkUART__include
366 
367 #include <stdint.h>
368 #include <stddef.h>
369 #include <ti/log/Log.h>
370 #include <ti/drivers/UART2.h>
372 
373 #if defined(__cplusplus)
374 extern "C" {
375 #endif
376 
380 #define Log_TI_LOG_SINK_UART_VERSION 0.1.0
381 
385 typedef struct
386 {
387  unsigned char *bufPtr;
388  size_t bufSize;
389  uint32_t baudRate;
391  uint32_t uartIndex;
393 
399 typedef struct
400 {
404 
412 typedef struct
413 {
415  void *object;
417  void const *hwAttrs;
419 
424 
431 typedef struct
432 {
435  uint_least8_t index;
437 
442 
450 extern void LogSinkUART_flush(void);
451 
474 extern void LogSinkUART_init(uint_least8_t index);
475 
486 extern void LogSinkUART_finalize(uint_least8_t index);
487 
509 extern void ti_log_LogSinkUART_printf(const Log_Module *handle, uint32_t header, uint32_t index, uint32_t numArgs, ...);
533 extern void ti_log_LogSinkUART_buf(const Log_Module *handle,
534  uint32_t header,
535  uint32_t index,
536  uint8_t *data,
537  size_t size);
543 #define Log_SINK_UART_DEFINE(name) LogSinkUART_Instance LogSinkUART_##name##_Config = {.index = name}
544 
549 #define Log_SINK_UART_USE(name) extern LogSinkUART_Instance LogSinkUART_##name##_Config
550 
554 #define Log_MODULE_INIT_SINK_UART(name, _levels) \
555  { \
556  .sinkConfig = &LogSinkUART_##name##_Config, .printf = ti_log_LogSinkUART_printf, \
557  .buf = ti_log_LogSinkUART_buf, .levels = _levels, \
558  }
559 
570 #if defined(__cplusplus)
571 }
572 #endif
573 
574 #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:441
LogSinkUART Sink parameters.
Definition: LogSinkUART.h:431
_Log_DEFINE_LOG_VERSION(LogSinkBuf, 0.1.0)
void LogSinkUART_flush(void)
Flush all the LogSinkUART sinks.
void const * hwAttrs
Definition: LogSinkUART.h:417
UART2_Parity
UART2 parity type settings.
Definition: UART2.h:571
size_t bufSize
Definition: LogSinkUART.h:388
PRELIMINARY UART driver interface
unsigned char * bufPtr
Definition: LogSinkUART.h:387
RingBuf_Object ringObj
Definition: LogSinkUART.h:402
uint_least8_t index
Definition: LogSinkUART.h:435
LogSinkUART Hardware attributes.
Definition: LogSinkUART.h:385
const LogSinkUART_Config LogSinkUART_config[]
Array with the configuration of each sink.
uint32_t baudRate
Definition: LogSinkUART.h:389
void * object
Definition: LogSinkUART.h:415
LogSinkUART Object.
Definition: LogSinkUART.h:399
#define Log_TI_LOG_SINK_UART_VERSION
LogSinkUART version.
Definition: LogSinkUART.h:380
UART2_Handle uartHandle
Definition: LogSinkUART.h:401
Definition: RingBuf.h:44
UART2_Parity parity
Definition: LogSinkUART.h:390
LogSinkUART Global configuration.
Definition: LogSinkUART.h:412
uint32_t uartIndex
Definition: LogSinkUART.h:391
void LogSinkUART_finalize(uint_least8_t index)
Finalize a given LogSinkUART sink.
© Copyright 1995-2024, Texas Instruments Incorporated. All rights reserved.
Trademarks | Privacy policy | Terms of use | Terms of sale