1 /*******************************************************************************
\r
2 * Trace Recorder Library for Tracealyzer v4.1.5
\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, 2018.
\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
70 /*******************************************************************************
\r
73 * This variable is should be updated by the Kernel tick interrupt. This does
\r
74 * not need to be modified when developing a new timer port. It is preferred to
\r
75 * keep any timer port changes in the HWTC macro definitions, which typically
\r
76 * give sufficient flexibility.
\r
77 ******************************************************************************/
\r
78 uint32_t uiTraceTickCount = 0;
\r
80 uint32_t trace_disable_timestamp = 0;
\r
82 static uint32_t last_timestamp = 0;
\r
84 /* Flag that shows if inside a critical section of the recorder */
\r
85 volatile int recorder_busy = 0;
\r
87 /* Holds the value set by vTraceSetFrequency */
\r
88 uint32_t timestampFrequency = 0;
\r
90 /* The last error message of the recorder. NULL if no error message. */
\r
91 const char* traceErrorMessage = NULL;
\r
93 int8_t nISRactive = 0;
\r
95 traceHandle handle_of_last_logged_task = 0;
\r
97 /* Called when the recorder is stopped, set by vTraceSetStopHook. */
\r
98 TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0;
\r
100 uint16_t CurrentFilterMask = 0xFFFF;
\r
102 uint16_t CurrentFilterGroup = FilterGroup0;
\r
104 extern int8_t nISRactive;
\r
106 extern traceHandle handle_of_last_logged_task;
\r
108 /*************** Private Functions *******************************************/
\r
109 static void prvStrncpy(char* dst, const char* src, uint32_t maxLength);
\r
110 static uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id);
\r
111 static void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength);
\r
112 static void* prvTraceNextFreeEventBufferSlot(void);
\r
113 static uint16_t prvTraceGetDTS(uint16_t param_maxDTS);
\r
114 static traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel);
\r
115 static void prvTraceUpdateCounters(void);
\r
117 void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size);
\r
119 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
120 static void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nEntries);
\r
123 static traceString prvTraceCreateSymbolTableEntry(const char* name,
\r
126 traceString channel);
\r
128 static traceString prvTraceLookupSymbolTableEntry(const char* name,
\r
131 traceString channel);
\r
134 #if (TRC_CFG_INCLUDE_ISR_TRACING == 0)
\r
135 /* ISR tracing is turned off */
\r
136 void prvTraceIncreaseISRActive(void);
\r
137 void prvTraceDecreaseISRActive(void);
\r
138 #endif /*(TRC_CFG_INCLUDE_ISR_TRACING == 0)*/
\r
140 #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
\r
141 static uint8_t prvTraceGet8BitHandle(traceHandle handle);
\r
143 #define prvTraceGet8BitHandle(x) ((uint8_t)x)
\r
147 #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1) && (TRC_CFG_SCHEDULING_ONLY == 0)
\r
148 static uint32_t heapMemUsage = 0;
\r
151 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
152 static uint32_t prvTraceGetParam(uint32_t, uint32_t);
\r
155 /*******************************************************************************
\r
156 * prvTraceInitTraceData
\r
158 * Allocates and initializes the recorder data structure, based on the constants
\r
159 * in trcConfig.h. This allows for allocating the data on the heap, instead of
\r
160 * using a static declaration.
\r
161 ******************************************************************************/
\r
162 static void prvTraceInitTraceData(void);
\r
164 /*******************************************************************************
\r
165 * prvTracePortGetTimeStamp
\r
167 * Returns the current time based on the HWTC macros which provide a hardware
\r
168 * isolation layer towards the hardware timer/counter.
\r
170 * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
\r
171 * or the trace recorder library. Typically you should not need to change
\r
172 * the code of prvTracePortGetTimeStamp if using the HWTC macros.
\r
174 ******************************************************************************/
\r
175 void prvTracePortGetTimeStamp(uint32_t *puiTimestamp);
\r
177 static void prvTraceTaskInstanceFinish(int8_t direct);
\r
179 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
180 static void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl);
\r
182 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
183 static void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl);
\r
184 static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl);
\r
185 static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots);
\r
186 #endif /*(TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)*/
\r
187 #endif /* ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) */
\r
189 /********* Public Functions **************************************************/
\r
191 uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass);
\r
193 /*******************************************************************************
\r
196 * Called by various parts in the recorder. Stops the recorder and stores a
\r
197 * pointer to an error message, which is printed by the monitor task.
\r
198 ******************************************************************************/
\r
199 void prvTraceError(const char* msg);
\r
201 /******************************************************************************
\r
202 * vTraceEnable(int startOption) - snapshot mode
\r
204 * Initializes and optionally starts the trace, depending on the start option.
\r
205 * To use the trace recorder, the startup must call vTraceEnable before any RTOS
\r
206 * calls are made (including "create" calls). Three start options are provided:
\r
208 * TRC_START: Starts the tracing directly. In snapshot mode this allows for
\r
209 * starting the trace at any point in your code, assuming vTraceEnable(TRC_INIT)
\r
210 * has been called in the startup.
\r
211 * Can also be used for streaming without Tracealyzer control, e.g. to a local
\r
212 * flash file system (assuming such a "stream port", see trcStreamingPort.h).
\r
214 * TRC_INIT: Initializes the trace recorder, but does not start the tracing.
\r
215 * In snapshot mode, this must be followed by a vTraceEnable(TRC_START) sometime
\r
218 * Usage examples, in snapshot mode:
\r
220 * Snapshot trace, from startup:
\r
222 * vTraceEnable(TRC_START);
\r
225 * Snapshot trace, from a later point:
\r
227 * vTraceEnable(TRC_INIT);
\r
230 * vTraceEnable(TRC_START); // e.g., in task context, at some relevant event
\r
233 * Note: See other implementation of vTraceEnable in trcStreamingRecorder.c
\r
234 ******************************************************************************/
\r
235 void vTraceEnable(int startOption)
\r
237 prvTraceInitTraceData();
\r
239 if (startOption == TRC_START)
\r
243 else if (startOption == TRC_START_AWAIT_HOST)
\r
245 prvTraceError("vTraceEnable(TRC_START_AWAIT_HOST) not allowed in Snapshot mode");
\r
247 else if (startOption != TRC_INIT)
\r
249 prvTraceError("Unexpected argument to vTraceEnable (snapshot mode)");
\r
253 /*******************************************************************************
\r
254 * vTraceSetRecorderDataBuffer
\r
256 * If custom allocation is used, this function must be called so the recorder
\r
257 * library knows where to save the trace data.
\r
258 ******************************************************************************/
\r
259 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
\r
260 void vTraceSetRecorderDataBuffer(void* pRecorderData)
\r
262 TRACE_ASSERT(pRecorderData != NULL, "vTraceSetRecorderDataBuffer, pRecorderData == NULL", TRC_UNUSED);
\r
263 RecorderDataPtr = pRecorderData;
\r
267 /*******************************************************************************
\r
268 * vTraceSetStopHook
\r
270 * Sets a function to be called when the recorder is stopped. This can be used
\r
271 * to save the trace to a file system, if available. This is only implemented
\r
272 * for snapshot mode.
\r
273 ******************************************************************************/
\r
274 void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction)
\r
276 vTraceStopHookPtr = stopHookFunction;
\r
279 /*******************************************************************************
\r
282 * Resets the recorder. Only necessary if a restart is desired - this is not
\r
283 * needed in the startup initialization.
\r
284 ******************************************************************************/
\r
285 void vTraceClear(void)
\r
287 TRACE_ALLOC_CRITICAL_SECTION();
\r
288 trcCRITICAL_SECTION_BEGIN();
\r
289 RecorderDataPtr->absTimeLastEventSecond = 0;
\r
290 RecorderDataPtr->absTimeLastEvent = 0;
\r
291 RecorderDataPtr->nextFreeIndex = 0;
\r
292 RecorderDataPtr->numEvents = 0;
\r
293 RecorderDataPtr->bufferIsFull = 0;
\r
294 traceErrorMessage = NULL;
\r
295 RecorderDataPtr->internalErrorOccured = 0;
\r
296 (void)memset(RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4);
\r
297 handle_of_last_logged_task = 0;
\r
298 trcCRITICAL_SECTION_END();
\r
301 /*******************************************************************************
\r
304 * Starts the recorder. The recorder will not be started if an error has been
\r
305 * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
\r
306 * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
\r
308 * Returns 1 if the recorder was started successfully.
\r
309 * Returns 0 if the recorder start was prevented due to a previous internal
\r
310 * error. In that case, check xTraceGetLastError to get the error message.
\r
311 * Any error message is also presented when opening a trace file.
\r
313 * This function is obsolete, but has been saved for backwards compatibility.
\r
314 * We recommend using vTraceEnable instead.
\r
315 ******************************************************************************/
\r
316 uint32_t uiTraceStart(void)
\r
318 traceHandle handle;
\r
319 TRACE_ALLOC_CRITICAL_SECTION();
\r
323 if (RecorderDataPtr == NULL)
\r
325 TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized. Use vTraceEnable() instead!", 0);
\r
329 if (RecorderDataPtr->recorderActive == 1)
\r
330 return 1; /* Already running */
\r
332 if (traceErrorMessage == NULL)
\r
334 trcCRITICAL_SECTION_BEGIN();
\r
335 RecorderDataPtr->recorderActive = 1;
\r
337 handle = TRACE_GET_TASK_NUMBER(TRACE_GET_CURRENT_TASK());
\r
340 /* This occurs if the scheduler is not yet started.
\r
341 This creates a dummy "(startup)" task entry internally in the
\r
343 handle = prvTraceGetObjectHandle(TRACE_CLASS_TASK);
\r
344 prvTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)");
\r
346 prvTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0);
\r
349 prvTraceStoreTaskswitch(handle); /* Register the currently running task */
\r
350 trcCRITICAL_SECTION_END();
\r
353 return RecorderDataPtr->recorderActive;
\r
356 /*******************************************************************************
\r
359 * Starts the recorder. The recorder will not be started if an error has been
\r
360 * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
\r
361 * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
\r
363 * This function is obsolete, but has been saved for backwards compatibility.
\r
364 * We recommend using vTraceEnable instead.
\r
365 ******************************************************************************/
\r
366 void vTraceStart(void)
\r
368 (void)uiTraceStart();
\r
371 /*******************************************************************************
\r
374 * Stops the recorder. The recording can be resumed by calling vTraceStart.
\r
375 * This does not reset the recorder. Use vTraceClear if that is desired.
\r
376 ******************************************************************************/
\r
377 void vTraceStop(void)
\r
379 if (RecorderDataPtr != NULL)
\r
381 RecorderDataPtr->recorderActive = 0;
\r
384 if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0)
\r
386 (*vTraceStopHookPtr)(); /* An application call-back function. */
\r
390 /*******************************************************************************
\r
391 * xTraceIsRecordingEnabled
\r
392 * Returns true (1) if the recorder is enabled (i.e. is recording), otherwise 0.
\r
393 ******************************************************************************/
\r
394 int xTraceIsRecordingEnabled(void)
\r
396 if (RecorderDataPtr != NULL)
\r
398 return (int)RecorderDataPtr->recorderActive;
\r
406 /*******************************************************************************
\r
407 * xTraceGetLastError
\r
409 * Gives the last error message, if any. NULL if no error message is stored.
\r
410 * Any error message is also presented when opening a trace file.
\r
411 ******************************************************************************/
\r
412 const char* xTraceGetLastError(void)
\r
414 return traceErrorMessage;
\r
417 /*******************************************************************************
\r
420 * Removes any previous error message generated by recorder calling prvTraceError.
\r
421 * By calling this function, it may be possible to start/restart the trace
\r
422 * despite errors in the recorder, but there is no guarantee that the trace
\r
423 * recorder will work correctly in that case, depending on the type of error.
\r
424 ******************************************************************************/
\r
425 void vTraceClearError(void)
\r
427 traceErrorMessage = NULL;
\r
428 if (RecorderDataPtr != NULL)
\r
430 RecorderDataPtr->internalErrorOccured = 0;
\r
434 /*******************************************************************************
\r
435 * xTraceGetTraceBuffer
\r
437 * Returns a pointer to the recorder data structure. Use this together with
\r
438 * uiTraceGetTraceBufferSize if you wish to implement an own store/upload
\r
439 * solution, e.g., in case a debugger connection is not available for uploading
\r
441 ******************************************************************************/
\r
442 void* xTraceGetTraceBuffer(void)
\r
444 return RecorderDataPtr;
\r
447 /*******************************************************************************
\r
448 * uiTraceGetTraceBufferSize
\r
450 * Gets the size of the recorder data structure. For use together with
\r
451 * vTraceGetTraceBuffer if you wish to implement an own store/upload solution,
\r
452 * e.g., in case a debugger connection is not available for uploading the data.
\r
453 ******************************************************************************/
\r
454 uint32_t uiTraceGetTraceBufferSize(void)
\r
456 return sizeof(RecorderDataType);
\r
459 /******************************************************************************
\r
460 * prvTraceTaskInstanceFinish
\r
462 * Private common function for the vTraceTaskInstanceFinishXXX functions.
\r
463 *****************************************************************************/
\r
464 static void prvTraceTaskInstanceFinish(int8_t direct)
\r
466 TaskInstanceStatusEvent* tis;
\r
469 TRACE_ALLOC_CRITICAL_SECTION();
\r
471 trcCRITICAL_SECTION_BEGIN();
\r
472 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
474 dts45 = (uint8_t)prvTraceGetDTS(0xFF);
\r
475 tis = (TaskInstanceStatusEvent*) prvTraceNextFreeEventBufferSlot();
\r
479 tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE;
\r
481 tis->type = TASK_INSTANCE_FINISHED_DIRECT;
\r
484 prvTraceUpdateCounters();
\r
487 trcCRITICAL_SECTION_END();
\r
490 /******************************************************************************
\r
491 * vTraceInstanceFinishedNext(void)
\r
493 * Marks the current task instance as finished on the next kernel call.
\r
495 * If that kernel call is blocking, the instance ends after the blocking event
\r
496 * and the corresponding return event is then the start of the next instance.
\r
497 * If the kernel call is not blocking, the viewer instead splits the current
\r
498 * fragment right before the kernel call, which makes this call the first event
\r
499 * of the next instance.
\r
501 * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
\r
507 * xQueueReceive(CommandQueue, &command, timeoutDuration);
\r
508 * processCommand(command);
\r
509 * vTraceInstanceFinishedNext();
\r
511 *****************************************************************************/
\r
512 void vTraceInstanceFinishedNext(void)
\r
514 prvTraceTaskInstanceFinish(0);
\r
517 /******************************************************************************
\r
518 * vTraceInstanceFinishedNow(void)
\r
520 * Marks the current task instance as finished at this very instant.
\r
521 * This makes the viewer to splits the current fragment at this point and begin
\r
522 * a new actor instance.
\r
524 * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
\r
528 * This example will generate two instances for each loop iteration.
\r
529 * The first instance ends at vTraceInstanceFinishedNow(), while the second
\r
530 * instance ends at the next xQueueReceive call.
\r
534 * xQueueReceive(CommandQueue, &command, timeoutDuration);
\r
535 * ProcessCommand(command);
\r
536 * vTraceInstanceFinishedNow();
\r
537 * DoSometingElse();
\r
538 * vTraceInstanceFinishedNext();
\r
540 *****************************************************************************/
\r
541 void vTraceInstanceFinishedNow(void)
\r
543 prvTraceTaskInstanceFinish(1);
\r
546 /*******************************************************************************
\r
547 * Interrupt recording functions
\r
548 ******************************************************************************/
\r
550 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
552 /*******************************************************************************
\r
553 * xTraceSetISRProperties
\r
555 * Stores a name and priority level for an Interrupt Service Routine, to allow
\r
556 * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin.
\r
559 * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
561 * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
\r
563 * void ISR_handler()
\r
565 * vTraceStoreISRBegin(Timer1Handle);
\r
567 * vTraceStoreISREnd(0);
\r
569 ******************************************************************************/
\r
570 traceHandle xTraceSetISRProperties(const char* name, uint8_t priority)
\r
572 static traceHandle handle = 0;
\r
573 TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
\r
574 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "xTraceSetISRProperties: Invalid value for handle", 0);
\r
575 TRACE_ASSERT(name != NULL, "xTraceSetISRProperties: name == NULL", 0);
\r
579 prvTraceSetObjectName(TRACE_CLASS_ISR, handle, name);
\r
580 prvTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);
\r
585 /*******************************************************************************
\r
586 * vTraceStoreISRBegin
\r
588 * Registers the beginning of an Interrupt Service Routine, using a traceHandle
\r
589 * provided by xTraceSetISRProperties.
\r
592 * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
594 * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
\r
596 * void ISR_handler()
\r
598 * vTraceStoreISRBegin(Timer1Handle);
\r
600 * vTraceStoreISREnd(0);
\r
602 ******************************************************************************/
\r
603 void vTraceStoreISRBegin(traceHandle handle)
\r
605 TRACE_ALLOC_CRITICAL_SECTION();
\r
609 /*************************************************************************
\r
610 * This occurs if an ISR calls a trace function, preempting a previous
\r
611 * trace call that is being processed in a different ISR or task.
\r
612 * If this occurs, there is probably a problem in the definition of the
\r
613 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
614 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
615 * and any other ISRs that calls the trace recorder directly or via
\r
616 * traced kernel functions. The ARM port disables all interrupts using the
\r
617 * PRIMASK register to avoid this issue.
\r
618 *************************************************************************/
\r
619 prvTraceError("vTraceStoreISRBegin - recorder busy! See code comment.");
\r
622 trcCRITICAL_SECTION_BEGIN();
\r
624 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
628 TRACE_ASSERT(handle != 0, "vTraceStoreISRBegin: Invalid ISR handle (NULL)", TRC_UNUSED);
\r
629 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid ISR handle (> NISR)", TRC_UNUSED);
\r
631 dts4 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
633 if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
\r
635 if (nISRactive < TRC_CFG_MAX_ISR_NESTING)
\r
638 uint8_t hnd8 = prvTraceGet8BitHandle(handle);
\r
639 isrstack[nISRactive] = handle;
\r
641 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
644 ts->type = TS_ISR_BEGIN;
\r
646 ts->objHandle = hnd8;
\r
647 prvTraceUpdateCounters();
\r
652 /* This should not occur unless something is very wrong */
\r
653 prvTraceError("Too many nested interrupts!");
\r
657 trcCRITICAL_SECTION_END();
\r
660 /*******************************************************************************
\r
661 * vTraceStoreISREnd
\r
663 * Registers the end of an Interrupt Service Routine.
\r
665 * The parameter pendingISR indicates if the interrupt has requested a
\r
666 * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the
\r
667 * interrupt is assumed to return to the previous context.
\r
670 * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
\r
671 * traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
\r
673 * traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
\r
675 * void ISR_handler()
\r
677 * vTraceStoreISRBegin(traceHandleIsrTimer1);
\r
679 * vTraceStoreISREnd(0);
\r
681 ******************************************************************************/
\r
682 void vTraceStoreISREnd(int pendingISR)
\r
686 uint8_t hnd8 = 0, type = 0;
\r
688 TRACE_ALLOC_CRITICAL_SECTION();
\r
690 if (! RecorderDataPtr->recorderActive || ! handle_of_last_logged_task)
\r
697 /*************************************************************************
\r
698 * This occurs if an ISR calls a trace function, preempting a previous
\r
699 * trace call that is being processed in a different ISR or task.
\r
700 * If this occurs, there is probably a problem in the definition of the
\r
701 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
702 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
703 * and any other ISRs that calls the trace recorder directly or via
\r
704 * traced kernel functions. The ARM port disables all interrupts using the
\r
705 * PRIMASK register to avoid this issue.
\r
706 *************************************************************************/
\r
707 prvTraceError("vTraceStoreISREnd - recorder busy! See code comment.");
\r
711 if (nISRactive == 0)
\r
713 prvTraceError("Unmatched call to vTraceStoreISREnd (nISRactive == 0, expected > 0)");
\r
717 trcCRITICAL_SECTION_BEGIN();
\r
718 isPendingContextSwitch |= pendingISR; /* Is there a pending context switch right now? */
\r
720 if (nISRactive > 0)
\r
722 /* Return to another ISR */
\r
723 type = TS_ISR_RESUME;
\r
724 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
726 else if ((isPendingContextSwitch == 0) || (prvTraceIsSchedulerSuspended()))
\r
728 /* Return to interrupted task, if no context switch will occur in between. */
\r
729 type = TS_TASK_RESUME;
\r
730 hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
\r
735 dts5 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
736 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
740 ts->objHandle = hnd8;
\r
742 prvTraceUpdateCounters();
\r
746 trcCRITICAL_SECTION_END();
\r
751 /* ISR tracing is turned off */
\r
752 void prvTraceIncreaseISRActive(void)
\r
754 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
758 void prvTraceDecreaseISRActive(void)
\r
760 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
763 #endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1)*/
\r
766 /********************************************************************************/
\r
767 /* User Event functions */
\r
768 /********************************************************************************/
\r
770 #define MAX_ARG_SIZE (4+32)
\r
772 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
773 static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value)
\r
775 TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0);
\r
777 if (i >= MAX_ARG_SIZE)
\r
782 ((uint8_t*)buffer)[i] = value;
\r
784 if (i + 1 > MAX_ARG_SIZE)
\r
789 return ((uint8_t) (i + 1));
\r
793 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
794 static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value)
\r
796 TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0);
\r
798 /* Align to multiple of 2 */
\r
799 while ((i % 2) != 0)
\r
801 if (i >= MAX_ARG_SIZE)
\r
806 ((uint8_t*)buffer)[i] = 0;
\r
810 if (i + 2 > MAX_ARG_SIZE)
\r
815 ((uint16_t*)buffer)[i/2] = value;
\r
817 return ((uint8_t) (i + 2));
\r
821 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
822 static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value)
\r
824 TRACE_ASSERT(buffer != NULL, "writeInt32: buffer == NULL", 0);
\r
826 /* A 32 bit value should begin at an even 4-byte address */
\r
827 while ((i % 4) != 0)
\r
829 if (i >= MAX_ARG_SIZE)
\r
834 ((uint8_t*)buffer)[i] = 0;
\r
838 if (i + 4 > MAX_ARG_SIZE)
\r
843 ((uint32_t*)buffer)[i/4] = value;
\r
845 return ((uint8_t) (i + 4));
\r
849 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
\r
850 static uint8_t writeFloat(void * buffer, uint8_t i, float value)
\r
852 TRACE_ASSERT(buffer != NULL, "writeFloat: buffer == NULL", 0);
\r
854 /* A 32 bit value should begin at an even 4-byte address */
\r
855 while ((i % 4) != 0)
\r
857 if (i >= MAX_ARG_SIZE)
\r
862 ((uint8_t*)buffer)[i] = 0;
\r
866 if (i + 4 > MAX_ARG_SIZE)
\r
871 ((float*)buffer)[i/4] = value;
\r
877 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
\r
878 static uint8_t writeDouble(void * buffer, uint8_t i, double value)
\r
881 uint32_t * src = (uint32_t*)&value;
\r
883 TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0);
\r
885 /* The double is written as two 32 bit values, and should begin at an even
\r
886 4-byte address (to avoid having to align with 8 byte) */
\r
889 if (i >= MAX_ARG_SIZE)
\r
894 ((uint8_t*)buffer)[i] = 0;
\r
898 if (i + 8 > MAX_ARG_SIZE)
\r
903 dest = &(((uint32_t *)buffer)[i/4]);
\r
912 /*******************************************************************************
\r
913 * prvTraceUserEventFormat
\r
915 * Parses the format string and stores the arguments in the buffer.
\r
916 ******************************************************************************/
\r
917 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
918 static uint8_t prvTraceUserEventFormat(const char* formatStr, va_list vl, uint8_t* buffer, uint8_t byteOffset)
\r
920 uint16_t formatStrIndex = 0;
\r
921 uint8_t argCounter = 0;
\r
922 uint8_t i = byteOffset;
\r
924 while (formatStr[formatStrIndex] != '\0')
\r
926 if (formatStr[formatStrIndex] == '%')
\r
930 if (argCounter > 15)
\r
932 prvTraceError("vTracePrintF - Too many arguments, max 15 allowed!");
\r
938 while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.')
\r
941 if (formatStr[formatStrIndex] != '\0')
\r
943 switch (formatStr[formatStrIndex])
\r
945 case 'd': i = writeInt32( buffer,
\r
947 (uint32_t)va_arg(vl, uint32_t));
\r
951 case 'u': i = writeInt32( buffer,
\r
953 (uint32_t)va_arg(vl, uint32_t));
\r
955 case 's': i = writeInt16( buffer,
\r
957 xTraceRegisterString((char*)va_arg(vl, char*)));
\r
960 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
\r
961 /* Yes, "double" as type also in the float
\r
962 case. This since "float" is promoted into "double"
\r
963 by the va_arg stuff. */
\r
964 case 'f': i = writeFloat( buffer,
\r
966 (float)va_arg(vl, double));
\r
969 /* No support for floats, but attempt to store a float user event
\r
970 avoid a possible crash due to float reference. Instead store the
\r
971 data on uint_32 format (will not be displayed anyway). This is just
\r
972 to keep va_arg and i consistent. */
\r
974 case 'f': i = writeInt32( buffer,
\r
976 (uint32_t)va_arg(vl, double));
\r
981 switch (formatStr[formatStrIndex])
\r
983 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
\r
984 case 'f': i = writeDouble(buffer,
\r
986 (double)va_arg(vl, double));
\r
989 /* No support for floats, but attempt to store a float user event
\r
990 avoid a possible crash due to float reference. Instead store the
\r
991 data on uint_32 format (will not be displayed anyway). This is just
\r
992 to keep va_arg and i consistent. */
\r
993 case 'f': i = writeInt32( buffer, /* In this case, the value will not be shown anyway */
\r
995 (uint32_t)va_arg(vl, double));
\r
997 i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */
\r
999 (uint32_t)va_arg(vl, double));
\r
1007 switch (formatStr[formatStrIndex])
\r
1009 case 'd': i = writeInt16( buffer,
\r
1011 (uint16_t)va_arg(vl, uint32_t));
\r
1013 case 'u': i = writeInt16( buffer,
\r
1015 (uint16_t)va_arg(vl, uint32_t));
\r
1021 switch (formatStr[formatStrIndex])
\r
1023 case 'd': i = writeInt8( buffer,
\r
1025 (uint8_t)va_arg(vl, uint32_t));
\r
1028 case 'u': i = writeInt8( buffer,
\r
1030 (uint8_t)va_arg(vl, uint32_t));
\r
1042 prvTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!");
\r
1046 return (uint8_t)(i+3)/4;
\r
1050 /*******************************************************************************
\r
1051 * prvTraceClearChannelBuffer
\r
1053 * Clears a number of items in the channel buffer, starting from nextSlotToWrite.
\r
1054 ******************************************************************************/
\r
1055 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1056 static void prvTraceClearChannelBuffer(uint32_t count)
\r
1060 TRACE_ASSERT((TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) >= count,
\r
1061 "prvTraceClearChannelBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
\r
1063 /* Check if we're close to the end of the buffer */
\r
1064 if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE))
\r
1066 slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
\r
1067 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, slots);
\r
1068 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[0], 0, (count - slots));
\r
1071 (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, count);
\r
1075 /*******************************************************************************
\r
1076 * prvTraceCopyToDataBuffer
\r
1078 * Copies a number of items to the data buffer, starting from nextSlotToWrite.
\r
1079 ******************************************************************************/
\r
1080 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1081 static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count)
\r
1085 TRACE_ASSERT(data != NULL,
\r
1086 "prvTraceCopyToDataBuffer: data == NULL.", TRC_UNUSED);
\r
1087 TRACE_ASSERT(count <= (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE),
\r
1088 "prvTraceCopyToDataBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
\r
1089 /* Check if we're close to the end of the buffer */
\r
1090 if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE))
\r
1092 slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
\r
1093 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, slots * 4);
\r
1094 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[0], data + slots, (count - slots) * 4);
\r
1098 (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, count * 4);
\r
1103 /*******************************************************************************
\r
1104 * prvTraceUBHelper1
\r
1106 * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on
\r
1107 * to the next helper function.
\r
1108 ******************************************************************************/
\r
1109 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1110 static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl)
\r
1112 uint32_t data[(3 + MAX_ARG_SIZE) / 4];
\r
1113 uint8_t byteOffset = 4; /* Need room for timestamp */
\r
1114 uint8_t noOfSlots;
\r
1118 /* We are dealing with an unknown channel format pair */
\r
1119 byteOffset = (uint8_t)(byteOffset + 4); /* Also need room for channel and format */
\r
1120 ((uint16_t*)data)[2] = eventLabel;
\r
1121 ((uint16_t*)data)[3] = formatLabel;
\r
1124 noOfSlots = prvTraceUserEventFormat((char*)&(RecorderDataPtr->SymbolTable.symbytes[formatLabel+4]), vl, (uint8_t*)data, byteOffset);
\r
1126 prvTraceUBHelper2(channel, data, noOfSlots);
\r
1130 /*******************************************************************************
\r
1131 * prvTraceUBHelper2
\r
1133 * This function simply copies the data buffer to the actual user event buffer.
\r
1134 ******************************************************************************/
\r
1135 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1136 static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots)
\r
1138 static uint32_t old_timestamp = 0;
\r
1139 uint32_t old_nextSlotToWrite = 0;
\r
1141 TRACE_ALLOC_CRITICAL_SECTION();
\r
1143 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
1145 trcCRITICAL_SECTION_BEGIN();
\r
1146 /* Store the timestamp */
\r
1147 prvTracePortGetTimeStamp(data);
\r
1149 if (*data < old_timestamp)
\r
1151 RecorderDataPtr->userEventBuffer.wraparoundCounter++;
\r
1154 old_timestamp = *data;
\r
1156 /* Start by erasing any information in the channel buffer */
\r
1157 prvTraceClearChannelBuffer(noOfSlots);
\r
1159 prvTraceCopyToDataBuffer(data, noOfSlots); /* Will wrap around the data if necessary */
\r
1161 old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */
\r
1162 RecorderDataPtr->userEventBuffer.nextSlotToWrite = (RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots) % (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE); /* Make sure we never end up outside the buffer */
\r
1164 /* Write to the channel buffer to indicate that this user event is ready to be used */
\r
1167 RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = channel;
\r
1171 /* 0xFF indicates that this is not a normal channel id */
\r
1172 RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (traceUBChannel)0xFF;
\r
1174 trcCRITICAL_SECTION_END();
\r
1178 /*******************************************************************************
\r
1179 * xTraceRegisterUBChannel
\r
1181 * Registers a channel for Separated User Events, i.e., those stored in the
\r
1182 * separate user event buffer.
\r
1184 * Note: Only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in
\r
1185 * trcSnapshotConfig.h
\r
1186 ******************************************************************************/
\r
1187 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1188 traceUBChannel xTraceRegisterUBChannel(traceString channel, traceString formatStr)
\r
1191 traceUBChannel retVal = 0;
\r
1193 TRACE_ALLOC_CRITICAL_SECTION();
\r
1195 TRACE_ASSERT(formatStr != 0, "xTraceRegisterChannelFormat: formatStr == 0", (traceUBChannel)0);
\r
1197 trcCRITICAL_SECTION_BEGIN();
\r
1198 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
1200 if(RecorderDataPtr->userEventBuffer.channels[i].name == 0 && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == 0)
\r
1202 /* Found empty slot */
\r
1203 RecorderDataPtr->userEventBuffer.channels[i].name = channel;
\r
1204 RecorderDataPtr->userEventBuffer.channels[i].defaultFormat = formatStr;
\r
1205 retVal = (traceUBChannel)i;
\r
1209 if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr)
\r
1211 /* Found a match */
\r
1212 retVal = (traceUBChannel)i;
\r
1216 trcCRITICAL_SECTION_END();
\r
1222 /******************************************************************************
\r
1225 * Slightly faster version of vTracePrintF() due to no lookups.
\r
1227 * Note: This is only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is
\r
1228 * enabled in trcSnapshotConfig.h
\r
1229 ******************************************************************************/
\r
1230 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1231 void vTraceUBData(traceUBChannel channelPair, ...)
\r
1235 TRACE_ASSERT(channelPair != 0, "vTraceUBData: Not a valid traceUBChannel!", TRC_UNUSED);
\r
1237 va_start(vl, channelPair);
\r
1238 vTraceUBData_Helper(channelPair, vl);
\r
1243 /* Extracts the channel name and format string from the traceUBChannel, then calls prvTraceUBHelper1. */
\r
1244 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1245 void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl)
\r
1247 traceString channel;
\r
1248 traceString formatStr;
\r
1250 TRACE_ASSERT(channelPair != 0, "vTraceUBData_Helper: channelPair == 0", TRC_UNUSED);
\r
1251 TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBData_Helper: ", TRC_UNUSED);
\r
1253 channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name;
\r
1254 formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat;
\r
1256 prvTraceUBHelper1(channelPair, channel, formatStr, vl);
\r
1260 /******************************************************************************
\r
1263 * Slightly faster version of ... due to no lookups.
\r
1264 ******************************************************************************/
\r
1265 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
\r
1266 void vTraceUBEvent(traceUBChannel channelPair)
\r
1268 uint32_t data[(3 + MAX_ARG_SIZE) / 4];
\r
1270 TRACE_ASSERT(channelPair != 0, "vTraceUBEvent: channelPair == 0", TRC_UNUSED);
\r
1271 TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBEvent: ", TRC_UNUSED);
\r
1273 prvTraceUBHelper2(channelPair, data, 1); /* Only need one slot for timestamp */
\r
1277 /******************************************************************************
\r
1280 * Generates User Event with formatted text and data, similar to a "printf".
\r
1281 * It is very fast compared to a normal "printf" since this function only
\r
1282 * stores the arguments. The actual formatting is done
\r
1283 * on the host PC when the trace is displayed in the viewer tool.
\r
1285 * User Event labels are created using xTraceRegisterString.
\r
1288 * traceString adc_uechannel = xTraceRegisterString("ADC User Events");
\r
1290 * vTracePrintF(adc_uechannel,
\r
1291 * "ADC channel %d: %lf volts",
\r
1292 * ch, (double)adc_reading/(double)scale);
\r
1294 * This can be combined into one line, if desired, but this is slower:
\r
1296 * vTracePrintF(xTraceRegisterString("ADC User Events"),
\r
1297 * "ADC channel %d: %lf volts",
\r
1298 * ch, (double)adc_reading/(double)scale);
\r
1300 * Calling xTraceRegisterString multiple times will not create duplicate entries, but
\r
1301 * it is of course faster to just do it once, and then keep the handle for later
\r
1302 * use. If you don't have any data arguments, only a text label/string, it is
\r
1303 * better to use vTracePrint - it is faster.
\r
1305 * Format specifiers supported:
\r
1306 * %d - 32 bit signed integer
\r
1307 * %u - 32 bit unsigned integer
\r
1308 * %f - 32 bit float
\r
1309 * %s - string (is copied to the recorder symbol table)
\r
1310 * %hd - 16 bit signed integer
\r
1311 * %hu - 16 bit unsigned integer
\r
1312 * %bd - 8 bit signed integer
\r
1313 * %bu - 8 bit unsigned integer
\r
1314 * %lf - double-precision float (Note! See below...)
\r
1316 * Up to 15 data arguments are allowed, with a total size of maximum 32 byte.
\r
1317 * In case this is exceeded, the user event is changed into an error message.
\r
1319 * The data is stored in trace buffer, and is packed to allow storing multiple
\r
1320 * smaller data entries in the same 4-byte record, e.g., four 8-bit values.
\r
1321 * A string requires two bytes, as the symbol table is limited to 64K. Storing
\r
1322 * a double (%lf) uses two records, so this is quite costly. Use float (%f)
\r
1323 * unless the higher precision is really necessary.
\r
1325 * Note that the double-precision float (%lf) assumes a 64 bit double
\r
1326 * representation. This does not seem to be the case on e.g. PIC24 and PIC32.
\r
1327 * Before using a %lf argument on a 16-bit MCU, please verify that
\r
1328 * "sizeof(double)" actually gives 8 as expected. If not, use %f instead.
\r
1329 ******************************************************************************/
\r
1330 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1331 void vTracePrintF(traceString eventLabel, const char* formatStr, ...)
\r
1335 va_start(vl, formatStr);
\r
1336 vTracePrintF_Helper(eventLabel, formatStr, vl);
\r
1341 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1342 void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl)
\r
1344 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
\r
1345 uint32_t noOfSlots;
\r
1347 uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
\r
1348 TRACE_ALLOC_CRITICAL_SECTION();
\r
1350 TRACE_ASSERT(formatStr != NULL, "vTracePrintF_Helper: formatStr == NULL", TRC_UNUSED);
\r
1352 trcCRITICAL_SECTION_BEGIN();
\r
1354 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
1356 /* First, write the "primary" user event entry in the local buffer, but
\r
1357 let the event type be "EVENT_BEING_WRITTEN" for now...*/
\r
1359 ue1 = (UserEvent*)(&tempDataBuffer[0]);
\r
1361 ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */
\r
1363 noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4);
\r
1365 /* Store the format string, with a reference to the channel symbol */
\r
1366 ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel);
\r
1368 ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);
\r
1370 /* prvTraceGetDTS might stop the recorder in some cases... */
\r
1371 if (RecorderDataPtr->recorderActive)
\r
1374 /* If the data does not fit in the remaining main buffer, wrap around to
\r
1375 0 if allowed, otherwise stop the recorder and quit). */
\r
1376 if (RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents)
\r
1378 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1379 (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
\r
1381 (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4);
\r
1382 RecorderDataPtr->nextFreeIndex = 0;
\r
1383 RecorderDataPtr->bufferIsFull = 1;
\r
1386 /* Stop recorder, since the event data will not fit in the
\r
1387 buffer and not circular buffer in this case... */
\r
1392 /* Check if recorder has been stopped (i.e., vTraceStop above) */
\r
1393 if (RecorderDataPtr->recorderActive)
\r
1395 /* Check that the buffer to be overwritten does not contain any user
\r
1396 events that would be partially overwritten. If so, they must be "killed"
\r
1397 by replacing the user event and following data with NULL events (i.e.,
\r
1398 using a memset to zero).*/
\r
1399 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1400 prvCheckDataToBeOverwrittenForMultiEntryEvents((uint8_t)noOfSlots);
\r
1402 /* Copy the local buffer to the main buffer */
\r
1403 (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
\r
1407 /* Update the event type, i.e., number of data entries following the
\r
1408 main USER_EVENT entry (Note: important that this is after the memcpy,
\r
1409 but within the critical section!)*/
\r
1410 RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] =
\r
1411 (uint8_t) ( USER_EVENT + noOfSlots - 1 );
\r
1413 /* Update the main buffer event index (already checked that it fits in
\r
1414 the buffer, so no need to check for wrapping)*/
\r
1416 RecorderDataPtr->nextFreeIndex += noOfSlots;
\r
1417 RecorderDataPtr->numEvents += noOfSlots;
\r
1419 if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
\r
1421 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1422 /* We have reached the end, but this is a ring buffer. Start from the beginning again. */
\r
1423 RecorderDataPtr->bufferIsFull = 1;
\r
1424 RecorderDataPtr->nextFreeIndex = 0;
\r
1426 /* We have reached the end so we stop. */
\r
1432 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
1433 /* Make sure the next entry is cleared correctly */
\r
1434 prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
\r
1439 trcCRITICAL_SECTION_END();
\r
1441 #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
1442 /* Use the separate user event buffer */
\r
1443 traceString formatLabel;
\r
1444 traceUBChannel channel;
\r
1446 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
1448 formatLabel = xTraceRegisterString(formatStr);
\r
1450 channel = xTraceRegisterUBChannel(eventLabel, formatLabel);
\r
1452 prvTraceUBHelper1(channel, eventLabel, formatLabel, vl);
\r
1458 /******************************************************************************
\r
1461 * Basic user event
\r
1463 * Generates a User Event with a text label. The label is created/looked up
\r
1464 * in the symbol table using xTraceRegisterString.
\r
1465 ******************************************************************************/
\r
1466 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1467 void vTracePrint(traceString chn, const char* str)
\r
1469 #if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
\r
1472 TRACE_ALLOC_CRITICAL_SECTION();
\r
1474 trcCRITICAL_SECTION_BEGIN();
\r
1475 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
1477 dts1 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1478 ue = (UserEvent*) prvTraceNextFreeEventBufferSlot();
\r
1482 ue->type = USER_EVENT;
\r
1483 ue->payload = prvTraceOpenSymbol(str, chn);
\r
1484 prvTraceUpdateCounters();
\r
1487 trcCRITICAL_SECTION_END();
\r
1489 #elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
\r
1490 traceUBChannel channel;
\r
1491 uint32_t noOfSlots = 1;
\r
1492 uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
\r
1493 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
1495 traceString trcStr = prvTraceOpenSymbol(str, chn);
\r
1496 channel = xTraceRegisterUBChannel(chn, trcStr);
\r
1500 /* We are dealing with an unknown channel format pair */
\r
1501 noOfSlots++; /* Also need room for channel and format */
\r
1502 ((uint16_t*)tempDataBuffer)[2] = chn;
\r
1503 ((uint16_t*)tempDataBuffer)[3] = trcStr;
\r
1506 prvTraceUBHelper2(channel, tempDataBuffer, noOfSlots);
\r
1512 /*******************************************************************************
\r
1513 * xTraceRegisterString
\r
1515 * Register strings in the recorder, e.g. for names of user event channels.
\r
1518 * myEventHandle = xTraceRegisterString("MyUserEvent");
\r
1520 * vTracePrintF(myEventHandle, "My value is: %d", myValue);
\r
1521 ******************************************************************************/
\r
1522 #if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
\r
1523 traceString xTraceRegisterString(const char* label)
\r
1525 TRACE_ASSERT(label != NULL, "xTraceRegisterString: label == NULL", (traceString)0);
\r
1526 TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
\r
1527 return prvTraceOpenSymbol(label, 0);
\r
1532 #if ((!defined TRC_CFG_INCLUDE_READY_EVENTS) || (TRC_CFG_INCLUDE_READY_EVENTS == 1))
\r
1534 void prvTraceSetReadyEventsEnabled(int status)
\r
1536 readyEventsEnabled = status;
\r
1539 /*******************************************************************************
\r
1540 * prvTraceStoreTaskReady
\r
1542 * This function stores a ready state for the task handle sent in as parameter.
\r
1543 ******************************************************************************/
\r
1544 void prvTraceStoreTaskReady(traceHandle handle)
\r
1550 TRACE_ALLOC_CRITICAL_SECTION();
\r
1554 /* On FreeRTOS v7.3.0, this occurs when creating tasks due to a bad
\r
1555 placement of the trace macro. In that case, the events are ignored. */
\r
1559 if (! readyEventsEnabled)
\r
1561 /* When creating tasks, ready events are also created. If creating
\r
1562 a "hidden" (not traced) task, we must therefore disable recording
\r
1563 of ready events to avoid an undesired ready event... */
\r
1567 TRACE_ASSERT(handle <= (TRC_CFG_NTASK), "prvTraceStoreTaskReady: Invalid value for handle", TRC_UNUSED);
\r
1569 if (recorder_busy)
\r
1571 /*************************************************************************
\r
1572 * This occurs if an ISR calls a trace function, preempting a previous
\r
1573 * trace call that is being processed in a different ISR or task.
\r
1574 * If this occurs, there is probably a problem in the definition of the
\r
1575 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1576 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1577 * and any other ISRs that calls the trace recorder directly or via
\r
1578 * traced kernel functions. The ARM port disables all interrupts using the
\r
1579 * PRIMASK register to avoid this issue.
\r
1580 *************************************************************************/
\r
1581 prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
\r
1585 trcCRITICAL_SECTION_BEGIN();
\r
1586 if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
\r
1588 dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1589 hnd8 = prvTraceGet8BitHandle(handle);
\r
1590 tr = (TREvent*)prvTraceNextFreeEventBufferSlot();
\r
1593 tr->type = DIV_TASK_READY;
\r
1595 tr->objHandle = hnd8;
\r
1596 prvTraceUpdateCounters();
\r
1599 trcCRITICAL_SECTION_END();
\r
1603 /*******************************************************************************
\r
1604 * prvTraceStoreLowPower
\r
1606 * This function stores a low power state.
\r
1607 ******************************************************************************/
\r
1608 void prvTraceStoreLowPower(uint32_t flag)
\r
1612 TRACE_ALLOC_CRITICAL_SECTION();
\r
1614 TRACE_ASSERT(flag <= 1, "prvTraceStoreLowPower: Invalid flag value", TRC_UNUSED);
\r
1616 if (recorder_busy)
\r
1618 /*************************************************************************
\r
1619 * This occurs if an ISR calls a trace function, preempting a previous
\r
1620 * trace call that is being processed in a different ISR or task.
\r
1621 * If this occurs, there is probably a problem in the definition of the
\r
1622 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1623 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1624 * and any other ISRs that calls the trace recorder directly or via
\r
1625 * traced kernel functions. The ARM port disables all interrupts using the
\r
1626 * PRIMASK register to avoid this issue.
\r
1627 *************************************************************************/
\r
1628 prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
\r
1632 trcCRITICAL_SECTION_BEGIN();
\r
1633 if (RecorderDataPtr->recorderActive)
\r
1635 dts = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1636 lp = (LPEvent*)prvTraceNextFreeEventBufferSlot();
\r
1639 lp->type = (uint8_t) (LOW_POWER_BEGIN + ( uint8_t ) flag); /* BEGIN or END depending on flag */
\r
1641 prvTraceUpdateCounters();
\r
1644 trcCRITICAL_SECTION_END();
\r
1647 /*******************************************************************************
\r
1648 * vTraceStoreMemMangEvent
\r
1650 * This function stores malloc and free events. Each call requires two records,
\r
1651 * for size and address respectively. The event code parameter (ecode) is applied
\r
1652 * to the first record (size) and the following address record gets event
\r
1653 * code "ecode + 1", so make sure this is respected in the event code table.
\r
1654 * Note: On "free" calls, the signed_size parameter should be negative.
\r
1655 ******************************************************************************/
\r
1656 #if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1)
\r
1657 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1658 void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size)
\r
1661 MemEventSize * ms;
\r
1662 MemEventAddr * ma;
\r
1663 uint16_t size_low;
\r
1664 uint16_t addr_low;
\r
1665 uint8_t addr_high;
\r
1667 TRACE_ALLOC_CRITICAL_SECTION();
\r
1669 if (RecorderDataPtr == NULL)
\r
1671 /* Occurs in vTraceInitTraceData, if using dynamic allocation. */
\r
1675 if (signed_size < 0)
\r
1676 size = (uint32_t)(- signed_size);
\r
1678 size = (uint32_t)(signed_size);
\r
1680 trcCRITICAL_SECTION_BEGIN();
\r
1682 heapMemUsage = heapMemUsage + (uint32_t)signed_size;
\r
1684 if (RecorderDataPtr->recorderActive)
\r
1686 dts1 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1687 size_low = (uint16_t)prvTraceGetParam(0xFFFF, size);
\r
1688 ms = (MemEventSize *)prvTraceNextFreeEventBufferSlot();
\r
1693 ms->type = NULL_EVENT; /* Updated when all events are written */
\r
1694 ms->size = size_low;
\r
1695 prvTraceUpdateCounters();
\r
1697 /* Storing a second record with address (signals "failed" if null) */
\r
1698 #if (TRC_CFG_HEAP_SIZE_BELOW_16M)
\r
1699 /* If the heap address range is within 16 MB, i.e., the upper 8 bits
\r
1700 of addresses are constant, this optimization avoids storing an extra
\r
1701 event record by ignoring the upper 8 bit of the address */
\r
1702 addr_low = address & 0xFFFF;
\r
1703 addr_high = (address >> 16) & 0xFF;
\r
1705 /* The whole 32 bit address is stored using a second event record
\r
1706 for the upper 16 bit */
\r
1707 addr_low = (uint16_t)prvTraceGetParam(0xFFFF, address);
\r
1711 ma = (MemEventAddr *) prvTraceNextFreeEventBufferSlot();
\r
1714 ma->addr_low = addr_low;
\r
1715 ma->addr_high = addr_high;
\r
1716 ma->type = (uint8_t) (ecode + 1); /* Note this! */
\r
1717 ms->type = (uint8_t) ecode;
\r
1718 prvTraceUpdateCounters();
\r
1719 RecorderDataPtr->heapMemUsage = heapMemUsage;
\r
1723 trcCRITICAL_SECTION_END();
\r
1725 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1728 /*******************************************************************************
\r
1729 * prvTraceStoreKernelCall
\r
1731 * This is the main integration point for storing kernel calls, and
\r
1732 * is called by the hooks in trcKernelHooks.h (see trcKernelPort.h for event codes).
\r
1733 ******************************************************************************/
\r
1734 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1735 void prvTraceStoreKernelCall(uint32_t ecode, traceObjectClass objectClass, uint32_t objectNumber)
\r
1740 TRACE_ALLOC_CRITICAL_SECTION();
\r
1742 TRACE_ASSERT(ecode < 0xFF, "prvTraceStoreKernelCall: ecode >= 0xFF", TRC_UNUSED);
\r
1743 TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCall: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
\r
1744 TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCall: Invalid value for objectNumber", TRC_UNUSED);
\r
1746 if (recorder_busy)
\r
1748 /*************************************************************************
\r
1749 * This occurs if an ISR calls a trace function, preempting a previous
\r
1750 * trace call that is being processed in a different ISR or task.
\r
1751 * If this occurs, there is probably a problem in the definition of the
\r
1752 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1753 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1754 * and any other ISRs that calls the trace recorder directly or via
\r
1755 * traced kernel functions. The ARM port disables all interrupts using the
\r
1756 * PRIMASK register to avoid this issue.
\r
1757 *************************************************************************/
\r
1758 prvTraceError("Recorder busy - high priority ISR using syscall? (2)");
\r
1762 if (handle_of_last_logged_task == 0)
\r
1767 trcCRITICAL_SECTION_BEGIN();
\r
1768 if (RecorderDataPtr->recorderActive)
\r
1770 dts1 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1771 hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber);
\r
1772 kse = (KernelCall*) prvTraceNextFreeEventBufferSlot();
\r
1776 kse->type = (uint8_t)ecode;
\r
1777 kse->objHandle = hnd8;
\r
1778 prvTraceUpdateCounters();
\r
1781 trcCRITICAL_SECTION_END();
\r
1783 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1785 /*******************************************************************************
\r
1786 * prvTraceStoreKernelCallWithParam
\r
1788 * Used for storing kernel calls with a handle and a numeric parameter. If the
\r
1789 * numeric parameter does not fit in one byte, and extra XPS event is inserted
\r
1790 * before the kernel call event containing the three upper bytes.
\r
1791 ******************************************************************************/
\r
1792 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1793 void prvTraceStoreKernelCallWithParam(uint32_t evtcode,
\r
1794 traceObjectClass objectClass,
\r
1795 uint32_t objectNumber,
\r
1798 KernelCallWithParamAndHandle * kse;
\r
1802 TRACE_ALLOC_CRITICAL_SECTION();
\r
1804 TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithParam: evtcode >= 0xFF", TRC_UNUSED);
\r
1805 TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCallWithParam: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
\r
1806 TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCallWithParam: Invalid value for objectNumber", TRC_UNUSED);
\r
1808 if (recorder_busy)
\r
1810 /*************************************************************************
\r
1811 * This occurs if an ISR calls a trace function, preempting a previous
\r
1812 * trace call that is being processed in a different ISR or task.
\r
1813 * If this occurs, there is probably a problem in the definition of the
\r
1814 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1815 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1816 * and any other ISRs that calls the trace recorder directly or via
\r
1817 * traced kernel functions. The ARM port disables all interrupts using the
\r
1818 * PRIMASK register to avoid this issue.
\r
1819 *************************************************************************/
\r
1820 prvTraceError("Recorder busy - high priority ISR using syscall? (3)");
\r
1824 trcCRITICAL_SECTION_BEGIN();
\r
1825 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\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
1840 trcCRITICAL_SECTION_END();
\r
1842 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1845 /*******************************************************************************
\r
1846 * prvTraceGetParam
\r
1848 * Used for storing extra bytes for kernel calls with numeric parameters.
\r
1850 * May only be called within a critical section!
\r
1851 ******************************************************************************/
\r
1852 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1853 static uint32_t prvTraceGetParam(uint32_t param_max, uint32_t param)
\r
1857 TRACE_ASSERT(param_max == 0xFF || param_max == 0xFFFF,
\r
1858 "prvTraceGetParam: Invalid value for param_max", param);
\r
1860 if (param <= param_max)
\r
1866 xps = (XPSEvent*) prvTraceNextFreeEventBufferSlot();
\r
1869 xps->type = DIV_XPS;
\r
1870 xps->xps_8 = (uint8_t)((param & (0xFF00 & ~param_max)) >> 8);
\r
1871 xps->xps_16 = (uint16_t)((param & (0xFFFF0000 & ~param_max)) >> 16);
\r
1872 prvTraceUpdateCounters();
\r
1875 return param & param_max;
\r
1880 /*******************************************************************************
\r
1881 * prvTraceStoreKernelCallWithNumericParamOnly
\r
1883 * Used for storing kernel calls with numeric parameters only. This is
\r
1884 * only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment.
\r
1885 ******************************************************************************/
\r
1886 #if (TRC_CFG_SCHEDULING_ONLY == 0)
\r
1887 void prvTraceStoreKernelCallWithNumericParamOnly(uint32_t evtcode, uint32_t param)
\r
1889 KernelCallWithParam16 * kse;
\r
1891 uint16_t restParam;
\r
1892 TRACE_ALLOC_CRITICAL_SECTION();
\r
1896 TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithNumericParamOnly: Invalid value for evtcode", TRC_UNUSED);
\r
1898 if (recorder_busy)
\r
1900 /*************************************************************************
\r
1901 * This occurs if an ISR calls a trace function, preempting a previous
\r
1902 * trace call that is being processed in a different ISR or task.
\r
1903 * If this occurs, there is probably a problem in the definition of the
\r
1904 * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
\r
1905 * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
\r
1906 * and any other ISRs that calls the trace recorder directly or via
\r
1907 * traced kernel functions. The ARM port disables all interrupts using the
\r
1908 * PRIMASK register to avoid this issue.
\r
1909 *************************************************************************/
\r
1910 prvTraceError("Recorder busy - high priority ISR using syscall? (4)");
\r
1914 trcCRITICAL_SECTION_BEGIN();
\r
1915 if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
\r
1917 dts6 = (uint8_t)prvTraceGetDTS(0xFF);
\r
1918 restParam = (uint16_t)prvTraceGetParam(0xFFFF, param);
\r
1919 kse = (KernelCallWithParam16*) prvTraceNextFreeEventBufferSlot();
\r
1923 kse->type = (uint8_t)evtcode;
\r
1924 kse->param = restParam;
\r
1925 prvTraceUpdateCounters();
\r
1928 trcCRITICAL_SECTION_END();
\r
1930 #endif /* TRC_CFG_SCHEDULING_ONLY */
\r
1932 /*******************************************************************************
\r
1933 * prvTraceStoreTaskswitch
\r
1934 * Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart.
\r
1935 * At this point interrupts are assumed to be disabled!
\r
1936 ******************************************************************************/
\r
1937 void prvTraceStoreTaskswitch(traceHandle task_handle)
\r
1942 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
1943 extern int32_t isPendingContextSwitch;
\r
1945 trcSR_ALLOC_CRITICAL_SECTION_ON_CORTEX_M_ONLY();
\r
1947 TRACE_ASSERT(task_handle <= (TRC_CFG_NTASK),
\r
1948 "prvTraceStoreTaskswitch: Invalid value for task_handle", TRC_UNUSED);
\r
1950 trcCRITICAL_SECTION_BEGIN_ON_CORTEX_M_ONLY();
\r
1952 if ((task_handle != handle_of_last_logged_task) && (RecorderDataPtr->recorderActive))
\r
1954 #if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
\r
1955 isPendingContextSwitch = 0;
\r
1958 dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
\r
1959 handle_of_last_logged_task = task_handle;
\r
1960 hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
\r
1961 ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
\r
1965 if (prvTraceGetObjectState(TRACE_CLASS_TASK,
\r
1966 handle_of_last_logged_task) == TASK_STATE_INSTANCE_ACTIVE)
\r
1968 ts->type = TS_TASK_RESUME;
\r
1972 ts->type = TS_TASK_BEGIN;
\r
1976 ts->objHandle = hnd8;
\r
1978 prvTraceSetObjectState(TRACE_CLASS_TASK,
\r
1979 handle_of_last_logged_task,
\r
1980 TASK_STATE_INSTANCE_ACTIVE);
\r
1982 prvTraceUpdateCounters();
\r
1986 trcCRITICAL_SECTION_END_ON_CORTEX_M_ONLY();
\r
1989 /*******************************************************************************
\r
1990 * prvTraceStoreObjectNameOnCloseEvent
\r
1992 * Updates the symbol table with the name of this object from the dynamic
\r
1993 * objects table and stores a "close" event, holding the mapping between handle
\r
1994 * and name (a symbol table handle). The stored name-handle mapping is thus the
\r
1995 * "old" one, valid up until this point.
\r
1996 ******************************************************************************/
\r
1997 void prvTraceStoreObjectNameOnCloseEvent(uint8_t evtcode, traceHandle handle,
\r
1998 traceObjectClass objectclass)
\r
2000 ObjCloseNameEvent * ce;
\r
2001 const char * name;
\r
2004 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2005 "prvTraceStoreObjectNameOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2006 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2007 "prvTraceStoreObjectNameOnCloseEvent: Invalid value for handle", TRC_UNUSED);
\r
2009 if (RecorderDataPtr->recorderActive)
\r
2011 uint8_t hnd8 = prvTraceGet8BitHandle(handle);
\r
2012 name = TRACE_PROPERTY_NAME_GET(objectclass, handle);
\r
2013 idx = prvTraceOpenSymbol(name, 0);
\r
2015 // Interrupt disable not necessary, already done in trcHooks.h macro
\r
2016 ce = (ObjCloseNameEvent*) prvTraceNextFreeEventBufferSlot();
\r
2019 ce->type = (uint8_t) evtcode;
\r
2020 ce->objHandle = hnd8;
\r
2021 ce->symbolIndex = idx;
\r
2022 prvTraceUpdateCounters();
\r
2027 void prvTraceStoreObjectPropertiesOnCloseEvent(uint8_t evtcode, traceHandle handle,
\r
2028 traceObjectClass objectclass)
\r
2030 ObjClosePropEvent * pe;
\r
2032 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2033 "prvTraceStoreObjectPropertiesOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2034 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2035 "prvTraceStoreObjectPropertiesOnCloseEvent: Invalid value for handle", TRC_UNUSED);
\r
2037 if (RecorderDataPtr->recorderActive)
\r
2039 // Interrupt disable not necessary, already done in trcHooks.h macro
\r
2040 pe = (ObjClosePropEvent*) prvTraceNextFreeEventBufferSlot();
\r
2043 if (objectclass == TRACE_CLASS_TASK)
\r
2045 pe->arg1 = TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, handle);
\r
2049 pe->arg1 = TRACE_PROPERTY_OBJECT_STATE(objectclass, handle);
\r
2051 pe->type = evtcode;
\r
2052 prvTraceUpdateCounters();
\r
2057 void prvTraceSetPriorityProperty(uint8_t objectclass, traceHandle id, uint8_t value)
\r
2059 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2060 "prvTraceSetPriorityProperty: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2061 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2062 "prvTraceSetPriorityProperty: Invalid value for id", TRC_UNUSED);
\r
2064 TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id) = value;
\r
2067 uint8_t prvTraceGetPriorityProperty(uint8_t objectclass, traceHandle id)
\r
2069 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2070 "prvTraceGetPriorityProperty: objectclass >= TRACE_NCLASSES", 0);
\r
2071 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2072 "prvTraceGetPriorityProperty: Invalid value for id", 0);
\r
2074 return TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id);
\r
2077 void prvTraceSetObjectState(uint8_t objectclass, traceHandle id, uint8_t value)
\r
2079 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2080 "prvTraceSetObjectState: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
\r
2081 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2082 "prvTraceSetObjectState: Invalid value for id", TRC_UNUSED);
\r
2084 TRACE_PROPERTY_OBJECT_STATE(objectclass, id) = value;
\r
2087 uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id)
\r
2089 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2090 "prvTraceGetObjectState: objectclass >= TRACE_NCLASSES", 0);
\r
2091 TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2092 "prvTraceGetObjectState: Invalid value for id", 0);
\r
2094 return TRACE_PROPERTY_OBJECT_STATE(objectclass, id);
\r
2097 void prvTraceSetTaskInstanceFinished(traceHandle handle)
\r
2099 TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_TASK],
\r
2100 "prvTraceSetTaskInstanceFinished: Invalid value for handle", TRC_UNUSED);
\r
2102 #if (TRC_CFG_USE_IMPLICIT_IFE_RULES == 1)
\r
2103 TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle) = 0;
\r
2107 /*******************************************************************************
\r
2108 * Static data initializations
\r
2109 ******************************************************************************/
\r
2111 /* A set of stacks that keeps track of available object handles for each class.
\r
2112 The stacks are empty initially, meaning that allocation of new handles will be
\r
2113 based on a counter (for each object class). Any delete operation will
\r
2114 return the handle to the corresponding stack, for reuse on the next allocate.*/
\r
2115 objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
\r
2117 /* Initial TRC_HWTC_COUNT value, for detecting if the time-stamping source is
\r
2118 enabled. If using the OS periodic timer for time-stamping, this might not
\r
2119 have been configured on the earliest events during the startup. */
\r
2120 uint32_t init_hwtc_count;
\r
2122 /*******************************************************************************
\r
2125 * The main data structure in snapshot mode, when using the default static memory
\r
2126 * allocation (TRC_RECORDER_BUFFER_ALLOCATION_STATIC). The recorder uses a pointer
\r
2127 * RecorderDataPtr to access the data, to also allow for dynamic or custom data
\r
2128 * allocation (see TRC_CFG_RECORDER_BUFFER_ALLOCATION).
\r
2129 ******************************************************************************/
\r
2130 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
\r
2131 RecorderDataType RecorderData;
\r
2134 /*******************************************************************************
\r
2137 * Pointer to the main data structure, when in snapshot mode.
\r
2138 ******************************************************************************/
\r
2139 RecorderDataType* RecorderDataPtr = NULL;
\r
2141 /* This version of the function dynamically allocates the trace data */
\r
2142 void prvTraceInitTraceData()
\r
2145 if (RecorderDataPtr == NULL)
\r
2147 #if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
\r
2148 RecorderDataPtr = &RecorderData;
\r
2149 #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC)
\r
2150 RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType));
\r
2151 if (! RecorderDataPtr)
\r
2153 prvTraceError("Failed allocating recorder buffer!");
\r
2156 #elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
\r
2157 if (! RecorderDataPtr)
\r
2159 prvTraceError("Recorder data pointer not set! Use vTraceSetRecorderDataBuffer().");
\r
2166 if (RecorderDataPtr->startmarker0 == 1)
\r
2168 /* Already initialized */
\r
2173 init_hwtc_count = TRC_HWTC_COUNT;
\r
2175 (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType));
\r
2177 RecorderDataPtr->version = TRACE_KERNEL_VERSION;
\r
2178 RecorderDataPtr->minor_version = TRACE_MINOR_VERSION;
\r
2179 RecorderDataPtr->irq_priority_order = TRC_IRQ_PRIORITY_ORDER;
\r
2180 RecorderDataPtr->filesize = sizeof(RecorderDataType);
\r
2181 RecorderDataPtr->maxEvents = (TRC_CFG_EVENT_BUFFER_SIZE);
\r
2182 RecorderDataPtr->debugMarker0 = (int32_t) 0xF0F0F0F0;
\r
2183 RecorderDataPtr->isUsing16bitHandles = TRC_CFG_USE_16BIT_OBJECT_HANDLES;
\r
2184 RecorderDataPtr->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
\r
2186 /* This function is kernel specific */
\r
2187 vTraceInitObjectPropertyTable();
\r
2189 RecorderDataPtr->debugMarker1 = (int32_t)0xF1F1F1F1;
\r
2190 RecorderDataPtr->SymbolTable.symTableSize = (TRC_CFG_SYMBOL_TABLE_SIZE);
\r
2191 RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1;
\r
2192 #if (TRC_CFG_INCLUDE_FLOAT_SUPPORT == 1)
\r
2193 RecorderDataPtr->exampleFloatEncoding = 1.0f; /* otherwise already zero */
\r
2195 RecorderDataPtr->debugMarker2 = (int32_t)0xF2F2F2F2;
\r
2196 prvStrncpy(RecorderDataPtr->systemInfo, "Trace Recorder Demo", 80);
\r
2197 RecorderDataPtr->debugMarker3 = (int32_t)0xF3F3F3F3;
\r
2198 RecorderDataPtr->endmarker0 = 0x0A;
\r
2199 RecorderDataPtr->endmarker1 = 0x0B;
\r
2200 RecorderDataPtr->endmarker2 = 0x0C;
\r
2201 RecorderDataPtr->endmarker3 = 0x0D;
\r
2202 RecorderDataPtr->endmarker4 = 0x71;
\r
2203 RecorderDataPtr->endmarker5 = 0x72;
\r
2204 RecorderDataPtr->endmarker6 = 0x73;
\r
2205 RecorderDataPtr->endmarker7 = 0x74;
\r
2206 RecorderDataPtr->endmarker8 = 0xF1;
\r
2207 RecorderDataPtr->endmarker9 = 0xF2;
\r
2208 RecorderDataPtr->endmarker10 = 0xF3;
\r
2209 RecorderDataPtr->endmarker11 = 0xF4;
\r
2211 #if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER
\r
2212 RecorderDataPtr->userEventBuffer.bufferID = 1;
\r
2213 RecorderDataPtr->userEventBuffer.version = 0;
\r
2214 RecorderDataPtr->userEventBuffer.numberOfSlots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE);
\r
2215 RecorderDataPtr->userEventBuffer.numberOfChannels = (TRC_CFG_UB_CHANNELS) + 1;
\r
2218 /* Kernel specific initialization of the objectHandleStacks variable */
\r
2219 vTraceInitObjectHandleStack();
\r
2222 /* Finally, the 12-byte "start markers" are initialized, allowing for
\r
2223 Tracealyzer to find the trace data in a larger RAM dump.
\r
2225 The start and end markers must be unique, but without proper precautions there
\r
2226 might be a risk of accidental duplicates of the start/end markers, e.g., due to
\r
2227 compiler optimizations.
\r
2229 The below initialization of the start marker is therefore made in reverse order
\r
2230 and the fields are volatile to ensure this assignment order. This to avoid any
\r
2231 chance of accidental duplicates of this elsewhere in memory.
\r
2233 Moreover, the fields are set byte-by-byte to avoid endian issues.*/
\r
2235 RecorderDataPtr->startmarker11 = 0xF4;
\r
2236 RecorderDataPtr->startmarker10 = 0xF3;
\r
2237 RecorderDataPtr->startmarker9 = 0xF2;
\r
2238 RecorderDataPtr->startmarker8 = 0xF1;
\r
2239 RecorderDataPtr->startmarker7 = 0x74;
\r
2240 RecorderDataPtr->startmarker6 = 0x73;
\r
2241 RecorderDataPtr->startmarker5 = 0x72;
\r
2242 RecorderDataPtr->startmarker4 = 0x71;
\r
2243 RecorderDataPtr->startmarker3 = 0x04;
\r
2244 RecorderDataPtr->startmarker2 = 0x03;
\r
2245 RecorderDataPtr->startmarker1 = 0x02;
\r
2246 RecorderDataPtr->startmarker0 = 0x01;
\r
2248 if (traceErrorMessage != NULL)
\r
2250 // An error was detected before vTraceEnable was called, make sure this is stored in the trace data.
\r
2251 prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);
\r
2252 RecorderDataPtr->internalErrorOccured = 1;
\r
2258 #ifdef TRC_PORT_SPECIFIC_INIT
\r
2259 TRC_PORT_SPECIFIC_INIT();
\r
2264 void* prvTraceNextFreeEventBufferSlot(void)
\r
2266 if (! RecorderDataPtr->recorderActive)
\r
2268 /* If an XTS or XPS event prior to the main event has filled the buffer
\r
2269 before saving the main event, and store mode is "stop when full". */
\r
2273 if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
\r
2275 prvTraceError("Attempt to index outside event buffer!");
\r
2278 return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]);
\r
2281 uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass)
\r
2283 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2284 "uiIndexOfObject: Invalid value for objectclass", 0);
\r
2285 TRACE_ASSERT(objecthandle > 0 && objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2286 "uiIndexOfObject: Invalid value for objecthandle", 0);
\r
2288 if ((objectclass < TRACE_NCLASSES) && (objecthandle > 0) &&
\r
2289 (objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]))
\r
2291 return (uint16_t)(RecorderDataPtr->ObjectPropertyTable.StartIndexOfClass[objectclass] +
\r
2292 (RecorderDataPtr->ObjectPropertyTable.TotalPropertyBytesPerClass[objectclass] * (objecthandle-1)));
\r
2295 prvTraceError("Object table lookup with invalid object handle or object class!");
\r
2299 traceHandle prvTraceGetObjectHandle(traceObjectClass objectclass)
\r
2301 traceHandle handle;
\r
2302 static int indexOfHandle;
\r
2304 TRACE_ALLOC_CRITICAL_SECTION();
\r
2306 TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
\r
2308 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2309 "prvTraceGetObjectHandle: Invalid value for objectclass", (traceHandle)0);
\r
2311 trcCRITICAL_SECTION_BEGIN();
\r
2312 indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
\r
2313 if (objectHandleStacks.objectHandles[indexOfHandle] == 0)
\r
2315 /* Zero is used to indicate a never before used handle, i.e.,
\r
2316 new slots in the handle stack. The handle slot needs to
\r
2317 be initialized here (starts at 1). */
\r
2318 objectHandleStacks.objectHandles[indexOfHandle] =
\r
2319 (traceHandle)(1 + indexOfHandle -
\r
2320 objectHandleStacks.lowestIndexOfClass[objectclass]);
\r
2323 handle = objectHandleStacks.objectHandles[indexOfHandle];
\r
2325 if (objectHandleStacks.indexOfNextAvailableHandle[objectclass]
\r
2326 > objectHandleStacks.highestIndexOfClass[objectclass])
\r
2328 prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
\r
2334 objectHandleStacks.indexOfNextAvailableHandle[objectclass]++;
\r
2336 hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] -
\r
2337 objectHandleStacks.lowestIndexOfClass[objectclass];
\r
2340 objectHandleStacks.handleCountWaterMarksOfClass[objectclass])
\r
2342 objectHandleStacks.handleCountWaterMarksOfClass[objectclass] =
\r
2343 (traceHandle)hndCount;
\r
2346 trcCRITICAL_SECTION_END();
\r
2351 void prvTraceFreeObjectHandle(traceObjectClass objectclass, traceHandle handle)
\r
2353 int indexOfHandle;
\r
2355 TRACE_ASSERT(objectclass < TRACE_NCLASSES,
\r
2356 "prvTraceFreeObjectHandle: Invalid value for objectclass", TRC_UNUSED);
\r
2357 TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
\r
2358 "prvTraceFreeObjectHandle: Invalid value for handle", TRC_UNUSED);
\r
2360 /* Check that there is room to push the handle on the stack */
\r
2361 if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) <
\r
2362 objectHandleStacks.lowestIndexOfClass[objectclass])
\r
2365 prvTraceError("Attempt to free more handles than allocated!");
\r
2369 objectHandleStacks.indexOfNextAvailableHandle[objectclass]--;
\r
2370 indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
\r
2371 objectHandleStacks.objectHandles[indexOfHandle] = handle;
\r
2375 /*******************************************************************************
\r
2376 * prvMarkObjectAsUsed
\r
2378 * Sets an "is used flag" on object creation, using the first byte of the name
\r
2379 * field. This allows for counting the number of used Object Table slots, even
\r
2380 * if no names have been set.
\r
2381 ******************************************************************************/
\r
2382 void prvMarkObjectAsUsed(traceObjectClass objectclass, traceHandle handle)
\r
2384 uint16_t idx = uiIndexOfObject(handle, objectclass);
\r
2385 RecorderDataPtr->ObjectPropertyTable.objbytes[idx] = 1;
\r
2388 /*******************************************************************************
\r
2391 * Private string copy function, to improve portability between compilers.
\r
2392 ******************************************************************************/
\r
2393 static void prvStrncpy(char* dst, const char* src, uint32_t maxLength)
\r
2396 for (i = 0; i < maxLength; i++)
\r
2404 /*******************************************************************************
\r
2405 * prvTraceSetObjectName
\r
2407 * Registers the names of queues, semaphores and other kernel objects in the
\r
2408 * recorder's Object Property Table, at the given handle and object class.
\r
2409 ******************************************************************************/
\r
2410 void prvTraceSetObjectName(traceObjectClass objectclass,
\r
2411 traceHandle handle,
\r
2414 static uint16_t idx;
\r
2416 TRACE_ASSERT(name != NULL, "prvTraceSetObjectName: name == NULL", TRC_UNUSED);
\r
2418 if (objectclass >= TRACE_NCLASSES)
\r
2420 prvTraceError("Illegal object class in prvTraceSetObjectName");
\r
2426 prvTraceError("Illegal handle (0) in prvTraceSetObjectName.");
\r
2430 if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])
\r
2433 prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
\r
2437 idx = uiIndexOfObject(handle, objectclass);
\r
2439 if (traceErrorMessage == NULL)
\r
2441 prvStrncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]),
\r
2443 RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]);
\r
2448 traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel)
\r
2453 TRACE_ALLOC_CRITICAL_SECTION();
\r
2458 TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceString)0);
\r
2460 prvTraceGetChecksum(name, &crc, &len);
\r
2462 trcCRITICAL_SECTION_BEGIN();
\r
2463 result = prvTraceLookupSymbolTableEntry(name, crc, len, userEventChannel);
\r
2466 result = prvTraceCreateSymbolTableEntry(name, crc, len, userEventChannel);
\r
2468 trcCRITICAL_SECTION_END();
\r
2474 /******************************************************************************
\r
2475 * vTraceSetFrequency
\r
2477 * Registers the clock rate of the time source for the event timestamping.
\r
2478 * This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ)
\r
2479 * should be incorrect for your setup, you can override it using this function.
\r
2481 * Must be called prior to vTraceEnable, and the time source is assumed to
\r
2482 * have a fixed clock frequency after the startup.
\r
2484 * Note that, in snapshot mode, the value is divided by the TRC_HWTC_DIVISOR.
\r
2485 * This is a software "prescaler" that is also applied on the timestamps.
\r
2486 *****************************************************************************/
\r
2487 void vTraceSetFrequency(uint32_t frequency)
\r
2489 timestampFrequency = frequency;
\r
2492 /*******************************************************************************
\r
2493 * Supporting functions
\r
2494 ******************************************************************************/
\r
2496 /*******************************************************************************
\r
2499 * Called by various parts in the recorder. Stops the recorder and stores a
\r
2500 * pointer to an error message, which is printed by the monitor task.
\r
2501 * If you are not using the monitor task, you may use xTraceGetLastError()
\r
2502 * from your application to check if the recorder is OK.
\r
2504 * Note: If a recorder error is registered before vTraceStart is called, the
\r
2505 * trace start will be aborted. This can occur if any of the Nxxxx constants
\r
2506 * (e.g., TRC_CFG_NTASK) in trcConfig.h is too small.
\r
2507 ******************************************************************************/
\r
2508 void prvTraceError(const char* msg)
\r
2510 /* Stop the recorder */
\r
2511 if (RecorderDataPtr != NULL)
\r
2516 /* If first error only... */
\r
2517 if (traceErrorMessage == NULL)
\r
2519 traceErrorMessage = (char*)(intptr_t) msg;
\r
2520 if (RecorderDataPtr != NULL)
\r
2522 prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);
\r
2523 RecorderDataPtr->internalErrorOccured = 1;
\r
2528 void vTraceSetFilterMask(uint16_t filterMask)
\r
2530 CurrentFilterMask = filterMask;
\r
2533 void vTraceSetFilterGroup(uint16_t filterGroup)
\r
2535 CurrentFilterGroup = filterGroup;
\r
2538 /******************************************************************************
\r
2539 * prvCheckDataToBeOverwrittenForMultiEntryEvents
\r
2541 * This checks if the next event to be overwritten is a multi-entry user event,
\r
2542 * i.e., a USER_EVENT followed by data entries.
\r
2543 * Such data entries do not have an event code at byte 0, as other events.
\r
2544 * All 4 bytes are user data, so the first byte of such data events must
\r
2545 * not be interpreted as type field. The number of data entries following
\r
2546 * a USER_EVENT is given in the event code of the USER_EVENT.
\r
2547 * Therefore, when overwriting a USER_EVENT (when using in ring-buffer mode)
\r
2548 * any data entries following must be replaced with NULL events (code 0).
\r
2550 * This is assumed to execute within a critical section...
\r
2551 *****************************************************************************/
\r
2553 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2554 void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nofEntriesToCheck)
\r
2556 /* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */
\r
2557 unsigned int i = 0;
\r
2558 unsigned int e = 0;
\r
2560 TRACE_ASSERT(nofEntriesToCheck != 0,
\r
2561 "prvCheckDataToBeOverwrittenForMultiEntryEvents: nofEntriesToCheck == 0", TRC_UNUSED);
\r
2563 while (i < nofEntriesToCheck)
\r
2565 e = RecorderDataPtr->nextFreeIndex + i;
\r
2566 if ((RecorderDataPtr->eventData[e*4] > USER_EVENT) &&
\r
2567 (RecorderDataPtr->eventData[e*4] < USER_EVENT + 16))
\r
2569 uint8_t nDataEvents = (uint8_t)(RecorderDataPtr->eventData[e*4] - USER_EVENT);
\r
2570 if ((e + nDataEvents) < RecorderDataPtr->maxEvents)
\r
2572 (void)memset(& RecorderDataPtr->eventData[e*4], 0, (size_t) (4 + 4 * nDataEvents));
\r
2575 else if (RecorderDataPtr->eventData[e*4] == DIV_XPS)
\r
2577 if ((e + 1) < RecorderDataPtr->maxEvents)
\r
2579 /* Clear 8 bytes */
\r
2580 (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4 + 4);
\r
2584 /* Clear 8 bytes, 4 first and 4 last */
\r
2585 (void)memset(& RecorderDataPtr->eventData[0], 0, 4);
\r
2586 (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4);
\r
2594 /*******************************************************************************
\r
2595 * prvTraceUpdateCounters
\r
2597 * Updates the index of the event buffer.
\r
2598 ******************************************************************************/
\r
2599 void prvTraceUpdateCounters(void)
\r
2601 if (RecorderDataPtr->recorderActive == 0)
\r
2606 RecorderDataPtr->numEvents++;
\r
2608 RecorderDataPtr->nextFreeIndex++;
\r
2610 if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
\r
2612 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2613 RecorderDataPtr->bufferIsFull = 1;
\r
2614 RecorderDataPtr->nextFreeIndex = 0;
\r
2620 #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
\r
2621 prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
\r
2625 /******************************************************************************
\r
2628 * Returns a differential timestamp (DTS), i.e., the time since
\r
2629 * last event, and creates an XTS event if the DTS does not fit in the
\r
2630 * number of bits given. The XTS event holds the MSB bytes of the DTS.
\r
2632 * The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for
\r
2633 * events with 16-bit dts fields.
\r
2634 *****************************************************************************/
\r
2635 uint16_t prvTraceGetDTS(uint16_t param_maxDTS)
\r
2637 static uint32_t old_timestamp = 0;
\r
2638 XTSEvent* xts = 0;
\r
2640 uint32_t timestamp = 0;
\r
2642 TRACE_ASSERT(param_maxDTS == 0xFF || param_maxDTS == 0xFFFF, "prvTraceGetDTS: Invalid value for param_maxDTS", 0);
\r
2645 if (RecorderDataPtr->frequency == 0)
\r
2647 if (timestampFrequency != 0)
\r
2649 /* If to override default TRC_HWTC_FREQ_HZ value with value set by vTraceSetFrequency */
\r
2650 RecorderDataPtr->frequency = timestampFrequency / (TRC_HWTC_DIVISOR);
\r
2652 else if (init_hwtc_count != (TRC_HWTC_COUNT))
\r
2654 /* If using default value and timer has been started.
\r
2655 Note: If the default frequency value set here would be incorrect, e.g.,
\r
2656 if the timer has actually not been configured yet, override this
\r
2657 with vTraceSetFrequency.
\r
2659 RecorderDataPtr->frequency = (TRC_HWTC_FREQ_HZ) / (TRC_HWTC_DIVISOR);
\r
2661 /* If no override (vTraceSetFrequency) and timer inactive -> no action */
\r
2664 /**************************************************************************
\r
2665 * The below statements read the timestamp from the timer port module.
\r
2666 * If necessary, whole seconds are extracted using division while the rest
\r
2667 * comes from the modulo operation.
\r
2668 **************************************************************************/
\r
2670 prvTracePortGetTimeStamp(×tamp);
\r
2672 /***************************************************************************
\r
2673 * Since dts is unsigned the result will be correct even if timestamp has
\r
2675 ***************************************************************************/
\r
2676 dts = timestamp - old_timestamp;
\r
2677 old_timestamp = timestamp;
\r
2679 if (RecorderDataPtr->frequency > 0)
\r
2681 /* Check if dts > 1 second */
\r
2682 if (dts > RecorderDataPtr->frequency)
\r
2684 /* More than 1 second has passed */
\r
2685 RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency;
\r
2686 /* The part that is not an entire second is added to absTimeLastEvent */
\r
2687 RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency;
\r
2691 RecorderDataPtr->absTimeLastEvent += dts;
\r
2694 /* Check if absTimeLastEvent >= 1 second */
\r
2695 if (RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency)
\r
2697 /* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */
\r
2698 RecorderDataPtr->absTimeLastEventSecond++;
\r
2699 RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency;
\r
2700 /* RecorderDataPtr->absTimeLastEvent is now less than 1 second */
\r
2705 /* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */
\r
2706 RecorderDataPtr->absTimeLastEvent = timestamp;
\r
2709 /* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */
\r
2710 if (dts > param_maxDTS)
\r
2712 /* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/
\r
2713 xts = (XTSEvent*) prvTraceNextFreeEventBufferSlot();
\r
2717 if (param_maxDTS == 0xFFFF)
\r
2719 xts->type = XTS16;
\r
2720 xts->xts_16 = (uint16_t)((dts / 0x10000) & 0xFFFF);
\r
2723 else if (param_maxDTS == 0xFF)
\r
2726 xts->xts_16 = (uint16_t)((dts / 0x100) & 0xFFFF);
\r
2727 xts->xts_8 = (uint8_t)((dts / 0x1000000) & 0xFF);
\r
2731 prvTraceError("Bad param_maxDTS in prvTraceGetDTS");
\r
2733 prvTraceUpdateCounters();
\r
2737 return (uint16_t)dts & param_maxDTS;
\r
2740 /*******************************************************************************
\r
2741 * prvTraceLookupSymbolTableEntry
\r
2743 * Find an entry in the symbol table, return 0 if not present.
\r
2745 * The strings are stored in a byte pool, with four bytes of "meta-data" for
\r
2747 * byte 0-1: index of next entry with same checksum (for fast lookup).
\r
2748 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
\r
2749 * format strings only (the handle of the destination channel).
\r
2750 * byte 4..(4 + length): the string (object name or user event label), with
\r
2751 * zero-termination
\r
2752 ******************************************************************************/
\r
2753 traceString prvTraceLookupSymbolTableEntry(const char* name,
\r
2758 uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ];
\r
2760 TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceString)0);
\r
2761 TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceString)0);
\r
2765 if (RecorderDataPtr->SymbolTable.symbytes[i + 2] == (chn & 0x00FF))
\r
2767 if (RecorderDataPtr->SymbolTable.symbytes[i + 3] == (chn / 0x100))
\r
2769 if (RecorderDataPtr->SymbolTable.symbytes[i + 4 + len] == '\0')
\r
2771 if (strncmp((char*)(& RecorderDataPtr->SymbolTable.symbytes[i + 4]), name, len) == 0)
\r
2773 break; /* found */
\r
2778 i = (uint16_t)(RecorderDataPtr->SymbolTable.symbytes[i] + (RecorderDataPtr->SymbolTable.symbytes[i + 1] * 0x100));
\r
2783 /*******************************************************************************
\r
2784 * prvTraceCreateSymbolTableEntry
\r
2786 * Creates an entry in the symbol table, independent if it exists already.
\r
2788 * The strings are stored in a byte pool, with four bytes of "meta-data" for
\r
2790 * byte 0-1: index of next entry with same checksum (for fast lookup).
\r
2791 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
\r
2792 * format strings only (the handle of the destination channel).
\r
2793 * byte 4..(4 + length): the string (object name or user event label), with
\r
2794 * zero-termination
\r
2795 ******************************************************************************/
\r
2796 uint16_t prvTraceCreateSymbolTableEntry(const char* name,
\r
2799 traceString channel)
\r
2803 TRACE_ASSERT(name != NULL, "prvTraceCreateSymbolTableEntry: name == NULL", 0);
\r
2804 TRACE_ASSERT(len != 0, "prvTraceCreateSymbolTableEntry: len == 0", 0);
\r
2806 if (RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= (TRC_CFG_SYMBOL_TABLE_SIZE))
\r
2808 prvTraceError("Symbol table full. Increase TRC_CFG_SYMBOL_TABLE_SIZE in trcConfig.h");
\r
2814 RecorderDataPtr->SymbolTable.symbytes
\r
2815 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex] =
\r
2816 (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF);
\r
2818 RecorderDataPtr->SymbolTable.symbytes
\r
2819 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1] =
\r
2820 (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100);
\r
2822 RecorderDataPtr->SymbolTable.symbytes
\r
2823 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2] =
\r
2824 (uint8_t)(channel & 0x00FF);
\r
2826 RecorderDataPtr->SymbolTable.symbytes
\r
2827 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3] =
\r
2828 (uint8_t)(channel / 0x100);
\r
2830 /* set name (bytes 4...4+len-1) */
\r
2831 prvStrncpy((char*)&(RecorderDataPtr->SymbolTable.symbytes
\r
2832 [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4]), name, len);
\r
2834 /* Set zero termination (at offset 4+len) */
\r
2835 RecorderDataPtr->SymbolTable.symbytes
\r
2836 [RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len] = '\0';
\r
2838 /* store index of entry (for return value, and as head of LL[crc6]) */
\r
2839 RecorderDataPtr->SymbolTable.latestEntryOfChecksum
\r
2840 [ crc6 ] = (uint16_t)RecorderDataPtr->SymbolTable.nextFreeSymbolIndex;
\r
2842 RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += (uint32_t) (len + 5);
\r
2844 ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - (uint8_t)(len + 5));
\r
2851 /*******************************************************************************
\r
2852 * prvTraceGetChecksum
\r
2854 * Calculates a simple 6-bit checksum from a string, used to index the string
\r
2855 * for fast symbol table lookup.
\r
2856 ******************************************************************************/
\r
2857 void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength)
\r
2860 int length = 1; /* Should be 1 to account for '\0' */
\r
2863 TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", TRC_UNUSED);
\r
2864 TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", TRC_UNUSED);
\r
2865 TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", TRC_UNUSED);
\r
2867 if (pname != (const char *) 0)
\r
2869 for (; (c = (unsigned char) *pname++) != '\0';)
\r
2875 *pcrc = (uint8_t)(crc & 0x3F);
\r
2876 *plength = (uint8_t)length;
\r
2879 #if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
\r
2881 static void prvTraceStoreXID(traceHandle handle);
\r
2883 /******************************************************************************
\r
2884 * prvTraceStoreXID
\r
2886 * Stores an XID (eXtended IDentifier) event.
\r
2887 * This is used if an object/task handle is larger than 255.
\r
2888 * The parameter "handle" is the full (16 bit) handle, assumed to be 256 or
\r
2889 * larger. Handles below 256 should not use this function.
\r
2891 * NOTE: this function MUST be called from within a critical section.
\r
2892 *****************************************************************************/
\r
2893 static void prvTraceStoreXID(traceHandle handle)
\r
2897 TRACE_ASSERT(handle >= 256, "prvTraceStoreXID: Handle < 256", TRC_UNUSED);
\r
2899 xid = (XPSEvent*)prvTraceNextFreeEventBufferSlot();
\r
2905 /* This function is (only) used when traceHandle is 16 bit... */
\r
2906 xid->xps_16 = handle;
\r
2908 prvTraceUpdateCounters();
\r
2912 static uint8_t prvTraceGet8BitHandle(traceHandle handle)
\r
2916 prvTraceStoreXID(handle);
\r
2917 /* The full handle (16 bit) is stored in the XID event.
\r
2918 This code (255) is used instead of zero (which is an error code).*/
\r
2921 return (uint8_t)(handle & 0xFF);
\r
2923 #endif /*(TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)*/
\r
2926 /* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */
\r
2927 #ifndef TRC_CFG_ARM_CM_USE_SYSTICK
\r
2928 #if ((TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M) && (defined (__CORTEX_M) && (__CORTEX_M >= 0x03)))
\r
2929 void prvTraceInitCortexM()
\r
2931 /* Ensure that the DWT registers are unlocked and can be modified. */
\r
2932 TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK;
\r
2934 /* Make sure DWT is enabled, if supported */
\r
2935 TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;
\r
2938 /* Verify that DWT is supported */
\r
2939 if (TRC_REG_DEMCR == 0)
\r
2941 /* This function is called on Cortex-M3, M4 and M7 devices to initialize
\r
2942 the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
\r
2944 If the below error is produced, the DWT unit does not seem to be available.
\r
2946 In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
\r
2947 to use SysTick timestamping instead, or define your own timestamping by
\r
2948 setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
\r
2949 and make the necessary definitions, as explained in trcHardwarePort.h.*/
\r
2951 prvTraceError("DWT unit not available, see code comment.");
\r
2955 /* Verify that DWT_CYCCNT is supported */
\r
2956 if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT)
\r
2958 /* This function is called on Cortex-M3, M4 and M7 devices to initialize
\r
2959 the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
\r
2961 If the below error is produced, the cycle counter does not seem to be available.
\r
2963 In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
\r
2964 to use SysTick timestamping instead, or define your own timestamping by
\r
2965 setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
\r
2966 and make the necessary definitions, as explained in trcHardwarePort.h.*/
\r
2968 prvTraceError("DWT_CYCCNT not available, see code comment.");
\r
2972 /* Reset the cycle counter */
\r
2973 TRC_REG_DWT_CYCCNT = 0;
\r
2975 /* Enable the cycle counter */
\r
2976 TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;
\r
2978 }while(0); /* breaks above jump here */
\r
2983 /******************************************************************************
\r
2984 * prvTracePortGetTimeStamp
\r
2986 * Returns the current time based on the HWTC macros which provide a hardware
\r
2987 * isolation layer towards the hardware timer/counter.
\r
2989 * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
\r
2990 * or the trace recorder library. Typically you should not need to change
\r
2991 * the code of prvTracePortGetTimeStamp if using the HWTC macros.
\r
2993 ******************************************************************************/
\r
2994 void prvTracePortGetTimeStamp(uint32_t *pTimestamp)
\r
2996 static uint32_t last_hwtc_count = 0;
\r
2997 uint32_t hwtc_count = 0;
\r
2999 #if TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR
\r
3000 /* systick based timer */
\r
3001 static uint32_t last_traceTickCount = 0;
\r
3002 uint32_t traceTickCount = 0;
\r
3003 #else /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
\r
3004 /* Free running timer */
\r
3005 static uint32_t last_hwtc_rest = 0;
\r
3006 uint32_t diff = 0;
\r
3007 uint32_t diff_scaled = 0;
\r
3008 #endif /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
\r
3010 if (trace_disable_timestamp == 1)
\r
3013 *pTimestamp = last_timestamp;
\r
3017 /* Retrieve TRC_HWTC_COUNT only once since the same value should be used all throughout this function. */
\r
3018 #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR)
\r
3019 /* Get the increasing tick count */
\r
3020 hwtc_count = (TRC_HWTC_COUNT);
\r
3021 #elif (TRC_HWTC_TYPE == TRC_OS_TIMER_DECR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR)
\r
3022 /* Convert decreasing tick count into increasing tick count */
\r
3023 hwtc_count = (TRC_HWTC_PERIOD) - (TRC_HWTC_COUNT);
\r
3025 #error "TRC_HWTC_TYPE has unexpected value"
\r
3028 #if (TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_Win32)
\r
3029 /* The Win32 port uses ulGetRunTimeCounterValue for timestamping, which in turn
\r
3030 uses QueryPerformanceCounter. That function is not always reliable when used over
\r
3031 multiple threads. We must therefore handle rare cases where the timestamp is less
\r
3032 than the previous. In practice, this should "never" roll over since the
\r
3033 performance counter is 64 bit wide. */
\r
3035 if (last_hwtc_count > hwtc_count)
\r
3037 hwtc_count = last_hwtc_count;
\r
3041 #if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)
\r
3042 /* Timestamping is based on a timer that wraps at TRC_HWTC_PERIOD */
\r
3043 if (last_traceTickCount - uiTraceTickCount - 1 < 0x80000000)
\r
3045 /* This means last_traceTickCount is higher than uiTraceTickCount,
\r
3046 so we have previously compensated for a missed tick.
\r
3047 Therefore we use the last stored value because that is more accurate. */
\r
3048 traceTickCount = last_traceTickCount;
\r
3052 /* Business as usual */
\r
3053 traceTickCount = uiTraceTickCount;
\r
3056 /* Check for overflow. May occur if the update of uiTraceTickCount has been
\r
3057 delayed due to disabled interrupts. */
\r
3058 if (traceTickCount == last_traceTickCount && hwtc_count < last_hwtc_count)
\r
3060 /* A trace tick has occurred but not been executed by the kernel, so we compensate manually. */
\r
3064 /* Check if the return address is OK, then we perform the calculation. */
\r
3067 /* Get timestamp from trace ticks. Scale down the period to avoid unwanted overflows. */
\r
3068 last_timestamp = traceTickCount * ((TRC_HWTC_PERIOD) / (TRC_HWTC_DIVISOR));
\r
3069 /* Increase timestamp by (hwtc_count + "lost hardware ticks from scaling down period") / TRC_HWTC_DIVISOR. */
\r
3070 last_timestamp += (hwtc_count + traceTickCount * ((TRC_HWTC_PERIOD) % (TRC_HWTC_DIVISOR))) / (TRC_HWTC_DIVISOR);
\r
3072 /* Store the previous value */
\r
3073 last_traceTickCount = traceTickCount;
\r
3075 #else /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
\r
3077 /* Timestamping is based on a free running timer */
\r
3078 /* This part handles free running clocks that can be scaled down to avoid too large DTS values.
\r
3079 Without this, the scaled timestamp will incorrectly wrap at (2^32 / TRC_HWTC_DIVISOR) ticks.
\r
3080 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
3082 /* 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
3083 diff = (hwtc_count - last_hwtc_count) + last_hwtc_rest;
\r
3085 /* Scale down the diff */
\r
3086 diff_scaled = diff / (TRC_HWTC_DIVISOR);
\r
3088 /* Find out how many ticks were lost when scaling down, so we can add them the next time */
\r
3089 last_hwtc_rest = diff % (TRC_HWTC_DIVISOR);
\r
3091 /* We increase the scaled timestamp by the scaled amount */
\r
3092 last_timestamp += diff_scaled;
\r
3093 #endif /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
\r
3095 /* Is anyone interested in the results? */
\r
3097 *pTimestamp = last_timestamp;
\r
3099 /* Store the previous value */
\r
3100 last_hwtc_count = hwtc_count;
\r
3103 #endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/
\r
3105 #endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)*/
\r