SAM4S bootloader not running application - arm

I am trying to develop a custom bootloader for the Atmel SAM4S, but am not meeting with much success.
I have seen a few other posts on forums around this issue, but having tried solutions from each post I have found, it's time for my own post.
So far my bootloader receives data over UART, and writes it into flash, starting at address 0x00410000, however it fails to launch my program as expected.
Written based on numerous forum posts and also the Atmel bootloader example, this is my jumpToApp function:
void jumpToApp(void)
{
uint32_t loop;
// // Disable IRQ
Disable_global_interrupt();
__disable_irq();
// Disable system timer
SysTick->CTRL = 0;
// //Disable IRQs
for (loop = 0; loop < 8; loop++)
{
NVIC->ICER[loop] = 0xFFFFFFFF;
}
// Clear pending IRQs
for (loop = 0; loop < 8; loop++)
{
NVIC->ICPR[loop] = 0xFFFFFFFF;
}
// -- Modify vector table location
// Barriers
__DSB();
__ISB();
// Change the vector table
SCB->VTOR = ((uint32_t)FLASH_APP_START_ADDR & SCB_VTOR_TBLOFF_Msk);
// Barriers
__DSB();
__ISB();
// -- Enable interrupts
__enable_irq();
// -- Execute application
//------------------------------------------
__asm volatile("movw r1, #0x4100 \n"
"mov.w r1, r1, lsl #8 \n"
"ldr r0, [r1, #4] \n"
"ldr sp, [r1] \n"
"blx r0"
);
}
I have also tried this C based approach:
uint32_t v=FLASH_APP_START_ADDR;
asm volatile ("ldr sp,[%0,#0]": "=r" (v) : "0" (v));
typedef int(*fn_prt_t)(void);
fn_prt_t main_prog;
main_prog = (fn_prt_t)(appStartAddress);
main_prog();
and this alternative:
__DSB();
__ISB();
__set_MSP(*(uint32_t *) FLASH_APP_START_ADDR);
/* Rebase the vector table base address */
SCB->VTOR = ((uint32_t) FLASH_APP_START_ADDR & SCB_VTOR_TBLOFF_Msk);
__DSB();
__ISB();
__enable_irq();
/* Jump to application Reset Handler in the application */
asm("bx %0"::"r"(appStartAddress));
All with the same results, which makes sense as they all do pretty much the same thing.
Debugging the flash sector, I can see that the initial stack pointer in the new vector table is 0x20003b50, and the reset vector address is 0x004104d5, which both seem reasonable.
Stepping through, when execution is meant to jump to my application, I can see that the Program Counter is sat at 0x004104D0, which is close to, but not the reset vector address it should be.
The Stack Pointer also appears to be slightly off, reading 0x20003B28 instead of 0x20003b50.
Dissassembly shows exectution sitting at :
004104D0 b #-4
and never moving away.
Given that I have tried so many variations on jumping to application in flash that seem to have worked for others, I am beginning to think its my flash data at fault.
It is taken directly from the Intel Hex file generated by Atmel Studio 7.0, compiled with the linker offset flag:
-Wl,--section-start=.text=0x00410000
I am new to bootloaders, and have reached the limits of my understanding of low level cpu execution to provide more observation than the above, so any help or observations would be greatly appreciated!

Related

What is the reason of this data abort in ZYNQ 7000 (armA9)?

I'm using ZYNQ 7000 SoC which has 2 arm A9 cores, core0 and core1. Sometimes a data abort happens in my core1 code (bare metal). At the default data abort handler Xil_DataAbortHandler, it says the FaultStatus is 0x1e, and the DataAbortAddr is 0x2001bc9c.
I use readelf -s a.elf to get the symbol table, and find no function address match the exact 0x2001bc9c. The closest function is Xil_L2CacheDisable at address 2001bc64. Does this mean that the data abort is from Xil_L2CacheDisable? This function is a bsp library function provided by Xilinx. I'm using it for direct access to the shared memory for the 2 cores.
The global variable u32 DataAbortAddr is captured by the following assembly:
DataAbortHandler: /* Data Abort handler */
#ifdef CONFIG_ARM_ERRATA_775420
dsb
#endif
stmdb sp!,{r0-r3,r12,lr} /* state save from compiled code */
ldr r0, =DataAbortAddr
sub r1, lr, #8
str r1, [r0] /* Stores instruction causing data abort */
bl DataAbortInterrupt /*DataAbortInterrupt :call C function here */
ldmia sp!,{r0-r3,r12,lr} /* state restore from compiled code */
subs pc, lr, #8 /* points to the instruction that caused the Data Abort exception */
The FaultStatus is captured in DataAbortInterrupt
#define mfcp(rn) ({u32 rval = 0U; \
__asm__ __volatile__(\
"mrc " rn "\n"\
: "=r" (rval)\
);\
rval;\
})
#endif
#define XREG_CP15_DATA_FAULT_STATUS "cp15:0:c5:c0:0"
u32 FaultStatus = mfcp(XREG_CP15_DATA_FAULT_STATUS);
ZYNQ 7000 SoC is based on ARMv7, all retrieved from ARMv7 reference manual.
"cp15:0:c5:c0:0" is reading DFSR, Data Fault Status Register.
Value 0x1E is 'Fault Status' bits in DFSR register (DFSR Link)
And in this case means "Synchronous parity error on translation table walk, Second level" (if you using short-descriptor translation table format) (FSR encodings). Which is likely ECC throwing exception due to bad memory chip.
It's not clear how DataAbortAddr is acquired. But I'm quite confident that's data memory address, not instruction address. Or by other words exception occurs when some instruction is reading data word at address 0x2001bc9c.
In order to get instruction address you would need backtrace PC/LR registers to the point where exception happens.
Also I'd recommend to use objdump instead of readelf tool to search for instructions.

ARM v6 IRQ context switch

I'm trying to write a context switch in a timer interrupt handler. Currently, the context switch is able to switch between contexts on command (cooperative). In the interrupt handler, I was trying to:
Save the current program counter as the place the old thread needs to keep executing
Switch into SVC mode to actually perform the context switch
Switch back into IRQ mode and change the link register to be the saved PC from the new thread
Return from the IRQ handler to the IRQ link register
I believe I can do the first two properly, but I was wondering: how can I switch back into interrupt mode, or at least modify the SVC R13 and R15 from the interrupt handler?
I'm using an ARM v6 processor; thanks so much for the help!
Edit: here's basically what my switch is:
void interrupt_yield() {
unsigned int old_mode;
__asm__("mrs %0, cpsr" : "=r" (old_mode));
__asm__("msr cpsr_c, %0" : : "r" (MODE_SVC));
PUSH_ALL; // Macro for push {r0-r12, lr}
__asm__("mov %0, sp" : "=r"(sp));
manager->threads[manager->current_thread].sp = sp;
unsigned nt = (manager->current_thread + 1) % manager->thread_counter;
if (CURRENT_THREAD.status == ACTIVE) {
CURRENT_THREAD.status = INACTIVE;
}
manager->current_thread = nt;
CURRENT_THREAD.status = ACTIVE;
SET_SP(CURRENT_THREAD.sp);
POP_ALL;
__asm__("msr cpsr, %0" : : "r" (old_mode));
}
void timer_vector() { // This is called by assembly in interrupt mode
armtimer_clear_interrupt(); // clear timer interrupt
interrupt_yield(); // Calls above function
}
The goal is to change the IRQ link register to return to the new function. I can't seem to switch back into interrupt mode, however, to do this.
1 more edit: I never actually switch the IRQ link register; I realize this but am not even switching back into IRQ mode so this is a later problem to fix.
For the ARMv6 you need to change modes to get the banked registers. Your sample code already has many of the necessary details.
#define MODE_IRQ 0x12
#define MODE_SVC 0x13
unsigned int mode; /* original mode */
/* target data... */
unsigned int lr_irq;
unsigned int sp_irq;
unsigned int spsr;
asm (" mrs %0, cpsr\n" /* Save mode. */
" msr cpsr_c,%4 \n" /* to irq mode */
" mov %1, lr\n" /* Get lr_irq */
" mov %2, sp\n" /* Get sp_irq */
" mrs %3, spsr\n" /* Get spsr_irq */
" msr cpsr, %0\n" /* back to old mode */
: "=&r" (mode), "=r"(lr_irq),
"=r"(sp_irq), "=r"(spsr)
: "I" (MODE_IRQ));
gcc will allocate the lr_irq etc to general registers (non-banked) and you can transfer the data across modes. The ARMv7 with virtualization extensions has an instruction to avoid this switch.
You should be aware that the timer interrupt could occur in many contexts. It is probably prudent to at least check the spsr from the IRQ mode and have some debug (assert like) that verifies it is user mode. If this never triggers and you think an IRQ can only happen in user mode then the 'debug' can be removed.
Another method is to do this in the assembler of the IRQ handler and pass them to the interrupt_yield() routine in r0-r2 for instance. The ARM EABI puts parameters in r0-r2 so interrupt yield needs parameters. Once you have this data there should be no need to return to the IRQ mode. I highly recommend this method for production code. The above is good for prototyping.
Related: Explicitly accessing banked registers on ARM

Bootloader for Cortex M4 - Jump to loaded Application

I am using a Atmel SAM4E-16e on Atmel SAM4E-EK Board. I have written a bootloader for this configuration.
The bootloader receives the .bin-File via UART and writes it into Flash. This works without problems, i made a hex-dump and it was exactly what i expected:
Bootloader at 0x400000 (Flash Start Address of AT SAM4E)
My Application at 0x420000
0x800000 is Flash End Address
This is the C-Code:
int main(void){
// Init and downloading the .bin to Flash
binary_exc((void*) 0x420000);
}
int binary_exec(void * vStart){
int i;
// -- Check parameters
// Should be at least 32 words aligned
if ((uint32_t)vStart & 0x7F)
return 1;
Disable_global_interrupt();
// Disable IRQs
for (i = 0; i < 8; i ++) NVIC->ICER[i] = 0xFFFFFFFF;
// Clear pending IRQs
for (i = 0; i < 8; i ++) NVIC->ICPR[i] = 0xFFFFFFFF;
// -- Modify vector table location
// Barriars
__DSB();
__ISB();
// Change the vector table
SCB->VTOR = ((uint32_t)vStart & SCB_VTOR_TBLOFF_Msk);
// Barriars
__DSB();
__ISB();
Enable_global_interrupt();
// -- Load Stack & PC
_binExec(vStart);
return 0;
}
void _binExec (void * l_code_addr){
__asm__ ("mov r1, r0 \n"
"ldr r0, [r1, #4] \n" //I also tryed #5 but that doesn't work, too
"ldr sp, [r1] \n"
"blx r0"
);
}
But when i try to jump to my application, the Application does not start.
The code for jumping to the program is out of an example of Atmel for the SAM8X (Cortex M3). The debugger says sometimes that it the PC jumps to another Address (0x004003E2) instead, but does not go on.
I found the old topic Bootloader for Cortex M3 where the solution was to just add one but this doesn't work for me, even if i used their code. Then the debugger does not responds any more.
I am using Atmel Studio 7 with GCC. The processor runs in Thumb-Mode.
I hope you can help me to solve this problem or give me some tipps what is going wrong here.
This code assumes that program loaded at address 0x420000 starts with a vector table:
SP at offset 0 (0x420000)
Reset address at offset 4 (0x420004).
For this, the code seems perfectly correct.
But are you sure that this vector table is correct ? Is bit 0 of data at 0x420004 set as this is Thumb code? When you compile this code, is it aware that it will run from this address (For any absolute address it might use). Do you have the possibility to use a debugger to understand when the first fault occurs?
I think you should provide the disass of the first instructions of the program you try to load at this address.
I have solved the problem now.
I still use the code I posted in my question. The problem was that the .bin-file i write on my processor's flash at 0x420000 was compiled in a way that it thought it is at flash start address (0x400000).
When it has loaded the reset vector's address it was at 0x400xyz instead of 0x420xyz so the application jumped to the wrong address.
The solution was to Change the Flash start address to 0x420000 in the project I want to upload via bootloader.

IAR's new EWAVR32 (4.20). Compiler more strict with inline assembly

I'm using the AVR32 AT32UC3C0512C Microcontroller and ASF 3.11 Framework. I'm having some issues with the IAR compiler after updating IAR Workbench from 4.10 to 4.20 new version. I have found in IAR's technical support notes that some changes have been made related to inline assembly. (Acuallly not for EWAVR32 but EWAVR):
Error [Og005] + [Og006] when using inline assembler: Labels must be referred in the same assembler statement as they are declared. The behavior was not correct in earlier versions of the compiler platform. The new release uses a new internal compiler platform which is a bit more strict."
I'm having the same issue but the code, which is not able to compile belongs to the FreeRTOS port. I assume that the compiler does not recognize the label LABEL_INT_SKIP_RESTORE_CONTEXT_ because it is not defined in the same asm statement. Here is the code:
#define portRESTORE_CONTEXT_OS_INT() { extern volatile unsigned portLONG ulCriticalNesting;
extern volatile void *volatile pxCurrentTCB;
/* Check if AVR32_INTC_INT0 or higher were being handled (case where the OS tick interrupted another */
/* interrupt handler (which was of a higher priority level but decided to lower its priority */
/* level and allow other lower interrupt level to occur). */
/* In this case we don't want to do a task switch because we don't know what the stack */
/* currently looks like (we don't know what the interrupted interrupt handler was doing). */
/* Saving SP in pxCurrentTCB and then later restoring it (thinking restoring the task) */
/* will just be restoring the interrupt handler, no way!!! */
__asm__ __volatile__ (
"ld.w r0, sp[9*4]\n\t" /* Read SR in stack */
"bfextu r0, r0, 22, 3\n\t" /* Extract the mode bits to R0. */
"cp.w r0, 1\n\t" /* Compare the mode bits with supervisor mode(b'001) */
"brhi LABEL_INT_SKIP_RESTORE_CONTEXT_"ASTRINGZ(__LINE__) );
/* Else */
/* because it is here safe, always call vTaskSwitchContext() since an OS tick occurred. */
/* A critical section has to be used here because vTaskSwitchContext handles FreeRTOS linked lists. */
portENTER_CRITICAL();
vTaskSwitchContext();
portEXIT_CRITICAL();
/* Restore all registers */
__asm__ __volatile__ (
/* Set SP to point to new stack */
"mov r8, LWRD("ASTRINGZ(pxCurrentTCB)")\n\t"
"orh r8, HWRD("ASTRINGZ(pxCurrentTCB)")\n\t"
"ld.w r0, r8[0]\n\t"
"ld.w sp, r0[0]\n"
"LABEL_INT_SKIP_RESTORE_CONTEXT_"ASTRINGZ(__LINE__)":\n\t"
/* Restore ulCriticalNesting variable */
"ld.w r0, sp++\n\t"
"mov r8, LWRD("ASTRINGZ(ulCriticalNesting)")\n\t"
"orh r8, HWRD("ASTRINGZ(ulCriticalNesting)")\n\t"
"st.w r8[0], r0\n\t"
/* Restore R0..R7 */
"ldm sp++, r0-r7\n\t"
/* Now, the stack should be R8..R12, LR, PC and SR */
"rete"
);
/* Force import of global symbols from assembly */
ulCriticalNesting;
pxCurrentTCB; }
#endif
I've been thinking to try to switch the context inside the asm statement (Call c function using inline assembly) but I'm not sure if this is the best option and if it really would work. So, it would be great to get some advice here, how to restore the context in another way and avoid the compiling error. Thank you!
In case you need it, you can easily find this code in the ASF as a FreeRTOS example (...asf-3.11.0\common\services\usb\class\msc\device\example_freertos\at32uc3c0512c_uc3c_ek\iar\example_freertos.eww)
Well, as I already said in my question I just tried to switch the context inside the asm statement (Call c function with an inline assembler instruction). So, using "RCALL" (Relative call to subroutine):
"RCALL vPortEnterCritical\n\t"
"RCALL vTaskSwitchContext\n\t"
"RCALL vPortExitCritical\n\t"
the hole code would look like this:
#define portRESTORE_CONTEXT_OS_INT()
{
extern volatile unsigned portLONG ulCriticalNesting;
extern volatile void *volatile pxCurrentTCB;
/* Check if AVR32_INTC_INT0 or higher were being handled (case where the OS tick interrupted another */
/* interrupt handler (which was of a higher priority level but decided to lower its priority */
/* level and allow other lower interrupt level to occur). */
/* In this case we don't want to do a task switch because we don't know what the stack */
/* currently looks like (we don't know what the interrupted interrupt handler was doing). */
/* Saving SP in pxCurrentTCB and then later restoring it (thinking restoring the task) */
/* will just be restoring the interrupt handler, no way!!! */
__asm__ __volatile__ (
"ld.w r0, sp[9*4]\n\t" /* Read SR in stack */
"bfextu r0, r0, 22, 3\n\t" /* Extract the mode bits to R0. */
"cp.w r0, 1\n\t" /* Compare the mode bits with supervisor mode(b'001) */
"brhi LABEL_INT_SKIP_RESTORE_CONTEXT_"ASTRINGZ(__LINE__)"\n\t"
/* Else */
/* because it is here safe, always call vTaskSwitchContext() since an OS tick occurred. */
/* A critical section has to be used here because vTaskSwitchContext handles FreeRTOS linked lists. */
"RCALL vPortEnterCritical\n\t"
"RCALL vTaskSwitchContext\n\t"
"RCALL vPortExitCritical\n\t"
/* Restore all registers */
/* Set SP to point to new stack */
"mov r8, LWRD("ASTRINGZ(pxCurrentTCB)")\n\t"
"orh r8, HWRD("ASTRINGZ(pxCurrentTCB)")\n\t"
"ld.w r0, r8[0]\n\t"
"ld.w sp, r0[0]\n"
"LABEL_INT_SKIP_RESTORE_CONTEXT_"ASTRINGZ(__LINE__)":\n\t"
/* Restore ulCriticalNesting variable */
"ld.w r0, sp++\n\t"
"mov r8, LWRD("ASTRINGZ(ulCriticalNesting)")\n\t"
"orh r8, HWRD("ASTRINGZ(ulCriticalNesting)")\n\t"
"st.w r8[0], r0\n\t"
/* Restore R0..R7 */
"ldm sp++, r0-r7\n\t"
/* Now, the stack should be R8..R12, LR, PC and SR */
"rete"
);
/* Force import of global symbols from assembly */
ulCriticalNesting;
pxCurrentTCB;
}
This worked for me and I did not see so far any differences in the behavior of our system. I hope this can help someone wo has to migrate from IAR Workbench 4.10 to 4.20.

Jump from bootloader generates exception

I've written a bootloader for my SAM4S that sits in sector 0 and loads an application in sector 1. The problem however is that when I attempt to jump to the new function it appears to generate an exception (debugger goes to Dummy_Handler()).
Bootloader contains the following entries in map:
.application 0x00410000 0x0
0x00410000 . = ALIGN (0x4)
0x00410000 _sappl = .
0x00410004 _sjump = (. + 0x4)
The application image map file has:
.vectors 0x00410000 0xd0 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x00410000 exception_table
…
.text.Reset_Handler
0x0041569c 0x100 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x0041569c Reset_Handler
Exception table is defined as follows:
const DeviceVectors exception_table = {
/* Configure Initial Stack Pointer, using linker-generated symbols */
.pvStack = (void*) (&_estack),
.pfnReset_Handler = (void*) Reset_Handler,
The bootloader declares the application jump point as:
extern void (*_sjump) ();
and then makes the following call:
_sjump();
The memory contents at 0x00410004 are 0x0041569d, and I notice that this is not word aligned. Is this because we are using Thumb instructions? Either way why is it not 0x0041569c? Or more importantly why is this going to an exception?
Thanks,
Devan
Update:
Found this but it does not appear to work for me:
void (*user_code_entry)(void);
unsigned *p;
p = (uint32_t)&_sappl + 4;
user_code_entry = (void (*)(void))(*p - 1);
if(applGood && tempGood) {
SCB->VTOR = &_sappl;
PrintHex(p);
PrintHex(*p);
PrintHex(user_code_entry);
user_code_entry();
}
The code prints:
00410004
0041569D
0041569C
Update Update:
The code that attempted to jump with a C function pointer produced the following Disassembly:
--- D:\Zebra\PSPT_SAM4S\PSPT_SAM4S\SAM4S_PSPT\BOOTLOADER\Debug/.././BOOTLOADER.c
user_code_entry();
004005BA ldr r3, [r7, #4]
004005BC blx r3
I was able to get this working with the following assembly:
"mov r1, r0 \n"
"ldr r0, [r1, #4] \n"
"ldr sp, [r1] \n"
"blx r0"
Based on this I wonder if the stack reset is required and, if so, is it possible to accomplish such in C?
I had the same problem with SAM4E. I cannot guess what your problem might be, but I can point out difficulties that I had and information I used.
My bootloader was not storing in the correct memory location a few parts of the firmware. This was leading to the dummy_handler exception. When I fixed the error in the address calculations the bootloader worked perfectly.
My suggestions:
Follow ATMEL's example: the Document and the Example Code should be enough. The main.c is enough to understand how the bootloader should work. It is not necessary to get into partitioning details at the beginning.
You may want to read how you can execute functions/ISRs from RAM
This webpage explains the Intel HEX format.
Finally, after the bootloader finishes with the upgrade, you can read the flash and send it back to the host computer. Then compare it with the original image (using a script). That is how I debugged my bootloader.
Other ideas that might help:
Do you erase each page before you write it?
Do you unlock each memory space before you erase/write it?
You could lock the Bootloader's Section to avoid overwriting it by mistake
You could lock the section(s) of the upgraded firmware.
The address you have to point to is 0x00410000 not 0x00410004. The Atmel's example code (see function binary_exec) in combination with the Intel Hex format (record type 05) should solve this question.
I hope this piece of information will be of some help!
I had the same problem on the SAM4S which brought me to this question. So if someone arrives here again, here is what I found.
As ChrisB mentions following the Atmel example code is a good start, however I found that the problem was the actual code requesting the jump which just didn’t work for the SAM4S.
What was missing was rebasing the stack pointer before the vector table and then loading the reset handler address.
Try something like this:
static void ExecuteApp(void)
{
uint32_t i;
// Pointer to the Application Section
void (*application_code_entry)(void);
// -- Disable interrupts and system timer
__disable_irq();
SysTick->CTRL = 0; // Disable System timer
// disable and clear pending IRQs
for (i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; // disable IRQ
NVIC->ICPR[i] = 0xFFFFFFFF; // clear pending IRQ
}
// Barriers
__DSB(); // data synchronization barrier
__ISB(); // instruction synchronization barrier
// Rebase the Stack Pointer
__set_MSP(*(uint32_t *) APPCODE_START_ADDR);
// Rebase the vector table base address
SCB->VTOR = ((uint32_t) APPCODE_START_ADDR & SCB_VTOR_TBLOFF_Msk);
// Load the Reset Handler address of the application
application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)
(APP_START_RESET_VEC_ADDRESS));
__DSB();
__ISB();
// -- Enable interrupts
__enable_irq();
// Jump to user Reset Handler in the application
application_code_entry();
}

Resources