ARM7 LPC2138 problem with portRESTORE_CONTEXT

kuppi wrote on Thursday, February 14, 2008:


at first i’ve to say that i’m new to FreeRTOS. I’m working with the WinIDEA application from iSystem and started using FreeRTOS by looking as it is handled by the ARM7_LPC2138_Rowley demo.
If i debug the complete SW i can see that the xTaskCreate for my only single task successfully completes, also i can see that the memory for the task structure is allocated and the task stack is also allocated. The task stack points even to valid memory.
In the moment i call vTaskStartScheduler it crashes, so i started debugging. First the IDLE task will be created, which works fine, too. Then xPortStartScheduler is called and initializes all registers to use timer0, this even works fine. I checked the values of the SFRs (Special Function Registers), so far so good.
Then i come to he point where the task(s) shall start and the function vPortISRStartFirstTask is called. The function calls the macro portRESTORE_CONTEXT and if this is called, my MCU gets reset.
I’m not very familiar with assembler programming, but i tried to find out why it crashes by debugging the assembler source. At the point where the instruction “ldmia  r14,{r0-r14}^” is called, all processor registers are initialized with pattern values. R0 = 00000000, R1 = 01010101, R2 = 02020202, R3 = 03030303, … R12 = 12121212. Then R13 contains a pointer into the memory and R14 = AAAAAAAA.
The register R14 set to AAAAAAAA is the problem, because the assembler instruction “ldr r14,[r14,003c]” tries to load the content pointed by R14 plus the 003c offset.

I don’t use the THUMB mode, just plain 32bit compilation. FreeRTOS used in version V4.7.1 and GCC in version 4.0.2.

If theres anybody here working with LPC2138 using FreeRTOS and has a minimal sample for me, or has helpful information regarding my problem, please help.

Thanks in advance,

jorick23 wrote on Thursday, February 14, 2008:

It appears you’re in user mode.  Make sure you’re in supervisor mode when you start the scheduler. 

The ^ at the end of the ldmia instruction tells the processor to load the user mode registers.  But the ldr r14 instruction expects you to be in supervisor mode because it’s the supervisor r14 that’s being used.

kuppi wrote on Friday, February 15, 2008:

Now at CPU start i set the different modes and initialize stack pointers. Then i set a breakpoint in main and verify that the CPSR register is correctly set to supervisor mode (0x13). In fact the portRESTORE_CONTEXT now really sets the PC (program counter) to the address of my only created task and if i set a breakpoint to the first assembler instruction of my task, the debugger stops there. So far it is as expected, but…
In the moment my task is called, i see that the mode in CPSR is set to 0x1F (Mode_SYS). Within the task (which is now been called) i use vTaskDelay and then my CPU runs into UNDEFINED INSTRUCTION INTERRUPT. :frowning:
I debugged the source tasks.c and found the point where it crashes, it’s the macro taskYIELD(). If i look into the assembler source, i see the instruction “swi 000000” and if this is executed the CPU runs into the exception.

kuppi wrote on Friday, February 15, 2008:

I’ve read about SWI (Software Interrupts) on a page <> i found by google and now i’ve to correct my last comment. It is not an UNDEFINED INSTRUCTION INTERRUPT, instead it is a wanted SOFTWARE INTERRUPT and the CPU does what it should do. The “swi 000000” instructions sets the PC (program counter) to address 0x8 and continues the work. If i understand correctly, then there should be an address (branch location) at position 8 that schedules the tasks.

Which address (or better which function) will be called if a SWI is executed on your side?

rtel wrote on Friday, February 15, 2008:

You need to install the SWI handler in the SWI vector.  Take a look at Demo\ARM7_LPC2106_GCC\boot.s for an example (search for vPortYieldProcessor in the file).  All the other GCC demos have similar start up asm files.


kuppi wrote on Friday, February 15, 2008:

Yepp, i found the definition for swi_handler within the FreeRTOSConfig.h and added it to the vector table. My only (stupid dummy) task is created and will be called, verified by setting breakpoint. If taskYIELD() now is called (due to the vTaskDelay in my task), the software interrupt is called and vPortYieldProcessor is called. If i debug through the many routines (especially the switchContext) and have a look on the variable pxCurrentTCB, then i see that the values from my own created task change to the idle task. This is also what i would expect, because my own task called vTaskDelay.
After this i come into the function
  static portTASK_FUNCTION( prvIdleTask, pvParameters )
While debugging in this function i can see that the line
  if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( unsigned portBASE_TYPE ) 1 )
is never true and so never taskYIELD() is called.
The more problem is, the breakpoint on taskYIELD() will never be reached. Stepping many times through this by hand is no problem, but setting a breakpoint at taskYIELD() and let the CPU run always crashes.
Breakpoint is never reached and the CPU restarts.

Do you have any further ideas what i can look for to solve this.

Thanks in advance,

rtel wrote on Friday, February 15, 2008:

You have shown that the manual context switch (yield) works, now it sounds like your preemptive switch is not working.

The idle task will not necessarily every yield, but should get preempted by your application task when its block period expires.

Have you verified that the tick interrupt is working?  How do you handle the tick interrupt?  There are two methods demonstrated by various demos - the first vectors directly to the interrupt and the interrupt performs the context save and restore, the second has a single save and restore point in a common interrupt entry point before calling the handler itself.


kuppi wrote on Monday, February 18, 2008:

Aaaargh, you got it Richard. The interrupt registers for Timer0 are loaded in function prvSetupTimerInterrupt, but the interrupt isn’t enabled. Even i can’t find the position (source) where the VIC is reset like VICVectAddr = 0x00000000.
The interrupt function vTickISR is never been called. What am I doing wrong? In my little starting project i just calling xTaskCreate and vTaskStartScheduler, anything more to do?
If i have to enable interrupts, or reset VIC, or whatever, please let me also know at which position i have to add it.

Many thanks for your help,

kuppi wrote on Monday, February 18, 2008:

Additional infos to my problem and my comment before from 2008-02-18 13:23. If i stop execution with breakpoint within my task and have a look on the SFR (Special Function Registers), then i see that TIMER0 bit in the VIC IRQ Status register is set. Within the VIC Raw Interrupt Status register the bits for RTC, PLL, TIMER0 and ARMCore1 are set. The VIC Interrupt Enable register has only bit TIMER0 set. The VIC Vector Address and the VIC Vector Address Register 0 are both pointing to address of vTickISR. T0IR (Timer 0 Interrupt) register has bit MR0 (Interrupt Flag for Match Ch.) set and also i can see that timer0 is counting, because of the T0TC (Timer 0 Timer Counter) register.
It seems that all is working, but the vTickISR routine is never called.


kuppi wrote on Monday, February 18, 2008:

And once again me (i’m sorry). Here are now additional information which should clarify my problem. I intialized the address table beginning from 0 as follows:
.global _start
  B    _start                /* RESET INTERRUPT */
  B    _start                /* UNDEFINED INSTRUCTION INTERRUPT */
  B    swi_handler           /* SOFTWARE INTERRUPT */
  B    _start                /* ABORT (PREFETCH) INTERRUPT */
  B    _start                /* ABORT (DATA) INTERRUPT */
  B    _start                /* RESERVED */
  B    irq_handler           /* IRQ INTERRUPT */
  B    _start              /* FIQ INTERRUPT */
After reset the PC is set to 0 and calls _start, which is the default startup assembler routine. If now taskYIELD() is called, the PC is set to 8 and the swi_handler is called. After calling xTaskCreate and vTaskStartScheduler i can see that PC is set to 0x18 and irq_handler is called, but this my default IRQ handler and i would have expected that the vectored interrupt for vTickISR will be called, too.
So it seems that my interrupt (VIC) isn’t setup correctly.
I have only the datasheet for LPC2138 MCU from NXP website and the documentation for programming is very bad (nearly nothing), especially for the VIC.

Thanks for your help,

kuppi wrote on Wednesday, February 20, 2008:

I downloaded on NXP website the additional documentation "LPC213x User Manual" and read about the interrupt processing. Now i adapted the interrupt handler to handle vectored interrupts and the vTickISR is called, but this happens only once. Additionally i read the section within the manual about timer and interrupt handling, i found that the clear condition is setting the corresponding bit to zero.

The following instruction within the vTickISR interrupt service routine
    /* Ready for the next interrupt. */
is not correct, from my point of view.
After modifying the portISR.c file at the given position to the following
    /* Ready for the next interrupt. */
    T0_IR &= ~portTIMER_MATCH_ISR_BIT;
everything works fine.

Now i have FreeRTOS running on my ARM7TDMI (LPC2138) board.

Many thanks for your support,