1 /*******************************************************************************
\r
2 * Trace Recorder Library for Tracealyzer v3.1.2
\r
3 * Percepio AB, www.percepio.com
\r
5 * trcSnapshotRecorder.c
\r
7 * The generic core of the trace recorder's snapshot mode.
\r
10 * This file is part of the trace recorder library (RECORDER), which is the
\r
11 * intellectual property of Percepio AB (PERCEPIO) and provided under a
\r
12 * license as follows.
\r
13 * The RECORDER may be used free of charge for the purpose of recording data
\r
14 * intended for analysis in PERCEPIO products. It may not be used or modified
\r
15 * for other purposes without explicit permission from PERCEPIO.
\r
16 * You may distribute the RECORDER in its original source code form, assuming
\r
17 * this text (terms of use, disclaimer, copyright notice) is unchanged. You are
\r
18 * allowed to distribute the RECORDER with minor modifications intended for
\r
19 * configuration or porting of the RECORDER, e.g., to allow using it on a
\r
20 * specific processor, processor family or with a specific communication
\r
21 * interface. Any such modifications should be documented directly below
\r
22 * this comment block.
\r
25 * The RECORDER is being delivered to you AS IS and PERCEPIO makes no warranty
\r
26 * as to its use or performance. PERCEPIO does not and cannot warrant the
\r
27 * performance or results you may obtain by using the RECORDER or documentation.
\r
28 * PERCEPIO make no warranties, express or implied, as to noninfringement of
\r
29 * third party rights, merchantability, or fitness for any particular purpose.
\r
30 * In no event will PERCEPIO, its technology partners, or distributors be liable
\r
31 * to you for any consequential, incidental or special damages, including any
\r
32 * lost profits or lost savings, even if a representative of PERCEPIO has been
\r
33 * advised of the possibility of such damages, or for any claim by any third
\r
34 * party. Some jurisdictions do not allow the exclusion or limitation of
\r
35 * incidental, consequential or special damages, or the exclusion of implied
\r
36 * warranties or limitations on how long an implied warranty may last, so the
\r
37 * above limitations may not apply to you.
\r
39 * Tabs are used for indent in this file (1 tab = 4 spaces)
\r
41 * Copyright Percepio AB, 2017.
\r
43 ******************************************************************************/
\r
45 #include "trcRecorder.h"
\r
47 #if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)
\r
49 #if (TRC_USE_TRACEALYZER_RECORDER == 1)
\r
55 #if ((TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR) || (TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR))
\r
56 #error "CUSTOM timestamping mode is not (yet) supported in snapshot mode!"
\r
60 #define TRACE_MINOR_VERSION 5
\r
61 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
62 static traceHandle isrstack[TRC_CFG_MAX_ISR_NESTING];
\r
63 int32_t isPendingContextSwitch = 0;
\r
64 #endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1) */
\r
66 #if !defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1
\r
67 static int readyEventsEnabled = 1;
\r
68 #endif /*!defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1*/
\r
69 /*******************************************************************************
\r
72 * This variable is should be updated by the Kernel tick interrupt. This does
\r
73 * not need to be modified when developing a new timer port. It is preferred to
\r
74 * keep any timer port changes in the HWTC macro definitions, which typically
\r
75 * give sufficient flexibility.
\r
76 ******************************************************************************/
\r
77 uint32_t uiTraceTickCount = 0;
\r
79 uint32_t trace_disable_timestamp = 0;
\r
80 static uint32_t last_timestamp = 0;
\r
81 /* Flag that shows if inside a critical section of the recorder */
\r
82 volatile int recorder_busy = 0;
\r
84 /* Holds the value set by vTraceSetFrequency */
\r
85 uint32_t timestampFrequency = 0;
\r
87 /* The last error message of the recorder. NULL if no error message. */
\r
88 const char* traceErrorMessage = NULL;
\r
91 int8_t nISRactive = 0;
\r
92 traceHandle handle_of_last_logged_task = 0;
\r
93 uint8_t inExcludedTask = 0;
\r
95 extern uint8_t inExcludedTask;
\r
96 extern int8_t nISRactive;
\r
97 extern traceHandle handle_of_last_logged_task;
\r
99 /* Called when the recorder is stopped, set by vTraceSetStopHook. */
\r
100 TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0;
\r
102 /*************** Private Functions *******************************************/
\r
103 static void prvStrncpy(char* dst, const char* src, uint32_t maxLength);
\r
104 static uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id);
\r
105 static void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength);
\r
106 static void* prvTraceNextFreeEventBufferSlot(void);
\r
107 static uint16_t prvTraceGetDTS(uint16_t param_maxDTS);
\r
108 static traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel);
\r
109 static void prvTraceUpdateCounters(void);
\r
111 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
112 static void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nEntries);
\r
115 static traceString prvTraceCreateSymbolTableEntry(const char* name,
\r
118 traceString channel);
\r
120 static traceString prvTraceLookupSymbolTableEntry(const char* name,
\r
123 traceString channel);
\r
126 #if (TRC_CFG_INCLUDE_ISR_TRACING == 0)
\r
127 /* ISR tracing is turned off */
\r
128 void prvTraceIncreaseISRActive(void);
\r
129 void prvTraceDecreaseISRActive(void);
\r
130 #endif /*(TRC_CFG_INCLUDE_ISR_TRACING == 0)*/
\r
132 #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
\r
133 static uint8_t prvTraceGet8BitHandle(traceHandle handle);
\r
135 #define prvTraceGet8BitHandle(x) ((uint8_t)x)
\r
139 #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1) && (TRC_CFG_SCHEDULING_ONLY == 0)
\r
140 /* Current heap usage. Always updated. */
\r
141 static uint32_t heapMemUsage = 0;
\r
144 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
145 static uint32_t prvTraceGetParam(uint32_t, uint32_t);
\r
148 /*******************************************************************************
\r
149 * prvTraceInitTraceData
\r
151 * Allocates and initializes the recorder data structure, based on the constants
\r
152 * in trcConfig.h. This allows for allocating the data on the heap, instead of
\r
153 * using a static declaration.
\r
154 ******************************************************************************/
\r
155 static void prvTraceInitTraceData(void);
\r
157 /*******************************************************************************
\r
158 * prvTracePortGetTimeStamp
\r
160 * Returns the current time based on the HWTC macros which provide a hardware
\r
161 * isolation layer towards the hardware timer/counter.
\r
163 * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
\r
164 * or the trace recorder library. Typically you should not need to change
\r
165 * the code of prvTracePortGetTimeStamp if using the HWTC macros.
\r
167 ******************************************************************************/
\r
168 void prvTracePortGetTimeStamp(uint32_t *puiTimestamp);
\r
170 static void prvTraceTaskInstanceFinish(int8_t direct);
\r
172 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
173 static void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl);
\r
175 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
176 static void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl);
\r
177 static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl);
\r
178 static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots);
\r
179 #endif /*(TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)*/
\r
180 #endif /* ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) */
\r
182 /********* Public Functions **************************************************/
\r
184 uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass);
\r
186 /*******************************************************************************
\r
189 * Called by various parts in the recorder. Stops the recorder and stores a
\r
190 * pointer to an error message, which is printed by the monitor task.
\r
191 ******************************************************************************/
\r
192 void prvTraceError(const char* msg);
\r
194 /******************************************************************************
\r
195 * vTraceEnable(int startOption) - snapshot mode
\r
197 * Initializes and optionally starts the trace, depending on the start option.
\r
198 * To use the trace recorder, the startup must call vTraceEnable before any RTOS
\r
199 * calls are made (including "create" calls). Three start options are provided:
\r
201 * TRC_START: Starts the tracing directly. In snapshot mode this allows for
\r
202 * starting the trace at any point in your code, assuming vTraceEnable(TRC_INIT)
\r
203 * has been called in the startup.
\r
204 * Can also be used for streaming without Tracealyzer control, e.g. to a local
\r
205 * flash file system (assuming such a "stream port", see trcStreamingPort.h).
\r
207 * TRC_INIT: Initializes the trace recorder, but does not start the tracing.
\r
208 * In snapshot mode, this must be followed by a vTraceEnable(TRC_START) sometime
\r
211 * Usage examples, in snapshot mode:
\r
213 * Snapshot trace, from startup:
\r
215 * vTraceEnable(TRC_START);
\r
218 * Snapshot trace, from a later point:
\r
220 * vTraceEnable(TRC_INIT);
\r
223 * vTraceEnable(TRC_START); // e.g., in task context, at some relevant event
\r
226 * Note: See other implementation of vTraceEnable in trcStreamingRecorder.c
\r
227 ******************************************************************************/
\r
228 void vTraceEnable(int startOption)
\r
230 prvTraceInitTraceData();
\r
232 if (startOption == TRC_START)
\r
236 else if (startOption == TRC_START_AWAIT_HOST)
\r
238 prvTraceError("vTraceEnable(TRC_START_AWAIT_HOST) not allowed in Snapshot mode");
\r
240 else if (startOption != TRC_INIT)
\r
242 prvTraceError("Unexpected argument to vTraceEnable (snapshot mode)");
\r
246 /*******************************************************************************
\r
247 * vTraceSetRecorderDataBuffer
\r
249 * If custom allocation is used, this function must be called so the recorder
\r
250 * library knows where to save the trace data.
\r
251 ******************************************************************************/
\r
252 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
\r
253 void vTraceSetRecorderDataBuffer(void* pRecorderData)
\r
255 TRACE_ASSERT(pRecorderData != NULL, "vTraceSetRecorderDataBuffer, pRecorderData == NULL", TRC_UNUSED);
\r
256 RecorderDataPtr = pRecorderData;
\r
260 /*******************************************************************************
\r
261 * vTraceSetStopHook
\r
263 * Sets a function to be called when the recorder is stopped. This can be used
\r
264 * to save the trace to a file system, if available.
\r
265 ******************************************************************************/
\r
266 void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction)
\r
268 vTraceStopHookPtr = stopHookFunction;
\r
271 /*******************************************************************************
\r
274 * Resets the recorder. Only necessary if a restart is desired - this is not
\r
275 * needed in the startup initialization.
\r
276 ******************************************************************************/
\r
277 void vTraceClear(void)
\r
279 TRACE_ALLOC_CRITICAL_SECTION();
\r
280 trcCRITICAL_SECTION_BEGIN();
\r
281 RecorderDataPtr->absTimeLastEventSecond = 0;
\r
282 RecorderDataPtr->absTimeLastEvent = 0;
\r
283 RecorderDataPtr->nextFreeIndex = 0;
\r
284 RecorderDataPtr->numEvents = 0;
\r
285 RecorderDataPtr->bufferIsFull = 0;
\r
286 traceErrorMessage = NULL;
\r
287 RecorderDataPtr->internalErrorOccured = 0;
\r
288 (void)memset(RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4);
\r
289 handle_of_last_logged_task = 0;
\r
290 trcCRITICAL_SECTION_END();
\r
293 /*******************************************************************************
\r
296 * Starts the recorder. The recorder will not be started if an error has been
\r
297 * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
\r
298 * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
\r
300 * Returns 1 if the recorder was started successfully.
\r
301 * Returns 0 if the recorder start was prevented due to a previous internal
\r
302 * error. In that case, check xTraceGetLastError to get the error message.
\r
303 * Any error message is also presented when opening a trace file.
\r
305 * This function is obsolete, but has been saved for backwards compatibility.
\r
306 * We recommend using vTraceEnable instead.
\r
307 ******************************************************************************/
\r
308 uint32_t uiTraceStart(void)
\r
310 traceHandle handle;
\r
311 TRACE_ALLOC_CRITICAL_SECTION();
\r
315 if (RecorderDataPtr == NULL)
\r
317 prvTraceError("RecorderDataPtr is NULL. Call vTraceInitTraceData() before starting trace.");
\r
321 if (RecorderDataPtr->recorderActive == 1)
\r
322 return 1; /* Already running */
\r
324 if (traceErrorMessage == NULL)
\r
326 trcCRITICAL_SECTION_BEGIN();
\r
327 RecorderDataPtr->recorderActive = 1;
\r
329 handle = TRACE_GET_TASK_NUMBER(TRACE_GET_CURRENT_TASK());
\r
332 /* This occurs if the scheduler is not yet started.
\r
333 This creates a dummy "(startup)" task entry internally in the
\r
335 handle = prvTraceGetObjectHandle(TRACE_CLASS_TASK);
\r
336 prvTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)");
\r
338 prvTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0);
\r
341 prvTraceStoreTaskswitch(handle); /* Register the currently running task */
\r
342 trcCRITICAL_SECTION_END();
\r
345 return RecorderDataPtr->recorderActive;
\r
348 /*******************************************************************************
\r
351 * Starts the recorder. The recorder will not be started if an error has been
\r
352 * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
\r
353 * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
\r
355 * This function is obsolete, but has been saved for backwards compatibility.
\r
356 * We recommend using vTraceEnable instead.
\r
357 ******************************************************************************/
\r
358 void vTraceStart(void)
\r
360 (void)uiTraceStart();
\r
363 /*******************************************************************************
\r
366 * Stops the recorder. The recording can be resumed by calling vTraceStart.
\r
367 * This does not reset the recorder. Use vTraceClear if that is desired.
\r
368 ******************************************************************************/
\r
369 void vTraceStop(void)
\r
371 RecorderDataPtr->recorderActive = 0;
\r
373 if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0)
\r
375 (*vTraceStopHookPtr)(); /* An application call-back function. */
\r
379 /*******************************************************************************
\r
380 * xTraceGetLastError
\r
382 * Gives the last error message, if any. NULL if no error message is stored.
\r
383 * Any error message is also presented when opening a trace file.
\r
384 ******************************************************************************/
\r
385 const char* xTraceGetLastError(void)
\r
387 return traceErrorMessage;
\r
390 /*******************************************************************************
\r
393 * Removes any previous error message generated by recorder calling prvTraceError.
\r
394 * By calling this function, it may be possible to start/restart the trace
\r
395 * despite errors in the recorder, but there is no guarantee that the trace
\r
396 * recorder will work correctly in that case, depending on the type of error.
\r
397 ******************************************************************************/
\r
398 void vTraceClearError(void)
\r
400 traceErrorMessage = NULL;
\r
401 if (RecorderDataPtr != NULL)
\r
403 RecorderDataPtr->internalErrorOccured = 0;
\r
407 /*******************************************************************************
\r
408 * xTraceGetTraceBuffer
\r
410 * Returns a pointer to the recorder data structure. Use this together with
\r
411 * uiTraceGetTraceBufferSize if you wish to implement an own store/upload
\r
412 * solution, e.g., in case a debugger connection is not available for uploading
\r
414 ******************************************************************************/
\r
415 void* xTraceGetTraceBuffer(void)
\r
417 return RecorderDataPtr;
\r
420 /*******************************************************************************
\r
421 * uiTraceGetTraceBufferSize
\r
423 * Gets the size of the recorder data structure. For use together with
\r
424 * vTraceGetTraceBuffer if you wish to implement an own store/upload solution,
\r
425 * e.g., in case a debugger connection is not available for uploading the data.
\r
426 ******************************************************************************/
\r
427 uint32_t uiTraceGetTraceBufferSize(void)
\r
429 return sizeof(RecorderDataType);
\r
432 /******************************************************************************
\r
433 * prvTraceTaskInstanceFinish
\r
435 * Private common function for the vTraceTaskInstanceFinishXXX functions.
\r
437 *****************************************************************************/
\r
438 static void prvTraceTaskInstanceFinish(int8_t direct)
\r
440 TaskInstanceStatusEvent* tis;
\r
443 TRACE_ALLOC_CRITICAL_SECTION();
\r
445 trcCRITICAL_SECTION_BEGIN();
\r
446 if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)
\r
448 dts45 = (uint8_t)prvTraceGetDTS(0xFF);
\r
449 tis = (TaskInstanceStatusEvent*) prvTraceNextFreeEventBufferSlot();
\r
453 tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE;
\r
455 tis->type = TASK_INSTANCE_FINISHED_DIRECT;
\r
458 prvTraceUpdateCounters();
\r
461 trcCRITICAL_SECTION_END();
\r
464 /******************************************************************************
\r
465 * vTraceInstanceFinishedNext(void)
\r
467 * Marks the current task instance as finished on the next kernel call.
\r
469 * If that kernel call is blocking, the instance ends after the blocking event
\r
470 * and the corresponding return event is then the start of the next instance.
\r
471 * If the kernel call is not blocking, the viewer instead splits the current
\r
472 * fragment right before the kernel call, which makes this call the first event
\r
473 * of the next instance.
\r
475 * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
\r
481 * xQueueReceive(CommandQueue, &command, timeoutDuration);
\r
482 * processCommand(command);
\r
483 * vTraceInstanceFinishedNext();
\r
486 * Note: This is only supported in Tracealyzer tools v2.7 or later
\r
488 *****************************************************************************/
\r
489 void vTraceInstanceFinishedNext(void)
\r
491 prvTraceTaskInstanceFinish(0);
\r
494 /******************************************************************************
\r
495 * vTraceInstanceFinishedNow(void)
\r
497 * Marks the current task instance as finished at this very instant.
\r
498 * This makes the viewer to splits the current fragment at this point and begin
\r
499 * a new actor instance.
\r
501 * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
\r
505 * This example will generate two instances for each loop iteration.
\r
506 * The first instance ends at vTraceInstanceFinishedNow(), while the second
\r
507 * instance ends at the next xQueueReceive call.
\r
511 * xQueueReceive(CommandQueue, &command, timeoutDuration);
\r
512 * ProcessCommand(command);
\r
513 * vTraceInstanceFinishedNow();
\r
514 * DoSometingElse();
\r
515 * vTraceInstanceFinishedNext();
\r
518 * Note: This is only supported in Tracealyzer tools v2.7 or later
\r
520 *****************************************************************************/
\r
521 void vTraceInstanceFinishedNow(void)
\r
523 prvTraceTaskInstanceFinish(1);
\r
526 /*******************************************************************************
\r
527 * Interrupt recording functions
\r
528 ******************************************************************************/
\r
530 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
532 /*******************************************************************************
\r
533 * xTraceSetISRProperties
\r
535 * Stores a name and priority level for an Interrupt Service Routine, to allow
\r
536 * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin.
\r
539 * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
541 * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
\r
543 * void ISR_handler()
\r
545 * vTraceStoreISRBegin(Timer1Handle);
\r
547 * vTraceStoreISREnd(0);
\r
550 ******************************************************************************/
\r
551 traceHandle xTraceSetISRProperties(const char* name, uint8_t priority)
\r
553 static traceHandle handle = 0;
\r
555 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "xTraceSetISRProperties: Invalid value for handle", 0);
\r
556 TRACE_ASSERT(name != NULL, "xTraceSetISRProperties: name == NULL", 0);
\r
558 prvTraceSetObjectName(TRACE_CLASS_ISR, handle, name);
\r
559 prvTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);
\r
564 /*******************************************************************************
\r
565 * vTraceStoreISRBegin
\r
567 * Registers the beginning of an Interrupt Service Routine, using a traceHandle
\r
568 * provided by xTraceSetISRProperties.
\r
571 * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
573 * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
\r
575 * void ISR_handler()
\r
577 * vTraceStoreISRBegin(Timer1Handle);
\r
579 * vTraceStoreISREnd(0);
\r
582 ******************************************************************************/
\r
583 void vTraceStoreISRBegin(traceHandle handle)
\r
585 TRACE_ALLOC_CRITICAL_SECTION();
\r
589 /*************************************************************************
\r
590 * This occurs if an ISR calls a trace function, preempting a previous
\r
591 * trace call that is being processed in a different ISR or task.
\r
592 * If this occurs, there is probably a problem in the definition of the
\r
593 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
594 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
595 * and any other ISRs that calls the trace recorder directly or via
\r
596 * traced kernel functions. The ARM port disables all interrupts using the
\r
597 * PRIMASK register to avoid this issue.
\r
598 *************************************************************************/
\r
599 prvTraceError("vTraceStoreISRBegin - recorder busy! See code comment.");
\r
602 trcCRITICAL_SECTION_BEGIN();
\r
604 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
608 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid value for handle", TRC_UNUSED);
\r
610 dts4 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
612 if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
\r
614 if (nISRactive < TRC_CFG_MAX_ISR_NESTING)
\r
617 uint8_t hnd8 = prvTraceGet8BitHandle(handle);
\r
618 isrstack[nISRactive] = handle;
\r
620 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
623 ts->type = TS_ISR_BEGIN;
\r
625 ts->objHandle = hnd8;
\r
626 prvTraceUpdateCounters();
\r
631 /* This should not occur unless something is very wrong */
\r
632 prvTraceError("Too many nested interrupts!");
\r
636 trcCRITICAL_SECTION_END();
\r
639 /*******************************************************************************
\r
640 * vTraceStoreISREnd
\r
642 * Registers the end of an Interrupt Service Routine.
\r
644 * The parameter pendingISR indicates if the interrupt has requested a
\r
645 * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the
\r
646 * interrupt is assumed to return to the previous context.
\r
649 * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
650 * traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
\r
652 * traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
\r
654 * void ISR_handler()
\r
656 * vTraceStoreISRBegin(traceHandleIsrTimer1);
\r
658 * vTraceStoreISREnd(0);
\r
661 ******************************************************************************/
\r
662 void vTraceStoreISREnd(int pendingISR)
\r
666 uint8_t hnd8 = 0, type = 0;
\r
668 TRACE_ALLOC_CRITICAL_SECTION();
\r
670 if (! RecorderDataPtr->recorderActive || ! handle_of_last_logged_task)
\r
677 /*************************************************************************
\r
678 * This occurs if an ISR calls a trace function, preempting a previous
\r
679 * trace call that is being processed in a different ISR or task.
\r
680 * If this occurs, there is probably a problem in the definition of the
\r
681 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
682 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
683 * and any other ISRs that calls the trace recorder directly or via
\r
684 * traced kernel functions. The ARM port disables all interrupts using the
\r
685 * PRIMASK register to avoid this issue.
\r
686 *************************************************************************/
\r
687 prvTraceError("vTraceStoreISREnd - recorder busy! See code comment.");
\r
691 if (nISRactive == 0)
\r
693 prvTraceError("Unmatched call to vTraceStoreISREnd (nISRactive == 0, expected > 0)");
\r
697 trcCRITICAL_SECTION_BEGIN();
\r
698 isPendingContextSwitch |= pendingISR; /* Is there a pending context switch right now? */
\r
700 if (nISRactive > 0)
\r
702 /* Return to another ISR */
\r
703 type = TS_ISR_RESUME;
\r
704 hnd8 = prvTraceGet8BitHandle(isrstack[nISRactive - 1]); /* isrstack[nISRactive] is the handle of the ISR we're currently exiting. isrstack[nISRactive - 1] is the handle of the ISR that was executing previously. */
\r
706 else if ((isPendingContextSwitch == 0) || (prvTraceIsSchedulerSuspended()))
\r
708 /* Return to interrupted task, if no context switch will occur in between. */
\r
709 type = TS_TASK_RESUME;
\r
710 hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
\r
715 dts5 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
716 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
720 ts->objHandle = hnd8;
\r
722 prvTraceUpdateCounters();
\r
726 trcCRITICAL_SECTION_END();
\r
731 /* ISR tracing is turned off */
\r
732 void prvTraceIncreaseISRActive(void)
\r
734 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
738 void prvTraceDecreaseISRActive(void)
\r
740 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
743 #endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1)*/
\r
745 /********************************************************************************/
\r
746 /* User Event functions */
\r
747 /********************************************************************************/
\r
749 #define MAX_ARG_SIZE (4+32)
\r
751 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
752 static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value)
\r
754 TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0);
\r
756 if (i >= MAX_ARG_SIZE)
\r
761 ((uint8_t*)buffer)[i] = value;
\r
763 if (i + 1 > MAX_ARG_SIZE)
\r
768 return ((uint8_t) (i + 1));
\r
772 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
773 static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value)
\r
775 TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0);
\r
777 /* Align to multiple of 2 */
\r
778 while ((i % 2) != 0)
\r
780 if (i >= MAX_ARG_SIZE)
\r
785 ((uint8_t*)buffer)[i] = 0;
\r
789 if (i + 2 > MAX_ARG_SIZE)
\r
794 ((uint16_t*)buffer)[i/2] = value;
\r
796 return ((uint8_t) (i + 2));
\r
800 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
801 static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value)
\r
803 TRACE_ASSERT(buffer != NULL, "writeInt32: buffer == NULL", 0);
\r
805 /* A 32 bit value should begin at an even 4-byte address */
\r
806 while ((i % 4) != 0)
\r
808 if (i >= MAX_ARG_SIZE)
\r
813 ((uint8_t*)buffer)[i] = 0;
\r
817 if (i + 4 > MAX_ARG_SIZE)
\r
822 ((uint32_t*)buffer)[i/4] = value;
\r
824 return ((uint8_t) (i + 4));
\r
828 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
\r
829 static uint8_t writeFloat(void * buffer, uint8_t i, float value)
\r
831 TRACE_ASSERT(buffer != NULL, "writeFloat: buffer == NULL", 0);
\r
833 /* A 32 bit value should begin at an even 4-byte address */
\r
834 while ((i % 4) != 0)
\r
836 if (i >= MAX_ARG_SIZE)
\r
841 ((uint8_t*)buffer)[i] = 0;
\r
845 if (i + 4 > MAX_ARG_SIZE)
\r
850 ((float*)buffer)[i/4] = value;
\r
856 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
\r
857 static uint8_t writeDouble(void * buffer, uint8_t i, double value)
\r
860 uint32_t * src = (uint32_t*)&value;
\r
862 TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0);
\r
864 /* The double is written as two 32 bit values, and should begin at an even
\r
865 4-byte address (to avoid having to align with 8 byte) */
\r
868 if (i >= MAX_ARG_SIZE)
\r
873 ((uint8_t*)buffer)[i] = 0;
\r
877 if (i + 8 > MAX_ARG_SIZE)
\r
882 dest = &(((uint32_t *)buffer)[i/4]);
\r
891 /*******************************************************************************
\r
892 * prvTraceUserEventFormat
\r
894 * Parses the format string and stores the arguments in the buffer.
\r
895 ******************************************************************************/
\r
896 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
897 static uint8_t prvTraceUserEventFormat(const char* formatStr, va_list vl, uint8_t* buffer, uint8_t byteOffset)
\r
899 uint16_t formatStrIndex = 0;
\r
900 uint8_t argCounter = 0;
\r
901 uint8_t i = byteOffset;
\r
903 while (formatStr[formatStrIndex] != '\0')
\r
905 if (formatStr[formatStrIndex] == '%')
\r
909 if (argCounter > 15)
\r
911 prvTraceError("vTracePrintF - Too many arguments, max 15 allowed!");
\r
917 while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.')
\r
920 if (formatStr[formatStrIndex] != '\0')
\r
922 switch (formatStr[formatStrIndex])
\r
924 case 'd': i = writeInt32( buffer,
\r
926 (uint32_t)va_arg(vl, uint32_t));
\r
930 case 'u': i = writeInt32( buffer,
\r
932 (uint32_t)va_arg(vl, uint32_t));
\r
934 case 's': i = writeInt16( buffer,
\r
936 xTraceRegisterString((char*)va_arg(vl, char*)));
\r
939 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
\r
940 /* Yes, "double" as type also in the float
\r
941 case. This since "float" is promoted into "double"
\r
942 by the va_arg stuff. */
\r
943 case 'f': i = writeFloat( buffer,
\r
945 (float)va_arg(vl, double));
\r
948 /* No support for floats, but attempt to store a float user event
\r
949 avoid a possible crash due to float reference. Instead store the
\r
950 data on uint_32 format (will not be displayed anyway). This is just
\r
951 to keep va_arg and i consistent. */
\r
953 case 'f': i = writeInt32( buffer,
\r
955 (uint32_t)va_arg(vl, double));
\r
960 switch (formatStr[formatStrIndex])
\r
962 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
\r
963 case 'f': i = writeDouble(buffer,
\r
965 (double)va_arg(vl, double));
\r
968 /* No support for floats, but attempt to store a float user event
\r
969 avoid a possible crash due to float reference. Instead store the
\r
970 data on uint_32 format (will not be displayed anyway). This is just
\r
971 to keep va_arg and i consistent. */
\r
972 case 'f': i = writeInt32( buffer, /* In this case, the value will not be shown anyway */
\r
974 (uint32_t)va_arg(vl, double));
\r
976 i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */
\r
978 (uint32_t)va_arg(vl, double));
\r
986 switch (formatStr[formatStrIndex])
\r
988 case 'd': i = writeInt16( buffer,
\r
990 (uint16_t)va_arg(vl, uint32_t));
\r
992 case 'u': i = writeInt16( buffer,
\r
994 (uint16_t)va_arg(vl, uint32_t));
\r
1000 switch (formatStr[formatStrIndex])
\r
1002 case 'd': i = writeInt8( buffer,
\r
1004 (uint8_t)va_arg(vl, uint32_t));
\r
1007 case 'u': i = writeInt8( buffer,
\r
1009 (uint8_t)va_arg(vl, uint32_t));
\r
1021 prvTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!");
\r
1025 return (uint8_t)(i+3)/4;
\r
1029 /*******************************************************************************
\r
1030 * prvTraceClearChannelBuffer
\r
1032 * Clears a number of items in the channel buffer, starting from nextSlotToWrite.
\r
1033 ******************************************************************************/
\r
1034 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1035 static void prvTraceClearChannelBuffer(uint32_t count)
\r
1039 TRACE_ASSERT(TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE >= count,
\r
1040 "prvTraceClearChannelBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
\r
1042 /* Check if we're close to the end of the buffer */
\r
1043 if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE)
\r
1045 slots = TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
\r
1046 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, slots);
\r
1047 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[0], 0, (count - slots));
\r
1050 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, count);
\r
1054 /*******************************************************************************
\r
1055 * prvTraceCopyToDataBuffer
\r
1057 * Copies a number of items to the data buffer, starting from nextSlotToWrite.
\r
1058 ******************************************************************************/
\r
1059 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1060 static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count)
\r
1064 TRACE_ASSERT(data != NULL,
\r
1065 "prvTraceCopyToDataBuffer: data == NULL.", TRC_UNUSED);
\r
1066 TRACE_ASSERT(count <= TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE,
\r
1067 "prvTraceCopyToDataBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
\r
1068 /* Check if we're close to the end of the buffer */
\r
1069 if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE)
\r
1071 slots = TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
\r
1072 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, slots * 4);
\r
1073 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[0], data + slots, (count - slots) * 4);
\r
1077 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, count * 4);
\r
1082 /*******************************************************************************
\r
1083 * prvTraceUBHelper1
\r
1085 * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on
\r
1086 * to the next helper function.
\r
1087 ******************************************************************************/
\r
1088 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1089 static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl)
\r
1091 uint32_t data[(3 + MAX_ARG_SIZE) / 4];
\r
1092 uint8_t byteOffset = 4; /* Need room for timestamp */
\r
1093 uint8_t noOfSlots;
\r
1097 /* We are dealing with an unknown channel format pair */
\r
1098 byteOffset = (uint8_t)(byteOffset + 4); /* Also need room for channel and format */
\r
1099 ((uint16_t*)data)[2] = eventLabel;
\r
1100 ((uint16_t*)data)[3] = formatLabel;
\r
1103 noOfSlots = prvTraceUserEventFormat((char*)&(RecorderDataPtr->SymbolTable.symbytes[formatLabel+4]), vl, (uint8_t*)data, byteOffset);
\r
1105 prvTraceUBHelper2(channel, data, noOfSlots);
\r
1109 /*******************************************************************************
\r
1110 * prvTraceUBHelper2
\r
1112 * This function simply copies the data buffer to the actual user event buffer.
\r
1113 ******************************************************************************/
\r
1114 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1115 static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots)
\r
1117 static uint32_t old_timestamp = 0;
\r
1118 uint32_t old_nextSlotToWrite = 0;
\r
1120 TRACE_ALLOC_CRITICAL_SECTION();
\r
1122 TRACE_ASSERT(TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE >= noOfSlots, "prvTraceUBHelper2: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
\r
1124 trcCRITICAL_SECTION_BEGIN();
\r
1125 /* Store the timestamp */
\r
1126 prvTracePortGetTimeStamp(data);
\r
1128 if (*data < old_timestamp)
\r
1130 RecorderDataPtr->userEventBuffer.wraparoundCounter++;
\r
1133 old_timestamp = *data;
\r
1135 /* Start by erasing any information in the channel buffer */
\r
1136 prvTraceClearChannelBuffer(noOfSlots);
\r
1138 prvTraceCopyToDataBuffer(data, noOfSlots); /* Will wrap around the data if necessary */
\r
1140 old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */
\r
1141 RecorderDataPtr->userEventBuffer.nextSlotToWrite = (RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots) % TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE; /* Make sure we never end up outside the buffer */
\r
1143 /* Write to the channel buffer to indicate that this user event is ready to be used */
\r
1146 RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = channel;
\r
1150 /* 0xFF indicates that this is not a normal channel id */
\r
1151 RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (traceUBChannel)0xFF;
\r
1153 trcCRITICAL_SECTION_END();
\r
1157 /*******************************************************************************
\r
1158 * xTraceRegisterUBChannel
\r
1160 * Registers a channel for Separated User Events, i.e., those stored in the
\r
1161 * separate user event buffer.
\r
1163 * Note: Only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in
\r
1164 * trcSnapshotConfig.h
\r
1165 ******************************************************************************/
\r
1166 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1167 traceUBChannel xTraceRegisterUBChannel(traceString channel, traceString formatStr)
\r
1170 traceUBChannel retVal = 0;
\r
1172 TRACE_ALLOC_CRITICAL_SECTION();
\r
1174 TRACE_ASSERT(formatStr != 0, "xTraceRegisterChannelFormat: formatStr == 0", (traceUBChannel)0);
\r
1176 trcCRITICAL_SECTION_BEGIN();
\r
1177 for (i = 1; i <= TRC_CFG_UB_CHANNELS; i++) /* Size of the channels buffer is TRC_CFG_UB_CHANNELS + 1. Index 0 is unused. */
\r
1179 if(RecorderDataPtr->userEventBuffer.channels[i].name == 0 && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == 0)
\r
1181 /* Found empty slot */
\r
1182 RecorderDataPtr->userEventBuffer.channels[i].name = channel;
\r
1183 RecorderDataPtr->userEventBuffer.channels[i].defaultFormat = formatStr;
\r
1184 retVal = (traceUBChannel)i;
\r
1188 if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr)
\r
1190 /* Found a match */
\r
1191 retVal = (traceUBChannel)i;
\r
1195 trcCRITICAL_SECTION_END();
\r
1201 /******************************************************************************
\r
1204 * Slightly faster version of vTracePrintF() due to no lookups.
\r
1206 * Note: This is only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in
\r
1209 ******************************************************************************/
\r
1210 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1211 void vTraceUBData(traceUBChannel channelPair, ...)
\r
1215 TRACE_ASSERT(channelPair != 0, "vTraceUBData: Not a valid traceUBChannel!", TRC_UNUSED);
\r
1217 va_start(vl, channelPair);
\r
1218 vTraceUBData_Helper(channelPair, vl);
\r
1223 /* Extracts the channel name and format string from the traceUBChannel, then calls prvTraceUBHelper1. */
\r
1224 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1225 void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl)
\r
1227 traceString channel;
\r
1228 traceString formatStr;
\r
1230 TRACE_ASSERT(channelPair != 0, "vTraceUBData_Helper: channelPair == 0", TRC_UNUSED);
\r
1231 TRACE_ASSERT(channelPair <= TRC_CFG_UB_CHANNELS, "vTraceUBData_Helper: ", TRC_UNUSED);
\r
1233 channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name;
\r
1234 formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat;
\r
1236 prvTraceUBHelper1(channelPair, channel, formatStr, vl);
\r
1240 /******************************************************************************
\r
1243 * Slightly faster version of ... due to no lookups.
\r
1244 ******************************************************************************/
\r
1245 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1246 void vTraceUBEvent(traceUBChannel channelPair)
\r
1248 uint32_t data[(3 + MAX_ARG_SIZE) / 4];
\r
1250 TRACE_ASSERT(channelPair != 0, "vTraceUBEvent: channelPair == 0", TRC_UNUSED);
\r
1251 TRACE_ASSERT(channelPair <= TRC_CFG_UB_CHANNELS, "vTraceUBEvent: ", TRC_UNUSED);
\r
1253 prvTraceUBHelper2(channelPair, data, 1); /* Only need one slot for timestamp */
\r
1257 /******************************************************************************
\r
1260 * Generates User Event with formatted text and data, similar to a "printf".
\r
1261 * It is very fast compared to a normal "printf" since this function only
\r
1262 * stores the arguments. The actual formatting is done
\r
1263 * on the host PC when the trace is displayed in the viewer tool.
\r
1265 * User Event labels are created using xTraceRegisterString.
\r
1268 * traceString adc_uechannel = xTraceRegisterString("ADC User Events");
\r
1270 * vTracePrintF(adc_uechannel,
\r
1271 * "ADC channel %d: %lf volts",
\r
1272 * ch, (double)adc_reading/(double)scale);
\r
1274 * This can be combined into one line, if desired, but this is slower:
\r
1276 * vTracePrintF(xTraceRegisterString("ADC User Events"),
\r
1277 * "ADC channel %d: %lf volts",
\r
1278 * ch, (double)adc_reading/(double)scale);
\r
1280 * Calling xTraceRegisterString multiple times will not create duplicate entries, but
\r
1281 * it is of course faster to just do it once, and then keep the handle for later
\r
1282 * use. If you don't have any data arguments, only a text label/string, it is
\r
1283 * better to use vTracePrint - it is faster.
\r
1285 * Format specifiers supported:
\r
1286 * %d - 32 bit signed integer
\r
1287 * %u - 32 bit unsigned integer
\r
1288 * %f - 32 bit float
\r
1289 * %s - string (is copied to the recorder symbol table)
\r
1290 * %hd - 16 bit signed integer
\r
1291 * %hu - 16 bit unsigned integer
\r
1292 * %bd - 8 bit signed integer
\r
1293 * %bu - 8 bit unsigned integer
\r
1294 * %lf - double-precision float (Note! See below...)
\r
1296 * Up to 15 data arguments are allowed, with a total size of maximum 32 byte.
\r
1297 * In case this is exceeded, the user event is changed into an error message.
\r
1299 * The data is stored in trace buffer, and is packed to allow storing multiple
\r
1300 * smaller data entries in the same 4-byte record, e.g., four 8-bit values.
\r
1301 * A string requires two bytes, as the symbol table is limited to 64K. Storing
\r
1302 * a double (%lf) uses two records, so this is quite costly. Use float (%f)
\r
1303 * unless the higher precision is really necessary.
\r
1305 * Note that the double-precision float (%lf) assumes a 64 bit double
\r
1306 * representation. This does not seem to be the case on e.g. PIC24 and PIC32.
\r
1307 * Before using a %lf argument on a 16-bit MCU, please verify that
\r
1308 * "sizeof(double)" actually gives 8 as expected. If not, use %f instead.
\r
1309 ******************************************************************************/
\r
1310 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1311 void vTracePrintF(traceString eventLabel, const char* formatStr, ...)
\r
1315 va_start(vl, formatStr);
\r
1316 vTracePrintF_Helper(eventLabel, formatStr, vl);
\r
1321 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1322 void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl)
\r
1324 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
\r
1325 uint32_t noOfSlots;
\r
1327 uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
\r
1328 TRACE_ALLOC_CRITICAL_SECTION();
\r
1330 TRACE_ASSERT(formatStr != NULL, "vTracePrintF_Helper: formatStr == NULL", TRC_UNUSED);
\r
1332 trcCRITICAL_SECTION_BEGIN();
\r
1334 if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)
\r
1336 /* First, write the "primary" user event entry in the local buffer, but
\r
1337 let the event type be "EVENT_BEING_WRITTEN" for now...*/
\r
1339 ue1 = (UserEvent*)(&tempDataBuffer[0]);
\r
1341 ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */
\r
1343 noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4);
\r
1345 /* Store the format string, with a reference to the channel symbol */
\r
1346 ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel);
\r
1348 ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);
\r
1350 /* prvTraceGetDTS might stop the recorder in some cases... */
\r
1351 if (RecorderDataPtr->recorderActive)
\r
1354 /* If the data does not fit in the remaining main buffer, wrap around to
\r
1355 0 if allowed, otherwise stop the recorder and quit). */
\r
1356 if (RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents)
\r
1358 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1359 (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
\r
1361 (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4);
\r
1362 RecorderDataPtr->nextFreeIndex = 0;
\r
1363 RecorderDataPtr->bufferIsFull = 1;
\r
1366 /* Stop recorder, since the event data will not fit in the
\r
1367 buffer and not circular buffer in this case... */
\r
1372 /* Check if recorder has been stopped (i.e., vTraceStop above) */
\r
1373 if (RecorderDataPtr->recorderActive)
\r
1375 /* Check that the buffer to be overwritten does not contain any user
\r
1376 events that would be partially overwritten. If so, they must be "killed"
\r
1377 by replacing the user event and following data with NULL events (i.e.,
\r
1378 using a memset to zero).*/
\r
1379 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1380 prvCheckDataToBeOverwrittenForMultiEntryEvents((uint8_t)noOfSlots);
\r
1382 /* Copy the local buffer to the main buffer */
\r
1383 (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
\r
1387 /* Update the event type, i.e., number of data entries following the
\r
1388 main USER_EVENT entry (Note: important that this is after the memcpy,
\r
1389 but within the critical section!)*/
\r
1390 RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] =
\r
1391 (uint8_t) ( USER_EVENT + noOfSlots - 1 );
\r
1393 /* Update the main buffer event index (already checked that it fits in
\r
1394 the buffer, so no need to check for wrapping)*/
\r
1396 RecorderDataPtr->nextFreeIndex += noOfSlots;
\r
1397 RecorderDataPtr->numEvents += noOfSlots;
\r
1399 if (RecorderDataPtr->nextFreeIndex >= TRC_CFG_EVENT_BUFFER_SIZE)
\r
1401 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1402 /* We have reached the end, but this is a ring buffer. Start from the beginning again. */
\r
1403 RecorderDataPtr->bufferIsFull = 1;
\r
1404 RecorderDataPtr->nextFreeIndex = 0;
\r
1406 /* We have reached the end so we stop. */
\r
1412 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1413 /* Make sure the next entry is cleared correctly */
\r
1414 prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
\r
1419 trcCRITICAL_SECTION_END();
\r
1421 #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
1422 /* Use the separate user event buffer */
\r
1423 traceString formatLabel;
\r
1424 traceUBChannel channel;
\r
1426 if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)
\r
1428 formatLabel = xTraceRegisterString(formatStr);
\r
1430 channel = xTraceRegisterUBChannel(eventLabel, formatLabel);
\r
1432 prvTraceUBHelper1(channel, eventLabel, formatLabel, vl);
\r
1438 /******************************************************************************
\r
1441 * Basic user event
\r
1443 * Generates a User Event with a text label. The label is created/looked up
\r
1444 * in the symbol table using xTraceRegisterString.
\r
1445 ******************************************************************************/
\r
1446 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1447 void vTracePrint(traceString chn, const char* str)
\r
1449 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
\r
1452 TRACE_ALLOC_CRITICAL_SECTION();
\r
1454 trcCRITICAL_SECTION_BEGIN();
\r
1455 if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)
\r
1457 dts1 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1458 ue = (UserEvent*) prvTraceNextFreeEventBufferSlot();
\r
1462 ue->type = USER_EVENT;
\r
1463 ue->payload = prvTraceOpenSymbol(str, chn);
\r
1464 prvTraceUpdateCounters();
\r
1467 trcCRITICAL_SECTION_END();
\r
1469 #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
1470 traceUBChannel channel;
\r
1471 uint32_t noOfSlots = 1;
\r
1472 uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
\r
1473 if (RecorderDataPtr->recorderActive && (! inExcludedTask || nISRactive) && handle_of_last_logged_task)
\r
1475 traceString trcStr = prvTraceOpenSymbol(str, chn);
\r
1476 channel = xTraceRegisterUBChannel(chn, trcStr);
\r
1480 /* We are dealing with an unknown channel format pair */
\r
1481 noOfSlots++; /* Also need room for channel and format */
\r
1482 ((uint16_t*)tempDataBuffer)[2] = chn;
\r
1483 ((uint16_t*)tempDataBuffer)[3] = trcStr;
\r
1486 prvTraceUBHelper2(channel, tempDataBuffer, noOfSlots);
\r
1492 /*******************************************************************************
\r
1493 * xTraceRegisterString
\r
1495 * Register strings in the recorder, e.g. for names of user event channels.
\r
1498 * myEventHandle = xTraceRegisterString("MyUserEvent");
\r
1500 * vTracePrintF(myEventHandle, "My value is: %d", myValue);
\r
1502 ******************************************************************************/
\r
1503 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1504 traceString xTraceRegisterString(const char* label)
\r
1506 TRACE_ASSERT(label != NULL, "xTraceRegisterString: label == NULL", (traceString)0);
\r
1508 return prvTraceOpenSymbol(label, 0);
\r
1513 #if ((!defined TRC_CFG_INCLUDE_READY_EVENTS) || (TRC_CFG_INCLUDE_READY_EVENTS == 1))
\r
1515 void prvTraceSetReadyEventsEnabled(int status)
\r
1517 readyEventsEnabled = status;
\r
1520 /*******************************************************************************
\r
1521 * prvTraceStoreTaskReady
\r
1523 * This function stores a ready state for the task handle sent in as parameter.
\r
1524 ******************************************************************************/
\r
1525 void prvTraceStoreTaskReady(traceHandle handle)
\r
1531 TRACE_ALLOC_CRITICAL_SECTION();
\r
1535 /* On FreeRTOS v7.3.0, this occurs when creating tasks due to a bad
\r
1536 placement of the trace macro. In that case, the events are ignored. */
\r
1540 if (! readyEventsEnabled)
\r
1542 /* When creating tasks, ready events are also created. If creating
\r
1543 a "hidden" (not traced) task, we must therefore disable recording
\r
1544 of ready events to avoid an undesired ready event... */
\r
1548 TRACE_ASSERT(handle <= TRC_CFG_NTASK, "prvTraceStoreTaskReady: Invalid value for handle", TRC_UNUSED);
\r
1550 if (recorder_busy)
\r
1552 /*************************************************************************
\r
1553 * This occurs if an ISR calls a trace function, preempting a previous
\r
1554 * trace call that is being processed in a different ISR or task.
\r
1555 * If this occurs, there is probably a problem in the definition of the
\r
1556 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1557 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1558 * and any other ISRs that calls the trace recorder directly or via
\r
1559 * traced kernel functions. The ARM port disables all interrupts using the
\r
1560 * PRIMASK register to avoid this issue.
\r
1561 *************************************************************************/
\r
1562 prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
\r
1566 trcCRITICAL_SECTION_BEGIN();
\r
1567 if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
\r
1569 if (!TRACE_GET_TASK_FLAG_ISEXCLUDED(handle))
\r
1571 dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1572 hnd8 = prvTraceGet8BitHandle(handle);
\r
1573 tr = (TREvent*)prvTraceNextFreeEventBufferSlot();
\r
1576 tr->type = DIV_TASK_READY;
\r
1578 tr->objHandle = hnd8;
\r
1579 prvTraceUpdateCounters();
\r
1583 trcCRITICAL_SECTION_END();
\r
1587 /*******************************************************************************
\r
1588 * prvTraceStoreLowPower
\r
1590 * This function stores a low power state.
\r
1591 ******************************************************************************/
\r
1592 void prvTraceStoreLowPower(uint32_t flag)
\r
1596 TRACE_ALLOC_CRITICAL_SECTION();
\r
1598 TRACE_ASSERT(flag <= 1, "prvTraceStoreLowPower: Invalid flag value", TRC_UNUSED);
\r
1600 if (recorder_busy)
\r
1602 /*************************************************************************
\r
1603 * This occurs if an ISR calls a trace function, preempting a previous
\r
1604 * trace call that is being processed in a different ISR or task.
\r
1605 * If this occurs, there is probably a problem in the definition of the
\r
1606 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1607 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1608 * and any other ISRs that calls the trace recorder directly or via
\r
1609 * traced kernel functions. The ARM port disables all interrupts using the
\r
1610 * PRIMASK register to avoid this issue.
\r
1611 *************************************************************************/
\r
1612 prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
\r
1616 trcCRITICAL_SECTION_BEGIN();
\r
1617 if (RecorderDataPtr->recorderActive)
\r
1619 dts = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1620 lp = (LPEvent*)prvTraceNextFreeEventBufferSlot();
\r
1623 lp->type = (uint8_t) (LOW_POWER_BEGIN + ( uint8_t ) flag); /* BEGIN or END depending on flag */
\r
1625 prvTraceUpdateCounters();
\r
1628 trcCRITICAL_SECTION_END();
\r
1631 /*******************************************************************************
\r
1632 * vTraceStoreMemMangEvent
\r
1634 * This function stores malloc and free events. Each call requires two records,
\r
1635 * for size and address respectively. The event code parameter (ecode) is applied
\r
1636 * to the first record (size) and the following address record gets event
\r
1637 * code "ecode + 1", so make sure this is respected in the event code table.
\r
1638 * Note: On "free" calls, the signed_size parameter should be negative.
\r
1639 ******************************************************************************/
\r
1640 #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1)
\r
1641 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1642 void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size)
\r
1645 MemEventSize * ms;
\r
1646 MemEventAddr * ma;
\r
1647 uint16_t size_low;
\r
1648 uint16_t addr_low;
\r
1649 uint8_t addr_high;
\r
1651 TRACE_ALLOC_CRITICAL_SECTION();
\r
1653 if (RecorderDataPtr == NULL)
\r
1655 /* Occurs in vTraceInitTraceData, if using dynamic allocation. */
\r
1659 if (signed_size < 0)
\r
1660 size = (uint32_t)(- signed_size);
\r
1662 size = (uint32_t)(signed_size);
\r
1664 trcCRITICAL_SECTION_BEGIN();
\r
1666 heapMemUsage = heapMemUsage + (uint32_t)signed_size;
\r
1668 if (RecorderDataPtr->recorderActive)
\r
1670 /* If it is an ISR or NOT an excluded task, this kernel call will be stored in the trace */
\r
1671 if (nISRactive || !inExcludedTask)
\r
1673 dts1 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1674 size_low = (uint16_t)prvTraceGetParam(0xFFFF, size);
\r
1675 ms = (MemEventSize *)prvTraceNextFreeEventBufferSlot();
\r
1680 ms->type = NULL_EVENT; /* Updated when all events are written */
\r
1681 ms->size = size_low;
\r
1682 prvTraceUpdateCounters();
\r
1684 /* Storing a second record with address (signals "failed" if null) */
\r
1685 #if (TRC_CFG_HEAP_SIZE_BELOW_16M)
\r
1686 /* If the heap address range is within 16 MB, i.e., the upper 8 bits
\r
1687 of addresses are constant, this optimization avoids storing an extra
\r
1688 event record by ignoring the upper 8 bit of the address */
\r
1689 addr_low = address & 0xFFFF;
\r
1690 addr_high = (address >> 16) & 0xFF;
\r
1692 /* The whole 32 bit address is stored using a second event record
\r
1693 for the upper 16 bit */
\r
1694 addr_low = (uint16_t)prvTraceGetParam(0xFFFF, address);
\r
1698 ma = (MemEventAddr *) prvTraceNextFreeEventBufferSlot();
\r
1701 ma->addr_low = addr_low;
\r
1702 ma->addr_high = addr_high;
\r
1703 ma->type = (uint8_t) (ecode + 1); /* Note this! */
\r
1704 ms->type = (uint8_t) ecode;
\r
1705 prvTraceUpdateCounters();
\r
1706 RecorderDataPtr->heapMemUsage = heapMemUsage;
\r
1711 trcCRITICAL_SECTION_END();
\r
1713 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1716 /*******************************************************************************
\r
1717 * prvTraceStoreKernelCall
\r
1719 * This is the main integration point for storing kernel calls, and
\r
1720 * is called by the hooks in trcKernelHooks.h (see trcKernelPort.h for event codes).
\r
1721 ******************************************************************************/
\r
1722 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1723 void prvTraceStoreKernelCall(uint32_t ecode, traceObjectClass objectClass, uint32_t objectNumber)
\r
1728 TRACE_ALLOC_CRITICAL_SECTION();
\r
1730 TRACE_ASSERT(ecode < 0xFF, "prvTraceStoreKernelCall: ecode >= 0xFF", TRC_UNUSED);
\r
1731 TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCall: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
\r
1732 TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCall: Invalid value for objectNumber", TRC_UNUSED);
\r
1734 if (recorder_busy)
\r
1736 /*************************************************************************
\r
1737 * This occurs if an ISR calls a trace function, preempting a previous
\r
1738 * trace call that is being processed in a different ISR or task.
\r
1739 * If this occurs, there is probably a problem in the definition of the
\r
1740 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1741 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1742 * and any other ISRs that calls the trace recorder directly or via
\r
1743 * traced kernel functions. The ARM port disables all interrupts using the
\r
1744 * PRIMASK register to avoid this issue.
\r
1745 *************************************************************************/
\r
1746 prvTraceError("Recorder busy - high priority ISR using syscall? (2)");
\r
1750 if (handle_of_last_logged_task == 0)
\r
1755 trcCRITICAL_SECTION_BEGIN();
\r
1756 if (RecorderDataPtr->recorderActive)
\r
1758 /* If it is an ISR or NOT an excluded task, this kernel call will be stored in the trace */
\r
1759 if (nISRactive || !inExcludedTask)
\r
1761 /* Check if the referenced object or the event code is excluded */
\r
1762 if (!uiTraceIsObjectExcluded(objectClass, (traceHandle)objectNumber) && !TRACE_GET_EVENT_CODE_FLAG_ISEXCLUDED(ecode))
\r
1764 dts1 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1765 hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber);
\r
1766 kse = (KernelCall*) prvTraceNextFreeEventBufferSlot();
\r
1770 kse->type = (uint8_t)ecode;
\r
1771 kse->objHandle = hnd8;
\r
1772 prvTraceUpdateCounters();
\r
1777 trcCRITICAL_SECTION_END();
\r
1779 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1781 /*******************************************************************************
\r
1782 * prvTraceStoreKernelCallWithParam
\r
1784 * Used for storing kernel calls with a handle and a numeric parameter. If the
\r
1785 * numeric parameter does not fit in one byte, and extra XPS event is inserted
\r
1786 * before the kernel call event containing the three upper bytes.
\r
1787 ******************************************************************************/
\r
1788 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1789 void prvTraceStoreKernelCallWithParam(uint32_t evtcode,
\r
1790 traceObjectClass objectClass,
\r
1791 uint32_t objectNumber,
\r
1794 KernelCallWithParamAndHandle * kse;
\r
1798 TRACE_ALLOC_CRITICAL_SECTION();
\r
1800 TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithParam: evtcode >= 0xFF", TRC_UNUSED);
\r
1801 TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCallWithParam: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
\r
1802 TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCallWithParam: Invalid value for objectNumber", TRC_UNUSED);
\r
1804 if (recorder_busy)
\r
1806 /*************************************************************************
\r
1807 * This occurs if an ISR calls a trace function, preempting a previous
\r
1808 * trace call that is being processed in a different ISR or task.
\r
1809 * If this occurs, there is probably a problem in the definition of the
\r
1810 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1811 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1812 * and any other ISRs that calls the trace recorder directly or via
\r
1813 * traced kernel functions. The ARM port disables all interrupts using the
\r
1814 * PRIMASK register to avoid this issue.
\r
1815 *************************************************************************/
\r
1816 prvTraceError("Recorder busy - high priority ISR using syscall? (3)");
\r
1820 trcCRITICAL_SECTION_BEGIN();
\r
1821 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task && (! inExcludedTask || nISRactive))
\r
1823 /* Check if the referenced object or the event code is excluded */
\r
1824 if (!uiTraceIsObjectExcluded(objectClass, (traceHandle)objectNumber) &&
\r
1825 !TRACE_GET_EVENT_CODE_FLAG_ISEXCLUDED(evtcode))
\r
1827 dts2 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1828 p8 = (uint8_t) prvTraceGetParam(0xFF, param);
\r
1829 hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber);
\r
1830 kse = (KernelCallWithParamAndHandle*) prvTraceNextFreeEventBufferSlot();
\r
1834 kse->type = (uint8_t)evtcode;
\r
1835 kse->objHandle = hnd8;
\r
1837 prvTraceUpdateCounters();
\r
1841 trcCRITICAL_SECTION_END();
\r
1843 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1846 /*******************************************************************************
\r
1847 * prvTraceGetParam
\r
1849 * Used for storing extra bytes for kernel calls with numeric parameters.
\r
1851 * May only be called within a critical section!
\r
1852 ******************************************************************************/
\r
1853 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1854 static uint32_t prvTraceGetParam(uint32_t param_max, uint32_t param)
\r
1858 TRACE_ASSERT(param_max == 0xFF || param_max == 0xFFFF,
\r
1859 "prvTraceGetParam: Invalid value for param_max", param);
\r
1861 if (param <= param_max)
\r
1867 xps = (XPSEvent*) prvTraceNextFreeEventBufferSlot();
\r
1870 xps->type = DIV_XPS;
\r
1871 xps->xps_8 = (uint8_t)(param & (0xFF00 & ~param_max)) >> 8;
\r
1872 xps->xps_16 = (uint16_t)(param & (0xFFFF0000 & ~param_max)) >> 16;
\r
1873 prvTraceUpdateCounters();
\r
1876 return param & param_max;
\r
1881 /*******************************************************************************
\r
1882 * prvTraceStoreKernelCallWithNumericParamOnly
\r
1884 * Used for storing kernel calls with numeric parameters only. This is
\r
1885 * only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment.
\r
1886 ******************************************************************************/
\r
1887 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1888 void prvTraceStoreKernelCallWithNumericParamOnly(uint32_t evtcode, uint32_t param)
\r
1890 KernelCallWithParam16 * kse;
\r
1892 uint16_t restParam;
\r
1893 TRACE_ALLOC_CRITICAL_SECTION();
\r
1897 TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithNumericParamOnly: Invalid value for evtcode", TRC_UNUSED);
\r
1899 if (recorder_busy)
\r
1901 /*************************************************************************
\r
1902 * This occurs if an ISR calls a trace function, preempting a previous
\r
1903 * trace call that is being processed in a different ISR or task.
\r
1904 * If this occurs, there is probably a problem in the definition of the
\r
1905 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1906 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1907 * and any other ISRs that calls the trace recorder directly or via
\r
1908 * traced kernel functions. The ARM port disables all interrupts using the
\r
1909 * PRIMASK register to avoid this issue.
\r
1910 *************************************************************************/
\r
1911 prvTraceError("Recorder busy - high priority ISR using syscall? (4)");
\r
1915 trcCRITICAL_SECTION_BEGIN();
\r
1916 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task
\r
1917 && (! inExcludedTask || nISRactive))
\r
1919 /* Check if the event code is excluded */
\r
1920 if (!TRACE_GET_EVENT_CODE_FLAG_ISEXCLUDED(evtcode))
\r
1922 dts6 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1923 restParam = (uint16_t)prvTraceGetParam(0xFFFF, param);
\r
1924 kse = (KernelCallWithParam16*) prvTraceNextFreeEventBufferSlot();
\r
1928 kse->type = (uint8_t)evtcode;
\r
1929 kse->param = restParam;
\r
1930 prvTraceUpdateCounters();
\r
1934 trcCRITICAL_SECTION_END();
\r
1936 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1938 /*******************************************************************************
\r
1939 * prvTraceStoreTaskswitch
\r
1940 * Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart.
\r
1941 * At this point interrupts are assumed to be disabled!
\r
1942 ******************************************************************************/
\r
1943 void prvTraceStoreTaskswitch(traceHandle task_handle)
\r
1949 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
1950 extern int32_t isPendingContextSwitch;
\r
1952 trcSR_ALLOC_CRITICAL_SECTION_ON_CORTEX_M_ONLY();
\r
1956 TRACE_ASSERT(task_handle <= TRC_CFG_NTASK,
\r
1957 "prvTraceStoreTaskswitch: Invalid value for task_handle", TRC_UNUSED);
\r
1959 /* Skip the event if the task has been excluded, using vTraceExcludeTask */
\r
1960 if (TRACE_GET_TASK_FLAG_ISEXCLUDED(task_handle))
\r
1963 inExcludedTask = 1;
\r
1967 inExcludedTask = 0;
\r
1970 trcCRITICAL_SECTION_BEGIN_ON_CORTEX_M_ONLY();
\r
1972 /* Skip the event if the same task is scheduled */
\r
1973 if (task_handle == handle_of_last_logged_task)
\r
1978 if (!RecorderDataPtr->recorderActive)
\r
1983 /* If this event should be logged, log it! */
\r
1984 if (skipEvent == 0)
\r
1986 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
1987 isPendingContextSwitch = 0;
\r
1990 dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1991 handle_of_last_logged_task = task_handle;
\r
1992 hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
\r
1993 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
1997 if (prvTraceGetObjectState(TRACE_CLASS_TASK,
\r
1998 handle_of_last_logged_task) == TASK_STATE_INSTANCE_ACTIVE)
\r
2000 ts->type = TS_TASK_RESUME;
\r
2004 ts->type = TS_TASK_BEGIN;
\r
2008 ts->objHandle = hnd8;
\r
2010 prvTraceSetObjectState(TRACE_CLASS_TASK,
\r
2011 handle_of_last_logged_task,
\r
2012 TASK_STATE_INSTANCE_ACTIVE);
\r
2014 prvTraceUpdateCounters();
\r
2018 trcCRITICAL_SECTION_END_ON_CORTEX_M_ONLY();
\r
2021 /*******************************************************************************
\r
2022 * prvTraceStoreObjectNameOnCloseEvent
\r
2024 * Updates the symbol table with the name of this object from the dynamic
\r
2025 * objects table and stores a "close" event, holding the mapping between handle
\r
2026 * and name (a symbol table handle). The stored name-handle mapping is thus the
\r
2027 * "old" one, valid up until this point.
\r
2028 ******************************************************************************/
\r
2029 #if (TRC_CFG_INCLUDE_OBJECT_DELETE == 1)
\r
2030 void prvTraceStoreObjectNameOnCloseEvent(traceHandle handle,
\r
2031 traceObjectClass objectclass)
\r
2033 ObjCloseNameEvent * ce;
\r
2034 const char * name;
\r
2037 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2038 "prvTraceStoreObjectNameOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2039 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2040 "prvTraceStoreObjectNameOnCloseEvent: Invalid value for handle", TRC_UNUSED);
\r
2042 if (RecorderDataPtr->recorderActive)
\r
2044 uint8_t hnd8 = prvTraceGet8BitHandle(handle);
\r
2045 name = TRACE_PROPERTY_NAME_GET(objectclass, handle);
\r
2046 idx = prvTraceOpenSymbol(name, 0);
\r
2048 // Interrupt disable not necessary, already done in trcHooks.h macro
\r
2049 ce = (ObjCloseNameEvent*) prvTraceNextFreeEventBufferSlot();
\r
2052 ce->type = (uint8_t) (EVENTGROUP_OBJCLOSE_NAME + objectclass);
\r
2053 ce->objHandle = hnd8;
\r
2054 ce->symbolIndex = idx;
\r
2055 prvTraceUpdateCounters();
\r
2060 void prvTraceStoreObjectPropertiesOnCloseEvent(traceHandle handle,
\r
2061 traceObjectClass objectclass)
\r
2063 ObjClosePropEvent * pe;
\r
2065 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2066 "prvTraceStoreObjectPropertiesOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2067 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2068 "prvTraceStoreObjectPropertiesOnCloseEvent: Invalid value for handle", TRC_UNUSED);
\r
2070 if (RecorderDataPtr->recorderActive)
\r
2072 // Interrupt disable not necessary, already done in trcHooks.h macro
\r
2073 pe = (ObjClosePropEvent*) prvTraceNextFreeEventBufferSlot();
\r
2076 if (objectclass == TRACE_CLASS_TASK)
\r
2078 pe->arg1 = TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, handle);
\r
2082 pe->arg1 = TRACE_PROPERTY_OBJECT_STATE(objectclass, handle);
\r
2084 pe->type = (uint8_t) (EVENTGROUP_OBJCLOSE_PROP + objectclass);
\r
2085 prvTraceUpdateCounters();
\r
2091 void prvTraceSetPriorityProperty(uint8_t objectclass, traceHandle id, uint8_t value)
\r
2093 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2094 "prvTraceSetPriorityProperty: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2095 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2096 "prvTraceSetPriorityProperty: Invalid value for id", TRC_UNUSED);
\r
2098 TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id) = value;
\r
2101 uint8_t prvTraceGetPriorityProperty(uint8_t objectclass, traceHandle id)
\r
2103 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2104 "prvTraceGetPriorityProperty: objectclass >= TRACE_NCLASSES", 0);
\r
2105 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2106 "prvTraceGetPriorityProperty: Invalid value for id", 0);
\r
2108 return TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id);
\r
2111 void prvTraceSetObjectState(uint8_t objectclass, traceHandle id, uint8_t value)
\r
2113 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2114 "prvTraceSetObjectState: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2115 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2116 "prvTraceSetObjectState: Invalid value for id", TRC_UNUSED);
\r
2118 TRACE_PROPERTY_OBJECT_STATE(objectclass, id) = value;
\r
2121 uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id)
\r
2123 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2124 "prvTraceGetObjectState: objectclass >= TRACE_NCLASSES", 0);
\r
2125 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2126 "prvTraceGetObjectState: Invalid value for id", 0);
\r
2128 return TRACE_PROPERTY_OBJECT_STATE(objectclass, id);
\r
2131 void prvTraceSetTaskInstanceFinished(traceHandle handle)
\r
2133 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_TASK],
\r
2134 "prvTraceSetTaskInstanceFinished: Invalid value for handle", TRC_UNUSED);
\r
2136 #if (TRC_CFG_USE_IMPLICIT_IFE_RULES == 1)
\r
2137 TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle) = 0;
\r
2141 /*******************************************************************************
\r
2142 * Static data initializations
\r
2143 ******************************************************************************/
\r
2145 /* Tasks and kernel objects can be explicitly excluded from the trace to reduce
\r
2146 buffer usage. This structure handles the exclude flags for all objects and tasks.
\r
2147 Note that slot 0 is not used, since not a valid handle. */
\r
2148 uint8_t trcExcludedObjects[(TRACE_KERNEL_OBJECT_COUNT + TRACE_NCLASSES) / 8 + 1] = { 0 };
\r
2150 /* Specific events can also be excluded, i.e., by the event code. This can be
\r
2151 used to exclude kernel calls that don't refer to a kernel object, like a delay.
\r
2152 This structure handle the exclude flags for all event codes */
\r
2153 uint8_t trcExcludedEventCodes[NEventCodes / 8 + 1] = { 0 };
\r
2155 /* A set of stacks that keeps track of available object handles for each class.
\r
2156 The stacks are empty initially, meaning that allocation of new handles will be
\r
2157 based on a counter (for each object class). Any delete operation will
\r
2158 return the handle to the corresponding stack, for reuse on the next allocate.*/
\r
2159 objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
\r
2161 /* Initial TRC_HWTC_COUNT value, for detecting if the time-stamping source is
\r
2162 enabled. If using the OS periodic timer for time-stamping, this might not
\r
2163 have been configured on the earliest events during the startup. */
\r
2164 uint32_t init_hwtc_count;
\r
2166 /*******************************************************************************
\r
2169 * The main data structure in snapshot mode, when using the default static memory
\r
2170 * allocation (TRC_RECORDER_BUFFER_ALLOCATION_STATIC). The recorder uses a pointer
\r
2171 * RecorderDataPtr to access the data, to also allow for dynamic or custom data
\r
2172 * allocation (see TRC_CFG_RECORDER_BUFFER_ALLOCATION).
\r
2173 ******************************************************************************/
\r
2174 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
\r
2175 RecorderDataType RecorderData;
\r
2178 /*******************************************************************************
\r
2181 * Pointer to the main data structure, when in snapshot mode.
\r
2182 ******************************************************************************/
\r
2183 RecorderDataType* RecorderDataPtr = NULL;
\r
2185 /* This version of the function dynamically allocates the trace data */
\r
2186 void prvTraceInitTraceData()
\r
2189 if (RecorderDataPtr == NULL)
\r
2191 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
\r
2192 RecorderDataPtr = &RecorderData;
\r
2193 #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC)
\r
2194 RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType));
\r
2195 if (! RecorderDataPtr)
\r
2197 prvTraceError("Failed allocating recorder buffer!");
\r
2200 #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
\r
2201 if (! RecorderDataPtr)
\r
2203 prvTraceError("Recorder data pointer not set! Use vTraceSetRecorderDataBuffer().");
\r
2210 if (RecorderDataPtr->startmarker0 == 1)
\r
2212 /* Already initialized */
\r
2217 init_hwtc_count = TRC_HWTC_COUNT;
\r
2219 (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType));
\r
2221 RecorderDataPtr->version = TRACE_KERNEL_VERSION;
\r
2222 RecorderDataPtr->minor_version = TRACE_MINOR_VERSION;
\r
2223 RecorderDataPtr->irq_priority_order = TRC_IRQ_PRIORITY_ORDER;
\r
2224 RecorderDataPtr->filesize = sizeof(RecorderDataType);
\r
2225 RecorderDataPtr->maxEvents = TRC_CFG_EVENT_BUFFER_SIZE;
\r
2226 RecorderDataPtr->debugMarker0 = (int32_t) 0xF0F0F0F0;
\r
2227 RecorderDataPtr->isUsing16bitHandles = TRC_CFG_USE_16BIT_OBJECT_HANDLES;
\r
2228 RecorderDataPtr->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
\r
2230 /* This function is kernel specific */
\r
2231 vTraceInitObjectPropertyTable();
\r
2233 RecorderDataPtr->debugMarker1 = (int32_t)0xF1F1F1F1;
\r
2234 RecorderDataPtr->SymbolTable.symTableSize = TRC_CFG_SYMBOL_TABLE_SIZE;
\r
2235 RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1;
\r
2236 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT == 1)
\r
2237 RecorderDataPtr->exampleFloatEncoding = 1.0f; /* otherwise already zero */
\r
2239 RecorderDataPtr->debugMarker2 = (int32_t)0xF2F2F2F2;
\r
2240 prvStrncpy(RecorderDataPtr->systemInfo, "Trace Recorder Demo", 80);
\r
2241 RecorderDataPtr->debugMarker3 = (int32_t)0xF3F3F3F3;
\r
2242 RecorderDataPtr->endmarker0 = 0x0A;
\r
2243 RecorderDataPtr->endmarker1 = 0x0B;
\r
2244 RecorderDataPtr->endmarker2 = 0x0C;
\r
2245 RecorderDataPtr->endmarker3 = 0x0D;
\r
2246 RecorderDataPtr->endmarker4 = 0x71;
\r
2247 RecorderDataPtr->endmarker5 = 0x72;
\r
2248 RecorderDataPtr->endmarker6 = 0x73;
\r
2249 RecorderDataPtr->endmarker7 = 0x74;
\r
2250 RecorderDataPtr->endmarker8 = 0xF1;
\r
2251 RecorderDataPtr->endmarker9 = 0xF2;
\r
2252 RecorderDataPtr->endmarker10 = 0xF3;
\r
2253 RecorderDataPtr->endmarker11 = 0xF4;
\r
2255 #if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER
\r
2256 RecorderDataPtr->userEventBuffer.bufferID = 1;
\r
2257 RecorderDataPtr->userEventBuffer.version = 0;
\r
2258 RecorderDataPtr->userEventBuffer.numberOfSlots = TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE;
\r
2259 RecorderDataPtr->userEventBuffer.numberOfChannels = TRC_CFG_UB_CHANNELS + 1;
\r
2262 /* Kernel specific initialization of the objectHandleStacks variable */
\r
2263 vTraceInitObjectHandleStack();
\r
2266 /* Finally, the 12-byte "start markers" are initialized, allowing for
\r
2267 Tracealyzer to find the trace data in a larger RAM dump.
\r
2269 The start and end markers must be unique, but without proper precautions there
\r
2270 might be a risk of accidental duplicates of the start/end markers, e.g., due to
\r
2271 compiler optimizations.
\r
2273 The below initialization of the start marker is therefore made in reverse order
\r
2274 and the fields are volatile to ensure this assignment order. This to avoid any
\r
2275 chance of accidental duplicates of this elsewhere in memory.
\r
2277 Moreover, the fields are set byte-by-byte to avoid endian issues.*/
\r
2279 RecorderDataPtr->startmarker11 = 0xF4;
\r
2280 RecorderDataPtr->startmarker10 = 0xF3;
\r
2281 RecorderDataPtr->startmarker9 = 0xF2;
\r
2282 RecorderDataPtr->startmarker8 = 0xF1;
\r
2283 RecorderDataPtr->startmarker7 = 0x74;
\r
2284 RecorderDataPtr->startmarker6 = 0x73;
\r
2285 RecorderDataPtr->startmarker5 = 0x72;
\r
2286 RecorderDataPtr->startmarker4 = 0x71;
\r
2287 RecorderDataPtr->startmarker3 = 0x04;
\r
2288 RecorderDataPtr->startmarker2 = 0x03;
\r
2289 RecorderDataPtr->startmarker1 = 0x02;
\r
2290 RecorderDataPtr->startmarker0 = 0x01;
\r
2293 #ifdef TRC_PORT_SPECIFIC_INIT
\r
2294 TRC_PORT_SPECIFIC_INIT();
\r
2299 void* prvTraceNextFreeEventBufferSlot(void)
\r
2301 if (! RecorderDataPtr->recorderActive)
\r
2303 /* If an XTS or XPS event prior to the main event has filled the buffer
\r
2304 before saving the main event, and store mode is "stop when full". */
\r
2308 if (RecorderDataPtr->nextFreeIndex >= TRC_CFG_EVENT_BUFFER_SIZE)
\r
2310 prvTraceError("Attempt to index outside event buffer!");
\r
2313 return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]);
\r
2316 uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass)
\r
2318 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2319 "uiIndexOfObject: Invalid value for objectclass", 0);
\r
2320 TRACE_ASSERT(objecthandle > 0 && objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2321 "uiIndexOfObject: Invalid value for objecthandle", 0);
\r
2323 if ((objectclass < TRACE_NCLASSES) && (objecthandle > 0) &&
\r
2324 (objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]))
\r
2326 return (uint16_t)(RecorderDataPtr->ObjectPropertyTable.StartIndexOfClass[objectclass] +
\r
2327 (RecorderDataPtr->ObjectPropertyTable.TotalPropertyBytesPerClass[objectclass] * (objecthandle-1)));
\r
2330 prvTraceError("Object table lookup with invalid object handle or object class!");
\r
2334 traceHandle prvTraceGetObjectHandle(traceObjectClass objectclass)
\r
2336 traceHandle handle;
\r
2337 static int indexOfHandle;
\r
2339 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2340 "prvTraceGetObjectHandle: Invalid value for objectclass", (traceHandle)0);
\r
2342 indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
\r
2343 if (objectHandleStacks.objectHandles[indexOfHandle] == 0)
\r
2345 /* Zero is used to indicate a never before used handle, i.e.,
\r
2346 new slots in the handle stack. The handle slot needs to
\r
2347 be initialized here (starts at 1). */
\r
2348 objectHandleStacks.objectHandles[indexOfHandle] =
\r
2349 (traceHandle)(1 + indexOfHandle -
\r
2350 objectHandleStacks.lowestIndexOfClass[objectclass]);
\r
2353 handle = objectHandleStacks.objectHandles[indexOfHandle];
\r
2355 if (objectHandleStacks.indexOfNextAvailableHandle[objectclass]
\r
2356 > objectHandleStacks.highestIndexOfClass[objectclass])
\r
2358 prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
\r
2364 objectHandleStacks.indexOfNextAvailableHandle[objectclass]++;
\r
2366 hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] -
\r
2367 objectHandleStacks.lowestIndexOfClass[objectclass];
\r
2370 objectHandleStacks.handleCountWaterMarksOfClass[objectclass])
\r
2372 objectHandleStacks.handleCountWaterMarksOfClass[objectclass] =
\r
2373 (traceHandle)hndCount;
\r
2375 TRACE_CLEAR_OBJECT_FLAG_ISEXCLUDED(objectclass, handle);
\r
2380 void prvTraceFreeObjectHandle(traceObjectClass objectclass, traceHandle handle)
\r
2382 int indexOfHandle;
\r
2384 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2385 "prvTraceFreeObjectHandle: Invalid value for objectclass", TRC_UNUSED);
\r
2386 TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2387 "prvTraceFreeObjectHandle: Invalid value for handle", TRC_UNUSED);
\r
2389 /* Check that there is room to push the handle on the stack */
\r
2390 if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) <
\r
2391 objectHandleStacks.lowestIndexOfClass[objectclass])
\r
2394 prvTraceError("Attempt to free more handles than allocated!");
\r
2398 objectHandleStacks.indexOfNextAvailableHandle[objectclass]--;
\r
2399 indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
\r
2400 objectHandleStacks.objectHandles[indexOfHandle] = handle;
\r
2404 /*******************************************************************************
\r
2405 * prvMarkObjectAsUsed
\r
2407 * Sets an "is used flag" on object creation, using the first byte of the name
\r
2408 * field. This allows for counting the number of used Object Table slots, even
\r
2409 * if no names have been set.
\r
2410 ******************************************************************************/
\r
2411 void prvMarkObjectAsUsed(traceObjectClass objectclass, traceHandle handle)
\r
2413 uint16_t idx = uiIndexOfObject(handle, objectclass);
\r
2414 RecorderDataPtr->ObjectPropertyTable.objbytes[idx] = 1;
\r
2417 /*******************************************************************************
\r
2420 * Private string copy function, to improve portability between compilers.
\r
2421 ******************************************************************************/
\r
2422 static void prvStrncpy(char* dst, const char* src, uint32_t maxLength)
\r
2425 for (i = 0; i < maxLength; i++)
\r
2433 /*******************************************************************************
\r
2434 * prvTraceSetObjectName
\r
2436 * Registers the names of queues, semaphores and other kernel objects in the
\r
2437 * recorder's Object Property Table, at the given handle and object class.
\r
2438 ******************************************************************************/
\r
2439 void prvTraceSetObjectName(traceObjectClass objectclass,
\r
2440 traceHandle handle,
\r
2443 static uint16_t idx;
\r
2445 TRACE_ASSERT(name != NULL, "prvTraceSetObjectName: name == NULL", TRC_UNUSED);
\r
2447 if (objectclass >= TRACE_NCLASSES)
\r
2449 prvTraceError("Illegal object class in prvTraceSetObjectName");
\r
2455 prvTraceError("Illegal handle (0) in prvTraceSetObjectName.");
\r
2459 if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])
\r
2462 prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
\r
2466 idx = uiIndexOfObject(handle, objectclass);
\r
2468 if (traceErrorMessage == NULL)
\r
2470 prvStrncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]),
\r
2472 RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]);
\r
2477 traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel)
\r
2482 TRACE_ALLOC_CRITICAL_SECTION();
\r
2487 TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceString)0);
\r
2489 prvTraceGetChecksum(name, &crc, &len);
\r
2491 trcCRITICAL_SECTION_BEGIN();
\r
2492 result = prvTraceLookupSymbolTableEntry(name, crc, len, userEventChannel);
\r
2495 result = prvTraceCreateSymbolTableEntry(name, crc, len, userEventChannel);
\r
2497 trcCRITICAL_SECTION_END();
\r
2503 /******************************************************************************
\r
2504 * vTraceSetFrequency
\r
2506 * Registers the clock rate of the time source for the event timestamping.
\r
2507 * This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ)
\r
2508 * should be incorrect for your setup, you can override it using this function.
\r
2510 * Must be called prior to vTraceEnable, and the time source is assumed to
\r
2511 * have a fixed clock frequency after the startup.
\r
2513 * Note that, in snapshot mode, the value is divided by the TRC_HWTC_DIVISOR.
\r
2514 * This is a software "prescaler" that is also applied on the timestamps.
\r
2515 *****************************************************************************/
\r
2516 void vTraceSetFrequency(uint32_t frequency)
\r
2518 timestampFrequency = frequency;
\r
2521 /*******************************************************************************
\r
2522 * Supporting functions
\r
2523 ******************************************************************************/
\r
2525 /*******************************************************************************
\r
2528 * Called by various parts in the recorder. Stops the recorder and stores a
\r
2529 * pointer to an error message, which is printed by the monitor task.
\r
2530 * If you are not using the monitor task, you may use xTraceGetLastError()
\r
2531 * from your application to check if the recorder is OK.
\r
2533 * Note: If a recorder error is registered before vTraceStart is called, the
\r
2534 * trace start will be aborted. This can occur if any of the Nxxxx constants
\r
2535 * (e.g., TRC_CFG_NTASK) in trcConfig.h is too small.
\r
2536 ******************************************************************************/
\r
2537 void prvTraceError(const char* msg)
\r
2539 /* Stop the recorder */
\r
2542 /* If first error only... */
\r
2543 if (traceErrorMessage == NULL)
\r
2545 traceErrorMessage = (char*)(intptr_t) msg;
\r
2546 if (RecorderDataPtr != NULL)
\r
2548 prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);
\r
2549 RecorderDataPtr->internalErrorOccured = 1;
\r
2554 /******************************************************************************
\r
2555 * prvCheckDataToBeOverwrittenForMultiEntryEvents
\r
2557 * This checks if the next event to be overwritten is a multi-entry user event,
\r
2558 * i.e., a USER_EVENT followed by data entries.
\r
2559 * Such data entries do not have an event code at byte 0, as other events.
\r
2560 * All 4 bytes are user data, so the first byte of such data events must
\r
2561 * not be interpreted as type field. The number of data entries following
\r
2562 * a USER_EVENT is given in the event code of the USER_EVENT.
\r
2563 * Therefore, when overwriting a USER_EVENT (when using in ring-buffer mode)
\r
2564 * any data entries following must be replaced with NULL events (code 0).
\r
2566 * This is assumed to execute within a critical section...
\r
2567 *****************************************************************************/
\r
2569 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2570 void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nofEntriesToCheck)
\r
2572 /* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */
\r
2573 unsigned int i = 0;
\r
2574 unsigned int e = 0;
\r
2576 TRACE_ASSERT(nofEntriesToCheck != 0,
\r
2577 "prvCheckDataToBeOverwrittenForMultiEntryEvents: nofEntriesToCheck == 0", TRC_UNUSED);
\r
2579 while (i < nofEntriesToCheck)
\r
2581 e = RecorderDataPtr->nextFreeIndex + i;
\r
2582 if ((RecorderDataPtr->eventData[e*4] > USER_EVENT) &&
\r
2583 (RecorderDataPtr->eventData[e*4] < USER_EVENT + 16))
\r
2585 uint8_t nDataEvents = (uint8_t)(RecorderDataPtr->eventData[e*4] - USER_EVENT);
\r
2586 if ((e + nDataEvents) < RecorderDataPtr->maxEvents)
\r
2588 (void)memset(& RecorderDataPtr->eventData[e*4], 0, (size_t) (4 + 4 * nDataEvents));
\r
2591 else if (RecorderDataPtr->eventData[e*4] == DIV_XPS)
\r
2593 if ((e + 1) < RecorderDataPtr->maxEvents)
\r
2595 /* Clear 8 bytes */
\r
2596 (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4 + 4);
\r
2600 /* Clear 8 bytes, 4 first and 4 last */
\r
2601 (void)memset(& RecorderDataPtr->eventData[0], 0, 4);
\r
2602 (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4);
\r
2610 /*******************************************************************************
\r
2611 * prvTraceUpdateCounters
\r
2613 * Updates the index of the event buffer.
\r
2614 ******************************************************************************/
\r
2615 void prvTraceUpdateCounters(void)
\r
2617 if (RecorderDataPtr->recorderActive == 0)
\r
2622 RecorderDataPtr->numEvents++;
\r
2624 RecorderDataPtr->nextFreeIndex++;
\r
2626 if (RecorderDataPtr->nextFreeIndex >= TRC_CFG_EVENT_BUFFER_SIZE)
\r
2628 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2629 RecorderDataPtr->bufferIsFull = 1;
\r
2630 RecorderDataPtr->nextFreeIndex = 0;
\r
2636 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2637 prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
\r
2641 /******************************************************************************
\r
2644 * Returns a differential timestamp (DTS), i.e., the time since
\r
2645 * last event, and creates an XTS event if the DTS does not fit in the
\r
2646 * number of bits given. The XTS event holds the MSB bytes of the DTS.
\r
2648 * The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for
\r
2649 * events with 16-bit dts fields.
\r
2650 *****************************************************************************/
\r
2651 uint16_t prvTraceGetDTS(uint16_t param_maxDTS)
\r
2653 static uint32_t old_timestamp = 0;
\r
2654 XTSEvent* xts = 0;
\r
2656 uint32_t timestamp = 0;
\r
2658 TRACE_ASSERT(param_maxDTS == 0xFF || param_maxDTS == 0xFFFF, "prvTraceGetDTS: Invalid value for param_maxDTS", 0);
\r
2661 if (RecorderDataPtr->frequency == 0)
\r
2663 if (timestampFrequency != 0)
\r
2665 /* If to override default TRC_HWTC_FREQ_HZ value with value set by vTraceSetFrequency */
\r
2666 RecorderDataPtr->frequency = timestampFrequency / TRC_HWTC_DIVISOR;
\r
2668 else if (init_hwtc_count != TRC_HWTC_COUNT)
\r
2670 /* If using default value and timer has been started.
\r
2671 Note: If the default frequency value set here would be incorrect, e.g.,
\r
2672 if the timer has actually not been configured yet, override this
\r
2673 with vTraceSetFrequency.
\r
2675 RecorderDataPtr->frequency = TRC_HWTC_FREQ_HZ / TRC_HWTC_DIVISOR;
\r
2677 /* If no override (vTraceSetFrequency) and timer inactive -> no action */
\r
2680 /**************************************************************************
\r
2681 * The below statements read the timestamp from the timer port module.
\r
2682 * If necessary, whole seconds are extracted using division while the rest
\r
2683 * comes from the modulo operation.
\r
2684 **************************************************************************/
\r
2686 prvTracePortGetTimeStamp(×tamp);
\r
2688 /***************************************************************************
\r
2689 * Since dts is unsigned the result will be correct even if timestamp has
\r
2691 ***************************************************************************/
\r
2692 dts = timestamp - old_timestamp;
\r
2693 old_timestamp = timestamp;
\r
2695 if (RecorderDataPtr->frequency > 0)
\r
2697 /* Check if dts > 1 second */
\r
2698 if (dts > RecorderDataPtr->frequency)
\r
2700 /* More than 1 second has passed */
\r
2701 RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency;
\r
2702 /* The part that is not an entire second is added to absTimeLastEvent */
\r
2703 RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency;
\r
2707 RecorderDataPtr->absTimeLastEvent += dts;
\r
2710 /* Check if absTimeLastEvent >= 1 second */
\r
2711 if (RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency)
\r
2713 /* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */
\r
2714 RecorderDataPtr->absTimeLastEventSecond++;
\r
2715 RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency;
\r
2716 /* RecorderDataPtr->absTimeLastEvent is now less than 1 second */
\r
2721 /* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */
\r
2722 RecorderDataPtr->absTimeLastEvent = timestamp;
\r
2725 /* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */
\r
2726 if (dts > param_maxDTS)
\r
2728 /* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/
\r
2729 xts = (XTSEvent*) prvTraceNextFreeEventBufferSlot();
\r
2733 if (param_maxDTS == 0xFFFF)
\r
2735 xts->type = XTS16;
\r
2736 xts->xts_16 = (uint16_t)((dts / 0x10000) & 0xFFFF);
\r
2739 else if (param_maxDTS == 0xFF)
\r
2742 xts->xts_16 = (uint16_t)((dts / 0x100) & 0xFFFF);
\r
2743 xts->xts_8 = (uint8_t)((dts / 0x1000000) & 0xFF);
\r
2747 prvTraceError("Bad param_maxDTS in prvTraceGetDTS");
\r
2749 prvTraceUpdateCounters();
\r
2753 return (uint16_t)dts & param_maxDTS;
\r
2756 /*******************************************************************************
\r
2757 * prvTraceLookupSymbolTableEntry
\r
2759 * Find an entry in the symbol table, return 0 if not present.
\r
2761 * The strings are stored in a byte pool, with four bytes of "meta-data" for
\r
2763 * byte 0-1: index of next entry with same checksum (for fast lookup).
\r
2764 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
\r
2765 * format strings only (the handle of the destination channel).
\r
2766 * byte 4..(4 + length): the string (object name or user event label), with
\r
2767 * zero-termination
\r
2768 ******************************************************************************/
\r
2769 traceString prvTraceLookupSymbolTableEntry(const char* name,
\r
2774 uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ];
\r
2776 TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceString)0);
\r
2777 TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceString)0);
\r
2781 if (RecorderDataPtr->SymbolTable.symbytes[i + 2] == (chn & 0x00FF))
\r
2783 if (RecorderDataPtr->SymbolTable.symbytes[i + 3] == (chn / 0x100))
\r
2785 if (RecorderDataPtr->SymbolTable.symbytes[i + 4 + len] == '\0')
\r
2787 if (strncmp((char*)(& RecorderDataPtr->SymbolTable.symbytes[i + 4]), name, len) == 0)
\r
2789 break; /* found */
\r
2794 i = (uint16_t)(RecorderDataPtr->SymbolTable.symbytes[i] + (RecorderDataPtr->SymbolTable.symbytes[i + 1] * 0x100));
\r
2799 /*******************************************************************************
\r
2800 * prvTraceCreateSymbolTableEntry
\r
2802 * Creates an entry in the symbol table, independent if it exists already.
\r
2804 * The strings are stored in a byte pool, with four bytes of "meta-data" for
\r
2806 * byte 0-1: index of next entry with same checksum (for fast lookup).
\r
2807 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
\r
2808 * format strings only (the handle of the destination channel).
\r
2809 * byte 4..(4 + length): the string (object name or user event label), with
\r
2810 * zero-termination
\r
2811 ******************************************************************************/
\r
2812 uint16_t prvTraceCreateSymbolTableEntry(const char* name,
\r
2815 traceString channel)
\r
2819 TRACE_ASSERT(name != NULL, "prvTraceCreateSymbolTableEntry: name == NULL", 0);
\r
2820 TRACE_ASSERT(len != 0, "prvTraceCreateSymbolTableEntry: len == 0", 0);
\r
2822 if (RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= TRC_CFG_SYMBOL_TABLE_SIZE)
\r
2824 prvTraceError("Symbol table full. Increase TRC_CFG_SYMBOL_TABLE_SIZE in trcConfig.h");
\r
2830 RecorderDataPtr->SymbolTable.symbytes
\r
2831 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex] =
\r
2832 (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF);
\r
2834 RecorderDataPtr->SymbolTable.symbytes
\r
2835 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1] =
\r
2836 (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100);
\r
2838 RecorderDataPtr->SymbolTable.symbytes
\r
2839 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2] =
\r
2840 (uint8_t)(channel & 0x00FF);
\r
2842 RecorderDataPtr->SymbolTable.symbytes
\r
2843 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3] =
\r
2844 (uint8_t)(channel / 0x100);
\r
2846 /* set name (bytes 4...4+len-1) */
\r
2847 prvStrncpy((char*)&(RecorderDataPtr->SymbolTable.symbytes
\r
2848 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4]), name, len);
\r
2850 /* Set zero termination (at offset 4+len) */
\r
2851 RecorderDataPtr->SymbolTable.symbytes
\r
2852 [RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len] = '\0';
\r
2854 /* store index of entry (for return value, and as head of LL[crc6]) */
\r
2855 RecorderDataPtr->SymbolTable.latestEntryOfChecksum
\r
2856 [ crc6 ] = (uint16_t)RecorderDataPtr->SymbolTable.nextFreeSymbolIndex;
\r
2858 RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += (uint32_t) (len + 5);
\r
2860 ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - (uint8_t)(len + 5));
\r
2867 /*******************************************************************************
\r
2868 * prvTraceGetChecksum
\r
2870 * Calculates a simple 6-bit checksum from a string, used to index the string
\r
2871 * for fast symbol table lookup.
\r
2872 ******************************************************************************/
\r
2873 void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength)
\r
2876 int length = 1; /* Should be 1 to account for '\0' */
\r
2879 TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", TRC_UNUSED);
\r
2880 TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", TRC_UNUSED);
\r
2881 TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", TRC_UNUSED);
\r
2883 if (pname != (const char *) 0)
\r
2885 for (; (c = (unsigned char) *pname++) != '\0';)
\r
2891 *pcrc = (uint8_t)(crc & 0x3F);
\r
2892 *plength = (uint8_t)length;
\r
2895 #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
\r
2897 static void prvTraceStoreXID(traceHandle handle);
\r
2899 /******************************************************************************
\r
2900 * prvTraceStoreXID
\r
2902 * Stores an XID (eXtended IDentifier) event.
\r
2903 * This is used if an object/task handle is larger than 255.
\r
2904 * The parameter "handle" is the full (16 bit) handle, assumed to be 256 or
\r
2905 * larger. Handles below 256 should not use this function.
\r
2907 * NOTE: this function MUST be called from within a critical section.
\r
2908 *****************************************************************************/
\r
2909 static void prvTraceStoreXID(traceHandle handle)
\r
2913 TRACE_ASSERT(handle >= 256, "prvTraceStoreXID: Handle < 256", TRC_UNUSED);
\r
2915 xid = (XPSEvent*)prvTraceNextFreeEventBufferSlot();
\r
2921 /* This function is (only) used when traceHandle is 16 bit... */
\r
2922 xid->xps_16 = handle;
\r
2924 prvTraceUpdateCounters();
\r
2928 static uint8_t prvTraceGet8BitHandle(traceHandle handle)
\r
2932 prvTraceStoreXID(handle);
\r
2933 /* The full handle (16 bit) is stored in the XID event.
\r
2934 This code (255) is used instead of zero (which is an error code).*/
\r
2937 return (uint8_t)(handle & 0xFF);
\r
2939 #endif /*(TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)*/
\r
2942 /* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */
\r
2943 #ifndef TRC_CFG_ARM_CM_USE_SYSTICK
\r
2944 #if ((TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M) && (defined (__CORTEX_M) && (__CORTEX_M >= 0x03)))
\r
2945 void prvTraceInitCortexM()
\r
2947 /* Ensure that the DWT registers are unlocked and can be modified. */
\r
2948 TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK;
\r
2950 /* Make sure DWT is enabled, if supported */
\r
2951 TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;
\r
2954 /* Verify that DWT is supported */
\r
2955 if (TRC_REG_DEMCR == 0)
\r
2957 /* This function is called on Cortex-M3, M4 and M7 devices to initialize
\r
2958 the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
\r
2960 If the below error is produced, the DWT unit does not seem to be available.
\r
2962 In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
\r
2963 to use SysTick timestamping instead, or define your own timestamping by
\r
2964 setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
\r
2965 and make the necessary definitions, as explained in trcHardwarePort.h.*/
\r
2967 prvTraceError("DWT unit not available, see code comment.");
\r
2971 /* Verify that DWT_CYCCNT is supported */
\r
2972 if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT)
\r
2974 /* This function is called on Cortex-M3, M4 and M7 devices to initialize
\r
2975 the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
\r
2977 If the below error is produced, the cycle counter does not seem to be available.
\r
2979 In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
\r
2980 to use SysTick timestamping instead, or define your own timestamping by
\r
2981 setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
\r
2982 and make the necessary definitions, as explained in trcHardwarePort.h.*/
\r
2984 prvTraceError("DWT_CYCCNT not available, see code comment.");
\r
2988 /* Reset the cycle counter */
\r
2989 TRC_REG_DWT_CYCCNT = 0;
\r
2991 /* Enable the cycle counter */
\r
2992 TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;
\r
2994 }while(0); /* breaks above jump here */
\r
2999 /******************************************************************************
\r
3000 * prvTracePortGetTimeStamp
\r
3002 * Returns the current time based on the HWTC macros which provide a hardware
\r
3003 * isolation layer towards the hardware timer/counter.
\r
3005 * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
\r
3006 * or the trace recorder library. Typically you should not need to change
\r
3007 * the code of prvTracePortGetTimeStamp if using the HWTC macros.
\r
3009 ******************************************************************************/
\r
3010 void prvTracePortGetTimeStamp(uint32_t *pTimestamp)
\r
3012 static uint32_t last_hwtc_count = 0;
\r
3013 uint32_t hwtc_count = 0;
\r
3015 #if TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR
\r
3016 /* systick based timer */
\r
3017 static uint32_t last_traceTickCount = 0;
\r
3018 uint32_t traceTickCount = 0;
\r
3019 #else /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
\r
3020 /* Free running timer */
\r
3021 static uint32_t last_hwtc_rest = 0;
\r
3022 uint32_t diff = 0;
\r
3023 uint32_t diff_scaled = 0;
\r
3024 #endif /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
\r
3026 if (trace_disable_timestamp == 1)
\r
3029 *pTimestamp = last_timestamp;
\r
3033 /* Retrieve TRC_HWTC_COUNT only once since the same value should be used all throughout this function. */
\r
3034 #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR)
\r
3035 /* Get the increasing tick count */
\r
3036 hwtc_count = TRC_HWTC_COUNT;
\r
3037 #elif (TRC_HWTC_TYPE == TRC_OS_TIMER_DECR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR)
\r
3038 /* Convert decreasing tick count into increasing tick count */
\r
3039 hwtc_count = TRC_HWTC_PERIOD - TRC_HWTC_COUNT;
\r
3041 #error "TRC_HWTC_TYPE has unexpected value"
\r
3044 #if (TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_Win32)
\r
3045 /* The Win32 port uses ulGetRunTimeCounterValue for timestamping, which in turn
\r
3046 uses QueryPerformanceCounter. That function is not always reliable when used over
\r
3047 multiple threads. We must therefore handle rare cases where the timestamp is less
\r
3048 than the previous. In practice, this should "never" roll over since the
\r
3049 performance counter is 64 bit wide. */
\r
3051 if (last_hwtc_count > hwtc_count)
\r
3053 hwtc_count = last_hwtc_count;
\r
3057 #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)
\r
3058 /* Timestamping is based on a timer that wraps at TRC_HWTC_PERIOD */
\r
3059 if (last_traceTickCount - uiTraceTickCount - 1 < 0x80000000)
\r
3061 /* This means last_traceTickCount is higher than uiTraceTickCount,
\r
3062 so we have previously compensated for a missed tick.
\r
3063 Therefore we use the last stored value because that is more accurate. */
\r
3064 traceTickCount = last_traceTickCount;
\r
3068 /* Business as usual */
\r
3069 traceTickCount = uiTraceTickCount;
\r
3072 /* Check for overflow. May occur if the update of uiTraceTickCount has been
\r
3073 delayed due to disabled interrupts. */
\r
3074 if (traceTickCount == last_traceTickCount && hwtc_count < last_hwtc_count)
\r
3076 /* A trace tick has occurred but not been executed by the kernel, so we compensate manually. */
\r
3080 /* Check if the return address is OK, then we perform the calculation. */
\r
3083 /* Get timestamp from trace ticks. Scale down the period to avoid unwanted overflows. */
\r
3084 last_timestamp = traceTickCount * (TRC_HWTC_PERIOD / TRC_HWTC_DIVISOR);
\r
3085 /* Increase timestamp by (hwtc_count + "lost hardware ticks from scaling down period") / TRC_HWTC_DIVISOR. */
\r
3086 last_timestamp += (hwtc_count + traceTickCount * (TRC_HWTC_PERIOD % TRC_HWTC_DIVISOR)) / TRC_HWTC_DIVISOR;
\r
3088 /* Store the previous value */
\r
3089 last_traceTickCount = traceTickCount;
\r
3091 #else /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
\r
3093 /* Timestamping is based on a free running timer */
\r
3094 /* This part handles free running clocks that can be scaled down to avoid too large DTS values.
\r
3095 Without this, the scaled timestamp will incorrectly wrap at (2^32 / TRC_HWTC_DIVISOR) ticks.
\r
3096 The scaled timestamp returned from this function is supposed to go from 0 -> 2^32, which in real time would represent (0 -> 2^32 * TRC_HWTC_DIVISOR) ticks. */
\r
3098 /* First we see how long time has passed since the last timestamp call, and we also add the ticks that was lost when we scaled down the last time. */
\r
3099 diff = (hwtc_count - last_hwtc_count) + last_hwtc_rest;
\r
3101 /* Scale down the diff */
\r
3102 diff_scaled = diff / TRC_HWTC_DIVISOR;
\r
3104 /* Find out how many ticks were lost when scaling down, so we can add them the next time */
\r
3105 last_hwtc_rest = diff % TRC_HWTC_DIVISOR;
\r
3107 /* We increase the scaled timestamp by the scaled amount */
\r
3108 last_timestamp += diff_scaled;
\r
3109 #endif /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
\r
3111 /* Is anyone interested in the results? */
\r
3113 *pTimestamp = last_timestamp;
\r
3115 /* Store the previous value */
\r
3116 last_hwtc_count = hwtc_count;
\r
3119 #endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/
\r
3121 #endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)*/
\r