summaryrefslogtreecommitdiff
path: root/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c')
-rw-r--r--FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c3105
1 files changed, 3105 insertions, 0 deletions
diff --git a/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c b/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c
new file mode 100644
index 000000000..9eec9d2ed
--- /dev/null
+++ b/FreeRTOS-Labs/Source/FreeRTOS-Plus-Trace/trcSnapshotRecorder.c
@@ -0,0 +1,3105 @@
+/*******************************************************************************
+ * Trace Recorder Library for Tracealyzer v4.1.5
+ * Percepio AB, www.percepio.com
+ *
+ * trcSnapshotRecorder.c
+ *
+ * The generic core of the trace recorder's snapshot mode.
+ *
+ * Terms of Use
+ * This file is part of the trace recorder library (RECORDER), which is the
+ * intellectual property of Percepio AB (PERCEPIO) and provided under a
+ * license as follows.
+ * The RECORDER may be used free of charge for the purpose of recording data
+ * intended for analysis in PERCEPIO products. It may not be used or modified
+ * for other purposes without explicit permission from PERCEPIO.
+ * You may distribute the RECORDER in its original source code form, assuming
+ * this text (terms of use, disclaimer, copyright notice) is unchanged. You are
+ * allowed to distribute the RECORDER with minor modifications intended for
+ * configuration or porting of the RECORDER, e.g., to allow using it on a
+ * specific processor, processor family or with a specific communication
+ * interface. Any such modifications should be documented directly below
+ * this comment block.
+ *
+ * Disclaimer
+ * The RECORDER is being delivered to you AS IS and PERCEPIO makes no warranty
+ * as to its use or performance. PERCEPIO does not and cannot warrant the
+ * performance or results you may obtain by using the RECORDER or documentation.
+ * PERCEPIO make no warranties, express or implied, as to noninfringement of
+ * third party rights, merchantability, or fitness for any particular purpose.
+ * In no event will PERCEPIO, its technology partners, or distributors be liable
+ * to you for any consequential, incidental or special damages, including any
+ * lost profits or lost savings, even if a representative of PERCEPIO has been
+ * advised of the possibility of such damages, or for any claim by any third
+ * party. Some jurisdictions do not allow the exclusion or limitation of
+ * incidental, consequential or special damages, or the exclusion of implied
+ * warranties or limitations on how long an implied warranty may last, so the
+ * above limitations may not apply to you.
+ *
+ * Tabs are used for indent in this file (1 tab = 4 spaces)
+ *
+ * Copyright Percepio AB, 2018.
+ * www.percepio.com
+ ******************************************************************************/
+
+#include "trcRecorder.h"
+
+#if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)
+
+#if (TRC_USE_TRACEALYZER_RECORDER == 1)
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#if ((TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR) || (TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR))
+ #error "CUSTOM timestamping mode is not (yet) supported in snapshot mode!"
+#endif
+
+/* DO NOT CHANGE */
+#define TRACE_MINOR_VERSION 5
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+static traceHandle isrstack[TRC_CFG_MAX_ISR_NESTING];
+int32_t isPendingContextSwitch = 0;
+#endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1) */
+
+#if !defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1
+static int readyEventsEnabled = 1;
+#endif /*!defined TRC_CFG_INCLUDE_READY_EVENTS || TRC_CFG_INCLUDE_READY_EVENTS == 1*/
+
+/*******************************************************************************
+ * uiTraceTickCount
+ *
+ * This variable is should be updated by the Kernel tick interrupt. This does
+ * not need to be modified when developing a new timer port. It is preferred to
+ * keep any timer port changes in the HWTC macro definitions, which typically
+ * give sufficient flexibility.
+ ******************************************************************************/
+uint32_t uiTraceTickCount = 0;
+
+uint32_t trace_disable_timestamp = 0;
+
+static uint32_t last_timestamp = 0;
+
+/* Flag that shows if inside a critical section of the recorder */
+volatile int recorder_busy = 0;
+
+/* Holds the value set by vTraceSetFrequency */
+uint32_t timestampFrequency = 0;
+
+/* The last error message of the recorder. NULL if no error message. */
+const char* traceErrorMessage = NULL;
+
+int8_t nISRactive = 0;
+
+traceHandle handle_of_last_logged_task = 0;
+
+/* Called when the recorder is stopped, set by vTraceSetStopHook. */
+TRACE_STOP_HOOK vTraceStopHookPtr = (TRACE_STOP_HOOK)0;
+
+uint16_t CurrentFilterMask = 0xFFFF;
+
+uint16_t CurrentFilterGroup = FilterGroup0;
+
+extern int8_t nISRactive;
+
+extern traceHandle handle_of_last_logged_task;
+
+/*************** Private Functions *******************************************/
+static void prvStrncpy(char* dst, const char* src, uint32_t maxLength);
+static uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id);
+static void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength);
+static void* prvTraceNextFreeEventBufferSlot(void);
+static uint16_t prvTraceGetDTS(uint16_t param_maxDTS);
+static traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel);
+static void prvTraceUpdateCounters(void);
+
+void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size);
+
+#if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+static void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nEntries);
+#endif
+
+static traceString prvTraceCreateSymbolTableEntry(const char* name,
+ uint8_t crc6,
+ uint8_t len,
+ traceString channel);
+
+static traceString prvTraceLookupSymbolTableEntry(const char* name,
+ uint8_t crc6,
+ uint8_t len,
+ traceString channel);
+
+
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 0)
+/* ISR tracing is turned off */
+void prvTraceIncreaseISRActive(void);
+void prvTraceDecreaseISRActive(void);
+#endif /*(TRC_CFG_INCLUDE_ISR_TRACING == 0)*/
+
+#if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
+static uint8_t prvTraceGet8BitHandle(traceHandle handle);
+#else
+#define prvTraceGet8BitHandle(x) ((uint8_t)x)
+#endif
+
+
+#if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1) && (TRC_CFG_SCHEDULING_ONLY == 0)
+static uint32_t heapMemUsage = 0;
+#endif
+
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+static uint32_t prvTraceGetParam(uint32_t, uint32_t);
+#endif
+
+/*******************************************************************************
+ * prvTraceInitTraceData
+ *
+ * Allocates and initializes the recorder data structure, based on the constants
+ * in trcConfig.h. This allows for allocating the data on the heap, instead of
+ * using a static declaration.
+ ******************************************************************************/
+static void prvTraceInitTraceData(void);
+
+/*******************************************************************************
+ * prvTracePortGetTimeStamp
+ *
+ * Returns the current time based on the HWTC macros which provide a hardware
+ * isolation layer towards the hardware timer/counter.
+ *
+ * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
+ * or the trace recorder library. Typically you should not need to change
+ * the code of prvTracePortGetTimeStamp if using the HWTC macros.
+ *
+ ******************************************************************************/
+void prvTracePortGetTimeStamp(uint32_t *puiTimestamp);
+
+static void prvTraceTaskInstanceFinish(int8_t direct);
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+static void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl);
+
+#if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
+static void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl);
+static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl);
+static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots);
+#endif /*(TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)*/
+#endif /* ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1)) */
+
+/********* Public Functions **************************************************/
+
+uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass);
+
+/*******************************************************************************
+ * prvTraceError
+ *
+ * Called by various parts in the recorder. Stops the recorder and stores a
+ * pointer to an error message, which is printed by the monitor task.
+ ******************************************************************************/
+void prvTraceError(const char* msg);
+
+/******************************************************************************
+* vTraceEnable(int startOption) - snapshot mode
+*
+* Initializes and optionally starts the trace, depending on the start option.
+* To use the trace recorder, the startup must call vTraceEnable before any RTOS
+* calls are made (including "create" calls). Three start options are provided:
+*
+* TRC_START: Starts the tracing directly. In snapshot mode this allows for
+* starting the trace at any point in your code, assuming vTraceEnable(TRC_INIT)
+* has been called in the startup.
+* Can also be used for streaming without Tracealyzer control, e.g. to a local
+* flash file system (assuming such a "stream port", see trcStreamingPort.h).
+*
+* TRC_INIT: Initializes the trace recorder, but does not start the tracing.
+* In snapshot mode, this must be followed by a vTraceEnable(TRC_START) sometime
+* later.
+*
+* Usage examples, in snapshot mode:
+*
+* Snapshot trace, from startup:
+* <board init>
+* vTraceEnable(TRC_START);
+* <RTOS init>
+*
+* Snapshot trace, from a later point:
+* <board init>
+* vTraceEnable(TRC_INIT);
+* <RTOS init>
+* ...
+* vTraceEnable(TRC_START); // e.g., in task context, at some relevant event
+*
+*
+* Note: See other implementation of vTraceEnable in trcStreamingRecorder.c
+******************************************************************************/
+void vTraceEnable(int startOption)
+{
+ prvTraceInitTraceData();
+
+ if (startOption == TRC_START)
+ {
+ vTraceStart();
+ }
+ else if (startOption == TRC_START_AWAIT_HOST)
+ {
+ prvTraceError("vTraceEnable(TRC_START_AWAIT_HOST) not allowed in Snapshot mode");
+ }
+ else if (startOption != TRC_INIT)
+ {
+ prvTraceError("Unexpected argument to vTraceEnable (snapshot mode)");
+ }
+}
+
+/*******************************************************************************
+ * vTraceSetRecorderDataBuffer
+ *
+ * If custom allocation is used, this function must be called so the recorder
+ * library knows where to save the trace data.
+ ******************************************************************************/
+#if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
+void vTraceSetRecorderDataBuffer(void* pRecorderData)
+{
+ TRACE_ASSERT(pRecorderData != NULL, "vTraceSetRecorderDataBuffer, pRecorderData == NULL", TRC_UNUSED);
+ RecorderDataPtr = pRecorderData;
+}
+#endif
+
+/*******************************************************************************
+ * vTraceSetStopHook
+ *
+ * Sets a function to be called when the recorder is stopped. This can be used
+ * to save the trace to a file system, if available. This is only implemented
+ * for snapshot mode.
+ ******************************************************************************/
+void vTraceSetStopHook(TRACE_STOP_HOOK stopHookFunction)
+{
+ vTraceStopHookPtr = stopHookFunction;
+}
+
+/*******************************************************************************
+ * vTraceClear
+ *
+ * Resets the recorder. Only necessary if a restart is desired - this is not
+ * needed in the startup initialization.
+ ******************************************************************************/
+void vTraceClear(void)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+ trcCRITICAL_SECTION_BEGIN();
+ RecorderDataPtr->absTimeLastEventSecond = 0;
+ RecorderDataPtr->absTimeLastEvent = 0;
+ RecorderDataPtr->nextFreeIndex = 0;
+ RecorderDataPtr->numEvents = 0;
+ RecorderDataPtr->bufferIsFull = 0;
+ traceErrorMessage = NULL;
+ RecorderDataPtr->internalErrorOccured = 0;
+ (void)memset(RecorderDataPtr->eventData, 0, RecorderDataPtr->maxEvents * 4);
+ handle_of_last_logged_task = 0;
+ trcCRITICAL_SECTION_END();
+}
+
+/*******************************************************************************
+ * uiTraceStart
+ *
+ * Starts the recorder. The recorder will not be started if an error has been
+ * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
+ * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
+ *
+ * Returns 1 if the recorder was started successfully.
+ * Returns 0 if the recorder start was prevented due to a previous internal
+ * error. In that case, check xTraceGetLastError to get the error message.
+ * Any error message is also presented when opening a trace file.
+ *
+ * This function is obsolete, but has been saved for backwards compatibility.
+ * We recommend using vTraceEnable instead.
+ ******************************************************************************/
+uint32_t uiTraceStart(void)
+{
+ traceHandle handle;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ handle = 0;
+
+ if (RecorderDataPtr == NULL)
+ {
+ TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized. Use vTraceEnable() instead!", 0);
+ return 0;
+ }
+
+ if (RecorderDataPtr->recorderActive == 1)
+ return 1; /* Already running */
+
+ if (traceErrorMessage == NULL)
+ {
+ trcCRITICAL_SECTION_BEGIN();
+ RecorderDataPtr->recorderActive = 1;
+
+ handle = TRACE_GET_TASK_NUMBER(TRACE_GET_CURRENT_TASK());
+ if (handle == 0)
+ {
+ /* This occurs if the scheduler is not yet started.
+ This creates a dummy "(startup)" task entry internally in the
+ recorder */
+ handle = prvTraceGetObjectHandle(TRACE_CLASS_TASK);
+ prvTraceSetObjectName(TRACE_CLASS_TASK, handle, "(startup)");
+
+ prvTraceSetPriorityProperty(TRACE_CLASS_TASK, handle, 0);
+ }
+
+ prvTraceStoreTaskswitch(handle); /* Register the currently running task */
+ trcCRITICAL_SECTION_END();
+ }
+
+ return RecorderDataPtr->recorderActive;
+}
+
+/*******************************************************************************
+ * vTraceStart
+ *
+ * Starts the recorder. The recorder will not be started if an error has been
+ * indicated using prvTraceError, e.g. if any of the Nx constants in trcConfig.h
+ * has a too small value (TRC_CFG_NTASK, TRC_CFG_NQUEUE, etc).
+ *
+ * This function is obsolete, but has been saved for backwards compatibility.
+ * We recommend using vTraceEnable instead.
+ ******************************************************************************/
+void vTraceStart(void)
+{
+ (void)uiTraceStart();
+}
+
+/*******************************************************************************
+ * vTraceStop
+ *
+ * Stops the recorder. The recording can be resumed by calling vTraceStart.
+ * This does not reset the recorder. Use vTraceClear if that is desired.
+ ******************************************************************************/
+void vTraceStop(void)
+{
+ if (RecorderDataPtr != NULL)
+ {
+ RecorderDataPtr->recorderActive = 0;
+ }
+
+ if (vTraceStopHookPtr != (TRACE_STOP_HOOK)0)
+ {
+ (*vTraceStopHookPtr)(); /* An application call-back function. */
+ }
+}
+
+/*******************************************************************************
+* xTraceIsRecordingEnabled
+* Returns true (1) if the recorder is enabled (i.e. is recording), otherwise 0.
+******************************************************************************/
+int xTraceIsRecordingEnabled(void)
+{
+ if (RecorderDataPtr != NULL)
+ {
+ return (int)RecorderDataPtr->recorderActive;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*******************************************************************************
+ * xTraceGetLastError
+ *
+ * Gives the last error message, if any. NULL if no error message is stored.
+ * Any error message is also presented when opening a trace file.
+ ******************************************************************************/
+const char* xTraceGetLastError(void)
+{
+ return traceErrorMessage;
+}
+
+/*******************************************************************************
+* vTraceClearError
+*
+* Removes any previous error message generated by recorder calling prvTraceError.
+* By calling this function, it may be possible to start/restart the trace
+* despite errors in the recorder, but there is no guarantee that the trace
+* recorder will work correctly in that case, depending on the type of error.
+******************************************************************************/
+void vTraceClearError(void)
+{
+ traceErrorMessage = NULL;
+ if (RecorderDataPtr != NULL)
+ {
+ RecorderDataPtr->internalErrorOccured = 0;
+ }
+}
+
+/*******************************************************************************
+ * xTraceGetTraceBuffer
+ *
+ * Returns a pointer to the recorder data structure. Use this together with
+ * uiTraceGetTraceBufferSize if you wish to implement an own store/upload
+ * solution, e.g., in case a debugger connection is not available for uploading
+ * the data.
+ ******************************************************************************/
+void* xTraceGetTraceBuffer(void)
+{
+ return RecorderDataPtr;
+}
+
+/*******************************************************************************
+ * uiTraceGetTraceBufferSize
+ *
+ * Gets the size of the recorder data structure. For use together with
+ * vTraceGetTraceBuffer if you wish to implement an own store/upload solution,
+ * e.g., in case a debugger connection is not available for uploading the data.
+ ******************************************************************************/
+uint32_t uiTraceGetTraceBufferSize(void)
+{
+ return sizeof(RecorderDataType);
+}
+
+/******************************************************************************
+ * prvTraceTaskInstanceFinish
+ *
+ * Private common function for the vTraceTaskInstanceFinishXXX functions.
+ *****************************************************************************/
+static void prvTraceTaskInstanceFinish(int8_t direct)
+{
+ TaskInstanceStatusEvent* tis;
+ uint8_t dts45;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ dts45 = (uint8_t)prvTraceGetDTS(0xFF);
+ tis = (TaskInstanceStatusEvent*) prvTraceNextFreeEventBufferSlot();
+ if (tis != NULL)
+ {
+ if (direct == 0)
+ tis->type = TASK_INSTANCE_FINISHED_NEXT_KSE;
+ else
+ tis->type = TASK_INSTANCE_FINISHED_DIRECT;
+
+ tis->dts = dts45;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+
+/******************************************************************************
+ * vTraceInstanceFinishedNext(void)
+ *
+ * Marks the current task instance as finished on the next kernel call.
+ *
+ * If that kernel call is blocking, the instance ends after the blocking event
+ * and the corresponding return event is then the start of the next instance.
+ * If the kernel call is not blocking, the viewer instead splits the current
+ * fragment right before the kernel call, which makes this call the first event
+ * of the next instance.
+ *
+ * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
+ *
+ * Example:
+ *
+ * while(1)
+ * {
+ * xQueueReceive(CommandQueue, &command, timeoutDuration);
+ * processCommand(command);
+ * vTraceInstanceFinishedNext();
+ * }
+ *****************************************************************************/
+void vTraceInstanceFinishedNext(void)
+{
+ prvTraceTaskInstanceFinish(0);
+}
+
+/******************************************************************************
+ * vTraceInstanceFinishedNow(void)
+ *
+ * Marks the current task instance as finished at this very instant.
+ * This makes the viewer to splits the current fragment at this point and begin
+ * a new actor instance.
+ *
+ * See also TRC_CFG_USE_IMPLICIT_IFE_RULES in trcConfig.h
+ *
+ * Example:
+ *
+ * This example will generate two instances for each loop iteration.
+ * The first instance ends at vTraceInstanceFinishedNow(), while the second
+ * instance ends at the next xQueueReceive call.
+ *
+ * while (1)
+ * {
+ * xQueueReceive(CommandQueue, &command, timeoutDuration);
+ * ProcessCommand(command);
+ * vTraceInstanceFinishedNow();
+ * DoSometingElse();
+ * vTraceInstanceFinishedNext();
+ * }
+ *****************************************************************************/
+void vTraceInstanceFinishedNow(void)
+{
+ prvTraceTaskInstanceFinish(1);
+}
+
+/*******************************************************************************
+ * Interrupt recording functions
+ ******************************************************************************/
+
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+
+/*******************************************************************************
+ * xTraceSetISRProperties
+ *
+ * Stores a name and priority level for an Interrupt Service Routine, to allow
+ * for better visualization. Returns a traceHandle used by vTraceStoreISRBegin.
+ *
+ * Example:
+ * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * ...
+ * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(Timer1Handle);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ ******************************************************************************/
+ traceHandle xTraceSetISRProperties(const char* name, uint8_t priority)
+{
+ static traceHandle handle = 0;
+ TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "xTraceSetISRProperties: Invalid value for handle", 0);
+ TRACE_ASSERT(name != NULL, "xTraceSetISRProperties: name == NULL", 0);
+
+ handle++;
+
+ prvTraceSetObjectName(TRACE_CLASS_ISR, handle, name);
+ prvTraceSetPriorityProperty(TRACE_CLASS_ISR, handle, priority);
+
+ return handle;
+}
+
+/*******************************************************************************
+ * vTraceStoreISRBegin
+ *
+ * Registers the beginning of an Interrupt Service Routine, using a traceHandle
+ * provided by xTraceSetISRProperties.
+ *
+ * Example:
+ * #define PRIO_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * ...
+ * traceHandle Timer1Handle = xTraceSetISRProperties("ISRTimer1", PRIO_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(Timer1Handle);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ ******************************************************************************/
+void vTraceStoreISRBegin(traceHandle handle)
+{
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("vTraceStoreISRBegin - recorder busy! See code comment.");
+ return;
+ }
+ trcCRITICAL_SECTION_BEGIN();
+
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ uint16_t dts4;
+
+ TRACE_ASSERT(handle != 0, "vTraceStoreISRBegin: Invalid ISR handle (NULL)", TRC_UNUSED);
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_ISR], "vTraceStoreISRBegin: Invalid ISR handle (> NISR)", TRC_UNUSED);
+
+ dts4 = (uint16_t)prvTraceGetDTS(0xFFFF);
+
+ if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
+ {
+ if (nISRactive < TRC_CFG_MAX_ISR_NESTING)
+ {
+ TSEvent* ts;
+ uint8_t hnd8 = prvTraceGet8BitHandle(handle);
+ isrstack[nISRactive] = handle;
+ nISRactive++;
+ ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
+ if (ts != NULL)
+ {
+ ts->type = TS_ISR_BEGIN;
+ ts->dts = dts4;
+ ts->objHandle = hnd8;
+ prvTraceUpdateCounters();
+ }
+ }
+ else
+ {
+ /* This should not occur unless something is very wrong */
+ prvTraceError("Too many nested interrupts!");
+ }
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+
+/*******************************************************************************
+ * vTraceStoreISREnd
+ *
+ * Registers the end of an Interrupt Service Routine.
+ *
+ * The parameter pendingISR indicates if the interrupt has requested a
+ * task-switch (= 1), e.g., by signaling a semaphore. Otherwise (= 0) the
+ * interrupt is assumed to return to the previous context.
+ *
+ * Example:
+ * #define PRIO_OF_ISR_TIMER1 3 // the hardware priority of the interrupt
+ * traceHandle traceHandleIsrTimer1 = 0; // The ID set by the recorder
+ * ...
+ * traceHandleIsrTimer1 = xTraceSetISRProperties("ISRTimer1", PRIO_OF_ISR_TIMER1);
+ * ...
+ * void ISR_handler()
+ * {
+ * vTraceStoreISRBegin(traceHandleIsrTimer1);
+ * ...
+ * vTraceStoreISREnd(0);
+ * }
+ ******************************************************************************/
+void vTraceStoreISREnd(int pendingISR)
+{
+ TSEvent* ts;
+ uint16_t dts5;
+ uint8_t hnd8 = 0, type = 0;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ if (! RecorderDataPtr->recorderActive || ! handle_of_last_logged_task)
+ {
+ return;
+ }
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("vTraceStoreISREnd - recorder busy! See code comment.");
+ return;
+ }
+
+ if (nISRactive == 0)
+ {
+ prvTraceError("Unmatched call to vTraceStoreISREnd (nISRactive == 0, expected > 0)");
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ isPendingContextSwitch |= pendingISR; /* Is there a pending context switch right now? */
+ nISRactive--;
+ if (nISRactive > 0)
+ {
+ /* Return to another ISR */
+ type = TS_ISR_RESUME;
+ 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. */
+ }
+ else if ((isPendingContextSwitch == 0) || (prvTraceIsSchedulerSuspended()))
+ {
+ /* Return to interrupted task, if no context switch will occur in between. */
+ type = TS_TASK_RESUME;
+ hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
+ }
+
+ if (type != 0)
+ {
+ dts5 = (uint16_t)prvTraceGetDTS(0xFFFF);
+ ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
+ if (ts != NULL)
+ {
+ ts->type = type;
+ ts->objHandle = hnd8;
+ ts->dts = dts5;
+ prvTraceUpdateCounters();
+ }
+ }
+
+ trcCRITICAL_SECTION_END();
+}
+
+#else
+
+/* ISR tracing is turned off */
+void prvTraceIncreaseISRActive(void)
+{
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ nISRactive++;
+}
+
+void prvTraceDecreaseISRActive(void)
+{
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ nISRactive--;
+}
+#endif /* (TRC_CFG_INCLUDE_ISR_TRACING == 1)*/
+
+
+/********************************************************************************/
+/* User Event functions */
+/********************************************************************************/
+
+#define MAX_ARG_SIZE (4+32)
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+static uint8_t writeInt8(void * buffer, uint8_t i, uint8_t value)
+{
+ TRACE_ASSERT(buffer != NULL, "writeInt8: buffer == NULL", 0);
+
+ if (i >= MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint8_t*)buffer)[i] = value;
+
+ if (i + 1 > MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ return ((uint8_t) (i + 1));
+}
+#endif
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+static uint8_t writeInt16(void * buffer, uint8_t i, uint16_t value)
+{
+ TRACE_ASSERT(buffer != NULL, "writeInt16: buffer == NULL", 0);
+
+ /* Align to multiple of 2 */
+ while ((i % 2) != 0)
+ {
+ if (i >= MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint8_t*)buffer)[i] = 0;
+ i++;
+ }
+
+ if (i + 2 > MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint16_t*)buffer)[i/2] = value;
+
+ return ((uint8_t) (i + 2));
+}
+#endif
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+static uint8_t writeInt32(void * buffer, uint8_t i, uint32_t value)
+{
+ TRACE_ASSERT(buffer != NULL, "writeInt32: buffer == NULL", 0);
+
+ /* A 32 bit value should begin at an even 4-byte address */
+ while ((i % 4) != 0)
+ {
+ if (i >= MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint8_t*)buffer)[i] = 0;
+ i++;
+ }
+
+ if (i + 4 > MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint32_t*)buffer)[i/4] = value;
+
+ return ((uint8_t) (i + 4));
+}
+#endif
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
+static uint8_t writeFloat(void * buffer, uint8_t i, float value)
+{
+ TRACE_ASSERT(buffer != NULL, "writeFloat: buffer == NULL", 0);
+
+ /* A 32 bit value should begin at an even 4-byte address */
+ while ((i % 4) != 0)
+ {
+ if (i >= MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint8_t*)buffer)[i] = 0;
+ i++;
+ }
+
+ if (i + 4 > MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((float*)buffer)[i/4] = value;
+
+ return i + 4;
+}
+#endif
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_INCLUDE_FLOAT_SUPPORT))
+static uint8_t writeDouble(void * buffer, uint8_t i, double value)
+{
+ uint32_t * dest;
+ uint32_t * src = (uint32_t*)&value;
+
+ TRACE_ASSERT(buffer != NULL, "writeDouble: buffer == NULL", 0);
+
+ /* The double is written as two 32 bit values, and should begin at an even
+ 4-byte address (to avoid having to align with 8 byte) */
+ while (i % 4 != 0)
+ {
+ if (i >= MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ ((uint8_t*)buffer)[i] = 0;
+ i++;
+ }
+
+ if (i + 8 > MAX_ARG_SIZE)
+ {
+ return 255;
+ }
+
+ dest = &(((uint32_t *)buffer)[i/4]);
+
+ dest[0] = src[0];
+ dest[1] = src[1];
+
+ return i + 8;
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceUserEventFormat
+ *
+ * Parses the format string and stores the arguments in the buffer.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+static uint8_t prvTraceUserEventFormat(const char* formatStr, va_list vl, uint8_t* buffer, uint8_t byteOffset)
+{
+ uint16_t formatStrIndex = 0;
+ uint8_t argCounter = 0;
+ uint8_t i = byteOffset;
+
+ while (formatStr[formatStrIndex] != '\0')
+ {
+ if (formatStr[formatStrIndex] == '%')
+ {
+ argCounter++;
+
+ if (argCounter > 15)
+ {
+ prvTraceError("vTracePrintF - Too many arguments, max 15 allowed!");
+ return 0;
+ }
+
+ formatStrIndex++;
+
+ while ((formatStr[formatStrIndex] >= '0' && formatStr[formatStrIndex] <= '9') || formatStr[formatStrIndex] == '#' || formatStr[formatStrIndex] == '.')
+ formatStrIndex++;
+
+ if (formatStr[formatStrIndex] != '\0')
+ {
+ switch (formatStr[formatStrIndex])
+ {
+ case 'd': i = writeInt32( buffer,
+ i,
+ (uint32_t)va_arg(vl, uint32_t));
+ break;
+ case 'x':
+ case 'X':
+ case 'u': i = writeInt32( buffer,
+ i,
+ (uint32_t)va_arg(vl, uint32_t));
+ break;
+ case 's': i = writeInt16( buffer,
+ i,
+ xTraceRegisterString((char*)va_arg(vl, char*)));
+ break;
+
+#if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
+ /* Yes, "double" as type also in the float
+ case. This since "float" is promoted into "double"
+ by the va_arg stuff. */
+ case 'f': i = writeFloat( buffer,
+ i,
+ (float)va_arg(vl, double));
+ break;
+#else
+ /* No support for floats, but attempt to store a float user event
+ avoid a possible crash due to float reference. Instead store the
+ data on uint_32 format (will not be displayed anyway). This is just
+ to keep va_arg and i consistent. */
+
+ case 'f': i = writeInt32( buffer,
+ i,
+ (uint32_t)va_arg(vl, double));
+ break;
+#endif
+ case 'l':
+ formatStrIndex++;
+ switch (formatStr[formatStrIndex])
+ {
+#if (TRC_CFG_INCLUDE_FLOAT_SUPPORT)
+ case 'f': i = writeDouble(buffer,
+ i,
+ (double)va_arg(vl, double));
+ break;
+#else
+ /* No support for floats, but attempt to store a float user event
+ avoid a possible crash due to float reference. Instead store the
+ data on uint_32 format (will not be displayed anyway). This is just
+ to keep va_arg and i consistent. */
+ case 'f': i = writeInt32( buffer, /* In this case, the value will not be shown anyway */
+ i,
+ (uint32_t)va_arg(vl, double));
+
+ i = writeInt32( buffer, /* Do it twice, to write in total 8 bytes */
+ i,
+ (uint32_t)va_arg(vl, double));
+ break;
+#endif
+
+ }
+ break;
+ case 'h':
+ formatStrIndex++;
+ switch (formatStr[formatStrIndex])
+ {
+ case 'd': i = writeInt16( buffer,
+ i,
+ (uint16_t)va_arg(vl, uint32_t));
+ break;
+ case 'u': i = writeInt16( buffer,
+ i,
+ (uint16_t)va_arg(vl, uint32_t));
+ break;
+ }
+ break;
+ case 'b':
+ formatStrIndex++;
+ switch (formatStr[formatStrIndex])
+ {
+ case 'd': i = writeInt8( buffer,
+ i,
+ (uint8_t)va_arg(vl, uint32_t));
+ break;
+
+ case 'u': i = writeInt8( buffer,
+ i,
+ (uint8_t)va_arg(vl, uint32_t));
+ break;
+ }
+ break;
+ }
+ }
+ else
+ break;
+ }
+ formatStrIndex++;
+ if (i == 255)
+ {
+ prvTraceError("vTracePrintF - Too large arguments, max 32 byte allowed!");
+ return 0;
+ }
+ }
+ return (uint8_t)(i+3)/4;
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceClearChannelBuffer
+ *
+ * Clears a number of items in the channel buffer, starting from nextSlotToWrite.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+static void prvTraceClearChannelBuffer(uint32_t count)
+{
+ uint32_t slots;
+
+ TRACE_ASSERT((TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) >= count,
+ "prvTraceClearChannelBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
+
+ /* Check if we're close to the end of the buffer */
+ if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE))
+ {
+ slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, slots);
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[0], 0, (count - slots));
+ }
+ else
+ (void)memset(&RecorderDataPtr->userEventBuffer.channelBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite], 0, count);
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceCopyToDataBuffer
+ *
+ * Copies a number of items to the data buffer, starting from nextSlotToWrite.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+static void prvTraceCopyToDataBuffer(uint32_t* data, uint32_t count)
+{
+ uint32_t slots;
+
+ TRACE_ASSERT(data != NULL,
+ "prvTraceCopyToDataBuffer: data == NULL.", TRC_UNUSED);
+ TRACE_ASSERT(count <= (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE),
+ "prvTraceCopyToDataBuffer: TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE is too small to handle this event.", TRC_UNUSED);
+ /* Check if we're close to the end of the buffer */
+ if (RecorderDataPtr->userEventBuffer.nextSlotToWrite + count > (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE))
+ {
+ slots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE) - RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Number of slots before end of buffer */
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, slots * 4);
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[0], data + slots, (count - slots) * 4);
+ }
+ else
+ {
+ (void)memcpy(&RecorderDataPtr->userEventBuffer.dataBuffer[RecorderDataPtr->userEventBuffer.nextSlotToWrite * 4], data, count * 4);
+ }
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceUBHelper1
+ *
+ * Calls on prvTraceUserEventFormat() to do the actual formatting, then goes on
+ * to the next helper function.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+static void prvTraceUBHelper1(traceUBChannel channel, traceString eventLabel, traceString formatLabel, va_list vl)
+{
+ uint32_t data[(3 + MAX_ARG_SIZE) / 4];
+ uint8_t byteOffset = 4; /* Need room for timestamp */
+ uint8_t noOfSlots;
+
+ if (channel == 0)
+ {
+ /* We are dealing with an unknown channel format pair */
+ byteOffset = (uint8_t)(byteOffset + 4); /* Also need room for channel and format */
+ ((uint16_t*)data)[2] = eventLabel;
+ ((uint16_t*)data)[3] = formatLabel;
+ }
+
+ noOfSlots = prvTraceUserEventFormat((char*)&(RecorderDataPtr->SymbolTable.symbytes[formatLabel+4]), vl, (uint8_t*)data, byteOffset);
+
+ prvTraceUBHelper2(channel, data, noOfSlots);
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceUBHelper2
+ *
+ * This function simply copies the data buffer to the actual user event buffer.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+static void prvTraceUBHelper2(traceUBChannel channel, uint32_t* data, uint32_t noOfSlots)
+{
+ static uint32_t old_timestamp = 0;
+ uint32_t old_nextSlotToWrite = 0;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ 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);
+
+ trcCRITICAL_SECTION_BEGIN();
+ /* Store the timestamp */
+ prvTracePortGetTimeStamp(data);
+
+ if (*data < old_timestamp)
+ {
+ RecorderDataPtr->userEventBuffer.wraparoundCounter++;
+ }
+
+ old_timestamp = *data;
+
+ /* Start by erasing any information in the channel buffer */
+ prvTraceClearChannelBuffer(noOfSlots);
+
+ prvTraceCopyToDataBuffer(data, noOfSlots); /* Will wrap around the data if necessary */
+
+ old_nextSlotToWrite = RecorderDataPtr->userEventBuffer.nextSlotToWrite; /* Save the index that we want to write the channel data at when we're done */
+ RecorderDataPtr->userEventBuffer.nextSlotToWrite = (RecorderDataPtr->userEventBuffer.nextSlotToWrite + noOfSlots) % (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE); /* Make sure we never end up outside the buffer */
+
+ /* Write to the channel buffer to indicate that this user event is ready to be used */
+ if (channel != 0)
+ {
+ RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = channel;
+ }
+ else
+ {
+ /* 0xFF indicates that this is not a normal channel id */
+ RecorderDataPtr->userEventBuffer.channelBuffer[old_nextSlotToWrite] = (traceUBChannel)0xFF;
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif
+
+/*******************************************************************************
+ * xTraceRegisterUBChannel
+ *
+ * Registers a channel for Separated User Events, i.e., those stored in the
+ * separate user event buffer.
+ *
+ * Note: Only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is enabled in
+ * trcSnapshotConfig.h
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+traceUBChannel xTraceRegisterUBChannel(traceString channel, traceString formatStr)
+{
+ uint8_t i;
+ traceUBChannel retVal = 0;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(formatStr != 0, "xTraceRegisterChannelFormat: formatStr == 0", (traceUBChannel)0);
+
+ trcCRITICAL_SECTION_BEGIN();
+ for (i = 1; i <= (TRC_CFG_UB_CHANNELS); i++) /* Size of the channels buffer is TRC_CFG_UB_CHANNELS + 1. Index 0 is unused. */
+ {
+ if(RecorderDataPtr->userEventBuffer.channels[i].name == 0 && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == 0)
+ {
+ /* Found empty slot */
+ RecorderDataPtr->userEventBuffer.channels[i].name = channel;
+ RecorderDataPtr->userEventBuffer.channels[i].defaultFormat = formatStr;
+ retVal = (traceUBChannel)i;
+ break;
+ }
+
+ if (RecorderDataPtr->userEventBuffer.channels[i].name == channel && RecorderDataPtr->userEventBuffer.channels[i].defaultFormat == formatStr)
+ {
+ /* Found a match */
+ retVal = (traceUBChannel)i;
+ break;
+ }
+ }
+ trcCRITICAL_SECTION_END();
+
+ return retVal;
+}
+#endif
+
+/******************************************************************************
+ * vTraceUBData
+ *
+ * Slightly faster version of vTracePrintF() due to no lookups.
+ *
+ * Note: This is only available if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER is
+ * enabled in trcSnapshotConfig.h
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+void vTraceUBData(traceUBChannel channelPair, ...)
+{
+ va_list vl;
+
+ TRACE_ASSERT(channelPair != 0, "vTraceUBData: Not a valid traceUBChannel!", TRC_UNUSED);
+
+ va_start(vl, channelPair);
+ vTraceUBData_Helper(channelPair, vl);
+ va_end(vl);
+}
+#endif
+
+/* Extracts the channel name and format string from the traceUBChannel, then calls prvTraceUBHelper1. */
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+void vTraceUBData_Helper(traceUBChannel channelPair, va_list vl)
+{
+ traceString channel;
+ traceString formatStr;
+
+ TRACE_ASSERT(channelPair != 0, "vTraceUBData_Helper: channelPair == 0", TRC_UNUSED);
+ TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBData_Helper: ", TRC_UNUSED);
+
+ channel = RecorderDataPtr->userEventBuffer.channels[channelPair].name;
+ formatStr = RecorderDataPtr->userEventBuffer.channels[channelPair].defaultFormat;
+
+ prvTraceUBHelper1(channelPair, channel, formatStr, vl);
+}
+#endif
+
+/******************************************************************************
+ * vTraceUBEvent
+ *
+ * Slightly faster version of ... due to no lookups.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1) && (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1))
+void vTraceUBEvent(traceUBChannel channelPair)
+{
+ uint32_t data[(3 + MAX_ARG_SIZE) / 4];
+
+ TRACE_ASSERT(channelPair != 0, "vTraceUBEvent: channelPair == 0", TRC_UNUSED);
+ TRACE_ASSERT(channelPair <= (TRC_CFG_UB_CHANNELS), "vTraceUBEvent: ", TRC_UNUSED);
+
+ prvTraceUBHelper2(channelPair, data, 1); /* Only need one slot for timestamp */
+}
+#endif
+
+/******************************************************************************
+ * vTracePrintF
+ *
+ * Generates User Event with formatted text and data, similar to a "printf".
+ * It is very fast compared to a normal "printf" since this function only
+ * stores the arguments. The actual formatting is done
+ * on the host PC when the trace is displayed in the viewer tool.
+ *
+ * User Event labels are created using xTraceRegisterString.
+ * Example:
+ *
+ * traceString adc_uechannel = xTraceRegisterString("ADC User Events");
+ * ...
+ * vTracePrintF(adc_uechannel,
+ * "ADC channel %d: %lf volts",
+ * ch, (double)adc_reading/(double)scale);
+ *
+ * This can be combined into one line, if desired, but this is slower:
+ *
+ * vTracePrintF(xTraceRegisterString("ADC User Events"),
+ * "ADC channel %d: %lf volts",
+ * ch, (double)adc_reading/(double)scale);
+ *
+ * Calling xTraceRegisterString multiple times will not create duplicate entries, but
+ * it is of course faster to just do it once, and then keep the handle for later
+ * use. If you don't have any data arguments, only a text label/string, it is
+ * better to use vTracePrint - it is faster.
+ *
+ * Format specifiers supported:
+ * %d - 32 bit signed integer
+ * %u - 32 bit unsigned integer
+ * %f - 32 bit float
+ * %s - string (is copied to the recorder symbol table)
+ * %hd - 16 bit signed integer
+ * %hu - 16 bit unsigned integer
+ * %bd - 8 bit signed integer
+ * %bu - 8 bit unsigned integer
+ * %lf - double-precision float (Note! See below...)
+ *
+ * Up to 15 data arguments are allowed, with a total size of maximum 32 byte.
+ * In case this is exceeded, the user event is changed into an error message.
+ *
+ * The data is stored in trace buffer, and is packed to allow storing multiple
+ * smaller data entries in the same 4-byte record, e.g., four 8-bit values.
+ * A string requires two bytes, as the symbol table is limited to 64K. Storing
+ * a double (%lf) uses two records, so this is quite costly. Use float (%f)
+ * unless the higher precision is really necessary.
+ *
+ * Note that the double-precision float (%lf) assumes a 64 bit double
+ * representation. This does not seem to be the case on e.g. PIC24 and PIC32.
+ * Before using a %lf argument on a 16-bit MCU, please verify that
+ * "sizeof(double)" actually gives 8 as expected. If not, use %f instead.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+void vTracePrintF(traceString eventLabel, const char* formatStr, ...)
+{
+ va_list vl;
+
+ va_start(vl, formatStr);
+ vTracePrintF_Helper(eventLabel, formatStr, vl);
+ va_end(vl);
+}
+#endif
+
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+void vTracePrintF_Helper(traceString eventLabel, const char* formatStr, va_list vl)
+{
+#if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
+ uint32_t noOfSlots;
+ UserEvent* ue1;
+ uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(formatStr != NULL, "vTracePrintF_Helper: formatStr == NULL", TRC_UNUSED);
+
+ trcCRITICAL_SECTION_BEGIN();
+
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ /* First, write the "primary" user event entry in the local buffer, but
+ let the event type be "EVENT_BEING_WRITTEN" for now...*/
+
+ ue1 = (UserEvent*)(&tempDataBuffer[0]);
+
+ ue1->type = EVENT_BEING_WRITTEN; /* Update this as the last step */
+
+ noOfSlots = prvTraceUserEventFormat(formatStr, vl, (uint8_t*)tempDataBuffer, 4);
+
+ /* Store the format string, with a reference to the channel symbol */
+ ue1->payload = prvTraceOpenSymbol(formatStr, eventLabel);
+
+ ue1->dts = (uint8_t)prvTraceGetDTS(0xFF);
+
+ /* prvTraceGetDTS might stop the recorder in some cases... */
+ if (RecorderDataPtr->recorderActive)
+ {
+
+ /* If the data does not fit in the remaining main buffer, wrap around to
+ 0 if allowed, otherwise stop the recorder and quit). */
+ if (RecorderDataPtr->nextFreeIndex + noOfSlots > RecorderDataPtr->maxEvents)
+ {
+ #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ (void)memset(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
+ 0,
+ (RecorderDataPtr->maxEvents - RecorderDataPtr->nextFreeIndex)*4);
+ RecorderDataPtr->nextFreeIndex = 0;
+ RecorderDataPtr->bufferIsFull = 1;
+ #else
+
+ /* Stop recorder, since the event data will not fit in the
+ buffer and not circular buffer in this case... */
+ vTraceStop();
+ #endif
+ }
+
+ /* Check if recorder has been stopped (i.e., vTraceStop above) */
+ if (RecorderDataPtr->recorderActive)
+ {
+ /* Check that the buffer to be overwritten does not contain any user
+ events that would be partially overwritten. If so, they must be "killed"
+ by replacing the user event and following data with NULL events (i.e.,
+ using a memset to zero).*/
+ #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ prvCheckDataToBeOverwrittenForMultiEntryEvents((uint8_t)noOfSlots);
+ #endif
+ /* Copy the local buffer to the main buffer */
+ (void)memcpy(& RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4],
+ tempDataBuffer,
+ noOfSlots * 4);
+
+ /* Update the event type, i.e., number of data entries following the
+ main USER_EVENT entry (Note: important that this is after the memcpy,
+ but within the critical section!)*/
+ RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex * 4] =
+ (uint8_t) ( USER_EVENT + noOfSlots - 1 );
+
+ /* Update the main buffer event index (already checked that it fits in
+ the buffer, so no need to check for wrapping)*/
+
+ RecorderDataPtr->nextFreeIndex += noOfSlots;
+ RecorderDataPtr->numEvents += noOfSlots;
+
+ if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
+ {
+ #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ /* We have reached the end, but this is a ring buffer. Start from the beginning again. */
+ RecorderDataPtr->bufferIsFull = 1;
+ RecorderDataPtr->nextFreeIndex = 0;
+ #else
+ /* We have reached the end so we stop. */
+ vTraceStop();
+ #endif
+ }
+ }
+
+ #if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ /* Make sure the next entry is cleared correctly */
+ prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
+ #endif
+
+ }
+ }
+ trcCRITICAL_SECTION_END();
+
+#elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
+ /* Use the separate user event buffer */
+ traceString formatLabel;
+ traceUBChannel channel;
+
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ formatLabel = xTraceRegisterString(formatStr);
+
+ channel = xTraceRegisterUBChannel(eventLabel, formatLabel);
+
+ prvTraceUBHelper1(channel, eventLabel, formatLabel, vl);
+ }
+#endif
+}
+#endif
+
+/******************************************************************************
+ * vTracePrint
+ *
+ * Basic user event
+ *
+ * Generates a User Event with a text label. The label is created/looked up
+ * in the symbol table using xTraceRegisterString.
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+void vTracePrint(traceString chn, const char* str)
+{
+#if (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 0)
+ UserEvent* ue;
+ uint8_t dts1;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ dts1 = (uint8_t)prvTraceGetDTS(0xFF);
+ ue = (UserEvent*) prvTraceNextFreeEventBufferSlot();
+ if (ue != NULL)
+ {
+ ue->dts = dts1;
+ ue->type = USER_EVENT;
+ ue->payload = prvTraceOpenSymbol(str, chn);
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+
+#elif (TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER == 1)
+ traceUBChannel channel;
+ uint32_t noOfSlots = 1;
+ uint32_t tempDataBuffer[(3 + MAX_ARG_SIZE) / 4];
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ traceString trcStr = prvTraceOpenSymbol(str, chn);
+ channel = xTraceRegisterUBChannel(chn, trcStr);
+
+ if (channel == 0)
+ {
+ /* We are dealing with an unknown channel format pair */
+ noOfSlots++; /* Also need room for channel and format */
+ ((uint16_t*)tempDataBuffer)[2] = chn;
+ ((uint16_t*)tempDataBuffer)[3] = trcStr;
+ }
+
+ prvTraceUBHelper2(channel, tempDataBuffer, noOfSlots);
+ }
+#endif
+}
+#endif
+
+/*******************************************************************************
+ * xTraceRegisterString
+ *
+ * Register strings in the recorder, e.g. for names of user event channels.
+ *
+ * Example:
+ * myEventHandle = xTraceRegisterString("MyUserEvent");
+ * ...
+ * vTracePrintF(myEventHandle, "My value is: %d", myValue);
+ ******************************************************************************/
+#if ((TRC_CFG_SCHEDULING_ONLY == 0) && (TRC_CFG_INCLUDE_USER_EVENTS == 1))
+traceString xTraceRegisterString(const char* label)
+{
+ TRACE_ASSERT(label != NULL, "xTraceRegisterString: label == NULL", (traceString)0);
+ TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
+ return prvTraceOpenSymbol(label, 0);
+}
+#endif
+
+
+#if ((!defined TRC_CFG_INCLUDE_READY_EVENTS) || (TRC_CFG_INCLUDE_READY_EVENTS == 1))
+
+void prvTraceSetReadyEventsEnabled(int status)
+{
+ readyEventsEnabled = status;
+}
+
+/*******************************************************************************
+ * prvTraceStoreTaskReady
+ *
+ * This function stores a ready state for the task handle sent in as parameter.
+ ******************************************************************************/
+void prvTraceStoreTaskReady(traceHandle handle)
+{
+ uint16_t dts3;
+ TREvent* tr;
+ uint8_t hnd8;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ if (handle == 0)
+ {
+ /* On FreeRTOS v7.3.0, this occurs when creating tasks due to a bad
+ placement of the trace macro. In that case, the events are ignored. */
+ return;
+ }
+
+ if (! readyEventsEnabled)
+ {
+ /* When creating tasks, ready events are also created. If creating
+ a "hidden" (not traced) task, we must therefore disable recording
+ of ready events to avoid an undesired ready event... */
+ return;
+ }
+
+ TRACE_ASSERT(handle <= (TRC_CFG_NTASK), "prvTraceStoreTaskReady: Invalid value for handle", TRC_UNUSED);
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
+ {
+ dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
+ hnd8 = prvTraceGet8BitHandle(handle);
+ tr = (TREvent*)prvTraceNextFreeEventBufferSlot();
+ if (tr != NULL)
+ {
+ tr->type = DIV_TASK_READY;
+ tr->dts = dts3;
+ tr->objHandle = hnd8;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceStoreLowPower
+ *
+ * This function stores a low power state.
+ ******************************************************************************/
+void prvTraceStoreLowPower(uint32_t flag)
+{
+ uint16_t dts;
+ LPEvent* lp;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(flag <= 1, "prvTraceStoreLowPower: Invalid flag value", TRC_UNUSED);
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("Recorder busy - high priority ISR using syscall? (1)");
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive)
+ {
+ dts = (uint16_t)prvTraceGetDTS(0xFFFF);
+ lp = (LPEvent*)prvTraceNextFreeEventBufferSlot();
+ if (lp != NULL)
+ {
+ lp->type = (uint8_t) (LOW_POWER_BEGIN + ( uint8_t ) flag); /* BEGIN or END depending on flag */
+ lp->dts = dts;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+
+/*******************************************************************************
+ * vTraceStoreMemMangEvent
+ *
+ * This function stores malloc and free events. Each call requires two records,
+ * for size and address respectively. The event code parameter (ecode) is applied
+ * to the first record (size) and the following address record gets event
+ * code "ecode + 1", so make sure this is respected in the event code table.
+ * Note: On "free" calls, the signed_size parameter should be negative.
+ ******************************************************************************/
+#if (TRC_CFG_INCLUDE_MEMMANG_EVENTS == 1)
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+void vTraceStoreMemMangEvent(uint32_t ecode, uint32_t address, int32_t signed_size)
+{
+ uint8_t dts1;
+ MemEventSize * ms;
+ MemEventAddr * ma;
+ uint16_t size_low;
+ uint16_t addr_low;
+ uint8_t addr_high;
+ uint32_t size;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ if (RecorderDataPtr == NULL)
+ {
+ /* Occurs in vTraceInitTraceData, if using dynamic allocation. */
+ return;
+ }
+
+ if (signed_size < 0)
+ size = (uint32_t)(- signed_size);
+ else
+ size = (uint32_t)(signed_size);
+
+ trcCRITICAL_SECTION_BEGIN();
+
+ heapMemUsage = heapMemUsage + (uint32_t)signed_size;
+
+ if (RecorderDataPtr->recorderActive)
+ {
+ dts1 = (uint8_t)prvTraceGetDTS(0xFF);
+ size_low = (uint16_t)prvTraceGetParam(0xFFFF, size);
+ ms = (MemEventSize *)prvTraceNextFreeEventBufferSlot();
+
+ if (ms != NULL)
+ {
+ ms->dts = dts1;
+ ms->type = NULL_EVENT; /* Updated when all events are written */
+ ms->size = size_low;
+ prvTraceUpdateCounters();
+
+ /* Storing a second record with address (signals "failed" if null) */
+ #if (TRC_CFG_HEAP_SIZE_BELOW_16M)
+ /* If the heap address range is within 16 MB, i.e., the upper 8 bits
+ of addresses are constant, this optimization avoids storing an extra
+ event record by ignoring the upper 8 bit of the address */
+ addr_low = address & 0xFFFF;
+ addr_high = (address >> 16) & 0xFF;
+ #else
+ /* The whole 32 bit address is stored using a second event record
+ for the upper 16 bit */
+ addr_low = (uint16_t)prvTraceGetParam(0xFFFF, address);
+ addr_high = 0;
+ #endif
+
+ ma = (MemEventAddr *) prvTraceNextFreeEventBufferSlot();
+ if (ma != NULL)
+ {
+ ma->addr_low = addr_low;
+ ma->addr_high = addr_high;
+ ma->type = (uint8_t) (ecode + 1); /* Note this! */
+ ms->type = (uint8_t) ecode;
+ prvTraceUpdateCounters();
+ RecorderDataPtr->heapMemUsage = heapMemUsage;
+ }
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif /* TRC_CFG_SCHEDULING_ONLY */
+#endif
+
+/*******************************************************************************
+ * prvTraceStoreKernelCall
+ *
+ * This is the main integration point for storing kernel calls, and
+ * is called by the hooks in trcKernelHooks.h (see trcKernelPort.h for event codes).
+ ******************************************************************************/
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+void prvTraceStoreKernelCall(uint32_t ecode, traceObjectClass objectClass, uint32_t objectNumber)
+{
+ KernelCall * kse;
+ uint16_t dts1;
+ uint8_t hnd8;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(ecode < 0xFF, "prvTraceStoreKernelCall: ecode >= 0xFF", TRC_UNUSED);
+ TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCall: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCall: Invalid value for objectNumber", TRC_UNUSED);
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("Recorder busy - high priority ISR using syscall? (2)");
+ return;
+ }
+
+ if (handle_of_last_logged_task == 0)
+ {
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive)
+ {
+ dts1 = (uint16_t)prvTraceGetDTS(0xFFFF);
+ hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber);
+ kse = (KernelCall*) prvTraceNextFreeEventBufferSlot();
+ if (kse != NULL)
+ {
+ kse->dts = dts1;
+ kse->type = (uint8_t)ecode;
+ kse->objHandle = hnd8;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif /* TRC_CFG_SCHEDULING_ONLY */
+
+/*******************************************************************************
+ * prvTraceStoreKernelCallWithParam
+ *
+ * Used for storing kernel calls with a handle and a numeric parameter. If the
+ * numeric parameter does not fit in one byte, and extra XPS event is inserted
+ * before the kernel call event containing the three upper bytes.
+ ******************************************************************************/
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+void prvTraceStoreKernelCallWithParam(uint32_t evtcode,
+ traceObjectClass objectClass,
+ uint32_t objectNumber,
+ uint32_t param)
+{
+ KernelCallWithParamAndHandle * kse;
+ uint8_t dts2;
+ uint8_t hnd8;
+ uint8_t p8;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithParam: evtcode >= 0xFF", TRC_UNUSED);
+ TRACE_ASSERT(objectClass < TRACE_NCLASSES, "prvTraceStoreKernelCallWithParam: objectClass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(objectNumber <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectClass], "prvTraceStoreKernelCallWithParam: Invalid value for objectNumber", TRC_UNUSED);
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("Recorder busy - high priority ISR using syscall? (3)");
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ dts2 = (uint8_t)prvTraceGetDTS(0xFF);
+ p8 = (uint8_t) prvTraceGetParam(0xFF, param);
+ hnd8 = prvTraceGet8BitHandle((traceHandle)objectNumber);
+ kse = (KernelCallWithParamAndHandle*) prvTraceNextFreeEventBufferSlot();
+ if (kse != NULL)
+ {
+ kse->dts = dts2;
+ kse->type = (uint8_t)evtcode;
+ kse->objHandle = hnd8;
+ kse->param = p8;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif /* TRC_CFG_SCHEDULING_ONLY */
+
+
+/*******************************************************************************
+ * prvTraceGetParam
+ *
+ * Used for storing extra bytes for kernel calls with numeric parameters.
+ *
+ * May only be called within a critical section!
+ ******************************************************************************/
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+static uint32_t prvTraceGetParam(uint32_t param_max, uint32_t param)
+{
+ XPSEvent* xps;
+
+ TRACE_ASSERT(param_max == 0xFF || param_max == 0xFFFF,
+ "prvTraceGetParam: Invalid value for param_max", param);
+
+ if (param <= param_max)
+ {
+ return param;
+ }
+ else
+ {
+ xps = (XPSEvent*) prvTraceNextFreeEventBufferSlot();
+ if (xps != NULL)
+ {
+ xps->type = DIV_XPS;
+ xps->xps_8 = (uint8_t)((param & (0xFF00 & ~param_max)) >> 8);
+ xps->xps_16 = (uint16_t)((param & (0xFFFF0000 & ~param_max)) >> 16);
+ prvTraceUpdateCounters();
+ }
+
+ return param & param_max;
+ }
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceStoreKernelCallWithNumericParamOnly
+ *
+ * Used for storing kernel calls with numeric parameters only. This is
+ * only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment.
+ ******************************************************************************/
+#if (TRC_CFG_SCHEDULING_ONLY == 0)
+void prvTraceStoreKernelCallWithNumericParamOnly(uint32_t evtcode, uint32_t param)
+{
+ KernelCallWithParam16 * kse;
+ uint8_t dts6;
+ uint16_t restParam;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ restParam = 0;
+
+ TRACE_ASSERT(evtcode < 0xFF, "prvTraceStoreKernelCallWithNumericParamOnly: Invalid value for evtcode", TRC_UNUSED);
+
+ if (recorder_busy)
+ {
+ /*************************************************************************
+ * This occurs if an ISR calls a trace function, preempting a previous
+ * trace call that is being processed in a different ISR or task.
+ * If this occurs, there is probably a problem in the definition of the
+ * recorder's internal critical sections (TRACE_ENTER_CRITICAL_SECTION and
+ * TRACE_EXIT_CRITICAL_SECTION). They must disable the RTOS tick interrupt
+ * and any other ISRs that calls the trace recorder directly or via
+ * traced kernel functions. The ARM port disables all interrupts using the
+ * PRIMASK register to avoid this issue.
+ *************************************************************************/
+ prvTraceError("Recorder busy - high priority ISR using syscall? (4)");
+ return;
+ }
+
+ trcCRITICAL_SECTION_BEGIN();
+ if (RecorderDataPtr->recorderActive && handle_of_last_logged_task)
+ {
+ dts6 = (uint8_t)prvTraceGetDTS(0xFF);
+ restParam = (uint16_t)prvTraceGetParam(0xFFFF, param);
+ kse = (KernelCallWithParam16*) prvTraceNextFreeEventBufferSlot();
+ if (kse != NULL)
+ {
+ kse->dts = dts6;
+ kse->type = (uint8_t)evtcode;
+ kse->param = restParam;
+ prvTraceUpdateCounters();
+ }
+ }
+ trcCRITICAL_SECTION_END();
+}
+#endif /* TRC_CFG_SCHEDULING_ONLY */
+
+/*******************************************************************************
+ * prvTraceStoreTaskswitch
+ * Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart.
+ * At this point interrupts are assumed to be disabled!
+ ******************************************************************************/
+void prvTraceStoreTaskswitch(traceHandle task_handle)
+{
+ uint16_t dts3;
+ TSEvent* ts;
+ uint8_t hnd8;
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+ extern int32_t isPendingContextSwitch;
+#endif
+ trcSR_ALLOC_CRITICAL_SECTION_ON_CORTEX_M_ONLY();
+
+ TRACE_ASSERT(task_handle <= (TRC_CFG_NTASK),
+ "prvTraceStoreTaskswitch: Invalid value for task_handle", TRC_UNUSED);
+
+ trcCRITICAL_SECTION_BEGIN_ON_CORTEX_M_ONLY();
+
+ if ((task_handle != handle_of_last_logged_task) && (RecorderDataPtr->recorderActive))
+ {
+#if (TRC_CFG_INCLUDE_ISR_TRACING == 1)
+ isPendingContextSwitch = 0;
+#endif
+
+ dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
+ handle_of_last_logged_task = task_handle;
+ hnd8 = prvTraceGet8BitHandle(handle_of_last_logged_task);
+ ts = (TSEvent*)prvTraceNextFreeEventBufferSlot();
+
+ if (ts != NULL)
+ {
+ if (prvTraceGetObjectState(TRACE_CLASS_TASK,
+ handle_of_last_logged_task) == TASK_STATE_INSTANCE_ACTIVE)
+ {
+ ts->type = TS_TASK_RESUME;
+ }
+ else
+ {
+ ts->type = TS_TASK_BEGIN;
+ }
+
+ ts->dts = dts3;
+ ts->objHandle = hnd8;
+
+ prvTraceSetObjectState(TRACE_CLASS_TASK,
+ handle_of_last_logged_task,
+ TASK_STATE_INSTANCE_ACTIVE);
+
+ prvTraceUpdateCounters();
+ }
+ }
+
+ trcCRITICAL_SECTION_END_ON_CORTEX_M_ONLY();
+}
+
+/*******************************************************************************
+ * prvTraceStoreObjectNameOnCloseEvent
+ *
+ * Updates the symbol table with the name of this object from the dynamic
+ * objects table and stores a "close" event, holding the mapping between handle
+ * and name (a symbol table handle). The stored name-handle mapping is thus the
+ * "old" one, valid up until this point.
+ ******************************************************************************/
+void prvTraceStoreObjectNameOnCloseEvent(uint8_t evtcode, traceHandle handle,
+ traceObjectClass objectclass)
+{
+ ObjCloseNameEvent * ce;
+ const char * name;
+ traceString idx;
+
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceStoreObjectNameOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceStoreObjectNameOnCloseEvent: Invalid value for handle", TRC_UNUSED);
+
+ if (RecorderDataPtr->recorderActive)
+ {
+ uint8_t hnd8 = prvTraceGet8BitHandle(handle);
+ name = TRACE_PROPERTY_NAME_GET(objectclass, handle);
+ idx = prvTraceOpenSymbol(name, 0);
+
+ // Interrupt disable not necessary, already done in trcHooks.h macro
+ ce = (ObjCloseNameEvent*) prvTraceNextFreeEventBufferSlot();
+ if (ce != NULL)
+ {
+ ce->type = (uint8_t) evtcode;
+ ce->objHandle = hnd8;
+ ce->symbolIndex = idx;
+ prvTraceUpdateCounters();
+ }
+ }
+}
+
+void prvTraceStoreObjectPropertiesOnCloseEvent(uint8_t evtcode, traceHandle handle,
+ traceObjectClass objectclass)
+{
+ ObjClosePropEvent * pe;
+
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceStoreObjectPropertiesOnCloseEvent: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceStoreObjectPropertiesOnCloseEvent: Invalid value for handle", TRC_UNUSED);
+
+ if (RecorderDataPtr->recorderActive)
+ {
+ // Interrupt disable not necessary, already done in trcHooks.h macro
+ pe = (ObjClosePropEvent*) prvTraceNextFreeEventBufferSlot();
+ if (pe != NULL)
+ {
+ if (objectclass == TRACE_CLASS_TASK)
+ {
+ pe->arg1 = TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, handle);
+ }
+ else
+ {
+ pe->arg1 = TRACE_PROPERTY_OBJECT_STATE(objectclass, handle);
+ }
+ pe->type = evtcode;
+ prvTraceUpdateCounters();
+ }
+ }
+}
+
+void prvTraceSetPriorityProperty(uint8_t objectclass, traceHandle id, uint8_t value)
+{
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceSetPriorityProperty: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceSetPriorityProperty: Invalid value for id", TRC_UNUSED);
+
+ TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id) = value;
+}
+
+uint8_t prvTraceGetPriorityProperty(uint8_t objectclass, traceHandle id)
+{
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceGetPriorityProperty: objectclass >= TRACE_NCLASSES", 0);
+ TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceGetPriorityProperty: Invalid value for id", 0);
+
+ return TRACE_PROPERTY_ACTOR_PRIORITY(objectclass, id);
+}
+
+void prvTraceSetObjectState(uint8_t objectclass, traceHandle id, uint8_t value)
+{
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceSetObjectState: objectclass >= TRACE_NCLASSES", TRC_UNUSED);
+ TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceSetObjectState: Invalid value for id", TRC_UNUSED);
+
+ TRACE_PROPERTY_OBJECT_STATE(objectclass, id) = value;
+}
+
+uint8_t prvTraceGetObjectState(uint8_t objectclass, traceHandle id)
+{
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceGetObjectState: objectclass >= TRACE_NCLASSES", 0);
+ TRACE_ASSERT(id <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceGetObjectState: Invalid value for id", 0);
+
+ return TRACE_PROPERTY_OBJECT_STATE(objectclass, id);
+}
+
+void prvTraceSetTaskInstanceFinished(traceHandle handle)
+{
+ TRACE_ASSERT(handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[TRACE_CLASS_TASK],
+ "prvTraceSetTaskInstanceFinished: Invalid value for handle", TRC_UNUSED);
+
+#if (TRC_CFG_USE_IMPLICIT_IFE_RULES == 1)
+ TRACE_PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle) = 0;
+#endif
+}
+
+/*******************************************************************************
+ * Static data initializations
+ ******************************************************************************/
+
+/* A set of stacks that keeps track of available object handles for each class.
+The stacks are empty initially, meaning that allocation of new handles will be
+based on a counter (for each object class). Any delete operation will
+return the handle to the corresponding stack, for reuse on the next allocate.*/
+objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
+
+/* Initial TRC_HWTC_COUNT value, for detecting if the time-stamping source is
+enabled. If using the OS periodic timer for time-stamping, this might not
+have been configured on the earliest events during the startup. */
+uint32_t init_hwtc_count;
+
+/*******************************************************************************
+ * RecorderData
+ *
+ * The main data structure in snapshot mode, when using the default static memory
+ * allocation (TRC_RECORDER_BUFFER_ALLOCATION_STATIC). The recorder uses a pointer
+ * RecorderDataPtr to access the data, to also allow for dynamic or custom data
+ * allocation (see TRC_CFG_RECORDER_BUFFER_ALLOCATION).
+ ******************************************************************************/
+#if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
+RecorderDataType RecorderData;
+#endif
+
+/*******************************************************************************
+ * RecorderDataPtr
+ *
+ * Pointer to the main data structure, when in snapshot mode.
+ ******************************************************************************/
+RecorderDataType* RecorderDataPtr = NULL;
+
+/* This version of the function dynamically allocates the trace data */
+void prvTraceInitTraceData()
+{
+
+ if (RecorderDataPtr == NULL)
+ {
+#if (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_STATIC)
+ RecorderDataPtr = &RecorderData;
+#elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_DYNAMIC)
+ RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType));
+ if (! RecorderDataPtr)
+ {
+ prvTraceError("Failed allocating recorder buffer!");
+ return;
+ }
+#elif (TRC_CFG_RECORDER_BUFFER_ALLOCATION == TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM)
+ if (! RecorderDataPtr)
+ {
+ prvTraceError("Recorder data pointer not set! Use vTraceSetRecorderDataBuffer().");
+ return;
+ }
+#endif
+ }
+ else
+ {
+ if (RecorderDataPtr->startmarker0 == 1)
+ {
+ /* Already initialized */
+ return;
+ }
+ }
+
+ init_hwtc_count = TRC_HWTC_COUNT;
+
+ (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType));
+
+ RecorderDataPtr->version = TRACE_KERNEL_VERSION;
+ RecorderDataPtr->minor_version = TRACE_MINOR_VERSION;
+ RecorderDataPtr->irq_priority_order = TRC_IRQ_PRIORITY_ORDER;
+ RecorderDataPtr->filesize = sizeof(RecorderDataType);
+ RecorderDataPtr->maxEvents = (TRC_CFG_EVENT_BUFFER_SIZE);
+ RecorderDataPtr->debugMarker0 = (int32_t) 0xF0F0F0F0;
+ RecorderDataPtr->isUsing16bitHandles = TRC_CFG_USE_16BIT_OBJECT_HANDLES;
+ RecorderDataPtr->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
+
+ /* This function is kernel specific */
+ vTraceInitObjectPropertyTable();
+
+ RecorderDataPtr->debugMarker1 = (int32_t)0xF1F1F1F1;
+ RecorderDataPtr->SymbolTable.symTableSize = (TRC_CFG_SYMBOL_TABLE_SIZE);
+ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1;
+#if (TRC_CFG_INCLUDE_FLOAT_SUPPORT == 1)
+ RecorderDataPtr->exampleFloatEncoding = 1.0f; /* otherwise already zero */
+#endif
+ RecorderDataPtr->debugMarker2 = (int32_t)0xF2F2F2F2;
+ prvStrncpy(RecorderDataPtr->systemInfo, "Trace Recorder Demo", 80);
+ RecorderDataPtr->debugMarker3 = (int32_t)0xF3F3F3F3;
+ RecorderDataPtr->endmarker0 = 0x0A;
+ RecorderDataPtr->endmarker1 = 0x0B;
+ RecorderDataPtr->endmarker2 = 0x0C;
+ RecorderDataPtr->endmarker3 = 0x0D;
+ RecorderDataPtr->endmarker4 = 0x71;
+ RecorderDataPtr->endmarker5 = 0x72;
+ RecorderDataPtr->endmarker6 = 0x73;
+ RecorderDataPtr->endmarker7 = 0x74;
+ RecorderDataPtr->endmarker8 = 0xF1;
+ RecorderDataPtr->endmarker9 = 0xF2;
+ RecorderDataPtr->endmarker10 = 0xF3;
+ RecorderDataPtr->endmarker11 = 0xF4;
+
+#if TRC_CFG_USE_SEPARATE_USER_EVENT_BUFFER
+ RecorderDataPtr->userEventBuffer.bufferID = 1;
+ RecorderDataPtr->userEventBuffer.version = 0;
+ RecorderDataPtr->userEventBuffer.numberOfSlots = (TRC_CFG_SEPARATE_USER_EVENT_BUFFER_SIZE);
+ RecorderDataPtr->userEventBuffer.numberOfChannels = (TRC_CFG_UB_CHANNELS) + 1;
+#endif
+
+ /* Kernel specific initialization of the objectHandleStacks variable */
+ vTraceInitObjectHandleStack();
+
+
+ /* Finally, the 12-byte "start markers" are initialized, allowing for
+ Tracealyzer to find the trace data in a larger RAM dump.
+
+ The start and end markers must be unique, but without proper precautions there
+ might be a risk of accidental duplicates of the start/end markers, e.g., due to
+ compiler optimizations.
+
+ The below initialization of the start marker is therefore made in reverse order
+ and the fields are volatile to ensure this assignment order. This to avoid any
+ chance of accidental duplicates of this elsewhere in memory.
+
+ Moreover, the fields are set byte-by-byte to avoid endian issues.*/
+
+ RecorderDataPtr->startmarker11 = 0xF4;
+ RecorderDataPtr->startmarker10 = 0xF3;
+ RecorderDataPtr->startmarker9 = 0xF2;
+ RecorderDataPtr->startmarker8 = 0xF1;
+ RecorderDataPtr->startmarker7 = 0x74;
+ RecorderDataPtr->startmarker6 = 0x73;
+ RecorderDataPtr->startmarker5 = 0x72;
+ RecorderDataPtr->startmarker4 = 0x71;
+ RecorderDataPtr->startmarker3 = 0x04;
+ RecorderDataPtr->startmarker2 = 0x03;
+ RecorderDataPtr->startmarker1 = 0x02;
+ RecorderDataPtr->startmarker0 = 0x01;
+
+ if (traceErrorMessage != NULL)
+ {
+ // An error was detected before vTraceEnable was called, make sure this is stored in the trace data.
+ prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);
+ RecorderDataPtr->internalErrorOccured = 1;
+ vTraceStop();
+ }
+
+
+
+#ifdef TRC_PORT_SPECIFIC_INIT
+ TRC_PORT_SPECIFIC_INIT();
+#endif
+}
+
+
+void* prvTraceNextFreeEventBufferSlot(void)
+{
+ if (! RecorderDataPtr->recorderActive)
+ {
+ /* If an XTS or XPS event prior to the main event has filled the buffer
+ before saving the main event, and store mode is "stop when full". */
+ return NULL;
+ }
+
+ if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
+ {
+ prvTraceError("Attempt to index outside event buffer!");
+ return NULL;
+ }
+ return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]);
+}
+
+uint16_t uiIndexOfObject(traceHandle objecthandle, uint8_t objectclass)
+{
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "uiIndexOfObject: Invalid value for objectclass", 0);
+ TRACE_ASSERT(objecthandle > 0 && objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "uiIndexOfObject: Invalid value for objecthandle", 0);
+
+ if ((objectclass < TRACE_NCLASSES) && (objecthandle > 0) &&
+ (objecthandle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]))
+ {
+ return (uint16_t)(RecorderDataPtr->ObjectPropertyTable.StartIndexOfClass[objectclass] +
+ (RecorderDataPtr->ObjectPropertyTable.TotalPropertyBytesPerClass[objectclass] * (objecthandle-1)));
+ }
+
+ prvTraceError("Object table lookup with invalid object handle or object class!");
+ return 0;
+}
+
+traceHandle prvTraceGetObjectHandle(traceObjectClass objectclass)
+{
+ traceHandle handle;
+ static int indexOfHandle;
+
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ TRACE_ASSERT(RecorderDataPtr != NULL, "Recorder not initialized, call vTraceEnable() first!", (traceHandle)0);
+
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceGetObjectHandle: Invalid value for objectclass", (traceHandle)0);
+
+ trcCRITICAL_SECTION_BEGIN();
+ indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
+ if (objectHandleStacks.objectHandles[indexOfHandle] == 0)
+ {
+ /* Zero is used to indicate a never before used handle, i.e.,
+ new slots in the handle stack. The handle slot needs to
+ be initialized here (starts at 1). */
+ objectHandleStacks.objectHandles[indexOfHandle] =
+ (traceHandle)(1 + indexOfHandle -
+ objectHandleStacks.lowestIndexOfClass[objectclass]);
+ }
+
+ handle = objectHandleStacks.objectHandles[indexOfHandle];
+
+ if (objectHandleStacks.indexOfNextAvailableHandle[objectclass]
+ > objectHandleStacks.highestIndexOfClass[objectclass])
+ {
+ prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
+ handle = 0;
+ }
+ else
+ {
+ int hndCount;
+ objectHandleStacks.indexOfNextAvailableHandle[objectclass]++;
+
+ hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] -
+ objectHandleStacks.lowestIndexOfClass[objectclass];
+
+ if (hndCount >
+ objectHandleStacks.handleCountWaterMarksOfClass[objectclass])
+ {
+ objectHandleStacks.handleCountWaterMarksOfClass[objectclass] =
+ (traceHandle)hndCount;
+ }
+ }
+ trcCRITICAL_SECTION_END();
+
+ return handle;
+}
+
+void prvTraceFreeObjectHandle(traceObjectClass objectclass, traceHandle handle)
+{
+ int indexOfHandle;
+
+ TRACE_ASSERT(objectclass < TRACE_NCLASSES,
+ "prvTraceFreeObjectHandle: Invalid value for objectclass", TRC_UNUSED);
+ TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass],
+ "prvTraceFreeObjectHandle: Invalid value for handle", TRC_UNUSED);
+
+ /* Check that there is room to push the handle on the stack */
+ if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) <
+ objectHandleStacks.lowestIndexOfClass[objectclass])
+ {
+ /* Error */
+ prvTraceError("Attempt to free more handles than allocated!");
+ }
+ else
+ {
+ objectHandleStacks.indexOfNextAvailableHandle[objectclass]--;
+ indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
+ objectHandleStacks.objectHandles[indexOfHandle] = handle;
+ }
+}
+
+/*******************************************************************************
+ * prvMarkObjectAsUsed
+ *
+ * Sets an "is used flag" on object creation, using the first byte of the name
+ * field. This allows for counting the number of used Object Table slots, even
+ * if no names have been set.
+ ******************************************************************************/
+void prvMarkObjectAsUsed(traceObjectClass objectclass, traceHandle handle)
+{
+ uint16_t idx = uiIndexOfObject(handle, objectclass);
+ RecorderDataPtr->ObjectPropertyTable.objbytes[idx] = 1;
+}
+
+/*******************************************************************************
+ * prvStrncpy
+ *
+ * Private string copy function, to improve portability between compilers.
+ ******************************************************************************/
+static void prvStrncpy(char* dst, const char* src, uint32_t maxLength)
+{
+ uint32_t i;
+ for (i = 0; i < maxLength; i++)
+ {
+ dst[i] = src[i];
+ if (src[i] == 0)
+ break;
+ }
+}
+
+/*******************************************************************************
+ * prvTraceSetObjectName
+ *
+ * Registers the names of queues, semaphores and other kernel objects in the
+ * recorder's Object Property Table, at the given handle and object class.
+ ******************************************************************************/
+void prvTraceSetObjectName(traceObjectClass objectclass,
+ traceHandle handle,
+ const char* name)
+{
+ static uint16_t idx;
+
+ TRACE_ASSERT(name != NULL, "prvTraceSetObjectName: name == NULL", TRC_UNUSED);
+
+ if (objectclass >= TRACE_NCLASSES)
+ {
+ prvTraceError("Illegal object class in prvTraceSetObjectName");
+ return;
+ }
+
+ if (handle == 0)
+ {
+ prvTraceError("Illegal handle (0) in prvTraceSetObjectName.");
+ return;
+ }
+
+ if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])
+ {
+ /* ERROR */
+ prvTraceError(pszTraceGetErrorNotEnoughHandles(objectclass));
+ }
+ else
+ {
+ idx = uiIndexOfObject(handle, objectclass);
+
+ if (traceErrorMessage == NULL)
+ {
+ prvStrncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]),
+ name,
+ RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]);
+ }
+ }
+}
+
+traceString prvTraceOpenSymbol(const char* name, traceString userEventChannel)
+{
+ uint16_t result;
+ uint8_t len;
+ uint8_t crc;
+ TRACE_ALLOC_CRITICAL_SECTION();
+
+ len = 0;
+ crc = 0;
+
+ TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceString)0);
+
+ prvTraceGetChecksum(name, &crc, &len);
+
+ trcCRITICAL_SECTION_BEGIN();
+ result = prvTraceLookupSymbolTableEntry(name, crc, len, userEventChannel);
+ if (!result)
+ {
+ result = prvTraceCreateSymbolTableEntry(name, crc, len, userEventChannel);
+ }
+ trcCRITICAL_SECTION_END();
+
+ return result;
+}
+
+
+/******************************************************************************
+* vTraceSetFrequency
+*
+* Registers the clock rate of the time source for the event timestamping.
+* This is normally not required, but if the default value (TRC_HWTC_FREQ_HZ)
+* should be incorrect for your setup, you can override it using this function.
+*
+* Must be called prior to vTraceEnable, and the time source is assumed to
+* have a fixed clock frequency after the startup.
+*
+* Note that, in snapshot mode, the value is divided by the TRC_HWTC_DIVISOR.
+* This is a software "prescaler" that is also applied on the timestamps.
+*****************************************************************************/
+void vTraceSetFrequency(uint32_t frequency)
+{
+ timestampFrequency = frequency;
+}
+
+/*******************************************************************************
+ * Supporting functions
+ ******************************************************************************/
+
+/*******************************************************************************
+ * prvTraceError
+ *
+ * Called by various parts in the recorder. Stops the recorder and stores a
+ * pointer to an error message, which is printed by the monitor task.
+ * If you are not using the monitor task, you may use xTraceGetLastError()
+ * from your application to check if the recorder is OK.
+ *
+ * Note: If a recorder error is registered before vTraceStart is called, the
+ * trace start will be aborted. This can occur if any of the Nxxxx constants
+ * (e.g., TRC_CFG_NTASK) in trcConfig.h is too small.
+ ******************************************************************************/
+void prvTraceError(const char* msg)
+{
+ /* Stop the recorder */
+ if (RecorderDataPtr != NULL)
+ {
+ vTraceStop();
+ }
+
+ /* If first error only... */
+ if (traceErrorMessage == NULL)
+ {
+ traceErrorMessage = (char*)(intptr_t) msg;
+ if (RecorderDataPtr != NULL)
+ {
+ prvStrncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);
+ RecorderDataPtr->internalErrorOccured = 1;
+ }
+ }
+}
+
+void vTraceSetFilterMask(uint16_t filterMask)
+{
+ CurrentFilterMask = filterMask;
+}
+
+void vTraceSetFilterGroup(uint16_t filterGroup)
+{
+ CurrentFilterGroup = filterGroup;
+}
+
+/******************************************************************************
+ * prvCheckDataToBeOverwrittenForMultiEntryEvents
+ *
+ * This checks if the next event to be overwritten is a multi-entry user event,
+ * i.e., a USER_EVENT followed by data entries.
+ * Such data entries do not have an event code at byte 0, as other events.
+ * All 4 bytes are user data, so the first byte of such data events must
+ * not be interpreted as type field. The number of data entries following
+ * a USER_EVENT is given in the event code of the USER_EVENT.
+ * Therefore, when overwriting a USER_EVENT (when using in ring-buffer mode)
+ * any data entries following must be replaced with NULL events (code 0).
+ *
+ * This is assumed to execute within a critical section...
+ *****************************************************************************/
+
+#if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+void prvCheckDataToBeOverwrittenForMultiEntryEvents(uint8_t nofEntriesToCheck)
+{
+ /* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */
+ unsigned int i = 0;
+ unsigned int e = 0;
+
+ TRACE_ASSERT(nofEntriesToCheck != 0,
+ "prvCheckDataToBeOverwrittenForMultiEntryEvents: nofEntriesToCheck == 0", TRC_UNUSED);
+
+ while (i < nofEntriesToCheck)
+ {
+ e = RecorderDataPtr->nextFreeIndex + i;
+ if ((RecorderDataPtr->eventData[e*4] > USER_EVENT) &&
+ (RecorderDataPtr->eventData[e*4] < USER_EVENT + 16))
+ {
+ uint8_t nDataEvents = (uint8_t)(RecorderDataPtr->eventData[e*4] - USER_EVENT);
+ if ((e + nDataEvents) < RecorderDataPtr->maxEvents)
+ {
+ (void)memset(& RecorderDataPtr->eventData[e*4], 0, (size_t) (4 + 4 * nDataEvents));
+ }
+ }
+ else if (RecorderDataPtr->eventData[e*4] == DIV_XPS)
+ {
+ if ((e + 1) < RecorderDataPtr->maxEvents)
+ {
+ /* Clear 8 bytes */
+ (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4 + 4);
+ }
+ else
+ {
+ /* Clear 8 bytes, 4 first and 4 last */
+ (void)memset(& RecorderDataPtr->eventData[0], 0, 4);
+ (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4);
+ }
+ }
+ i++;
+ }
+}
+#endif
+
+/*******************************************************************************
+ * prvTraceUpdateCounters
+ *
+ * Updates the index of the event buffer.
+ ******************************************************************************/
+void prvTraceUpdateCounters(void)
+{
+ if (RecorderDataPtr->recorderActive == 0)
+ {
+ return;
+ }
+
+ RecorderDataPtr->numEvents++;
+
+ RecorderDataPtr->nextFreeIndex++;
+
+ if (RecorderDataPtr->nextFreeIndex >= (TRC_CFG_EVENT_BUFFER_SIZE))
+ {
+#if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ RecorderDataPtr->bufferIsFull = 1;
+ RecorderDataPtr->nextFreeIndex = 0;
+#else
+ vTraceStop();
+#endif
+ }
+
+#if (TRC_CFG_SNAPSHOT_MODE == TRC_SNAPSHOT_MODE_RING_BUFFER)
+ prvCheckDataToBeOverwrittenForMultiEntryEvents(1);
+#endif
+}
+
+/******************************************************************************
+ * prvTraceGetDTS
+ *
+ * Returns a differential timestamp (DTS), i.e., the time since
+ * last event, and creates an XTS event if the DTS does not fit in the
+ * number of bits given. The XTS event holds the MSB bytes of the DTS.
+ *
+ * The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for
+ * events with 16-bit dts fields.
+ *****************************************************************************/
+uint16_t prvTraceGetDTS(uint16_t param_maxDTS)
+{
+ static uint32_t old_timestamp = 0;
+ XTSEvent* xts = 0;
+ uint32_t dts = 0;
+ uint32_t timestamp = 0;
+
+ TRACE_ASSERT(param_maxDTS == 0xFF || param_maxDTS == 0xFFFF, "prvTraceGetDTS: Invalid value for param_maxDTS", 0);
+
+
+ if (RecorderDataPtr->frequency == 0)
+ {
+ if (timestampFrequency != 0)
+ {
+ /* If to override default TRC_HWTC_FREQ_HZ value with value set by vTraceSetFrequency */
+ RecorderDataPtr->frequency = timestampFrequency / (TRC_HWTC_DIVISOR);
+ }
+ else if (init_hwtc_count != (TRC_HWTC_COUNT))
+ {
+ /* If using default value and timer has been started.
+ Note: If the default frequency value set here would be incorrect, e.g.,
+ if the timer has actually not been configured yet, override this
+ with vTraceSetFrequency.
+ */
+ RecorderDataPtr->frequency = (TRC_HWTC_FREQ_HZ) / (TRC_HWTC_DIVISOR);
+ }
+ /* If no override (vTraceSetFrequency) and timer inactive -> no action */
+ }
+
+ /**************************************************************************
+ * The below statements read the timestamp from the timer port module.
+ * If necessary, whole seconds are extracted using division while the rest
+ * comes from the modulo operation.
+ **************************************************************************/
+
+ prvTracePortGetTimeStamp(&timestamp);
+
+ /***************************************************************************
+ * Since dts is unsigned the result will be correct even if timestamp has
+ * wrapped around.
+ ***************************************************************************/
+ dts = timestamp - old_timestamp;
+ old_timestamp = timestamp;
+
+ if (RecorderDataPtr->frequency > 0)
+ {
+ /* Check if dts > 1 second */
+ if (dts > RecorderDataPtr->frequency)
+ {
+ /* More than 1 second has passed */
+ RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency;
+ /* The part that is not an entire second is added to absTimeLastEvent */
+ RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency;
+ }
+ else
+ {
+ RecorderDataPtr->absTimeLastEvent += dts;
+ }
+
+ /* Check if absTimeLastEvent >= 1 second */
+ if (RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency)
+ {
+ /* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */
+ RecorderDataPtr->absTimeLastEventSecond++;
+ RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency;
+ /* RecorderDataPtr->absTimeLastEvent is now less than 1 second */
+ }
+ }
+ else
+ {
+ /* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */
+ RecorderDataPtr->absTimeLastEvent = timestamp;
+ }
+
+ /* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */
+ if (dts > param_maxDTS)
+ {
+ /* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/
+ xts = (XTSEvent*) prvTraceNextFreeEventBufferSlot();
+
+ if (xts != NULL)
+ {
+ if (param_maxDTS == 0xFFFF)
+ {
+ xts->type = XTS16;
+ xts->xts_16 = (uint16_t)((dts / 0x10000) & 0xFFFF);
+ xts->xts_8 = 0;
+ }
+ else if (param_maxDTS == 0xFF)
+ {
+ xts->type = XTS8;
+ xts->xts_16 = (uint16_t)((dts / 0x100) & 0xFFFF);
+ xts->xts_8 = (uint8_t)((dts / 0x1000000) & 0xFF);
+ }
+ else
+ {
+ prvTraceError("Bad param_maxDTS in prvTraceGetDTS");
+ }
+ prvTraceUpdateCounters();
+ }
+ }
+
+ return (uint16_t)dts & param_maxDTS;
+}
+
+/*******************************************************************************
+ * prvTraceLookupSymbolTableEntry
+ *
+ * Find an entry in the symbol table, return 0 if not present.
+ *
+ * The strings are stored in a byte pool, with four bytes of "meta-data" for
+ * every string.
+ * byte 0-1: index of next entry with same checksum (for fast lookup).
+ * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
+ * format strings only (the handle of the destination channel).
+ * byte 4..(4 + length): the string (object name or user event label), with
+ * zero-termination
+ ******************************************************************************/
+traceString prvTraceLookupSymbolTableEntry(const char* name,
+ uint8_t crc6,
+ uint8_t len,
+ traceString chn)
+{
+ uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ];
+
+ TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceString)0);
+ TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceString)0);
+
+ while (i != 0)
+ {
+ if (RecorderDataPtr->SymbolTable.symbytes[i + 2] == (chn & 0x00FF))
+ {
+ if (RecorderDataPtr->SymbolTable.symbytes[i + 3] == (chn / 0x100))
+ {
+ if (RecorderDataPtr->SymbolTable.symbytes[i + 4 + len] == '\0')
+ {
+ if (strncmp((char*)(& RecorderDataPtr->SymbolTable.symbytes[i + 4]), name, len) == 0)
+ {
+ break; /* found */
+ }
+ }
+ }
+ }
+ i = (uint16_t)(RecorderDataPtr->SymbolTable.symbytes[i] + (RecorderDataPtr->SymbolTable.symbytes[i + 1] * 0x100));
+ }
+ return i;
+}
+
+/*******************************************************************************
+ * prvTraceCreateSymbolTableEntry
+ *
+ * Creates an entry in the symbol table, independent if it exists already.
+ *
+ * The strings are stored in a byte pool, with four bytes of "meta-data" for
+ * every string.
+ * byte 0-1: index of next entry with same checksum (for fast lookup).
+ * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
+ * format strings only (the handle of the destination channel).
+ * byte 4..(4 + length): the string (object name or user event label), with
+ * zero-termination
+ ******************************************************************************/
+uint16_t prvTraceCreateSymbolTableEntry(const char* name,
+ uint8_t crc6,
+ uint8_t len,
+ traceString channel)
+{
+ uint16_t ret = 0;
+
+ TRACE_ASSERT(name != NULL, "prvTraceCreateSymbolTableEntry: name == NULL", 0);
+ TRACE_ASSERT(len != 0, "prvTraceCreateSymbolTableEntry: len == 0", 0);
+
+ if (RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= (TRC_CFG_SYMBOL_TABLE_SIZE))
+ {
+ prvTraceError("Symbol table full. Increase TRC_CFG_SYMBOL_TABLE_SIZE in trcConfig.h");
+ ret = 0;
+ }
+ else
+ {
+
+ RecorderDataPtr->SymbolTable.symbytes
+ [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex] =
+ (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF);
+
+ RecorderDataPtr->SymbolTable.symbytes
+ [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1] =
+ (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100);
+
+ RecorderDataPtr->SymbolTable.symbytes
+ [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2] =
+ (uint8_t)(channel & 0x00FF);
+
+ RecorderDataPtr->SymbolTable.symbytes
+ [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3] =
+ (uint8_t)(channel / 0x100);
+
+ /* set name (bytes 4...4+len-1) */
+ prvStrncpy((char*)&(RecorderDataPtr->SymbolTable.symbytes
+ [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4]), name, len);
+
+ /* Set zero termination (at offset 4+len) */
+ RecorderDataPtr->SymbolTable.symbytes
+ [RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len] = '\0';
+
+ /* store index of entry (for return value, and as head of LL[crc6]) */
+ RecorderDataPtr->SymbolTable.latestEntryOfChecksum
+ [ crc6 ] = (uint16_t)RecorderDataPtr->SymbolTable.nextFreeSymbolIndex;
+
+ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += (uint32_t) (len + 5);
+
+ ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - (uint8_t)(len + 5));
+ }
+
+ return ret;
+}
+
+
+/*******************************************************************************
+ * prvTraceGetChecksum
+ *
+ * Calculates a simple 6-bit checksum from a string, used to index the string
+ * for fast symbol table lookup.
+ ******************************************************************************/
+void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength)
+{
+ unsigned char c;
+ int length = 1; /* Should be 1 to account for '\0' */
+ int crc = 0;
+
+ TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", TRC_UNUSED);
+ TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", TRC_UNUSED);
+ TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", TRC_UNUSED);
+
+ if (pname != (const char *) 0)
+ {
+ for (; (c = (unsigned char) *pname++) != '\0';)
+ {
+ crc += c;
+ length++;
+ }
+ }
+ *pcrc = (uint8_t)(crc & 0x3F);
+ *plength = (uint8_t)length;
+}
+
+#if (TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)
+
+static void prvTraceStoreXID(traceHandle handle);
+
+/******************************************************************************
+ * prvTraceStoreXID
+ *
+ * Stores an XID (eXtended IDentifier) event.
+ * This is used if an object/task handle is larger than 255.
+ * The parameter "handle" is the full (16 bit) handle, assumed to be 256 or
+ * larger. Handles below 256 should not use this function.
+ *
+ * NOTE: this function MUST be called from within a critical section.
+ *****************************************************************************/
+static void prvTraceStoreXID(traceHandle handle)
+{
+ XPSEvent* xid;
+
+ TRACE_ASSERT(handle >= 256, "prvTraceStoreXID: Handle < 256", TRC_UNUSED);
+
+ xid = (XPSEvent*)prvTraceNextFreeEventBufferSlot();
+
+ if (xid != NULL)
+ {
+ xid->type = XID;
+
+ /* This function is (only) used when traceHandle is 16 bit... */
+ xid->xps_16 = handle;
+
+ prvTraceUpdateCounters();
+ }
+}
+
+static uint8_t prvTraceGet8BitHandle(traceHandle handle)
+{
+ if (handle > 255)
+ {
+ prvTraceStoreXID(handle);
+ /* The full handle (16 bit) is stored in the XID event.
+ This code (255) is used instead of zero (which is an error code).*/
+ return 255;
+ }
+ return (uint8_t)(handle & 0xFF);
+}
+#endif /*(TRC_CFG_USE_16BIT_OBJECT_HANDLES == 1)*/
+
+
+/* If using DWT timestamping (default on ARM Cortex-M3, M4 and M7), make sure the DWT unit is initialized. */
+#ifndef TRC_CFG_ARM_CM_USE_SYSTICK
+#if ((TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_ARM_Cortex_M) && (defined (__CORTEX_M) && (__CORTEX_M >= 0x03)))
+void prvTraceInitCortexM()
+{
+ /* Ensure that the DWT registers are unlocked and can be modified. */
+ TRC_REG_ITM_LOCKACCESS = TRC_ITM_LOCKACCESS_UNLOCK;
+
+ /* Make sure DWT is enabled, if supported */
+ TRC_REG_DEMCR |= TRC_DEMCR_TRCENA;
+
+ do{
+ /* Verify that DWT is supported */
+ if (TRC_REG_DEMCR == 0)
+ {
+ /* This function is called on Cortex-M3, M4 and M7 devices to initialize
+ the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
+
+ If the below error is produced, the DWT unit does not seem to be available.
+
+ In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
+ to use SysTick timestamping instead, or define your own timestamping by
+ setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
+ and make the necessary definitions, as explained in trcHardwarePort.h.*/
+
+ prvTraceError("DWT unit not available, see code comment.");
+ break;
+ }
+
+ /* Verify that DWT_CYCCNT is supported */
+ if (TRC_REG_DWT_CTRL & TRC_DWT_CTRL_NOCYCCNT)
+ {
+ /* This function is called on Cortex-M3, M4 and M7 devices to initialize
+ the DWT unit, assumed present. The DWT cycle counter is used for timestamping.
+
+ If the below error is produced, the cycle counter does not seem to be available.
+
+ In that case, define the macro TRC_CFG_ARM_CM_USE_SYSTICK in your build
+ to use SysTick timestamping instead, or define your own timestamping by
+ setting TRC_CFG_HARDWARE_PORT to TRC_HARDWARE_PORT_APPLICATION_DEFINED
+ and make the necessary definitions, as explained in trcHardwarePort.h.*/
+
+ prvTraceError("DWT_CYCCNT not available, see code comment.");
+ break;
+ }
+
+ /* Reset the cycle counter */
+ TRC_REG_DWT_CYCCNT = 0;
+
+ /* Enable the cycle counter */
+ TRC_REG_DWT_CTRL |= TRC_DWT_CTRL_CYCCNTENA;
+
+ }while(0); /* breaks above jump here */
+}
+#endif
+#endif
+
+/******************************************************************************
+ * prvTracePortGetTimeStamp
+ *
+ * Returns the current time based on the HWTC macros which provide a hardware
+ * isolation layer towards the hardware timer/counter.
+ *
+ * The HWTC macros and prvTracePortGetTimeStamp is the main porting issue
+ * or the trace recorder library. Typically you should not need to change
+ * the code of prvTracePortGetTimeStamp if using the HWTC macros.
+ *
+ ******************************************************************************/
+void prvTracePortGetTimeStamp(uint32_t *pTimestamp)
+{
+ static uint32_t last_hwtc_count = 0;
+ uint32_t hwtc_count = 0;
+
+#if TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR
+ /* systick based timer */
+ static uint32_t last_traceTickCount = 0;
+ uint32_t traceTickCount = 0;
+#else /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
+ /* Free running timer */
+ static uint32_t last_hwtc_rest = 0;
+ uint32_t diff = 0;
+ uint32_t diff_scaled = 0;
+#endif /*TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR*/
+
+ if (trace_disable_timestamp == 1)
+ {
+ if (pTimestamp)
+ *pTimestamp = last_timestamp;
+ return;
+ }
+
+ /* Retrieve TRC_HWTC_COUNT only once since the same value should be used all throughout this function. */
+#if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR)
+ /* Get the increasing tick count */
+ hwtc_count = (TRC_HWTC_COUNT);
+#elif (TRC_HWTC_TYPE == TRC_OS_TIMER_DECR || TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR)
+ /* Convert decreasing tick count into increasing tick count */
+ hwtc_count = (TRC_HWTC_PERIOD) - (TRC_HWTC_COUNT);
+#else
+ #error "TRC_HWTC_TYPE has unexpected value"
+#endif
+
+#if (TRC_CFG_HARDWARE_PORT == TRC_HARDWARE_PORT_Win32)
+ /* The Win32 port uses ulGetRunTimeCounterValue for timestamping, which in turn
+ uses QueryPerformanceCounter. That function is not always reliable when used over
+ multiple threads. We must therefore handle rare cases where the timestamp is less
+ than the previous. In practice, this should "never" roll over since the
+ performance counter is 64 bit wide. */
+
+ if (last_hwtc_count > hwtc_count)
+ {
+ hwtc_count = last_hwtc_count;
+ }
+#endif
+
+#if (TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)
+ /* Timestamping is based on a timer that wraps at TRC_HWTC_PERIOD */
+ if (last_traceTickCount - uiTraceTickCount - 1 < 0x80000000)
+ {
+ /* This means last_traceTickCount is higher than uiTraceTickCount,
+ so we have previously compensated for a missed tick.
+ Therefore we use the last stored value because that is more accurate. */
+ traceTickCount = last_traceTickCount;
+ }
+ else
+ {
+ /* Business as usual */
+ traceTickCount = uiTraceTickCount;
+ }
+
+ /* Check for overflow. May occur if the update of uiTraceTickCount has been
+ delayed due to disabled interrupts. */
+ if (traceTickCount == last_traceTickCount && hwtc_count < last_hwtc_count)
+ {
+ /* A trace tick has occurred but not been executed by the kernel, so we compensate manually. */
+ traceTickCount++;
+ }
+
+ /* Check if the return address is OK, then we perform the calculation. */
+ if (pTimestamp)
+ {
+ /* Get timestamp from trace ticks. Scale down the period to avoid unwanted overflows. */
+ last_timestamp = traceTickCount * ((TRC_HWTC_PERIOD) / (TRC_HWTC_DIVISOR));
+ /* Increase timestamp by (hwtc_count + "lost hardware ticks from scaling down period") / TRC_HWTC_DIVISOR. */
+ last_timestamp += (hwtc_count + traceTickCount * ((TRC_HWTC_PERIOD) % (TRC_HWTC_DIVISOR))) / (TRC_HWTC_DIVISOR);
+ }
+ /* Store the previous value */
+ last_traceTickCount = traceTickCount;
+
+#else /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
+
+ /* Timestamping is based on a free running timer */
+ /* This part handles free running clocks that can be scaled down to avoid too large DTS values.
+ Without this, the scaled timestamp will incorrectly wrap at (2^32 / TRC_HWTC_DIVISOR) ticks.
+ 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. */
+
+ /* 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. */
+ diff = (hwtc_count - last_hwtc_count) + last_hwtc_rest;
+
+ /* Scale down the diff */
+ diff_scaled = diff / (TRC_HWTC_DIVISOR);
+
+ /* Find out how many ticks were lost when scaling down, so we can add them the next time */
+ last_hwtc_rest = diff % (TRC_HWTC_DIVISOR);
+
+ /* We increase the scaled timestamp by the scaled amount */
+ last_timestamp += diff_scaled;
+#endif /*(TRC_HWTC_TYPE == TRC_OS_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)*/
+
+ /* Is anyone interested in the results? */
+ if (pTimestamp)
+ *pTimestamp = last_timestamp;
+
+ /* Store the previous value */
+ last_hwtc_count = hwtc_count;
+}
+
+#endif /*(TRC_USE_TRACEALYZER_RECORDER == 1)*/
+
+#endif /*(TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)*/