hardfault when lpc1788 bootloader jumps to user application

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 

hardfault when lpc1788 bootloader jumps to user application

3,267 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Tue Jun 10 07:41:36 MST 2014
I've written a bootloader for the lpc1788.
The bootloader successfully reads an image from a sd-card during startup and loads it in the flash memory when the version, length or crc differs from the image currently in flash.
The problem is when I call the user code using a function pointer the microcontroller gets a hardfault error.
This is how I store the code in flash:
[list]
  [*]the bootloader is stored in address 0 to 0x5000(sector 0 to 4)
  [*]meta data about image in flash is stored at address 0x5000(sector 5)
  [*]user application is only 1440 bytes and blinks an led(no interrupts) and is stored from address 0x6000(sector 6)
  [*]flash is written in blocks of 4kiB and is padded with zeros so a flash signature can be calculated(this works as well)
  [*]start and size of flash has been properly entered in keil
[/list]

Before I call the user code I disable the systick timer. No other peripherals are enabled. This is how I call the user code:

typedef void (*user_code_pointer_type)(void);

void run_user_code(void){
  user_code_pointer_type user_code_entry;
  //user_code_entry = (user_code_pointer_type)((uint32_t*)(user_start_sector_address + 4));//causes invstate at 0x6004
  user_code_entry = (user_code_pointer_type)(user_start_sector_address|1);//causes impreciserr at 0x6006
  //user_code_entry = (user_code_pointer_type)(user_start_sector_address);//causes invstate at 0x6000
  //NVIC_SetVTOR(user_start_sector_address);//hardfault handler of user code is called instead
  (user_code_entry)();
} 


I've tried disabling interrupts, moving interrupt vectors, moving vectors to ram and other things. Nothing works. What am I doing wrong?

Regards,
Chris
标签 (1)
0 项奖励
回复
12 回复数

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by rajeshreddy on Wed Apr 01 04:03:44 MST 2015
Add the below code just before setting the MSP

__set_CONTROL(0); // Change from PSP to MSP

0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Thu Jun 12 23:40:37 MST 2014

Quote: starblue

UM10470 5.4 Vector table remapping:
"The vector table should be located on a 256 word (1024 byte) boundary to insure [sic] alignment on LPC178x/177x family devices."
So that had better be
  SCB->VTOR  =  _vector_table_location  & 0x3FFFFC00;

The reason is that the LPC178x has more than the minimal number of interrupt sources.

Though that won't help much if the table is not aligned (as the hard fault handler will surely tell you).



Exactly. So that whole mask is pointless(and incorrect as you pointed out).

  #if (_vector_table_location)&(1024-1) 
  #error vector table not properly aligned    
  #endif    
  __disable_irq();//no interrupt should be enabled by bootloader, but disable interrupts to be on safe side  
  SCB->VTOR  =  _vector_table_location;
  __enable_irq();


I rather have a compiler error than a hardfault error.
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Thu Jun 12 03:18:42 MST 2014

Quote: shoeloader

  SCB->VTOR  =  _vector_table_location  & 0x3FFFFF80;



UM10470 5.4 Vector table remapping:
"The vector table should be located on a 256 word (1024 byte) boundary to insure [sic] alignment on LPC178x/177x family devices."
So that had better be
  SCB->VTOR  =  _vector_table_location  & 0x3FFFFC00;

The reason is that the LPC178x has more than the minimal number of interrupt sources.

Though that won't help much if the table is not aligned (as the hard fault handler will surely tell you).


Quote: shoeloader

Now interrupts work. Does anybody know a way to access the IROM1 start value in c-code in keil using a macro?
Now I have to use a define a preprocessor symbol myself.
How does the linker know where to put startup_LPC177x_8x.s?


UV4 creates a linker script (a .sct scatter loading file in their jargon), but it doesn't define symbols for the memory regions, so as far as I can see you'll need to do it yourself.

Jürgen
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Thu Jun 12 02:12:11 MST 2014
Thanks for the tip. The reason I use CMSIS is because we have a lot of code that relies on CMSIS. I will look into LPCOpen.
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by TheFallGuy on Thu Jun 12 01:50:11 MST 2014
Why are you using CMSIS? NXP Now use LPCOpen.

0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Thu Jun 12 01:50:07 MST 2014
Thanks! It works! Now I'm going to see if I can get interrupts to work and run an rtos.

edit:
I've cleaned up some of my code. All those magic pointers, magic pointers to pointers and magic offsets have been bypassed:
typedef struct{ 
  uint32_t stack_address;       //initial value of stack pointer is stored at top of vector table
  void (*reset_address)(void);  //reset vector with entry address is located at second 32-bit word  
}vector_table_layout;

void run_user_code(void){
  vector_table_layout const*const user_vector_table=(vector_table_layout*)user_start_sector_address;  
  __disable_irq();                            //no interrupt should be enabled by bootloader, but disable interrupts to be on safe side
  NVIC_SetVTOR(user_start_sector_address);    //set vector table offset to user code
  __enable_irq();
  __set_MSP(user_vector_table->stack_address);//load stackpointer with initial value  
  (user_vector_table->reset_address)();       //call user code
} 


I've found a bug in the CMSIS system_LPC177x_8x.c code of the user code:
#else
  //SCB->VTOR  = 0x00000000 & 0x3FFFFF80; //loads incorrect address in case user code has an offset
  __disable_irq();//no interrupt should be enabled by bootloader, but disable interrupts to be on safe side
  SCB->VTOR  =  _vector_table_location  & 0x3FFFFF80;
  __enable_irq();
#endif


Now interrupts work. Does anybody know a way to access the IROM1 start value in c-code in keil using a macro? Now I have to use a define a preprocessor symbol myself.
How does the linker know where to put startup_LPC177x_8x.s?

off topic:
I've never liked CMSIS. It is not high level enough to be flexible and user friendly and you often have to implement extra functions. It is not low level enough, because you can't access all features. CMSIS code compiles with hundreds of warnings. It is very inefficient. Take the pinsel library for instance you won't save a single line of code and it cost you more than 896 bytes of code.
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Wed Jun 11 06:03:08 MST 2014
  user_code_entry = (user_code_pointer_type)((uint32_t*)(user_start_sector_address + 4));//causes invstate at 0x6004


This is wrong. You jump to address 0x6004, instead of the code pointed to by the address at 0x6004. Note this is a difference between ARM Cortex-M and ARM7.

Jürgen
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Wed Jun 11 05:54:40 MST 2014
Thanks for the tip. I tried it but it doesn't work. I now read the initial value of the stack pointer in the user application from the vector table.
I've made a stripped down version of the bootloader. I limit the ram size to 1kiB so there is no stack conflict.
#include < stdint.h >
#include "LPC177x_8x.h"  

#define user_start_sector_address 0x06000 

typedef void (*user_code_pointer_type)(void);

void run_user_code(void){
  uint32_t user_stack_pointer;
  user_code_pointer_type user_code_entry;
  user_code_entry = (user_code_pointer_type)((uint32_t*)(user_start_sector_address + 4));//causes invstate at 0x6004
  //user_code_entry = (user_code_pointer_type)(user_start_sector_address|1);//causes impreciserr at 0x6006
  //user_code_entry = (user_code_pointer_type)(user_start_sector_address);//causes invstate at 0x6000
  //NVIC_SetVTOR(user_start_sector_address);//hardfault handler of user code is called instead
  user_stack_pointer=*((uint32_t*)user_start_sector_address);//initial value of stack pointer is stored at top of vector table
  __set_MSP(user_stack_pointer);
  (user_code_entry)();
} 

int main(void){
  run_user_code();
  while(1);
}


The stack pointer is now initialized with 0x10000670. I still get hardfaults.
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Wed Jun 11 04:59:36 MST 2014
How about something like this:

typedef void (*user_code_pointer_type)(void);

void run_user_code(void){
  uint32_t initial_sp = *(uint32_t*)(user_start_sector_address + 0);
  user_code_pointer_type user_code_entry = (user_code_pointer_type)(*(uint32_t*)(user_start_sector_address + 4));
  NVIC_SetVTOR(user_start_sector_address);
  __set_MSP (initial_sp);
  (user_code_entry)();
} 


Note the extra dereferencing of the code pointer.

This assumes that the code pointers in the vector table have bit 0 set for thumb mode.

No guarantees (especially about __set_MSP()), use at your own risk.

Jürgen
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by whitecoe on Wed Jun 11 00:19:30 MST 2014
Check your stack pointer…

http://www.lpcware.com/content/forum/lpc1768-secondary-usb-bootloader-lock#comment-1133905

HTH!
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shoeloader on Tue Jun 10 23:38:14 MST 2014
That's why I set bit 0 to 1 by bitwise-OR-ing the address with 1. I've checked it with dissasembly and with the values loaded in the registers. Address 0x6001 is loaded and the code jumps to 0x6000. But at address 0x6006 the microcontroller gets an impreciserr hardfault error. Even stripping the code to just calling user code gives me that error.
The reason I tried other address offsets is that NXP uses an offset of 4 in their bootloader examples.

I use keil uvision 4.73 with an Ulink2. Optimization is set to -O1.
0 项奖励
回复

2,689 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by wmues on Tue Jun 10 10:40:27 MST 2014
The Cortex M3 has a THUMB2 command set. To differentiate ARM code from THUMB code, THUMB code has bit 0 of each address set to 1.

Regards

Wolfgang
0 项奖励
回复