This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Can't access CP15 coprocessor

I am trying to change the Vector Base for interrupts by pointing it to my ISRs written in C.  I call inline assembly once I have the address to write the new value to the coprocessor:

LDR r0, =fnRAMVectors

MCR p15, 0 r0, c12, c0, 0

Everything compiles okay, but when I run the program, I get an "Illegal Instruction" error and the program crashes.  The Cortex-A8 Tech Ref says there is a pin CP15SDISABLE which must be high in order for a user to access the registers within CP15.  How do I access these?  Other relevant facts - I am running this under the Angstrom distro of Linux on a BeagleBone.  Is this maybe an issue with running a program from user space rather than kernel space?  If so, how can I get around that?

Thanks, Jamie

  • Hi Jamie, 

    CP15SDISABLE pin is just about disabling write access to CP15 registers and you dont have to worry about that in this context.

    Coming to your query, 

    You are actually populating the wrong value in the Vector Base Addr Register (VBAR). The VBAR shall contain the base address of the vector table and I think you are giving the base address of the ISR function pointer table. When an IRQ happens, the core will actually "execute" the instruction at the location (VBAR + IRQ_offset_in_Vector_Table). And, in your case, it will try to execute the instruction at (fnRAMVectors + 0x30). If you had populated the ISR address there, it will not find any valid instruction and it will abort.

    Regards,

    Sujith.

  • Thanks Sujith, I think I am beginning to understand.  However, the MCR instruction is what causes the abort so I still cannot write to the CP15 registers.  For that matter, I cannot read it either, but I can read other CP15 registers.

    But it sounds like I may not need to touch this register value since I should not be modifying the VBAR.  My question is simply this: how do I get the processor to "know where" my custom ISR code is?  How do I link up the (VBAR + IRQ_offset address with the actual routine that could end up anywhere in memory at run time?  Do I need to insert a branch instruction at the (VBAR + IRQ_offset) to jump to the ISR subroutine?

    Thanks, Jamie

  • Jamie,

    Can you try reading the value in the VBAR register and see whether the instruction succeeds without any error?

    MRC p15, 0 r0, c12, c0, 0

    If this also is failing, can you check which is the current CPU mode in which you are running you code? 

  • Hi Renjith,

    I cannot read it either.  Talking it over with a colleague this morning and he says that it is not possible to access these registers from Linux user space.  Which means if I want to access them, I would have to write a kernel module and recompile.  I'm not really sure how to do that, so if there is another way, that would be preferred.  Thanks!

    -Jamie

  • Jamie,

    I thought you were trying this from a boot loader or inside kernel module. This will never work sitting in user mode. This totally against user space/kernel space funda. :)

  • Is there a way around this without getting into kernel space?  I have been working with micros for most of my career and not too familiar with Linux.  Thanks.

  • Jamie,

    Why are you attempting this? In kernel generally the handler sits in the address 0xFFFF_0000 as virtual memory is enabled and the kernel address space is from 0xC000_0000 to 0xFFFF_FFFF. So, if you want you custom handler to be running, you can copy the handler to the address 0xFFFF_0000 and get things done. It will override the kernel's handler. 

    In any case it will become tough to handle this situation. 

    And if you a particular address mentioned in your code to become the handler it will be really tough to achieve without the kernel getting crashed. Primarily because kernel expects bare minimum exception handlers to be running for successful execution of applications and kernel itself.

    1. IRQ handler -> This is required for handling timer interrupts which is the heart of the OS.

    2. Software Interrupt -> This is required to implement system calls from your application. System calls are required for even the simplest application. 

    So I don't think its easy to implement this. If you can explain the problem that you are trying to solve, I might be able to help you.

  • Thanks Renjith.  What I am trying to do is write handlers for specific hardware interrupts within the MPU.  For example, I want to be able to calculate motor speed by responding to the eQEP timer interrupt, grab the latched value in the register and do some calculations based on the encoder line count to find motor speed.  I would be doing similar things custom code for eCAP, PWM, SPI, and I2C.  What I don't understand is how to point the interrupt vector to the functions I am writing.

    This is very easy in PIC and similar micros that I am used to dealing with, but now that Linux is in the picture (a non-negotiable part of this project), it is confounding me a bit.  I really appreciate the help.

    Thanks, Jamie

  • Hi Jamie, 

    So, We understand you are looking for interrupt handling in a non-OS enviornment.

    In StarterWare_02.00.00.07, you can find the vector table in system_config/armv7a/am335x/startup.c. As you got , the IRQ offset from the base shall contain the Branch instruction to the IRQHandler. This is defined in system_config/armv7a/am335x/gcc/exceptionHandler.S. The IRQHandler only reads the SIR_IRQ register to figureout which interrupt happened (it gives the interrupt number) and reads the corresponding ISR address from the array fnRAMVectors[] . Then jumps to the handler.

    For eg., if your timer interrupt number is 7, when we register the Timer ISR, fnRAMVectors[7] will be written with the address of Timer ISR. When the timer interrupt happens, SIR_IRQ will contain 7. Now the IRQ handler jumps to the function defined in fnRAMVectors[7]. Hope this helps

    Regards,

    Sujith.

  • I will give this a try.  But I think it will still need to be adapted somehow to work under Linux.  I see that is manipulating the CP15 register and I already know I can't do that from user space.  Do I have to write a kernel module to get this done?  Or better yet, can I just leave the Vector Base Address where it is and link the IRQHandler function to that address?  Thanks.

    -Jamie

  • Also, can you explain the meaning of the vector table.  Specifically, what are the first instructions that move the PC around used for?  Why jump forward by 0x18 - what does that do?  Thanks.

  • Hi Jamie, 

    StarterWare will not work with Linux. And I am confused why you have to worry about these vector tables/VBAR in Linux. Its already implemented its own way. 

    And, regarding the vector table, how IRQs will be handled etc. are explained in ARM Architecture Reference Manual in detail. You can also look at the StarterWare implementation in the source files I gave earlier.

    Regards,

    Sujith.

  • Jamie,

    Interrupt handling is done in a totally different way in Linux. The exception handlers that you've written are not really mapped to interrupts. If you've gone through ARM architecture reference manual, the exceptions are

    a. Reset
    b. Data Abort
    c. Prefetch Abort
    d. Undefined Instruction
    e. Software Interrupt
    f. IRQ
    g. FIQ


    Out of this only IRQ and FIQ corresponds to device interrupts, especially IRQ. So the flow of interrupt is like this.

    In the IRQ exception handler, you've to read the AM335x interrupt controller registers to determine which peripheral has generated the interrupt. From there you can identify your peripheral, and then you've to jump to the handler of the particular interrupt.

    So, to write a handler for your particular device, then you've to really explore Linux interrupt handler subsystem.

  • Okay, thanks.  It looks like I am barking up the wrong tree with my current approach and will need to take the time to learn a lot more about Linux.  I am very used to doing this by directly manipulating memory due to my uC background, but it sounds like I am going to break something else in Linux if I try it this way.  I will start reading up on the LInux interrupt handler.

    Thanks to both of you for taking the time to help me out.

    -Jamie