| /******************************************************************************* | |
| * Tracealyzer v2.6.0 Recorder Library | |
| * Percepio AB, www.percepio.com | |
| * | |
| * trcBase.c | |
| * | |
| * Core functionality of the trace recorder library. | |
| * | |
| * Terms of Use | |
| * This software is copyright Percepio AB. The recorder library is free for | |
| * use together with Percepio products. You may distribute the recorder library | |
| * in its original form, including modifications in trcHardwarePort.c/.h | |
| * given that these modification are clearly marked as your own modifications | |
| * and documented in the initial comment section of these source files. | |
| * This software is the intellectual property of Percepio AB and may not be | |
| * sold or in other ways commercially redistributed without explicit written | |
| * permission by Percepio AB. | |
| * | |
| * Disclaimer | |
| * The trace tool and recorder library is being delivered to you AS IS and | |
| * Percepio AB makes no warranty as to its use or performance. Percepio AB does | |
| * not and cannot warrant the performance or results you may obtain by using the | |
| * software or documentation. Percepio AB 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 AB, 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 AB 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. | |
| * | |
| * Copyright Percepio AB, 2013. | |
| * www.percepio.com | |
| ******************************************************************************/ | |
| #include "trcBase.h" | |
| #if (USE_TRACEALYZER_RECORDER == 1) | |
| #include <stdint.h> | |
| /******************************************************************************* | |
| * Static data initializations | |
| ******************************************************************************/ | |
| /* Structure to handle the exclude flags for all objects and tasks. We add some extra objects since index 0 is not used for each object class. */ | |
| uint8_t excludedObjects[(TRACE_KERNEL_OBJECT_COUNT + TRACE_NCLASSES) / 8 + 1] = { 0 }; | |
| /* Structure to handle the exclude flags for all event codes */ | |
| uint8_t excludedEventCodes[NEventCodes / 8 + 1] = { 0 }; | |
| /* Keeps track of available handles */ | |
| objectHandleStackType objectHandleStacks = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; | |
| uint32_t init_hwtc_count; | |
| /******************************************************************************* | |
| * RecorderData | |
| * | |
| * The main data structure. This is the data read by Tracealyzer, typically | |
| * through a debugger RAM dump. The recorder access this through the pointer | |
| * RecorderDataPtr, to allow for dynamic memory allocation as well. | |
| * | |
| * On the NXP LPC17xx you may use the secondary RAM bank (AHB RAM) for this | |
| * purpose. For instance, the LPC1766 has 32 KB AHB RAM which allows for | |
| * allocating a buffer size of at least 7500 events without affecting the main | |
| * RAM. To place RecorderData in this RAM bank, use the below declaration. | |
| * | |
| * #pragma location="AHB_RAM_MEMORY" | |
| * RecorderDataType RecorderData = ... | |
| * | |
| * This of course works for other hardware architectures with additional RAM | |
| * banks as well, just replace "AHB_RAM_MEMORY" with the name of the right | |
| * address section from the linker file. | |
| * | |
| * However, to keep trcBase.c portable and still have a preconfigured IAR demo | |
| * using AHB RAM, we don't add the pragma directly in trcBase.c but in a header | |
| * included where the pragma should go. This is used depending on the setting | |
| * USE_LINKER_PRAGMA, defined in trcConfig.h. | |
| * | |
| * If using GCC, this is instead done by adding a "section" attribute: | |
| * | |
| * RecorderDataType RecorderData __attribute__ ((section ("name"))) = ... | |
| * | |
| * Remember to replace "name" with the correct section name. | |
| ******************************************************************************/ | |
| static void vInitStartMarkers(void); | |
| #if (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC) | |
| #if (USE_LINKER_PRAGMA == 1) | |
| #include "recorderdata_linker_pragma.h" | |
| #endif | |
| RecorderDataType RecorderData; | |
| #endif | |
| RecorderDataType* RecorderDataPtr = NULL; | |
| /* This version of the function dynamically allocates the trace data */ | |
| void prvTraceInitTraceData() | |
| { | |
| init_hwtc_count = HWTC_COUNT; | |
| #if TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC | |
| RecorderDataPtr = &RecorderData; | |
| #elif TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_DYNAMIC | |
| RecorderDataPtr = (RecorderDataType*)TRACE_MALLOC(sizeof(RecorderDataType)); | |
| #elif TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_CUSTOM | |
| /* DO NOTHING */ | |
| #endif | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "prvTraceInitTraceData, RecorderDataPtr == NULL", ); | |
| if (! RecorderDataPtr) | |
| { | |
| vTraceError("No recorder data structure allocated!"); | |
| return; | |
| } | |
| (void)memset(RecorderDataPtr, 0, sizeof(RecorderDataType)); | |
| RecorderDataPtr->startmarker0 = 0x00; | |
| RecorderDataPtr->startmarker1 = 0x01; | |
| RecorderDataPtr->startmarker2 = 0x02; | |
| RecorderDataPtr->startmarker3 = 0x03; | |
| RecorderDataPtr->startmarker4 = 0x70; | |
| RecorderDataPtr->startmarker5 = 0x71; | |
| RecorderDataPtr->startmarker6 = 0x72; | |
| RecorderDataPtr->startmarker7 = 0x73; | |
| RecorderDataPtr->startmarker8 = 0xF0; | |
| RecorderDataPtr->startmarker9 = 0xF1; | |
| RecorderDataPtr->startmarker10 = 0xF2; | |
| RecorderDataPtr->startmarker11 = 0xF3; | |
| RecorderDataPtr->version = TRACE_KERNEL_VERSION; | |
| RecorderDataPtr->minor_version = TRACE_MINOR_VERSION; | |
| RecorderDataPtr->irq_priority_order = IRQ_PRIORITY_ORDER; | |
| RecorderDataPtr->filesize = sizeof(RecorderDataType); | |
| RecorderDataPtr->maxEvents = EVENT_BUFFER_SIZE; | |
| RecorderDataPtr->debugMarker0 = 0xF0F0F0F0; | |
| RecorderDataPtr->isUsing16bitHandles = USE_16BIT_OBJECT_HANDLES; | |
| /* This function is kernel specific */ | |
| vTraceInitObjectPropertyTable(); | |
| RecorderDataPtr->debugMarker1 = 0xF1F1F1F1; | |
| RecorderDataPtr->SymbolTable.symTableSize = SYMBOL_TABLE_SIZE; | |
| RecorderDataPtr->SymbolTable.nextFreeSymbolIndex = 1; | |
| #if (INCLUDE_FLOAT_SUPPORT == 1) | |
| RecorderDataPtr->exampleFloatEncoding = (float)1.0; /* otherwise already zero */ | |
| #endif | |
| RecorderDataPtr->debugMarker2 = 0xF2F2F2F2; | |
| (void)strncpy(RecorderDataPtr->systemInfo, TRACE_DESCRIPTION, TRACE_DESCRIPTION_MAX_LENGTH); | |
| RecorderDataPtr->debugMarker3 = 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 USE_SEPARATE_USER_EVENT_BUFFER | |
| RecorderDataPtr->userEventBuffer.bufferID = 1; | |
| RecorderDataPtr->userEventBuffer.version = 0; | |
| RecorderDataPtr->userEventBuffer.numberOfSlots = USER_EVENT_BUFFER_SIZE; | |
| RecorderDataPtr->userEventBuffer.numberOfChannels = CHANNEL_FORMAT_PAIRS + 1; | |
| #endif | |
| /* Kernel specific initialization of the objectHandleStacks variable */ | |
| vTraceInitObjectHandleStack(); | |
| /* Fix the start markers of the trace data structure */ | |
| vInitStartMarkers(); | |
| #ifdef PORT_SPECIFIC_INIT | |
| PORT_SPECIFIC_INIT(); | |
| #endif | |
| } | |
| static void vInitStartMarkers() | |
| { | |
| uint32_t i; | |
| uint8_t *ptr = (uint8_t*)&(RecorderDataPtr->startmarker0); | |
| if ((*ptr) == 0) | |
| { | |
| for (i = 0; i < 12; i++) | |
| { | |
| ptr[i] += 1; | |
| } | |
| } | |
| else | |
| { | |
| vTraceError("Trace start markers already initialized!"); | |
| } | |
| } | |
| volatile int recorder_busy = 0; | |
| /* Gives the last error message of the recorder. NULL if no error message. */ | |
| char* traceErrorMessage = NULL; | |
| void* xTraceNextFreeEventBufferSlot(void) | |
| { | |
| if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE) | |
| { | |
| vTraceError("Attempt to index outside event buffer!"); | |
| return NULL; | |
| } | |
| return (void*)(&RecorderDataPtr->eventData[RecorderDataPtr->nextFreeIndex*4]); | |
| } | |
| uint16_t uiIndexOfObject(objectHandleType 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))); | |
| } | |
| vTraceError("Object table lookup with invalid object handle or object class!"); | |
| return 0; | |
| } | |
| /******************************************************************************* | |
| * Object handle system | |
| * This provides a mechanism to assign each kernel object (tasks, queues, etc) | |
| * with a 1-byte handle, that is used to identify the object in the trace. | |
| * This way, only one byte instead of four is necessary to identify the object. | |
| * This allows for maximum 255 objects, of each object class, active at any | |
| * moment. | |
| * Note that zero is reserved as an error code and is not a valid handle. | |
| * | |
| * In order to allow for fast dynamic allocation and release of object handles, | |
| * the handles of each object class (e.g., TASK) are stored in a stack. When a | |
| * handle is needed, e.g., on task creation, the next free handle is popped from | |
| * the stack. When an object (e.g., task) is deleted, its handle is pushed back | |
| * on the stack and can thereby be reused for other objects. | |
| * | |
| * Since this allows for reuse of object handles, a specific handle (e.g, "8") | |
| * may refer to TASK_X at one point, and later mean "TASK_Y". To resolve this, | |
| * the recorder uses "Close events", which are stored in the main event buffer | |
| * when objects are deleted and their handles are released. The close event | |
| * contains the mapping between object handle and object name which was valid up | |
| * to this point in time. The object name is stored as a symbol table entry. | |
| ******************************************************************************/ | |
| objectHandleType xTraceGetObjectHandle(traceObjectClass objectclass) | |
| { | |
| objectHandleType handle; | |
| static int indexOfHandle; | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, "xTraceGetObjectHandle: Invalid value for objectclass", (objectHandleType)0); | |
| 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] = | |
| (objectHandleType)(1 + indexOfHandle - | |
| objectHandleStacks.lowestIndexOfClass[objectclass]); | |
| } | |
| handle = objectHandleStacks.objectHandles[indexOfHandle]; | |
| if (objectHandleStacks.indexOfNextAvailableHandle[objectclass] | |
| > objectHandleStacks.highestIndexOfClass[objectclass]) | |
| { | |
| /* ERROR */ | |
| vTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); | |
| handle = 0; /* an invalid/anonymous handle - but the recorder is stopped now... */ | |
| } | |
| else | |
| { | |
| int hndCount; | |
| objectHandleStacks.indexOfNextAvailableHandle[objectclass]++; | |
| hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] - | |
| objectHandleStacks.lowestIndexOfClass[objectclass]; | |
| if (hndCount > | |
| objectHandleStacks.handleCountWaterMarksOfClass[objectclass]) | |
| { | |
| objectHandleStacks.handleCountWaterMarksOfClass[objectclass] = | |
| (objectHandleType)hndCount; | |
| } | |
| TRACE_CLEAR_OBJECT_FLAG_ISEXCLUDED(objectclass, handle); | |
| } | |
| return handle; | |
| } | |
| void vTraceFreeObjectHandle(traceObjectClass objectclass, objectHandleType handle) | |
| { | |
| int indexOfHandle; | |
| TRACE_ASSERT(objectclass < TRACE_NCLASSES, "vTraceFreeObjectHandle: Invalid value for objectclass", ); | |
| TRACE_ASSERT(handle > 0 && handle <= RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass], "vTraceFreeObjectHandle: Invalid value for handle", ); | |
| /* Check that there is room to push the handle on the stack */ | |
| if ((objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) < | |
| objectHandleStacks.lowestIndexOfClass[objectclass]) | |
| { | |
| /* Error */ | |
| vTraceError("Attempt to free more handles than allocated!"); | |
| } | |
| else | |
| { | |
| objectHandleStacks.indexOfNextAvailableHandle[objectclass]--; | |
| indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass]; | |
| objectHandleStacks.objectHandles[indexOfHandle] = handle; | |
| } | |
| } | |
| /******************************************************************************* | |
| * Objects Property Table | |
| * | |
| * This holds the names and properties of the currently active objects, such as | |
| * tasks and queues. This is developed to support "dynamic" objects which might | |
| * be deleted during runtime. Their handles are only valid during their | |
| * lifetime, i.e., from create to delete, as they might be reused on later | |
| * create operations. When an object is deleted from the OPT, its data is moved | |
| * to the trace buffer and/or the symbol table. | |
| * When an object (task, queue, etc.) is created, it receives a handle, which | |
| * together with the object class specifies its location in the OPT. Thus, | |
| * objects of different types may share the same name and/or handle, but still | |
| * be independent objects. | |
| ******************************************************************************/ | |
| /******************************************************************************* | |
| * vTraceSetObjectName | |
| * | |
| * 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 vTraceSetObjectName(traceObjectClass objectclass, | |
| objectHandleType handle, | |
| const char* name) | |
| { | |
| static uint16_t idx; | |
| TRACE_ASSERT(name != NULL, "vTraceSetObjectName: name == NULL", ); | |
| if (objectclass >= TRACE_NCLASSES) | |
| { | |
| vTraceError("Illegal object class in vTraceSetObjectName"); | |
| return; | |
| } | |
| if (handle == 0) | |
| { | |
| vTraceError("Illegal handle (0) in vTraceSetObjectName."); | |
| return; | |
| } | |
| if (handle > RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]) | |
| { | |
| /* ERROR */ | |
| vTraceError(pszTraceGetErrorNotEnoughHandles(objectclass)); | |
| } | |
| else | |
| { | |
| idx = uiIndexOfObject(handle, objectclass); | |
| if (traceErrorMessage == NULL) | |
| { | |
| (void)strncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]), | |
| name, | |
| RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ]); | |
| } | |
| } | |
| } | |
| traceLabel prvTraceOpenSymbol(const char* name, traceLabel userEventChannel) | |
| { | |
| uint16_t result; | |
| uint8_t len; | |
| uint8_t crc; | |
| TRACE_SR_ALLOC_CRITICAL_SECTION(); | |
| len = 0; | |
| crc = 0; | |
| TRACE_ASSERT(name != NULL, "prvTraceOpenSymbol: name == NULL", (traceLabel)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; | |
| } | |
| /******************************************************************************* | |
| * Supporting functions | |
| ******************************************************************************/ | |
| /******************************************************************************* | |
| * vTraceError | |
| * | |
| * 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., NTask) in trcConfig.h is too small. | |
| ******************************************************************************/ | |
| void vTraceError(const char* msg) | |
| { | |
| TRACE_ASSERT(msg != NULL, "vTraceError: msg == NULL", ); | |
| TRACE_ASSERT(RecorderDataPtr != NULL, "vTraceError: RecorderDataPtr == NULL", ); | |
| /* Stop the recorder. Note: We do not call vTraceStop, since that adds a weird | |
| and unnecessary dependency to trcUser.c */ | |
| RecorderDataPtr->recorderActive = 0; | |
| if (traceErrorMessage == NULL) | |
| { | |
| traceErrorMessage = (char*)msg; | |
| (void)strncpy(RecorderDataPtr->systemInfo, traceErrorMessage, TRACE_DESCRIPTION_MAX_LENGTH); | |
| RecorderDataPtr->internalErrorOccured = 1; | |
| } | |
| } | |
| /****************************************************************************** | |
| * 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 ringbuffer mode) | |
| * any data entries following must be replaced with NULL events (code 0). | |
| * | |
| * This is assumed to execute within a critical section... | |
| *****************************************************************************/ | |
| 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", ); | |
| 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, 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++; | |
| } | |
| } | |
| /******************************************************************************* | |
| * prvTraceUpdateCounters | |
| * | |
| * Updates the index of the event buffer. | |
| ******************************************************************************/ | |
| void prvTraceUpdateCounters(void) | |
| { | |
| if (RecorderDataPtr->recorderActive == 0) | |
| { | |
| return; | |
| } | |
| RecorderDataPtr->numEvents++; | |
| RecorderDataPtr->nextFreeIndex++; | |
| if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE) | |
| { | |
| #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER) | |
| RecorderDataPtr->bufferIsFull = 1; | |
| RecorderDataPtr->nextFreeIndex = 0; | |
| #else | |
| vTraceStop(); | |
| #endif | |
| } | |
| #if (TRACE_RECORDER_STORE_MODE == TRACE_STORE_MODE_RING_BUFFER) | |
| prvCheckDataToBeOverwrittenForMultiEntryEvents(1); | |
| #endif | |
| #ifdef STOP_AFTER_N_EVENTS | |
| #if (STOP_AFTER_N_EVENTS > -1) | |
| if (RecorderDataPtr->numEvents >= STOP_AFTER_N_EVENTS) | |
| { | |
| vTraceStop(); | |
| } | |
| #endif | |
| #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 (SELECTED_PORT != PORT_ARM_CortexM) | |
| if (RecorderDataPtr->frequency == 0 && init_hwtc_count != HWTC_COUNT) | |
| { | |
| /* If HWTC_PERIOD is mapped to the timer reload register, | |
| it might not be initialized before the scheduler has been started. | |
| We therefore store the frequency of the timer when the counter | |
| register has changed from its initial value. | |
| (Note that this function is called also by vTraceStart and | |
| uiTraceStart, which might be called before the scheduler | |
| has been started.) */ | |
| #if (SELECTED_PORT == PORT_Win32) | |
| RecorderDataPtr->frequency = 100000; | |
| #elif (SELECTED_PORT == PORT_HWIndependent) | |
| RecorderDataPtr->frequency = TRACE_TICK_RATE_HZ; | |
| #else | |
| RecorderDataPtr->frequency = (HWTC_PERIOD * TRACE_TICK_RATE_HZ) / (uint32_t)HWTC_DIVISOR; | |
| #endif | |
| } | |
| #endif | |
| /************************************************************************** | |
| * 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. | |
| **************************************************************************/ | |
| vTracePortGetTimeStamp(×tamp); | |
| /*************************************************************************** | |
| * 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*) xTraceNextFreeEventBufferSlot(); | |
| 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 | |
| { | |
| vTraceError("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 | |
| ******************************************************************************/ | |
| traceLabel prvTraceLookupSymbolTableEntry(const char* name, | |
| uint8_t crc6, | |
| uint8_t len, | |
| traceLabel chn) | |
| { | |
| uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ]; | |
| TRACE_ASSERT(name != NULL, "prvTraceLookupSymbolTableEntry: name == NULL", (traceLabel)0); | |
| TRACE_ASSERT(len != 0, "prvTraceLookupSymbolTableEntry: len == 0", (traceLabel)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, | |
| traceLabel 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 >= SYMBOL_TABLE_SIZE) | |
| { | |
| vTraceError("Symbol table full. Increase 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) */ | |
| (void)strncpy((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 += (len + 5); | |
| ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - | |
| (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; | |
| int crc = 0; | |
| TRACE_ASSERT(pname != NULL, "prvTraceGetChecksum: pname == NULL", ); | |
| TRACE_ASSERT(pcrc != NULL, "prvTraceGetChecksum: pcrc == NULL", ); | |
| TRACE_ASSERT(plength != NULL, "prvTraceGetChecksum: plength == NULL", ); | |
| if (pname != (const char *) 0) | |
| { | |
| for (; (c = *pname++) != '\0';) | |
| { | |
| crc += c; | |
| length++; | |
| } | |
| } | |
| *pcrc = (uint8_t)(crc & 0x3F); | |
| *plength = (uint8_t)length; | |
| } | |
| #if (USE_16BIT_OBJECT_HANDLES == 1) | |
| void prvTraceStoreXID(objectHandleType 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. | |
| *****************************************************************************/ | |
| void prvTraceStoreXID(objectHandleType handle) | |
| { | |
| XPSEvent* xid; | |
| TRACE_ASSERT(handle >= 256, "prvTraceStoreXID: Handle < 256", ); | |
| xid = (XPSEvent*)xTraceNextFreeEventBufferSlot(); | |
| if (xid != NULL) | |
| { | |
| xid->type = XID; | |
| /* This function is (only) used when objectHandleType is 16 bit... */ | |
| xid->xps_16 = handle; | |
| prvTraceUpdateCounters(); | |
| } | |
| } | |
| unsigned char prvTraceGet8BitHandle(objectHandleType 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 (unsigned char)(handle & 0xFF); | |
| } | |
| #endif | |
| #endif |