FreeRTOS Bootloader and external RAM

racemouse wrote on Wednesday, August 08, 2007:


I have the following setup :
AT91SAM7SE32 with a serial DataFlash and SDRAM on the external bus interface.
The startup is as follows:

1 - A BootStrapper written en FreeRTOS in internal Flash/Ram starts up and transfers an image from the serial dataflash to the external RAM. Then it sets the PC to address 0x20000000 which is the start address of the external RAM.

2 - The image loaded in external RAM is a FreeRTOS based BootLoader which starts with the normal boot.s to setup the new stacks etc. It has a file system and connectivity (USART, USB, Ethernet…). When this BootLoader exits it loads the Application Image from the serial flash into another place in external RAM. Right now it just blinks an LED on the evaluation kit.

3 - The application starts and runs from external RAM.

So far I have managed to stop the FreeRTOS like this :
vTaskEndScheduler ();
// Stop the PIT
// Load address 0x20000000 into the PC
asm volatile ( "LDR      PC, =0x20000000");

The BootLoader now starts and runs from RAM BUT when the BootLoader calls taskENTER_CRITICAL()/taskEXIT_CRITICAL() the blinking LEDs now blinks erratically.

Do I need to do something extra before the jump to 0x2000000 in order to make sure that the OS is reset to a known state ?

Best Regards

racemouse wrote on Wednesday, August 08, 2007:

I see in the PC/DOS examples that the processor state is restored to what it was before the OS started. If that is what I’m missing I’m very open to suggestions as to how to do that.


davedoors wrote on Wednesday, August 08, 2007:

I don’t think restoring the processor state should be necessary.  This is done for returning to DOS in the PC port.

So if I understand your application correctly, the bootloader runs FreeRTOS.  Therefore when the bootloader executes the C start up code will execute, setting all your static variables to 0, and setting ulCriticalNesting to a negative value.  Next you will create your tasks, during which time interrupts will most probably be disabled.  Then start the scheduler which will configure the timer to start generating tick interrupts and some other peripherals are probably generating interrupts too.

Next you disable the PIT so no more ticks occur, and switch to your application which is now in RAM.  This re-runs the start up code so your variables should get set back to their initial states, creates tasks, then reconfigures the PIT.

This would seem ok, but some things to check.

I think from your code you have only disabled the PIT.  So other peripherals interrupts could remain enabled along with global interrupts.  If such an interrupt occurred while the FreeRTOS was being initialized either in the startup code or in the FreeRTOS code and the interrupt used an API call then you could get a problem.  Try calling portENTER_CRITICAL() a few times before jumping to 0x20000000.  This should ensure interrupts do not occur until FreeRTOS starts again.

Also, check that when you are running the start up code from 0x20000000 that the variables are initialised correctly.  After running the start up code you should see that ulCriticalNesting is set to 9999 (in port.c).  This will check that the linker is setup correctly for executing this code from RAM.  The tick could should likewise get set to 0.

If you use your debugger to load the application image to 0x20000000 directly and start running it via the debugger rather than bootloader, does the application behave as expected?

racemouse wrote on Wednesday, August 08, 2007:

First : Thanks for the effort. It is really appreciated !

If I call portENTER_CRITICAL() just before ‘asm volatile ( “LDR      PC, =0x20000000”);’, The result is the same. It still seems as if a timer tick occurs sporadically…

Using the debugger with a breakpoint at ‘void main ()’ I can veryfy that ulCriticalNesting is ‘9999’. As you said, that indicates that the linker sets it up correctly.

The software work as expected when loaded via SAM-BA. (It has to linked differently in order to put it into internal flash and RAM).

Any other thoughts ?


sotd wrote on Wednesday, August 08, 2007:

This is just a wild guess. 

I assume in your code you setup a PLL to generate the clock frequency you want. 

When the bootload runs the clock will not yet have been configured so you configure it from ‘clean’.  When you app runs the clock will already have been configured so you are reconfiguring it but not from its start condition. 

Maybe you have to unconfigure the clock before setting it up again?  Stop the PLL before starting the PLL again?  Maybe there is some guard in the processor that prevents you from unlocking the PLL once it is locked?

If the linker is getting all your startup code to initialize the variables correctly then I’m thinking it must be something to do with a register on the processor that is not in its initial condition when your app runs?  Maybe I am just writing what is obvious here?

racemouse wrote on Wednesday, August 08, 2007:

Hi soth,

Thanks for your input.

Yes it’s true :
The BootStrapper : boot.s -> AT91F_LowLevelInit() -> main (start OS) -> upload BootLoader to external RAM -> stop OS -> jump to 0x20000000

The BootLoader (@ 0x20000000) : boot.s -> main (start OS) -> (right now just blinking LEDs)

This makes no difference :
The BootLoader (@ 0x20000000) : boot.s -> AT91F_LowLevelInit() -> main (start OS)
since AT91F_LowLevelInit() only sets up the PLL…

I agree with you that it must be som register not having the correct initial value but which one ?

Any hints ?


racemouse wrote on Thursday, August 09, 2007:

Hi again,

I have now tried manually reset all the periphials registers to the defaults before the jump to address 0x20000000, but still no go. More good ideas are more than welcome :slight_smile:


neilbradley wrote on Thursday, August 09, 2007:

Might be obvious things to try, but have you tried:

1) Disabling interrupts
2) Ensuring that whatever is running out of RAM initializes the interrupt controller properly
3) Doesn’t attempt to reprogram the PLLs

Those things can really mess with you if you’re not careful.

It’s possible that there is some other peripheral hogging the interrupt (for whatever reason) that may get serviced and block your timer interrupts. What is your timer’s interval set to currently?

racemouse wrote on Thursday, August 09, 2007:

Hi again,

1) The interrupts are all disabled before I jump to 0x20000000. This is how I do it:
void vStop (void)
  unsigned char ucCount;
  // Reset Controller Periphials one by one
  // RTT
  // Not used
  // Stop the PIT
  // WDT
  // Can only be written once
  // VREG
  // Not used
  // MC
  // Memory Controller. Configured to SDRAM. Do not touch
  // EBI
  // SDRAM sits here. Do not touch
  // PDC
  // Not used
  // AIC
  for (ucCount = 0; ucCount < 32; ucCount++)
    AT91C_BASE_AIC->AIC_SMR[ucCount] = 0x00000000;
  for (ucCount = 0; ucCount < 32; ucCount++)
    AT91C_BASE_AIC->AIC_SVR[ucCount] = 0x00000000;
  AT91C_BASE_AIC->AIC_SPU = 0x00000000;
  AT91C_BASE_AIC->AIC_DCR = 0x00000000;

  // PMC
  // Setup via AT91F_LowLevelInit() and not touched again
  // DBGU
  // Not used
  // PIO
  // Only interrupts are disabled here
  // SPI
  AT91C_BASE_SPI->SPI_CR = 0x00000082;
  AT91C_BASE_SPI->SPI_MR = 0x00000000;
  // TWI
  // Not used
  // USART
  AT91C_BASE_US0->US_CR = 0x0000E1AC;
  AT91C_BASE_US1->US_CR = 0x0000E1AC;

  // SSC
  AT91C_BASE_SSC->SSC_CR = 0x00008000;
  // TC
  AT91C_BASE_TCB->TCB_BMR = 0x00000015;
  AT91C_BASE_TCB->TCB_BCR = 0x00000002;
  // PWM
  AT91C_BASE_PWMC->PWMC_MR = 0x00000000;
  // UDP
  AT91C_BASE_UDP->UDP_GLBSTATE = 0x00000000;
  AT91C_BASE_UDP->UDP_FADDR = 0x00000100;
  AT91C_BASE_UDP->UDP_RSTEP = 0x000000FF;
  AT91C_BASE_UDP->UDP_CSR[0] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[1] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[2] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[3] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[4] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[5] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[6] = 0x00000000;
  AT91C_BASE_UDP->UDP_CSR[7] = 0x00000000;
  // ADC
  AT91C_BASE_ADC->ADC_CR = 0x00000001;
  AT91C_BASE_ADC->ADC_CHDR = 0x000000FF;
  // Bask to superviser mode
  asm volatile ( "msr cpsr_c,#0xD3");
  // Load address 0x20000000 into the PC
  asm volatile ( "LDR      PC, =0x20000000");

2: The same code initializes correctly when running fra Flash/internal RAM
3: The PLL’s are left untouched.

Further info:
When only one task is running (blinking led) which looks like this :
  for (;:wink:
    vTaskDelay (50 / portTICK_RATE_MS);
    AT91F_PIO_ClearOutput( AT91C_BASE_PIOA, AT91C_PIO_PA1 ) ;
    vTaskDelay (100 / portTICK_RATE_MS);
    AT91F_PIO_SetOutput( AT91C_BASE_PIOA, AT91C_PIO_PA1 ) ;

I have checked on a scope and the timings are correct.

Introducing an other task that just blinks another LED completely screws up the timing. One of the LEDs starts to blink really fast. As if the task gets preempted all the time… Really hard to explain without pictures…

More ideas ?

racemouse wrote on Thursday, August 09, 2007:

Hi again,

As fair as I can decipher from its odd behavior I believe that it goes wrong when switching context. Could it be that something goes wrong when initializing the stack the second time. I mean : The first time the tasks stacks are initialized it happends in internal RAM. The next time it happends in external RAM… Am I completely off here or … ?


davedoors wrote on Thursday, August 09, 2007:

I’m just making some guesses here.

If it works in internal RAM but not external RAM could there be a problem with the external bus interface configuration or timing?

Also, the code you gave showed the tasks writing to a port directly to set and clear an LED.  Could there be some mutual exclusion problem there?

You say that the issue occurs when the first context switch happens.  But when you have a single task it will still be switching between your task and the idle task.  And this seems to work well you say.

racemouse wrote on Thursday, August 09, 2007:

Well, hmmm… It’s true that the context switch still happends between the task and idle. Didn’t think of that… My hair is getting thin…
I suppose that the timings on the external bus interface are OK. They have been taken from some sample code from IAR to this board (SAM7SE512-EK) and when using internal FLASH and external RAM there are no problems.

I’m fiddling around (more like frobnicating, actually) with the Re-map command. Could it be a re-map issue that is pulling my leg ?


racemouse wrote on Friday, August 10, 2007:

Ok. This was the theory :

BootStrapper sets up the SWI interrupt vector to swi_handler in internal RAM. When the BootLoader in external ram interrupts it does not vector to the swi_handler in external RAM but to the swi_handler in internal RAM. I therefore needed to setup the correct interrupt vectors. I did it like this:

void main( void )
  extern void swi_handler(void);

  // Copy contents from Flash address 0x00 to 0x20 to RAM 0x00 to 0x20
  memcpy ((int *)0x00200000, (int *)0x00100000, 128);
  // Load the address of interrupt handlers into the correct places
  dest_address = (int*)0x00200020;
  *dest_address = (int*)0x20000034;
  dest_address = (int*)0x00200024;
  *dest_address = &swi_handler;
  dest_address = (int*)0x00200028;
  *dest_address = &Kernel_reboot;
  dest_address = (int*)0x0020002C;
  *dest_address = &Kernel_reboot;
  dest_address = (int*)0x00200030;
  *dest_address = (int*)0x2000004C;

  // Remap so the RAM starts at address 0x00000000
  vMainInit ();
  vTaskStartScheduler ();

And now it works as it should :slight_smile:

Thanks for the help to those who helped and I hope someone else can use this.