Vector Table for LPC2103 using GNUARM/OpenOCD

I think this is a GCC/GNUARM question, for those of you who are gurus with that.

Alright! Trying to get the Lynch tutorial to work on the LPC2103. I’m kind of there. I’ve got the micro fired up. I was able to flash software into it. Heck, I even managed to get it to not have a fit and jump straight to the bootloader. It actually runs user code! The problem is I think the interrupt vector table is getting generated incorrectly. Here’s my startup code:

/* Stack Sizes */
.set  UND_STACK_SIZE, 0x00000004		/* stack for "undefined instruction" interrupts is 4 bytes  */
.set  ABT_STACK_SIZE, 0x00000004		/* stack for "abort" interrupts is 4 bytes                  */
.set  FIQ_STACK_SIZE, 0x00000004		/* stack for "FIQ" interrupts  is 4 bytes         			*/
.set  IRQ_STACK_SIZE, 0X00000004		/* stack for "IRQ" normal interrupts is 4 bytes    			*/
.set  SVC_STACK_SIZE, 0x00000004		/* stack for "SVC" supervisor mode is 4 bytes  				*/



/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */
.set  MODE_USR, 0x10            		/* Normal User Mode 										*/
.set  MODE_FIQ, 0x11            		/* FIQ Processing Fast Interrupts Mode 						*/
.set  MODE_IRQ, 0x12            		/* IRQ Processing Standard Interrupts Mode 					*/
.set  MODE_SVC, 0x13            		/* Supervisor Processing Software Interrupts Mode 			*/
.set  MODE_ABT, 0x17            		/* Abort Processing memory Faults Mode 						*/
.set  MODE_UND, 0x1B            		/* Undefined Processing Undefined Instructions Mode 		*/
.set  MODE_SYS, 0x1F            		/* System Running Priviledged Operating System Tasks  Mode	*/

.set  I_BIT, 0x80               		/* when I bit is set, IRQ is disabled (program status registers) */
.set  F_BIT, 0x40               		/* when F bit is set, FIQ is disabled (program status registers) */


.text
.arm

.global	Reset_Handler
.global _startup
.func   _startup

_startup:

# Exception Vectors

_vectors:       ldr     PC, Reset_Addr         
                ldr     PC, Undef_Addr
                ldr     PC, SWI_Addr
                ldr     PC, PAbt_Addr
                ldr     PC, DAbt_Addr
                nop							/* Reserved Vector (holds Philips ISP checksum) */
                ldr     PC, [PC,#-0xFF0]	/* see page 71 of "Insiders Guide to the Philips ARM7-Based Microcontrollers" by Trevor Martin  */
                ldr     PC, FIQ_Addr

Reset_Addr:     .word   Reset_Handler		/* defined in this module below  */
Undef_Addr:     .word   UNDEF_Routine		/* defined in main.c  */
SWI_Addr:       .word   SWI_Routine			/* defined in main.c  */
PAbt_Addr:      .word   UNDEF_Routine		/* defined in main.c  */
DAbt_Addr:      .word   UNDEF_Routine		/* defined in main.c  */
IRQ_Addr:       .word   IRQ_Routine			/* defined in main.c  */
FIQ_Addr:       .word   FIQ_Routine			/* defined in main.c  */
                .word   0					/* rounds the vectors and ISR addresses to 64 bytes total  */


# Reset Handler

Reset_Handler:  

				/* Setup a stack for each mode - note that this only sets up a usable stack
				for User mode.   Also each mode is setup with interrupts initially disabled. */
    			  
    			ldr   r0, =_stack_end
    			msr   CPSR_c, #MODE_UND|I_BIT|F_BIT 	/* Undefined Instruction Mode  */
    			mov   sp, r0
    			sub   r0, r0, #UND_STACK_SIZE
    			msr   CPSR_c, #MODE_ABT|I_BIT|F_BIT 	/* Abort Mode */
    			mov   sp, r0
    			sub   r0, r0, #ABT_STACK_SIZE
    			msr   CPSR_c, #MODE_FIQ|I_BIT|F_BIT 	/* FIQ Mode */
    			mov   sp, r0	
   				sub   r0, r0, #FIQ_STACK_SIZE
    			msr   CPSR_c, #MODE_IRQ|I_BIT|F_BIT 	/* IRQ Mode */
    			mov   sp, r0
    			sub   r0, r0, #IRQ_STACK_SIZE
    			msr   CPSR_c, #MODE_SVC|I_BIT|F_BIT 	/* Supervisor Mode */
    			mov   sp, r0
    			sub   r0, r0, #SVC_STACK_SIZE
    			msr   CPSR_c, #MODE_SYS|I_BIT|F_BIT 	/* User Mode */
    			mov   sp, r0

				/* copy .data section (Copy from ROM to RAM) */
                ldr     R1, =_etext
                ldr     R2, =_data
                ldr     R3, =_edata
1:        		cmp     R2, R3
                ldrlo   R0, [R1], #4
                strlo   R0, [R2], #4
                blo     1b

				/* Clear .bss section (Zero init)  */
                mov     R0, #0
                ldr     R1, =_bss_start
                ldr     R2, =_bss_end
2:				cmp     R1, R2
                strlo   R0, [R1], #4
                blo     2b

				/* Enter the C code  */
                b       main

.endfunc
.end

But what’s generated is as follows:

main.out:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000001f8  00000000  00000000  00008000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .debug_abbrev 0000005c  00000000  00000000  000081f8  2**0
                  CONTENTS, READONLY, DEBUGGING
  2 .debug_info   0000011c  00000000  00000000  00008254  2**0
                  CONTENTS, READONLY, DEBUGGING
  3 .debug_line   00000048  00000000  00000000  00008370  2**0
                  CONTENTS, READONLY, DEBUGGING
  4 .debug_frame  000000b0  00000000  00000000  000083b8  2**2
                  CONTENTS, READONLY, DEBUGGING
  5 .debug_loc    000000d2  00000000  00000000  00008468  2**0
                  CONTENTS, READONLY, DEBUGGING
  6 .debug_pubnames 0000005d  00000000  00000000  0000853a  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_aranges 00000020  00000000  00000000  00008597  2**0
                  CONTENTS, READONLY, DEBUGGING
  8 .comment      00000012  00000000  00000000  000085b7  2**0
                  CONTENTS, READONLY
Disassembly of section .text:

00000000 <_startup>:
   0:	e59ff018 	ldr	pc, [pc, #24]	; 20 <Reset_Addr>
   4:	e59ff018 	ldr	pc, [pc, #24]	; 24 <Undef_Addr>
   8:	e59ff018 	ldr	pc, [pc, #24]	; 28 <SWI_Addr>
   c:	e59ff018 	ldr	pc, [pc, #24]	; 2c <PAbt_Addr>
  10:	e59ff018 	ldr	pc, [pc, #24]	; 30 <DAbt_Addr>
  14:	e1a00000 	nop			(mov r0,r0)
  18:	e51ffff0 	ldr	pc, [pc, #-4080]	; fffff030 <_stack_end+0xbffef154>
  1c:	e59ff014 	ldr	pc, [pc, #20]	; 38 <FIQ_Addr>

00000020 <Reset_Addr>:
  20:	00000040 	andeq	r0, r0, r0, asr #32

00000024 <Undef_Addr>:
  24:	000001e4 	andeq	r0, r0, r4, ror #3

00000028 <SWI_Addr>:
  28:	000001d0 	ldreqd	r0, [r0], -r0

0000002c <PAbt_Addr>:
  2c:	000001e4 	andeq	r0, r0, r4, ror #3

00000030 <DAbt_Addr>:
  30:	000001e4 	andeq	r0, r0, r4, ror #3

00000034 <IRQ_Addr>:
  34:	000001a8 	andeq	r0, r0, r8, lsr #3

00000038 <FIQ_Addr>:
  38:	000001bc 	streqh	r0, [r0], -ip
  3c:	00000000 	andeq	r0, r0, r0

00000040 <Reset_Handler>:
  40:	e59f0078 	ldr	r0, [pc, #120]	; c0 <.text+0xc0>
  44:	e321f0db 	msr	CPSR_c, #219	; 0xdb
  48:	e1a0d000 	mov	sp, r0
  4c:	e2400004 	sub	r0, r0, #4	; 0x4
  50:	e321f0d7 	msr	CPSR_c, #215	; 0xd7
  54:	e1a0d000 	mov	sp, r0

8<----- snip!

So what gives? Is that correct? All those vectors are just set indiscriminately to #24, when they really ought to all be different, right? Is this a misconfiguration of my assembler? 24 as it happens is my undefined vector, which takes me off into an infinite loop, and the target just sits there until I jump in with JTAG.

I can jump in, and I can force it to start up at main(), and things seem to work pretty well when I do. My LED blinks and everything works well, but only when I hold it’s hand through that process.

Is this an assembler misconfiguration, a link misconfiguration, or am I interpreting something as incorrect that in fact is right?[/code]

ryansturmer:
So what gives? Is that correct? All those vectors are just set indiscriminately to #24, when they really ought to all be different, right?

So far, it looks correct. The 0x24 value is an offset from your current program counter location. Your vector table is using the LDR instruction to load the program counter from the table of addresses that you created immediately following your vector table. Since they are all listed in the same order the offest into the table remains constant from the current PC value in the vector table.

ryansturmer:
Is this a misconfiguration of my assembler? 24 as it happens is my undefined vector, which takes me off into an infinite loop, and the target just sits there until I jump in with JTAG.

Where is your infinite loop located? There are two loops in your startup code that could become infinite if there was a problem in your linker script. They are the “copy .data section” and “Clear .bss section”. If either of those loops do not terminate, you will be stuck in the startup code forever.

ryansturmer:
I can jump in, and I can force it to start up at main(), and things seem to work pretty well when I do. My LED blinks and everything works well, but only when I hold it’s hand through that process.

Is this an assembler misconfiguration, a link misconfiguration, or am I interpreting something as incorrect that in fact is right?

IMHO, there is probably nothing wrong with that simple startup code, but there is a good chance that the linker variables that are being passed in are causing one of your loops not to terminate. I would check to see which loop is the infinite loop, and then check the linker variables associated with it.

P.S. Glad that you decided to erase any old code before trying to put new code in your FLASH

–Dave

Ah! Those are relative! Ok, that makes sense.

The infinite loop is just a while(1); in the interrupt handler for Undef_Addr The program just seems to jump there unless I walk it into main by hand.

Yeah, erasing the flash is important. I don’t know why I assumed OpenOCD was doing it for me. :oops:

ryansturmer:
Ah! Those are relative! Ok, that makes sense.

The infinite loop is just a while(1); in the interrupt handler for Undef_Addr The program just seems to jump there unless I walk it into main by hand.

Yeah, erasing the flash is important. I don’t know why I assumed OpenOCD was doing it for me. :oops:

Your listing shows that your undefined abort handler address is located at 0x000001E4, and your comments say that the handler itself is defined in main.c . Actually your prefetch abort handler and your data abort handler seem to be located at the same address as your undefined abort handler so you would really have to examine the CPSR to figure out which abort actually happened. Have you actually loaded your PC with 0x0 and stepped through your code to find where the abort is occuring.? My bet would be that it would be in one of your two loops that I previously mentioned, and that the abort would actually be a data abort and not the undefined abort. If that is actually the case the reason would still be due to getting bad values passed in from the linker.

–Dave

That seems likely. I’m at work now :? so I’ll have to wait until I get home to try anything further.

Thanks for the help!

Since I’m working on a target for which reset_halt isn’t supported in openOCD, does anyone have suggestions regarding how I might go about debugging this code that lies right at the beginning?

ryansturmer:
Since I’m working on a target for which reset_halt isn’t supported in openOCD, does anyone have suggestions regarding how I might go about debugging this code that lies right at the beginning?

I use NoICE as my debugger with OpenOCD, so I can’t say how you do it directly with OpenOCD. What I do is to simply set the program counter back to 0x00000000 and then step through the code. I built my own startup code from scratch, so I had plenty of debugging to do in the startup code. :wink:

None of the NXP chips that I know of can set a breakpoint at 0x00000000 because the TAP controller is reset at the same time as system reset due to NXP’s CRP scheme. That means that you simply have to start over again by setting the program counter back to the reset vector. To stay as close to a real reset startup as possible, you can put a endless loop ahead of any code that is suspect or that configures the peripherals so that they are still in the state immediately after reset. Then you can reset the PC back to zero and move through your code, bypassing the endless loop when you reach it.

–Dave