Tracealyzer in extended memory?

ST Micro L562 processor, 256K SRAM, 512K Flash, custom hardware, STLink MINI as a debugger, latest version of trace recorder installed by CubeMXIDE.

I have available a segment of 100K bytes of extended memory on a QSPI interface mapped into processor memory space. It’s working fine.

Given the free version of Tracealyzer, is there a way to locate the snapshot buffer into extended (not main processor) RAM?

I understand, I think, that streaming is not supported in the free version.

Thanks

Harvey

Looking at the code, you can use TRC_CFG_RECORDER_DATA_ATTRIBUTE to place tracerecorder data in a section and then place it in the desired RAM using linker script.

That is correct. However, note that using QSPI RAM might be a lot slower than internal SRAM. Every RTOS event will cause trace data to be written over QSPI, possibly thousands of times per second. I suggest you check how this impacts the TraceRecorder performance (time to write an event). For example, put a breakpoint in one of the xTraceEventCreate functions,( TraceRecorderSource/trcEvent.c at 225720467bd286267377d588e8581dc337abc4c8 · percepio/TraceRecorderSource · GitHub ) step trough them and watch the DWT clock cycle counter. Compare with SRAM.

Thanks, that’s what I was looking for.

Now to see what I can do with it.

I know that QSPI is slower, unfortunately, due to pinouts, I can’t use the FMC (fast memory controller) so I had to trade capacity for speed. The processor has an area of fast RAM that I don’t use, and that is likely the best solution. I’ll see what it does and how badly this impacts system performance.

Since I’m at the “is it working” stage (just redefined memory partitioning and added the trace recorder, and not the “make it work faster” stage. This will do for now.

This is an L5 processor design, so it has one OSPI interface, and pin limitations restricted me to one QSPI chip only. The OSPI version of this chip is BGA, and I won’t use it because I can’t assemble it (do my own assembly on my own boards).

The upgraded design for faster boards uses a (ST Micro) U575 chip, but I went to a 144 pin package. It has two OSPI interfaces, so I can use one as a dual QSPI (halves the number of transactions), and retain the other for either more RAM or on board FLASH as needed.

Thanks

OK, got stuck here.

in trcConfig.h.added:

#define TRC_CFG_RECORDER_DATA_ATTRIBUTE __attribute__((section(".trace_data")))

Now, regardless of what I set TRC_CFG_RECORDER_BUFFER_ALLOCATION to, it can’t find the recorder buffer. I’m also not really finding where that buffer is set.

What next?

Have you also defined that section in your linker script and mapped it to your QSPI ram area?

An example by ChatGPT. Not tested it, mainly to illustrate what I mean:

On STM32L5, the QSPI interface can be memory-mapped, typically starting at 0x90000000 (when configured as external flash/XIP). If you’re using QSPI RAM instead of NOR flash, it will also be mapped into that region. To place a variable or symbol there, you define a memory region and a section in your linker script.

Here’s a minimal snippet you can add to your linker script (.ld) to place a symbol in QSPI RAM:

/* Define memory regions */
MEMORY
{
    FLASH   (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
    RAM     (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
    QSPI    (rwx) : ORIGIN = 0x90000000, LENGTH = 8M   /* Adjust size to your device */
}

/* Sections */
SECTIONS
{
    /* Normal RAM/Flash mappings above ... */

    /* QSPI RAM section */
    .qspi (NOLOAD) :
    {
        . = ALIGN(4);
        __qspi_start__ = .;
        *(.qspi)         /* Any variable tagged with __attribute__((section(".qspi"))) */
        *(.qspi.*)
        . = ALIGN(4);
        __qspi_end__ = .;
    } >QSPI
}

Usage in C code

You can now put a variable into QSPI RAM like this:

__attribute__((section(".qspi"))) uint8_t qspi_buffer[1024];

This variable will be linked into the QSPI memory region at 0x90000000 + offset.
Because of the NOLOAD, the linker won’t try to initialize it from flash at startup — it just reserves the address space.


:warning: Notes:

  • If you need initialized data in QSPI (copied from flash at startup), drop the NOLOAD and add startup code to copy from LOADADDR(.qspi) to __qspi_start__.

  • Make sure the QSPI controller is initialized before you access that memory (e.g., MX25L, HyperRAM, etc., depending on your board).

  • Adjust the size in the MEMORY map (LENGTH) to match your QSPI RAM device.


Do you want me to also show the variant with initialized data copy (so the data section in QSPI is prefilled with values), or is the NOLOAD/uninitialized buffer case enough for your use case?

Yep, got lots of them. Problem is how to tell tracealyzer how to do it, see below.

So TraceRecorder compiles, but Tracealyzer doesn’t find any trace data in the file? Is there an error message from Tracealyzer? How do you save the data? Plesee be more specific.

Or do you mean there is a build error?

Fortunately, I have a lot of this.

QSPI MEMORY is initialized, and tested as part of the startup.

Linker script is created and mapped by a C++ program which automatically creates the memory regions and the sections. That works in other areas (I have a static buffer for FreeRTOS and the user heap area defined that way. Ditto for the XHEAP which is an area for a memory manager I wrote.

MEMORY
{
  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 192K
  RAM2	(xrw)	: ORIGIN = 0x20030000,	LENGTH = 64K
  FLASH	(rx)	: ORIGIN = 0x8000000,	LENGTH = 512K
  QSPI  (rwx) 	: ORIGIN = 0x90000000, LENGTH = 2M   
}



I don’t need initialized memory, and I’m using the eclipse plugin to dump to tracalyzer.

I modified trcConfig.h to add:

 
#define TRC_CFG_RECORDER_DATA_ATTRIBUTE __attribute__((section(".qspi")))

also in trcConfig.h

  * Static and dynamic mode does the allocation for you, either in compile time
  * (static) or in runtime (malloc).
  * The custom mode allows you to control how and where the allocation is made,
  * for details see TRC_ALLOC_CUSTOM_BUFFER and vTraceSetRecorderDataBuffer().
  */
#define TRC_CFG_RECORDER_BUFFER_ALLOCATION TRC_RECORDER_BUFFER_ALLOCATION_CUSTOM


and in main.c

#ifdef _PERCEPIO
		 uint8_t  trace_buffer[sizeof(RecorderDataType)] __attribute__((section(".qspi")));
#endif

and also in main.c after the extended memory has been initialized

	#if (defined _PERCEPIO)

  		 xTraceSetBuffer( (RecorderDataType*) &trace_buffer);
  		 /*Initialise the trace recorder library*/
  		 xTraceInitialize();
  	//		trace_init();
  	   if (xTraceEnable(TRC_START) != TRC_SUCCESS)
  	   {
  	     /* treat error */;
  	     ERROR1(__FILE__, __LINE__, (char*)"Trace Start Error");
  	   }
		#ifdef _PERCEPIO_EVENT
		//		TraceStringHandle_t MyChannel = xTraceRegisterString("EventChannel");
			Trace_Channel = xTraceRegisterString("EventChannel");

		#endif

	#endif

Unable to read trace address, recorder might not be set up properly.

Something’s still a problem, but I’m a lot closer.

Please see below, Unable to read trace buffer address.

So it is the eclipse plugin that reports the error?

The plugin reads a global pointer called RecorderDataPtr. If you inspect that in a debug session (after xTraceEnable) and expanding the struct, can you see valid data?

it seems to be valid. Markers make more or less sense, etc.

This is probably not the problem but you should still change it to the following:

xTraceSetBuffer( (RecorderDataType*) &trace_buffer[ 0 ]);

Could you provide a screenshot of the settings for the eclipse plugin?

A note on how the TRC_CFG_RECORDER_DATA_ATTRIBUTE works:
If you set this then it will be used by the recorder for all buffers. You do not need to manually allocate a buffer and use custom allocation settings as well (TRC_CFG_RECORDER_BUFFER_ALLOCATION).

If you instead want to use your custom buffer then you don’t really need to set the attribute in trcConfig.h as pretty much all data requirements will be from your custom buffer.

Whichever method you use, the recorder buffer is passed along to streamports/RingBuffer/trcStreamPort.c via xTraceStreamPortInitialize() (it will be called from xTraceInitialize()/xTraceEnable()).
In that function we assign a variable called RecorderDataPtr to point to the relevant TraceRingBuffer_t. This is the part containing all trace events and object data.
You were able to locate this buffer and it looked OK? Can you post a screenshot of its’ contents (the start markers and header should be enough)?

1 Like

Did that. Thanks.

Here’s the setup code. the snapshot shows from the ST provided hardware inits. User code section 2 sets the board LEDS to identify the section. The QuadMode and MemMappedQuadMode modify the QSPI memory from SPI mode to QSPI mode and map it into the processor’s memory at 0x90000000. There’s also a section that checks and validates memory reads and writes. The section _PERCEPIO is the complete initialization of the trace system.

You can see that the osKernel is then initialized. CPP_LINK sets up the infrastructure and then calls the application setup, which initializes such things as displays, mesh networking and the like. It also sets up an application task.

So I may have set up the application wrongly.

  MX_USART2_UART_Init();
  MX_TIM2_Init();
  MX_TIM4_Init();
  MX_TIM7_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
  // USER CODE BEGIN 2
    // turn off red, amber, green LEDS   turn on Blue LEDS
    	  // red off
  #if (defined _LED) && (defined _DIRECT_LED) && (defined RED_LED_Pin)
  	 if (RED_ON_HIGH)
  	 {
  			HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
  	 }
  	 else
  	 {
  			HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_SET);
  	 }
  #endif
  #if (defined _LED) && (defined _DIRECT_LED) && (defined BLUE_LED_Pin)
  	 // blue on
  	 if (BLUE_ON_HIGH)
  	 {
  			HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, GPIO_PIN_SET);
  	 }
  	 else
  	 {
  			HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, GPIO_PIN_RESET);
  	 }
  #endif
  #if (defined _LED) && (defined _DIRECT_LED) && (defined GREEN_LED_Pin)

  	 // green off
  	 if (GREEN_ON_HIGH)
  	 {
  			HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_RESET);
  	 }
  	 else
  	 {
  			HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_SET);
  	 }
  #endif
  #if (defined _LED) && (defined _DIRECT_LED) && (defined AMBER_LED_Pin)

  	 // amber off
  	 if (AMBER_ON_HIGH)
  	 {
  			HAL_GPIO_WritePin(AMBER_LED_GPIO_Port, AMBER_LED_Pin, GPIO_PIN_RESET);
  	 }
  	 else
  	 {
  			HAL_GPIO_WritePin(AMBER_LED_GPIO_Port, AMBER_LED_Pin, GPIO_PIN_SET);
  	 }
  #endif




  	 /* Enter Quad Mode 4-4-4 --------------------------------------------- */
  	 EnterQuadMode();

  	 /* Enable Memory mapped in Quad mode -------------------------------- */
  	 EnableMemMappedQuadMode();

  	 //  while (1)
  	 //  {
  	 	  address = 0;
  	 	  /* Writing Sequence of 1Mbyte --------------------------------------- */
  	 	  mem_addr = (__IO uint8_t *)(OCTOSPI1_BASE + address);
  	 	  for (index2 = 0; index2 < EXTENDEDBUFFERSIZE/BUFFERSIZE; index2++)

  	 	  /*Writing 1Mbyte (256Byte BUFFERSIZE x 4096 times) */
  	 	  {
  	 		  for (index1 = 0; index1 < BUFFERSIZE; index1++)
  	 		  {
  	 			  *(mem_addr) = aTxBuffer[index1];
  	 			  mem_addr++;
  	 		  }
  	 	  }
  	 	  /* Reading Sequence of 1Mbyte ---------------------------------------- */
  	 	  mem_addr = (__IO uint8_t *)(OCTOSPI1_BASE + address);
  	 	  for (index2 = 0; index2 < EXTENDEDBUFFERSIZE/BUFFERSIZE; index2++)
  	 	  /*Reading 1Mbyte (256Byte BUFFERSIZE x 4096 times)*/
  	 	  {
  	 		  for (index1 = 0; index1 < BUFFERSIZE; index1++)
  	 		  {
  	 			  if (*(mem_addr) != aTxBuffer[index1])
  	 			  {
  	 				  /*can toggle led here*/
  	 				  // turn on blue LED
  #if (defined _LED) && (defined _DIRECT_LED) && (defined BLUE_LED_Pin)
  	 				 if (BLUE_ON_HIGH)
  	 				 {
  	 						HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_SET);
  	 				 }
  	 				 else
  	 				 {
  	 						HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
  	 				 }
  #endif
  	 				  Error_Handler();
  	 			  }
  	 			  mem_addr++;
  	 		  }
  	 	  }
  	 	  /*can toggle led here*/

  	 	  // turn off RED and Blue LEDS
  	  	  // red off
  #if (defined _LED) && (defined _DIRECT_LED) && (defined RED_LED_Pin)
  		 if (RED_ON_HIGH)
  		 {
  				HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
  		 }
  		 else
  		 {
  				HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_SET);
  		 }
  #endif
  #if (defined _LED) && (defined _DIRECT_LED) && (defined BLUE_LED_Pin)

  		 // blue on
  		 if (BLUE_ON_HIGH)
  		 {
  				HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, GPIO_PIN_RESET);
  		 }
  		 else
  		 {
  				HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, GPIO_PIN_SET);
  		 }

  #endif
  #if (defined _LED) && (defined _DIRECT_LED) && (defined GREEN_LED_Pin)

  		 // green on
  		 if (GREEN_ON_HIGH)
  		 {
  				HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_SET);
  		 }
  		 else
  		 {
  				HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_RESET);
  		 }
  //		 vTaskDelay(100);
  //		 // green off
  //		 if (GREEN_ON_HIGH)
  //		 {
  //				HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_RESET);
  //		 }
  //		 else
  //		 {
  //				HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_SET);
  //		 }

  #endif

	#if (defined _PERCEPIO)

  		 xTraceSetBuffer( (RecorderDataType*) &trace_buffer[0]);
  		 /*Initialise the trace recorder library*/
  		 xTraceInitialize();
  	//		trace_init();
  	   if (xTraceEnable(TRC_START) != TRC_SUCCESS)
  	   {
  	     /* treat error */;
  	     ERROR1(__FILE__, __LINE__, (char*)"Trace Start Error");
  	   }
		#ifdef _PERCEPIO_EVENT
			Trace_Channel = xTraceRegisterString("EventChannel");
		#endif

	#endif


//	#ifdef _CPP_LINK
//		cpp_link();
//	#endif

//#include "../INCLUDE FILES\USER_CODE_BEGIN_2.inc"
  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
#ifdef _CPP_LINK
	cpp_link();
#endif
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}