开发者

Branch to Interrupt Handler on Microblaze CPU, Assembly Language

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)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜