Using sprintf in PXROS-HR: Application Note

Terms & Abbreviations

UTF

UCS (Unicode) Transformation Format

Introduction

This document provides a context of the issues with using the sprintf family of functions (in the following text only print functions) in the MPU-protected multitasking environment of PXROS-HR. Within this document, multitasking (or multithreading) covers both situations when the tasks run on the same or different cores.

This document, in particular, describes the necessary steps to be done to use print functions with PXROS-HR when using HighTec Compiler Suite version 4.9.3.0 or higher.

The application note is valid both for sprintf and snprintf functions. However, it is recommended to use the snprintf variant to avoid the overflow of the provided buffer.

The application must ensure that the task calling the print function has data access to all function arguments (read or read/write based on how the argument is used), including, for example, access to strings passed via pointers. When a pointer is passed for printing a string, the task must have access to the complete string area.

Libraries

The HighTec’s TriCore C/C++ Development Platform is shipped with three libraries (description is provided in the toolchain User’s Guide):

  • libgcc.a

  • libos.a

  • libdnk_c.a

The print functions are implemented in the Dinkumware library libdnk_c.a, and the function declarations are provided in <stdio.h> header file. The libos.a and libgcc.a libraries provide supportive functions called by the print functions internally.

Thread safety and re-entrance

There are two function categories in a multitasking (multithreading) environment:

  • Thread-safe function

    "A function is thread-safe if multiple threads can execute the same function at the same time safely."

    All print functions are thread-safe when the Thread-safety assumptions are fulfilled.

  • Re-entrant function

    "A function is re-entrant if it can be interrupted in the middle of execution, and it’s safe to call the same function again in the same thread."

    As far as the print functions do not modify any global or static variables, they are re-entrant.

Thread-safety assumptions

List of allowed print functions:

  • sprintf

  • snprintf

  • vsprintf

  • vsnprintf

All the other functions are not allowed to use. For the allowed functions, the following assumptions must be fulfilled:

  • tasks have access to all variables that are passed as arguments (including indirect access via pointers)

  • buffers, where to put the formatted output, are used by the tasks exclusively

  • pointer to a buffer, where to put the formatted output, cannot be NULL

  • the pointer to the format string cannot be NULL

  • any pointer passed as argument to be printed by %s cannot be NULL

  • any pointer passed as argument to be printed by %n cannot be NULL

Furthermore, it is not allowed to:

  • use concurrently any function that modifies static or global variables of the Dinkumware library

  • use wide-character strings

Linker file

This section covers changes that have to be done to the linker script to use print functions in PXROS-HR.

Placement of library data

The data and bss sections from the Dinkumware library must be collected and inserted into output sections that will occupy a contiguous range of memory. The beginning and end of this memory block must be marked with symbols that are aligned to 8 bytes (general requirement of TriCore™ MPU). Those symbols are then used to configure data access for a task that uses the print functions. The data must be collected prior collection of all remaining common data, i.e., before the .data and .bss sections.

The following code shows where to add the libdnk_c.a and libos.a libraries. The original linker file comes from the TC39x PXROS BSP example.

Code 1. Modified TC39x PXROS BSP example linker file — placement of library data
...
/* ----- Restore CORE_ID ----- */
CORE_ID = GLOBAL;

/* ========================================================================================
 * Dinkumware and libos library .data and .bss sections
 * ======================================================================================*/

SECTION
{
    /* The library data are needed because of the sprintf family functions.
     * The .library section will cover data from dinkumware and libos libraries.
     */
    .library.data :
    {
        . = ALIGN(8);
        LIBRARY_DATA_BASE = .;
        *libdnk_c.a: (*.data*)  // All symbols that were originaly placed in .data section
                                // of the libdnk_c.a will be placed in the .library.data
                                // output section

        *libos.a: (*.data*)     // Similar for symbols comming from the libos.a

    } > DATA AT > RODATA

    .library.bss :
    {
        *libdnk_c.a: (*.bss*)   // All symbols that were originaly placed in .bss section
                                // of the libdnk_c.a will be placed in the .library.bss
                                // output section
        . = ALIGN(8);
        LIBRARY_DATA_END = .;
    } > DATA
}

/* ========================================================================================
 * Common CODE & DATA sections
 * ======================================================================================*/

SECTIONS
{
    ...

    /* All common initialized data not yet collected */
	.data : ALIGN(4)
	{
	   *(.data*)
	} > DATA AT > RODATA

	/* All common non-initialized data not yet collected */
	.bss : ALIGN(4)
	{
	   *(.bss*);
	   *(COMMON);
	} > DATA

	/* HEAP area for stdlib functions */
	.heap : ALIGN(8)
	{
	   __HEAP = .;
	   . += __HEAP_SIZE;
	   . = ALIGN(8);
	   __HEAP_END = .;
	} > DATA
}

Initialization of library data

The .library.bss and .library.data sections must be added to the clear and copy tables so that the crt0 routine properly initializes the data. All common data are initialized by CPU0.

The following code shows where to add the clear and copy records. The original linker file comes from the TC39x PXROS BSP example.

Code 2. Modified TC39x PXROS BSP example linker file - clear and copy tables
/* ========================================================================================
 * CLEAR & COPY TABLES with END delimiter to support crt0 init
 * Each core has its own table to process during its init to allow multicore execution.
 * Shared resources are inserted to Core[0] tables (the RESET core)
 * clear_sec:
 *    data memory ranges to clear to zero
 * copy_sec:
 *    data memory ranges that needs to be value initialized
 *    (init values are stored in FLASH and copied to RAM)
 * ======================================================================================*/

SECTIONS
{
	/* ---- CORE 0 ---- */
	.CPU0.clear_sec :
	{
	    LONG(ADDR(.library.bss)); LONG(SIZEOF(.library.bss));
        LONG(ADDR(.bss)); LONG(SIZEOF(.bss));
		LONG(ADDR(.heap)); LONG(SIZEOF(.heap));
		LONG(-1); LONG(-1);
	} > RODATA_CPU0_

	.CPU0.copy_sec :
	{
	    LONG(LOADADDR(.library.data)); LONG(ADDR(.library.data)); LONG(SIZEOF(.library.data));
        LONG(LOADADDR(.data)); LONG(ADDR(.data)); LONG(SIZEOF(.data));
		LONG(-1); LONG(-1); LONG(-1);
	} > RODATA_CPU0_

    /* ---- CORE 1 ---- */

	.CPU1.clear_sec :
	{
		LONG(-1); LONG(-1);
	} > RODATA_CPU1_

	.CPU1.copy_sec :
	{
		LONG(-1); LONG(-1); LONG(-1);
	} > RODATA_CPU1_

    ...

Task protection

The task must have read access to the data of the Dinkumware and libos libraries. Setting the read-only access is essential, as we assume that the print functions do not modify global data. If they would, the application will cause the MPU trap, informing us about possibly using a non-thread-safe function.

The read-only data access to the library data can be provided by setting either the task context or the extended memory regions. The decision of which approach to use depends on how efficient the access needs to be:

  • using the task context results in fast data access (without any overhead by the Kernel interference)

  • the use of extended memory regions may cause data access delay (the impact on application performance depends on the number of additional extended memory regions and other runtime factors).

Code 3. Task protection — task context
extern PxUInt_t LIBRARY_DATA_BASE[];
extern PxUInt_t LIBRARY_DATA_END[];

/* TASK DATA CONTEXT
 * Data regions that stay permanently programmed in Task MPU regions
 * Notes:
 * .lowerbound = 0 : region inherited from the parent
 */
static const PxTaskContext_T task_Context =
{
	.protection[0] =
	{
		.lowerBound = 0,
		.upperBound = 0,
		.prot = NoAccessProtection
	},
	.protection[1] =
	{
		.lowerBound = LIBRARY_DATA_BASE,
		.upperBound = LIBRARY_DATA_END,
		.prot = ReadProtection
	}
};
Code 4. Task protection — extended regions
extern PxUInt_t LIBRARY_DATA_BASE[];
extern PxUInt_t LIBRARY_DATA_END[];

/* TASK EXTENDED MEMORY REGIONS
 */
static const PxProtectRegion_T  taskAPRegions[] =
{
	{(unsigned int) &LIBRARY_DATA_BASE, (unsigned int) &LIBRARY_DATA_END, ReadProtection},
    {0, 0, NoAccessProtection}
};

Document References

[1] "TC39x PXROS BSP Example Quick Guide", HighTec EDV-Systeme GmbH, 2020

[2] "TriCore™ Development Platform User’s Guide, v4.9.4.1, HighTec EDV-Systeme GmbH, 2020

Document history

Version Date Changes to the previous version

1.0

June 2023

Initial version

Disclaimer

 
 
 

Please Read Carefully:

This document contains descriptions for copyrighted products that are not explicitly indicated as such. The absence of the TM symbol does not infer that a product is not protected. Additionally, registered patents and trademarks are similarly not expressly indicated in this document.

The information in this document has been carefully checked and is believed to be entirely reliable. However, HighTec EDV-Systeme GmbH assumes no responsibility for any inaccuracies. HighTec EDV-Systeme GmbH neither gives any guarantee nor accepts any liability whatsoever for consequential damages resulting from the use of this document or its associated product. HighTec EDV-Systeme GmbH reserves the right to alter the information contained herein without prior notification and accepts no responsibility for any damages that might result.

HighTec EDV-Systeme hereby disclaims any and all warranties and liabilities of any kind, including without limitation, warranties of non-infringement of intellectual property rights of any third party.

Rights - including those of translation, reprint, broadcast, photomechanical or similar reproduction and storage or processing in computer systems, in whole or in part - are reserved. No reproduction may occur without the express written consent from HighTec EDV-Systeme GmbH.

Copyright © 2023 HighTec EDV-Systeme GmbH, D-66113 Saarbrucken.