Branch to Interrupt Handler on Microblaze CPU, Assembly Language - gcc

i'm pretty new to assembly language and microblaze cpus and i have a problem getting interrupt handling working.
i'm forced to write my own startup and init routines. (stuff that is usually contained in crt0) and i guess i'm doing something wrong with branching to the interrupt handler at the specified address. in the documentation it is said, to branch from code address 0x10 to the interrupt handler, however in my test program it doesn't work.
here you can find the microblaze doc. i'm referring to p. 102 (Interrupt and Exception Handling)
here you can see the code for 'my_crt0_intc.asm'
bri _start
nop
bri _exception_handler
nop
bri _interrupt_handler
nop
bri _hw_exception_handler
nop
/* Starting Point */
.global _start
_start:
brlid r15, _init_routine
nop
/* Exception Handler *
.global _exception_handler
_exception_handler:
/**/
/* Interrupt Handler */
.global _interrupt_handler
_interrupt_handler:
/* set led 0010 */
ori r20, r20, 0x00000002
swi r20, r0, 0x81400000
rtid r14, 8
nop
/* HW Exception Handler *
.global _hw_exception_handler
_hw_exception_handler:
/**/
/* Init Routine */
.global _init_routine
_init_routine:
/*** Initialize Stack Pointer ***/
addi r1, r0, 0x00004000
/*** ***/
/*** Initialize Interrupts ***/
/** GPIO **/
/* enable interrupts in GIE */
addi r19, r0, 0x80000000
swi r19, r0, 0x8146011C
/* enable interrupts in channel 1 (and 2, not) in IER */
addi r19, r0, 0x00000003
swi r19, r0, 0x81460128
/** INTC **/
/* enable HW interrupts in INTC */
addi r19, r0, 0x00000003
swi r19, r0, 0x8180001C
/* enable interrupts in INTC IER */
addi r19, r0, 0xffffffff
swi r19, r0, 0x81800008
/** CPU, enable interrupts in MSR **/
msrset r19, 0x00000002
/*** ***/
/* Initialize Constants */
/* r11 = word size, for increment and decrement stack pointer */
/* addi r11, r0, 4 */
/*** call main function ***/
brlid r15, main
nop
/*** ***/
/*** halting loop ***/
_halting:
bri _halting
/*** ***/
/* Return */
rtsd r15, 8
nop
my test program 'test_interrupt_cpu_intc_gpio.asm' is intended to signal interrupts via leds at address 0x81400000
/* Main */
.global main
main:
addi r20, r0, 0x0
_loop:
/* set/unset alive led 0001 */
andi r21, r20, 0x00000001
bnei r21, _unset_alive
_set_alive:
ori r20, r20, 0x1
swi r20, r0, 0x81400000
bri _no_alive
_unset_alive:
andi r20, r20, 0xfffffffe
swi r20, r0, 0x81400000
_no_alive:
/* if gpio isr is set, set led 0100 */
lwi r21, r0, 0x81460120
beqi r21, _unset_gpio
_set_gpio:
ori r20, r20, 0x4
swi r20, r0, 0x81400000
bri _noset_gpio
_unset_gpio:
andi r20, r20, 0xfffffffb
swi r20, r0, 0x81400000
_noset_gpio:
/* if intc isr is set, set led 1000 */
lwi r21, r0, 0x81800000
beqi r21, _unset_intc
_set_intc:
ori r20, r20, 0x8
swi r20, r0, 0x81400000
bri _noset_intc
_unset_intc:
andi r20, r20, 0xfffffff7
swi r20, r0, 0x81400000
_noset_intc:
/* begin time loop */
addi r21, r0, 0x004af080
_loop_time:
addi r21, r21, -1
bnei r21, _loop_time
/* end time loop*/
bri _loop
/* return 0*/
addi r3, r0, 0
rtsd r15, 8
nop
the error symptoms are: if i do not enable interrupts in the MSR, led 0001 blinks and leds 1000 and 0100 are active if trigger the interrupt by pushing the appropriate button. however, in this situation of course the cpu doesn't handle interrupts and doesn't branch to the interrupt handler routine.
BUT: if i enable interrupts in the MSR to enable interrupt handling, led 0001 blinks in the beginning. after triggering the interrupt led 0001 is permanently set (or resp. unset if led 0001 is already unset when the interrupt occurs). led 1000 and 0100 stay inactive, which seems like the cpu stops working.
i compiled the code as follows with the mb-gcc tools:
mb-as test_interrupt_cpu_intc_gpio.o -o test_interrupt_cpu_intc_gpio.o
mb-as my_crt0_intc.asm -o my_crt0_intc.o
mb-ld my_crt0_intc.o test_interrupt_cpu_intc_gpio.o -o ../executable.elf -N
any help would be great. this problem is really annoying and i'm working on this for days. i'm sure i miss something essentially in the code. if you need any additional information, please let me know.
UPDATE:
as far as i can trust mb-gdb my program starts at address 0x50. if this is true, this explains why my branch operations are never executed.
i tried several things to ensure placing the essential instructions at the correct positions
(1)
.org 0x0
brai _start
.org 0x8
brai _exception_handler
.org 0x10
brai _interrupt_handler
.org 0x20
brai _hw_exception_handler
(2) i tried linking with mb-gcc and option x1-mode-xmdstub (as it is said in the documentation, that the standard crt0.o is not linked using this option) -> however, in this case i get the error, mutliple defintions of _start
(3) i tried
mb-ld test_interrupt_cpu_intc_gpio.o my_crt0_intc.o -o ../executable.elf -N
this way, the behavior changes, but is still uncorrect since now test_interrupt_cpu_intc_gpio.asm starts at address 0x50.
THANKS,
sema

just for record:
the problem was that the program was linked with start address 0x50. however, as specified in the microblaze documentation, the branch (vector) to the interrupt handler must be located at address 0x10.
'mb-ld --verbose ' gave a hint to this problem
ENTRY(_start) _TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50;
'mb-objdump -d executable.elf' shows
../executable.elf: file format elf32-microblaze
Disassembly of section .text:
00000050 <_start-0x28>:
50: b0000000 imm 0
54: b8080078 brai 120 // 78 <_start>
58: b0000000 imm 0
5c: b80800cc brai 204 // cc <_exception_handler>
60: b0000000 imm 0
64: b80800d4 brai 212 // d4 <_interrupt_handler>
68: b0000000 imm 0
the problem is solved by option -Ttest 0x0:
mb-ld $CRT_OBJ $OBJ -o ../executable.elf -Ttext 0x0
i also missed the clearing the interrupt status registers in the interrupt handler routine which should be as follows (note rtid r14, 0 instead of rtid r14,8):
.global _interrupt_handler
_interrupt_handler:
/* set led 0010 */
ori r20, r20, 0x00000002
swi r23, r0, 0x81400000
/**/
/* clear channel 1 in gpio isr */
addi r22, r0, 0x00000001
swi r22, r0, 0x81460120
/* acknowledge interrupts in intc from channel 1 of gpio 3b (bit 2) */
addi r22, r0, 0x00000002
swi r22, r0, 0x8180000C
/* return from interrupt */
rtid r14, 0
nop
SUMMARY:
OBJDUMP is very useful tool! (it was new to me)

Related

AVR TWI (I2C) problem: Operand 1 out of range

I´m using Atmega2560 and 24c16A EEPROM for testing an I2C code. For every state transition, controller responds by changing the status register TWSR. After start condition is transmitted, EEPROM responds and then also for Device address and write instruction (SLA+R/W), status register updates fine. But when I transmit next 8 bits, instead of ACK (Data byte sent ACK), status changes to repeated start. The code is given below. I never could find the possible solution to make it work.
.include "./m2560def.inc"
.list
.cseg
.org 0x00
jmp inicio ; PC = 0x0000 RESET
inicio:
LDI R21, HIGH(RAMEND) ;Set Up Stack
OUT SPH, R21
LDI R21, LOW(RAMEND)
OUT SPL, R21
CALL I2C_INIT ;Initialize TWI(I2C)
CALL I2C_START ;Transmit START condition
LDI R27, 0b11010000 ;SLA(0b1001100) + W(0)
CALL I2C_WRITE ;Write R27 ato the I2C bus
LDI R27, 0b11110000 ;Data to be transmitted
CALL I2C_WRITE ;Write R27 ato the I2C bus
CALL I2C_STOP ;Transmit STOP condition
HERE: RJMP HERE
;----------------------------I2C_INIT-----------------------------
I2C_INIT:
LDI R21, 0
OUT TWSR, R21 ;Set prescaler bits to 0
LDI R21, 0x47 ;R21 = 0x47
OUT TWBR, R21 ;Fclk = 50 KHz (8 MHz Xtal)
LDI R21, (1<<TWEN) ;R21 = 0x04
OUT TWCR, R21 ;HEnable TWI (I2C)
RET
;----------------------------I2C_START-----------------------------
I2C_START:
LDI R21, (1<<TWINT)|1<<(TWSTA)|(1<<TWEN)
OUT TWCR, R21 ;Transmit START condition
WAIT1:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT1 ;Jump a WAIT1 if TWINT is 1
RET
;----------------------------I2C_WRITE -----------------------------
I2C_WRITE:
OUT TWDR, R27 ;Move the byte into TWRD
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21 ;Configure TWCR to send TWDR
WAIT3:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT3 ;Jump a WAIT3 if TWINT is 1
RET
;----------------------------I2C_STOP------------------------------
I2C_STOP:
LDI R21, (1<<TWINT)|1<<(TWSTO)|(1<<TWEN)
OUT TWCR, R21 ;Transmit STOP condition
RET
;----------------------------I2C_READ------------------------------
I2C_READ:
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21
WAIT2:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT2 ;Jump a WAIT2 if TWINT is 1
IN R27, TWCR ;Read received data into R21
RET
The output is
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(41,0): error: Operand 1 out of range: 0xb9
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(43,0): error: Operand 1 out of range: 0xb8
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(45,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(51,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(53,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(60,0): error: Operand 1 out of range: 0xbb
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(62,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(64,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(72,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(78,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(80,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(83,0): error: Operand 2 out of range: 0xbc
Assembly failed, 12 errors, 0 warnings
The code lines involved in these errors are:
OUT TWSR, R21
OUT TWBR, R21
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
OUT TWDR, R27
OUT TWCR, R21
IN R21, TWCR
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
IN R27, TWCR
In AVR normally all I/O registers are mapped into the memory space, starting from address 0x0020
in and out instructions could be used ONLY with the first 64 of them (with 0x20...0x5F memory addresses)
sbi, cbi, sbis and sbic has further limitation and can be used only with first 32 I/O registres (with memory address 0x20...0x3F). Please refer to the AVR Instruction Set Manual
In the datasheet to ATmega2560, section 33 "Register Summary", page 401, you can see the TWI registers have addresses 0xB8 thru 0xBD, i.e. they cannot be accessed using in and out instructions. You have to use STS and LDS.
I.e. LDS R21, TWCR instead of IN R21, TWCR etc.
Also, always be careful when accessing the first 64 registers. Make sure the corresponding names are defining their I/O and not RAM addresses. I.e. PORTA should be equal to 0x02 (IO address for in / out instruction) and not 0x22 (RAM address or PORTA, which, when accessed using in or out, equals to EEARH register).

Stack Pointer does not load at the startup in ARM Cortex M4 (Tiva C Series TM4C123GH6PM)

I'm trying to write the most simple program for Tiva C launchpad. Stack pointer value and program counter value are automaticaly taken from the two first 32-bits words of flash. But, for somehow reason, when I debug with gdb, the stack pointer gets 0x0. This causes that program fails. I'm using this instructions to debug:
(gdb) target extended-remote :3333
(gdb) monitor reset halt
(gdb) load
(gdb) monitor reset init
Program in assembly is startup.s:
.syntax unified
.section .vector_interrupt, "x"
g_pfnVectors:
.word 0x20007FFF
.word _Reset
.text
.global _Reset
_Reset:
mov r0, #0
b stop
stop:
add r0, r0, #1
b stop
the linker file Tiva.lds:
ENTRY(_Reset)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS {
.vector_interrupt : {
KEEP(*(.vector_interrupt));
} > FLASH
.text : {
. = 0x0000026c;
* (.text);
} > FLASH
}
and the makefile:
gcc=arm-none-eabi-gcc
objcopy=arm-none-eabi-objcopy
FLAGS= -ggdb3 -nostdlib -std=c99 -mcpu=cortex-m4 \
-mfloat-abi=softfp -mfpu=fpv4-sp-d16 -Wall \
-Werror -nostartfiles
csum.bin: csum.elf
$(objcopy) -O binary csum.elf csum.bin
csum.elf: startup.s
$(gcc) $(FLAGS) -T Tiva.lds -o csum.elf \
startup.s
openocd:
openocd -f ../openOCD/ek-tm4c123gxl.cfg
What is wrong?
Actualization
I was trying to avoid put the exception handlers. However, Now I put them. The problem is that i'm getting an UsageFault at startup.
I did this modifications:
startup.s:
.syntax unified
.section .vector_interrupt, "x"
g_pfnVectors:
.word _stack_start
.word _Reset
.word NMI /* NMI Handler */
.word HardFault /* Hard Fault Handler */
.word MemManage /* MPU Fault Handler */
.word BusFault /* Bus Fault Handler */
.word UsageFault /* Usage Fault Handler */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word SVC /* SVCall Handler */
.word DebugMon /* Debug Monitor Handler */
.word 0 /* Reserved */
.word PendSV /* PendSV Handler */
.word SysTick /* SysTick Handler */
.text
.global _Reset
_Reset:
mov r0, #0
b stop
stop:
add r0, r0, #1
b stop # Infinite loop to stop execution
.align 1
.thumb_func
.weak Default_Handler
.type Default_Handler, %function
Default_Handler:
b .
/* Macro to define default handlers */
.macro def_handler handler_name
.weak \handler_name
.set \handler_name, Default_Handler
.endm
def_handler NMI
def_handler HardFault
def_handler MemManage
def_handler BusFault
def_handler UsageFault
def_handler SVC
def_handler DebugMon
def_handler PendSV
def_handler SysTick
def_handler DEF_IRQHandler
Tiva.lds:
ENTRY(_Reset)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
_stack_start = ORIGIN(SRAM)+LENGTH(SRAM);
SECTIONS {
.text : {
KEEP(*(.vector_interrupt));
* (.text);
} > FLASH
}
This is the output from arm-none-eabi-objdump -d csum.elf
csum.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <g_pfnVectors>:
0: 20008000 .word 0x20008000
4: 00000040 .word 0x00000040
8: 0000004d .word 0x0000004d
c: 0000004d .word 0x0000004d
10: 0000004d .word 0x0000004d
14: 0000004d .word 0x0000004d
18: 0000004d .word 0x0000004d
...
2c: 0000004d .word 0x0000004d
30: 0000004d .word 0x0000004d
34: 00000000 .word 0x00000000
38: 0000004d .word 0x0000004d
3c: 0000004d .word 0x0000004d
00000040 <_Reset>:
40: f04f 0000 mov.w r0, #0
44: e7ff b.n 46 <stop>
00000046 <stop>:
46: f100 0001 add.w r0, r0, #1
4a: e7fc b.n 46 <stop>
0000004c <BusFault>:
4c: f7ff bff8 b.w 4c <BusFault>
The reasons for the UsageFault can be:
An undefined instruction
– An illegal unaligned access
– Invalid state on instruction execution
– An error on exception return
but i don't figure out what is the reason.
Writing in ARM assembly is tricky. There were three problems with this code.
The .vector_interrupt section did not make it to the binary file because it did not have the ALLOC attribute. objcopy ignores sections without the ALLOC attribute. As a result, the initial stack pointer and reset vector were zeroes. To fix this, the section attributes should read "xa":
.section .vector_interrupt, "xa"
Another problem: initial stack pointer is unaligned. Change it to:
.word 0x20008000
This is assuming the MCU has at least 32KB RAM.
Branch target addresses should have the LSB bit set to one to indicate Thumb mode. Cortex-M4 supports the Thumb instruction set only, therefore, it generates a fault when trying to jump to an even address. In order to let the assembler generate correct address values, the .thumb_func should precede each label that identified a branch target.
See https://sourceware.org/binutils/docs/as/ARM-Directives.html

IMX53 initialize stacks

I am booting Android on an IMX53 Sabre tablet. I am trying to initialize stacks for the different processor modes. The following is my monitor initialization code:
# Install Secure Monitor
# -----------------------
ldr r1, =ns_image # R1 is used
str r0, [r1]
ldr r0, =tz_monitor # Get address of Monitors vector table
mcr p15, 0, r0, c12, c0, 1 # Write Monitor Vector Base Address Register
# Save Secure state
# ------------------
ldr r0, =S_STACK_LIMIT # Get address of Secure state stack
stmfd r0!, {r4-r12} # Save general purpose registers
# ADD support for SPs
mrs r1, cpsr # Also get a copy of the CPSR
stmfd r0!, {r1, lr} # Save CPSR and LR
ldr r1, =STACK_ADDR
msr cpsr_c, #Mode_FIQ | I_Bit | F_Bit
sub sp, r1, #Offset_FIQ_Stack
msr cpsr_c, #Mode_IRQ | I_Bit | F_Bit
sub sp, r1, #Offset_IRQ_Stack
msr cpsr_c, #Mode_ABT | I_Bit | F_Bit
sub sp, r1, #Offset_ABT_Stack
msr cpsr_c, #Mode_UND | I_Bit | F_Bit
sub sp, r1, #Offset_UND_Stack
msr cpsr_c, #Mode_SYS | I_Bit | F_Bit
sub sp, r1, #Offset_SYS_Stack
msr cpsr_c, #Mode_SVC | I_Bit | F_Bit
sub sp, r1, #Offset_SVC_Stack
msr cpsr_c, #Mode_MON | I_Bit | F_Bit
sub sp, r1, #Offset_MON_Stack
cps #Mode_MON # Move to Monitor mode after saving Secure state
# Save Secure state stack pointer
# --------------------------------
ldr r1, =S_STACK_SP # Get address of global
str r0, [r1] # Save pointer
# Set up initial NS state stack pointer
# --------------------------------------
ldr r0, =NS_STACK_SP # Get address of global
ldr r1, =NS_STACK_LIMIT # Get top of Normal state stack (assuming FD model)
str r1, [r0] # Save pointer
# Set up exception return information
# ------------------------------------
#IMPORT ns_image
ldr lr, ns_image # ns_image
msr spsr_cxsf, #Mode_SVC # Set SPSR to be SVC mode
# Switch to Normal world
# -----------------------
mrc p15, 0, r4, c1, c1, 0 # Read Secure Configuration Register data
bic r4, #0x66
orr r4, #0x19
//orr r4, #NS_BIT # Set NS bit
mcr p15, 0, r4, c1, c1, 0 # Write Secure Configuration Register data
# Clear general purpose registers
# --------------------------------
mov r0, #0
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov r9, #0
mov r10, #0
mov r11, #0
mov r12, #0
movs pc, lr
Android booting happens fine with this, but I am not sure if the stack pointers are set up correctly, as I am not able to use them as described at IMX53 external abort. Are the stack initializations correct?
Other relevant code snippets:
.equ Mode_USR, 0x10 # User Mode
.equ Mode_FIQ, 0x11 # Fast Interrupt Mode
.equ Mode_IRQ, 0x12 # Interrupt Mode
.equ Mode_SVC, 0x13 # Supervisor Mode
.equ Mode_ABT, 0x17 # Abort Mode
.equ Mode_UND, 0x1B # Undefined Mode
.equ Mode_SYS, 0x1F # System Mode
.equ Mode_MON, 0x16 # Monitor Mode
.equ STACK_ADDR, 0xa0000000
.equ I_Bit, 0x80 # IRQ interrupts disabled
.equ F_Bit, 0x40 # FIQ interrupts disabled
.equ NS_BIT, 0x1
/* memory reserved (in bytes) for stacks of different mode */
.equ Len_FIQ_Stack, 64
.equ Len_IRQ_Stack, 64
.equ Len_ABT_Stack, 64
.equ Len_UND_Stack, 64
.equ Len_SVC_Stack, 64
.equ Len_USR_Stack, 64
.equ Len_MON_Stack, 64
.equ Len_SYS_Stack, 64
.equ Offset_FIQ_Stack, 0
.equ Offset_IRQ_Stack, Offset_FIQ_Stack + Len_FIQ_Stack
.equ Offset_ABT_Stack, Offset_IRQ_Stack + Len_IRQ_Stack
.equ Offset_UND_Stack, Offset_ABT_Stack + Len_ABT_Stack
.equ Offset_SVC_Stack, Offset_UND_Stack + Len_UND_Stack
.equ Offset_USR_Stack, Offset_SVC_Stack + Len_SVC_Stack
.equ Offset_MON_Stack, Offset_USR_Stack + Len_USR_Stack
.equ Offset_SYS_Stack, Offset_MON_Stack + Len_MON_Stack

unexpected behaviour of ldr [pc, #value]

I have built a simple application using GNU gcc (4.8.1429) for ARM (ATsam4LC4B: cortexM4 core, 256K flash) that:
loads at 0x3F000 (linker option -Ttext=0x3F000) and initializes,
copies itself (mirrors) at 0
jumps to the mirror Reset_Handler() (near 0) using assembly
the mirror application begins to run as expected
The problem raises when the flash memory location of the mirror main() call (flash location 0x532) is reached:
The instruction ldr [pc, #32] that is located there, loads r3 with 0x3F565 (thus calling the original main()), instead of the expected 0x565 (so that to call the mirror main() as expected).
This happens even if all the registers containing 0x3F... are zeroed prior to the ldr instruction.
The debugger reports the PC register to be 0x534 as expected and in compliance to the flash region within the stepping is performed.
Can anybody please explain to me what is going on?
Thank you in advance.
The program is compiled with [-mthumb -nostdlib -nostartfiles -ffreestanding -msingle-pic-base -mno-pic-data-is-text-relative -mlong-calls -mpoke-function-name] flags and linked with the [-Wl,--gc-sections -mcpu=cortex-m4 -Ttext=0x3f000 -nostdlib -nostartfiles -ffreestanding -Wl,-dead-strip -Wl,-static -Wl,--entry=Reset_Handler -Wl,--cref -mthumb] flags. The linker script defines rom start=0.
The region of interest within the produced .lss file contains (omitted lines are marked as ...):
...
void Reset_Handler(void)
{
...
/* Initialize the relocate segment */
...
/* Clear the zero segment */
...
/* Set the vector table base address */
/* Initialize the C library */
// __libc_init_array();
/* Branch to main function */
main();
3f532: 4b08 ldr r3, [pc, #32] ; (3f554 <Reset_Handler+0x5c>)
3f534: 4798 blx r3
3f536: e7fe b.n 3f536 <Reset_Handler+0x3e>
3f538: 20000000 .word 0x20000000
3f53c: 0003f59c .word 0x0003f59c
3f540: 20000004 .word 0x20000004
3f544: 20000004 .word 0x20000004
3f548: 20000008 .word 0x20000008
3f54c: e000ed00 .word 0xe000ed00
3f550: 0003f000 .word 0x0003f000
3f554: 0003f565 .word 0x0003f565
3f558: 6e69616d .word 0x6e69616d
3f55c: 00 .byte 0x00
3f55d: 00 .byte 0x00
3f55e: bf00 nop
3f560: ff000008 .word 0xff000008
0003f564 <main>:
int main (void)
{
3f564: b510 push {r4, lr}
...
asm("bx %0"::"r"(*(unsigned*)0x3F004 - 0x3F000));
3f580: 4b05 ldr r3, [pc, #20] ; (3f598 <main+0x34>)
3f582: 681b ldr r3, [r3, #0]
3f584: f5a3 337c sub.w r3, r3, #258048 ; 0x3f000
3f588: 4718 bx r3
}
return 0;
}
3f58a: 2000 movs r0, #0
3f58c: bd10 pop {r4, pc}
3f58e: bf00 nop
3f590: e0001000 .word 0xe0001000
3f594: 0003f329 .word 0x0003f329
3f598: 0003f004 .word 0x0003f004
...
#Notlikethat answered my question.
I was confused assuming that pc+32 address should be loaded to r3 directly, instead of the contents of this address.

Problem calling linux C code from FIQ handler

I'm working on an armv6 core and have an FIQ hander that works great when I do all of my work in it. However I need to branch to some additional code that's too large for the FIQ memory area.
The FIQ handler gets copied from fiq_start to fiq_end to 0xFFFF001C when registered
static void test_fiq_handler(void)
{
asm volatile("\
.global fiq_start\n\
fiq_start:");
// clear gpio irq
asm("ldr r10, GPIO_BASE_ISR");
asm("ldr r9, [r10]");
asm("orr r9, #0x04");
asm("str r9, [r10]");
// clear force register
asm("ldr r10, AVIC_BASE_INTFRCH");
asm("ldr r9, [r10]");
asm("mov r9, #0");
asm("str r9, [r10]");
// prepare branch register
asm(" ldr r11, fiq_handler");
// save all registers, build sp and branch to C
asm(" adr r9, regpool");
asm(" stmia r9, {r0 - r8, r14}");
asm(" adr sp, fiq_sp");
asm(" ldr sp, [sp]");
asm(" add lr, pc,#4");
asm(" mov pc, r11");
#if 0
asm("ldr r10, IOMUX_ADDR12");
asm("ldr r9, [r10]");
asm("orr r9, #0x08 # top/vertex LED");
asm("str r9,[r10] #turn on LED");
asm("bic r9, #0x08 # top/vertex LED");
asm("str r9,[r10] #turn on LED");
#endif
asm(" adr r9, regpool");
asm(" ldmia r9, {r0 - r8, r14}");
// return
asm("subs pc, r14, #4");
asm("IOMUX_ADDR12: .word 0xFC2A4000");
asm("AVIC_BASE_INTCNTL: .word 0xFC400000");
asm("AVIC_BASE_INTENNUM: .word 0xFC400008");
asm("AVIC_BASE_INTDISNUM: .word 0xFC40000C");
asm("AVIC_BASE_FIVECSR: .word 0xFC400044");
asm("AVIC_BASE_INTFRCH: .word 0xFC400050");
asm("GPIO_BASE_ISR: .word 0xFC2CC018");
asm(".globl fiq_handler");
asm("fiq_sp: .long fiq_stack+120");
asm("fiq_handler: .long 0");
asm("regpool: .space 40");
asm(".pool");
asm(".align 5");
asm("fiq_stack: .space 124");
asm(".global fiq_end");
asm("fiq_end:");
}
fiq_hander gets set to the following function:
static void fiq_flip_pins(void)
{
asm("ldr r10, IOMUX_ADDR12_k");
asm("ldr r9, [r10]");
asm("orr r9, #0x08 # top/vertex LED");
asm("str r9,[r10] #turn on LED");
asm("bic r9, #0x08 # top/vertex LED");
asm("str r9,[r10] #turn on LED");
asm("IOMUX_ADDR12_k: .word 0xFC2A4000");
}
EXPORT_SYMBOL(fiq_flip_pins);
I know that since the FIQ handler operates outside of any normal kernel API's and that it is a rather high priority interrupt I must ensure that whatever I call is already swapped into memory. I do this by having the fiq_flip_pins function defined in the monolithic kernel and not as a module which gets vmalloc.
If I don't branch to the fiq_flip_pins function, and instead do the work in the test_fiq_handler function everything works as expected. It's the branching that's causing me problems at the moment. Right after branching I get a kernel panic about a paging request. I don't understand why I'm getting the paging request.
fiq_flip_pins is in the kernel at:
c00307ec t fiq_flip_pins
Unable to handle kernel paging request at virtual address 736e6f63
pgd = c3dd0000
[736e6f63] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT
Modules linked in: hello_1
CPU: 0 Not tainted (2.6.31-207-g7286c01-svn4 #122)
PC is at strnlen+0x10/0x28
LR is at string+0x38/0xcc
pc : [<c016b004>] lr : [<c016c754>] psr: a00001d3
sp : c3817ea0 ip : 736e6f63 fp : 00000400
r10: c03cab5c r9 : c0339ae0 r8 : 736e6f63
r7 : c03caf5c r6 : c03cab6b r5 : ffffffff r4 : 00000000
r3 : 00000004 r2 : 00000000 r1 : ffffffff r0 : 736e6f63
Flags: NzCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment user
Control: 00c5387d Table: 83dd0008 DAC: 00000015
Process sh (pid: 1663, stack limit = 0xc3816268)
Stack: (0xc3817ea0 to 0xc3818000)
Since there are no API calls in my code I have to assume that something is going wrong in the C call and back. Any help solving this is appreciated.
Here's the assembly with comments for fiq_flip_pins:
static void fiq_flip_pins(void)
{
asm("ldr r10, IOMUX_ADDR12_k");
0: e59fa010 ldr sl, [pc, #16] ; 18 <IOMUX_ADDR12_k>
asm("ldr r9, [r10]");
4: e59a9000 ldr r9, [sl]
asm("orr r9, #0x08 # top/vertex LED");
8: e3899008 orr r9, r9, #8 ; 0x8
asm("str r9,[r10] #turn on LED");
c: e58a9000 str r9, [sl]
asm("bic r9, #0x08 # top/vertex LED");
10: e3c99008 bic r9, r9, #8 ; 0x8
asm("str r9,[r10] #turn on LED");
14: e58a9000 str r9, [sl]
00000018 <IOMUX_ADDR12_k>:
18: fc2a4000 .word 0xfc2a4000
asm("IOMUX_ADDR12_k: .word 0xFC2A4000");
}
1c: e12fff1e bx lr
Unless I'm misunderstanding something, it looks like fiq_handler points to address 0, not fiq_flip_pins:
asm("fiq_handler: .long 0");
Another possible problem (assuming that there's code that fixes up the fiq_handler pointer when fiq_test is copied over) is that you have this at the end of fiq_flip_pins:
asm("IOMUX_ADDR12_k: .word 0xFC2A4000");
You'll need to have some code that jumps over that data or have your own return sequence for fiq_flip_pins prior to that data word, otherwise the CPU will try to execute whatever opcode 0xFC2A4000 is, and I imagine it's not likely to be something benign.

Resources