Problems Using FreeRTOS-MPU on NXP K66

marcbe wrote on Thursday, March 23, 2017:

Hello,

I have some problems getting the FreeRTOS-MPU port for Cortex-M4 to work.
I am using NXP’s Kinetis Design Studio with SDK v2 for a Kinetis K66 (FRDM-K66F Platform) under GCC.

I first created a new SDK Project from FreeRTOS Hello World Example.
Then replaced the FreeRTOS port (port.c, port.h), added mpu_wrappers.c and added some entries in Linker Script (see below).

I set up the parameters for hello_task (User-Mode, MPU region which allows read/write access to UART peripheral) and created the task with xTaskCreateRestricted.

Unfortunately there comes a HardFault when the task is trying to access the UART registers (read or write) so it seems my MPU setting is wrong.
Starting the task as privileged task everything works fine.

Could someone provide help?

Below an excerpt of the linker script:


/* Variables used by FreeRTOS-MPU. */
_Privileged_Functions_Region_Size = 32K;
_Privileged_Data_Region_Size = 512;

__FLASH_segment_start__ = ORIGIN( m_interrupts );
__FLASH_segment_end__ = __FLASH_segment_start__ + LENGTH( m_interrupts ) + LENGTH( m_flash_config ) + LENGTH ( m_text );

__privileged_functions_start__ = ORIGIN( m_text );
__privileged_functions_end__ = __privileged_functions_start__ + _Privileged_Functions_Region_Size;

__SRAM_segment_start__ = ORIGIN( m_data );
__SRAM_segment_end__ = __SRAM_segment_start__ + LENGTH( m_data ) + LENGTH( m_data_2 );

__privileged_data_start__ = ORIGIN( m_data );
__privileged_data_end__ = __privileged_data_start__ + _Privileged_Data_Region_Size;


/* Define output sections */
SECTIONS
{
  /* The startup code goes first into internal flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    . = ALIGN(4);    
  } > m_interrupts

  .flash_config :
  {
    . = ALIGN(4);
    KEEP(*(.FlashConfig))    /* Flash Configuration Field (FCF) */
    . = ALIGN(4);
  } > m_flash_config

  privileged_functions :
    {
        . = ALIGN(4);
        *(privileged_functions)
        . = ALIGN(4);

        /* Non privileged code is after _Privileged_Functions_Region_Size. */
        __privileged_functions_actual_end__ = .;
        . = _Privileged_Functions_Region_Size;
    } > m_text

	...
	
	
  .interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start */
    *(.m_interrupts_ram)     /* This is a user defined section */
    . += M_VECTOR_RAM_SIZE;
    . = ALIGN(4);
    __interrupts_ram_end__ = .; /* Define a global symbol at data end */
  } > m_data

  __VECTOR_RAM = DEFINED(__ram_vector_table__) ? __VECTOR_RAM__ : ORIGIN(m_interrupts);
  __RAM_VECTOR_TABLE_SIZE_BYTES = DEFINED(__ram_vector_table__) ? (__interrupts_ram_end__ - __interrupts_ram_start__) : 0x0;
  
  privileged_data :
    {
        *(privileged_data)
        /* Non kernel data is kept out of the first _Privileged_Data_Region_Size
        bytes of SRAM. */
        __privileged_data_actual_end__ = .;
        . = _Privileged_Data_Region_Size;
    } > m_data

  .data : AT(__DATA_ROM)
  {
  ...
  

And here the task set up in freertos_hello.c:

static void hello_task(void *pvParameters);

...

/* GCC specifics. */
#define mainALIGN_TO( x )				__attribute__((aligned(x)))

/* Task priorities. */
#define hello_task_PRIORITY ((configMAX_PRIORITIES - 1))

#define mainHELLO_TASK_STACK_SIZE_WORDS 128
#define mainHELLO_TASK_STACK_ALIGNMENT ( mainHELLO_TASK_STACK_SIZE_WORDS * sizeof( portSTACK_TYPE ) )

#define mainMPU_DEBUG_UART_SIZE			(0x80)
#define mainMPU_DEBUG_UART_BASE_ADDR	((uint32_t *) BOARD_DEBUG_UART_BASEADDR)

static portSTACK_TYPE xHelloTaskStack[ mainHELLO_TASK_STACK_SIZE_WORDS ] mainALIGN_TO( mainHELLO_TASK_STACK_ALIGNMENT );

static const TaskParameters_t xHelloTaskParameters =
{
		hello_task,
		"Hello",
		mainHELLO_TASK_STACK_SIZE_WORDS,
		NULL,
		hello_task_PRIORITY,
		xHelloTaskStack,
		{
				{ mainMPU_DEBUG_UART_BASE_ADDR, 	mainMPU_DEBUG_UART_SIZE, 	portMPU_REGION_READ_WRITE},
				{ 0x00, 							0x00, 						0x00 },
				{ 0x00, 							0x00,						0x00 }

		}
};

...


int main(void)
{
    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    xTaskCreateRestricted( &xHelloTaskParameters, NULL);
    vTaskStartScheduler();
    for (;;)
        ;
}


/*!
 * @brief Task responsible for printing of "Hello world." message.
 */
static void hello_task(void *pvParameters)
{
    for (;;)
    {
    	GETCHAR();
        PRINTF("Hello world.\r\n");
        vTaskSuspend(NULL);
    }
}

rtel wrote on Thursday, March 23, 2017:

Can you confirm the task starts, and the fault only occurs when you
attempt to access the UART? If so, what is the address of the data
access that causes the fault?

What is BOARD_DEBUG_UART_BASEADDR set to? The Cortex-M hardware
restricts MPU region start addresses to power of 2 boundaries. Likewise
the length (mainMPU_DEBUG_UART_SIZE) must be a power of 2 (from memory).

marcbe wrote on Friday, March 24, 2017:

marcbe wrote on Monday, March 27, 2017:

BOARD_DEBUG_UART_BASEADDR is set to the address of UART0 which is (0x4006A000u). The size of the region is set to 0x80. If I understood the reference manual right, these settings should be correct for the MPU?

The task starts correctly and by stepping through the debugger I found out the following line in function UART_ReadBlocking leads to the HardFault:

#if defined(FSL_FEATURE_UART_HAS_FIFO) && FSL_FEATURE_UART_HAS_FIFO
        while (!base->RCFIFO)
#else

With base pointing to address 0x4006a000 (which is UART0) and RCFIFO at Offset 0x0016.

marcbe wrote on Friday, March 31, 2017:

Anybody there who could give some hints?
I am still not able to get the MPU port run :frowning:

rtel wrote on Friday, March 31, 2017:

The base address and region size look to conform with the requirements
(region size is binary power of 2 and base address is a multiple of the
region size). Are there any additional configuration items required to
allow access to the peripherals?

marcbe wrote on Monday, April 03, 2017:

After some more debugging I found out the HardFault Exception was not caused by the MPU. The exact fault was a BusFault and not a MemManage Fault as I expected it to be.

The reference manual says, the peripherals connected to eiter AIPS-Lite0 or AIPS-Lite1 Peripheral Bridge are not protected by the MPU but have their own protection included.

So the solution was to make the peripheral accessible from user mode by clearing the corresponding Supervisor Protect Bit in AIPSx_PACRn Register:

AIPS0->PACRN &= ~(AIPS_PACRN_SP2_MASK);	/* clear Supervisor Protect Bit */

The MPU setting for this region is not used.

Thanks for your help.

rtel wrote on Monday, April 03, 2017:

Thanks for taking the time to report the solution.