PSoC5 Support

joryn wrote on Tuesday, July 06, 2010:

Hello, I’ve managed to get my hands on a PSoC5 evaulation board and am trying to get a port of FreeRTOS up and running on it.  I’m working from the LPC17XX template for things like core_cm3.h and the like and have so far been able to compile a simple demo project with no warnings/errors from the PSoC creator compiler.

Unfortunately the OS itself does not run properly.  I believe that the problem lies in the task/context switching not being handled correctly on this core and I’m hoping that someone here might be able to offer some insight with regards to how I should best tackle this problem.  Specifically the micro keeps jumping to the default interrupt handler instead of to the SVC handler after the StartFirstTask function is called.  The problem is likely related to how I have the interrupts setup so any input on that would be great also.

The portable code for the CM3 core should work just as well on the PSoC5 so that is what I’m using right now.

Thanks in advance for the help.  I’m excited to get the demo code working on this device.
Best regards,
Joe McDonald

rtel wrote on Wednesday, July 07, 2010:

I have not attempted to run FreeRTOS on a PSoC5 myself yet (hope to soon), but if this is truly a Cortex-M3 device then there should (in theory) not be any issues in doing so as the Cortex-M3 port is identical for all Cortex-M3 implementations.  This is because the FreeRTOS port does not rely on anything outside of the Cortex-M3 core itself (the NVIC and SysTick being part of the licensed core).

How far have you got?  I would suggest a very simple project that does nothing but create a single task then start the scheduler.  Have the single task just toggle an LED using something like:

void vATask( void *pvParameters )
____for( ;; )
________set_led( 0 );
________vTaskDelay( 500 );
________clear_led( 0 );
________vTaskDelay( 500 );

You will need to set up the IO to set and clear the LEDs, and write the set_led() and clear_led() functions too. 

You will also need to set up the vector table so the PendSV, SVCall and SysTick interrupts call the appropriate FreeRTOS handlers.


joryn wrote on Wednesday, July 07, 2010:

We are thinking along the same lines as that is precisely what I have done so far…or at least attempted to do.  I have my main function initialize 2 tasks, one of which toggles an LED and the other toggles a different LED with a different task delay between them. 

I have also used the Cypress built in functions for adding the SVC, PendSV and systick handlers to the vector table and have confirmed that they appear to be in memory in the correct locations.

Everything compiles correctly and I am able to attempt to debug the code with the IDE.

Where it all goes wrong is between the vPortStartFirstTask and the first vPortSVCHandler call.  It seems that when the startfirsttask function runs the “svc #0” command instead of jumping to the SVChandler it jumps immediately to the default interrupt handler (from which it never returns).

I have been able to get it to jump to the SVCHandler if I add a global interrupt enable call at the beginning of the StartFirstTask assembly, and this does let me get into my first task as well.  But as soon as the vTaskDelay is called it never returns (again gets stuck in the default interrupt).

This might be something simple that I’m neglecting with regards to what the PSoC 5 supports that perhaps the LPC17xx doesn’t or vice versa (hoping so) but I’m definitely stuck for now.  Any input would be great.


davedoors wrote on Wednesday, July 07, 2010:

Is the vector table defined statically in flash, at compile time, or dynamically in RAM at run time? Is the vector table remapped or moved in any way while the application is executing, even during the C start up code? Which compiler is being used?

joryn wrote on Wednesday, July 07, 2010:

There is a default vector table defined compile time but that is not the one being used.  The NVIC offset register is pointing to the RAM based vector table which by stepping through the the code I’ve verified to be the one that is being used.  That table is initialized with the correct handlers for the aforementioned FreeRTOS handlers prior to scheduler being started. 

As for the compiler, PSoC Creator uses ARM CM3 GCC-4.2.1.

davedoors wrote on Wednesday, July 07, 2010:

Can you find the code where the vector table is swapped to RAM? Does it copy the flash table to RAM, or set up a new table?

The first position in the table tells FreeRTOS where the system stack is located. FreeRTOS uses the NVIC offset register to find this. I’m not sure what will happen if the table has moved. I presume use of the offset in this way will prevent anything going wrong, but its worth looking at. See the function vPortStartFirstTask() in FreeRTOS\Source\Portable\GCC\ARM_CM3\port.c.

I don’t think its worth looking at vPortSVCHandler() too closely yet as from what you have said so far it is a matter of having the wrong handler installed as the SVC handler, rather than the FreeRTOS SVC handler itself going wrong.

joryn wrote on Wednesday, July 07, 2010:

I may have found something of a problem with the way that the PSoC ide manages the RAM vector table.  Below is the code for the ROM version of the vector table.  Note that it properly sets up the initial stack pointer:

__attribute__ ((section(".romvectors")))
void (* RomVectors[])(void) =
    (void (*)(void))((unsigned long) &Image$$ARM_LIB_STACK$$ZI$$Limit), /* The initial stack pointer    */
    Reset,                                                              /* The reset handler          0 */
    IntDefaultHandler,                                                  /* The NMI handler            1 */
    IntDefaultHandler,                                                  /* The hard fault handler     2 */
    IntDefaultHandler,                                                  /* The MPU fault handler      3 */
    IntDefaultHandler,                                                  /* The bus fault handler      4 */
    IntDefaultHandler,                                                  /* The usage fault handler    5 */
    0,                                                                  /* Reserved                   6 */
    0,                                                                  /* Reserved                   7 */
    0,                                                                  /* Reserved                   8 */
    0,                                                                  /* Reserved                   9 */
    IntDefaultHandler,                                                  /* SVCall handler            10 */
    IntDefaultHandler,                                                  /* Debug monitor handler     11 */
    0,                                                                  /* Reserved                  12 *//
    IntDefaultHandler,                                                  /* The PendSV handler        13 */
    IntDefaultHandler,                                                  /* The SysTick handler       14 */
    IntDefaultHandler,                                                  /* External Interrupt(0)     15 */
    IntDefaultHandler,                                                  /* External Interrupt(1)     16 */
    IntDefaultHandler,                                                  /* External Interrupt(2)     17 */
    IntDefaultHandler,                                                  /* External Interrupt(3)     18 */
    IntDefaultHandler,                                                  /* External Interrupt(4)     19 */
    IntDefaultHandler,                                                  /* External Interrupt(5)     20 */
    IntDefaultHandler,                                                  /* External Interrupt(6)     21 */
    IntDefaultHandler,                                                  /* External Interrupt(7)     22 */
    IntDefaultHandler,                                                  /* External Interrupt(8)     23 */
    IntDefaultHandler,                                                  /* External Interrupt(9)     24 */
    IntDefaultHandler,                                                  /* External Interrupt(A)     25 */
    IntDefaultHandler,                                                  /* External Interrupt(B)     26 */
    IntDefaultHandler,                                                  /* External Interrupt(C)     27 */
    IntDefaultHandler,                                                  /* External Interrupt(D)     28 */
    IntDefaultHandler,                                                  /* External Interrupt(E)     29 */
    IntDefaultHandler,                                                  /* External Interrupt(F)     30 */
    IntDefaultHandler,                                                  /* External Interrupt(10)    31 */
    IntDefaultHandler,                                                  /* External Interrupt(11)    32 */
    IntDefaultHandler,                                                  /* External Interrupt(12)    33 */
    IntDefaultHandler,                                                  /* External Interrupt(13)    34 */
    IntDefaultHandler,                                                  /* External Interrupt(14)    35 */
    IntDefaultHandler,                                                  /* External Interrupt(15)    36 */
    IntDefaultHandler,                                                  /* External Interrupt(16)    37 */
    IntDefaultHandler,                                                  /* External Interrupt(17)    38 */
    IntDefaultHandler,                                                  /* External Interrupt(18)    39 */
    IntDefaultHandler,                                                  /* External Interrupt(19)    40 */
    IntDefaultHandler,                                                  /* External Interrupt(1A)    41 */
    IntDefaultHandler,                                                  /* External Interrupt(1B)    42 */
    IntDefaultHandler,                                                  /* External Interrupt(1C)    43 */
    IntDefaultHandler,                                                  /* External Interrupt(1D)    44 */
    IntDefaultHandler,                                                  /* External Interrupt(1E)    45 */
    IntDefaultHandler                                                   /* External Interrupt(1F)    46 */

And here is the code for how it sets up the RAM vector table…at least initially.  The FreeRTOS handlers are initialized later.  What I find most interesting is that they don’t re-specify the initial stack pointer. 

__attribute__ ((section(".ramvectors"))) cyisraddress CyRamVectors[NUM_INTERRUPTS];
void $Sub$$main(void)
    unsigned long index;
    /* Setup the M3. */
    /* Set Priority group 5. */
    /* Writes to the NVIC_APINT register require the VECTKEY in the upper half */
    /* Set Ram interrupt vectors to default functions. */
    for(index = 0; index < NUM_INTERRUPTS; index++)
        CyRamVectors[index] = IntDefaultHandler;
    /* Point NVIC at the RAM vector table. */
    *CYINT_VECTORS = (cyisraddress) CyRamVectors;

I’m thinking that it’s possible that the stack pointer is not being setup correctly by FreeRTOS and thus is causing the issues that I’m seeing here.  What do you guys think?

Thanks again,

davedoors wrote on Wednesday, July 07, 2010:

That is getting beyond my knowledge.  I cannot see how leaving the first vector off would let anything work, unless the offset to the vector table is later set to CyRamVectors. Can you ask on the Cypress technical support?

You could try modifying vPortStartFirstTask() to:

void vPortStartFirstTask( void )
	__asm volatile(
					" svc 0					\n" /* System call to start first task. */

but that is not a good solution even if it works because it would make the Cypress Cortex port different to any other and waste RAM space.

joryn wrote on Wednesday, July 07, 2010:

Well happy news.  I think I have the OS up and running on the PSoC5.  It turns out that the problem was indeed related to the way that the PSoC creator generated startup code was initializing the RAM based vector table.  The first two addresses are supposed to be the Initial stack pointer(as mentioned earlier) and the second is supposed to be the reset vector.  The PSoC default startup code wrote both of these to the default interrupt handler. 

By readdressing those two vectors runtime I was able to get the OS to stop runnnig to the default Int handler all the time.  Unfortunately the SVC handler still was not being called with the “svc #0” instruction.  It wasn’t until I added a line of

__asm volatile (  " cpsie i                   \n" );

to the vPortStartFirstTask code that everything started humming along.

This is the only thing that bugs me about my workaround; that I have to modify the port.c file to get it to work.  Any suggestions as to why global interrupts are not enabled by the time the svc 0 instruction isbeing called? 

Thanks again for all the help.  Definitely appreciated.

davedoors wrote on Wednesday, July 07, 2010:

The only thing I can think is that the start up code is globally disabling interrupts, which would not be normal. I would expect the FreeRTOS code to disable interrupts before the kernel was started too, but only up to the priority set by configMAX_SYSCALL_INTERRUPT_PRIORITY, and then only until the first task was started. Disabling up to configMAX_SYSCALL_INTERRUPT_PRIORITY should not prevent an SVC call from executing.

psocsensei wrote on Wednesday, July 07, 2010:

Joe -

I’m doubtful that the initial stack pointer or reset vector are an issue.  Neither of these locations should ever be used from the RAM vector table.

The initial stack pointer is only ever referenced from a reset condition to set the initial stack pointer.  After that it is never used.  The reset vector is only referenced from a reset.  If a reset happens, then the NVIC is reset to using the ROM vector table.  The space for these two locations needs to be present (and is) in order that the other vectors are in the correct place, but the values in these two locations shouldn’t be needed.

- Brad

psocsensei wrote on Wednesday, July 07, 2010:

The PSoC Creator generated startup code will turn off global interrupts as part of the startup process.  By the time that main() is hit the interrupts are disabled.  That was done with CyGlobalIntDisable during the configuration process.
        #define CYGlobalIntDisable          {__asm(“CPSID   i”);}

I’m not sure where the enabling of interrupts should be placed in your FreeRTOS port, but they will need to be enabled as you’ve found:
       #define CYGlobalIntEnable           {__asm(“CPSIE   i”);}

- Brad

joryn wrote on Wednesday, July 07, 2010:


I would tend to agree with you as the demo projects and everything definitely run without issue with no modifications to the RAM vector table, however, the way that FreeRTOS uses the vector table for the Cortex cores requires those first two addresses to be set correctly, or at least that seems to be the case.  the vPortStartFirstTask code definitely is looking for the initial stack pointer and if the NVIC offset is set to the RAM vector table it is going to look for that stack pointer in that location also. 

Perhaps there needs to be a change to the CM3 port code to only look for that information from the ROM table?  This certainly isn’t my area of expertise so feel free to correct me.

P.S. since you wrote again while I was replying :slight_smile: I agree that all I may need to do is call that globalintenable functions to allow everything to work properly.  I’ll try that in a moment.

I think this may be the reason that I’m having issues.  With regards to what I have changed compared to the LPC17XX version of the FreeRTOS project, the PSoC only has 3bits available for interrupt priorities so it is likely that this might require some changes that I have not yet implemented.  Currently I have this for my configMAX_SYSCALL_INTERRUPT_PRIORITY:

#define configMAX_SYSCALL_INTERRUPT_PRIORITY  ( 5 << (8 - configPRIO_BITS) )

With the LPC version that would have come out as 40 but with only 3 bits that comes out as 160.  Perhaps that should be changed also (or is it irrelevant)?

Thanks again.

rtel wrote on Wednesday, July 07, 2010:

The FreeRTOS code itself does not use global interrupt flags, but instead uses the basepri register to effectively set a mask level.  It would therefore seem most appropriate to re-enable global interrupts from the start up code as part of the individual application code.  This could be done just before main is called.  Alternatively  global interrupts could be re-enabled from anywhere in main(), or even the official FreeRTOS CM3 port code could be altered to ensure interrupts are globally enabled prior to SVC 0 being called to start the scheduler.  Although this latter option involves changing the port code it should make no difference to anything that has gone before as evidently this issue has not arisen in any other CM3 projects (at least its never been reported before).

With regard to the value stored in the first position in the RAM vector table.  While the CPU itself might not use that location the FreeRTOS code does, as prior to starting the scheduler the system stack is set back to its original (starting) value, and the value is obtained from the vector table.  This is done to ensure no RAM is wasted as some of the system stack will have been consumed by the C code used to start the scheduler.  The full code is shown below:

void vPortStartFirstTask( void )
	__asm volatile(
	" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
	" ldr r0, [r0] 			\n"
	" ldr r0, [r0] 			\n"
	" msr msp, r0		\n" /* Set the msp back to the start of the stack. */
	" svc 0				\n" /* System call to start first task. */


joryn wrote on Wednesday, July 07, 2010:

Well kudos to Brad as re-enabling global interrupts prior to starting the scheduler did indeed fix the requirement for having that call in the port.c file.  So I am now using a bone stock CM3 port.c file which is great news. 

For reference, this is pretty much all the initialization that I have to do to get the OS working properly. 

void SystemInit()
	CyIntSetSysVector(16 + SVCall_IRQn, (cyisraddress)vPortSVCHandler);
	CyIntSetSysVector(16 + SysTick_IRQn, (cyisraddress)xPortSysTickHandler);
	CyIntSetSysVector(16 + PendSV_IRQn, (cyisraddress)xPortPendSVHandler);
	/* Setup the initial stack pointer */
	CyRamVectors[0] = (cyisraddress)*(UInt32 *)(0x00000000);
	CyRamVectors[1] = (cyisraddress)*(UInt32 *)(0x00000004);

Would there be much interest in making the whole demo project available?  I’m not exactly sure how getting new ports available works but I’m definitely open to letting others play with the code.


psocsensei wrote on Wednesday, July 07, 2010:

With Richard’s explanation for FreeRTOS’s usage of the initial stack pointer, now I understand why that was important for this port.  I’ve not looked yet in any depth what the specific requirements are for porting this RTOS to PSoC 5.  I have ported other RTOSs to PSoC 5 and in those ports we did not set this initial stack pointer to any specific value, so I was confident that it was not being used by the CM3 directly.

Cypress has been talking with Richard and Wittenstein to get an official port for PSoC 5 done.  It would be great if you post your implementation for those that want to use FreeRTOS now could get a headstart.

- Brad Budlong - Cypress Semiconductor

rtel wrote on Wednesday, July 07, 2010:

Mop up a few replies:

the PSoC only has 3bits available for interrupt priorities so it is likely that this might require some changes that I have not yet implemented

I think the Luminary Micro/TI Stellaris parts likewise have 3 priority bits.  Presumably, this being a CM3, the three implemented bits at the most significant bits?  You will need to consider the values assigned to configKERNEL_INTERRUPT_PRIORITY (which is normally safe to set to 255, no matter how many bits are implemented) and configMAX_SYSCALL_INTERRUPT_PRIORITY (which you will want to ensure only relies on use of the top three bits).

Well kudos to Brad as re-enabling global interrupts prior to starting the scheduler did indeed fix the requirement for having that call in the port.c file.  So I am now using a bone stock CM3 port.c file which is great news.

Excellent.  That is after all one of the benefits of using the CM3.

Would there be much interest in making the whole demo project available?  I'm not exactly sure how getting new ports available works but I'm definitely open to letting others play with the code.

Absolutely.  I have created a forum for Cypress related code on the FreeRTOS Interactive site -  I would be very appreciative if you could upload it there, with a few words on how to use it (tools, hardware, etc.).  This is on the list to get added as an official port - but the list is very long!

Thanks and regards.

joryn wrote on Friday, July 09, 2010:

Thank you Richard for getting that Cypress section going.  I have just attached a very simple project there for others to peruse at their leisure.  Now comes the fun part of porting the driver stubs I need over and getting all the goodies working. 

Thanks again to everyone for their input and advice, especially so to Brad and Richard.  I still look forward to an official port sometime down the line, if only so that the code base is more similar to the other devices we have running the OS.  Keep up the good work, the PSoC5 looks like a great part.

Joe McDonald