• Join
  • Sign In with my.TI Login
Texas Instruments
  • Products
  • Applications
  • Tools & Software
  • Support & Community
  • Sample & Buy
  • About TI
Sample & Purchase Cart Sample & Purchase Cart
  • Search
  • Advanced
TI E2E™ Community
  • Support Forums
  • Blogs
  • Groups
  • Videos
  • 简体中文
  • More ...
TI Home » TI E2E Community » Support Forums » Microcontrollers » Hercules™ Safety Microcontrollers » Hercules™ Safety Microcontrollers Forum » Trying to find a solution for this interrupt problem
Share
Hercules™ Safety Microcontrollers
  • Forum
  • E2E Wiki
Options
  • Subscribe via RSS

Forums

Trying to find a solution for this interrupt problem

This question is answered
Cor Jansen
Posted by Cor Jansen
on Aug 28 2012 12:27 PM
Intellectual665 points

Hi,

I have an interrupt problem for which I'm trying to find a solution.

I need a fastLoop() function that has to be called each 1 mS and a slowLoop() function that has to be called each 10 mS. The slowLoop can run for more than 1 mS and thus must be interruptable bij the fastLoop. Both loops need to be interrupt based.The FIQ is already in use so I can only use the IRQ level interrupt for this. I hope I do not  need to go into the compete re-entrant IRQ solution.

I may have a solution, but I'm not sure if it will work. This is the idea:

An IRQ is generated each 1 mS and the irqRoutine() interrupt function is called.This irqRoutine() does the following:

  • First the fastLoop() function is called so it can do its work.
  • After the fastLoop() function a counter is incremented.
  • When this counter reaches 10 the counter wil be reset and the slowLoop() function is called.
  • Return from IRQ

The slowLoop() function will do the following:

  • Save the current IRQ interrupt masks
  • Enable only the irqRoutine() IRQ mask
  • Re-enable the processor IRQ interrupts
  • Doing the slowLoop stuff
  • Disable the processor IRQ interrupts
  • Restoring the IRQ interrupt maks

This way a new irqRoutine() interrupt can interrupt the previous one as soon as it is doing the slowLoop stuff. I can make it such that the new  irqRoutine() wil never call the slowLoop() if the previous slowLoop() is not finished yet. So the irqRoutine is only interrupted by one new irqRoutine().

Will this work?

Or do I need to do more than just enabling a new irqRoutine() inside the irqRoutine()?

Report Abuse
  • Reply
You have posted to a forum that requires a moderator to approve posts before they are publicly available.
All Replies
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 28 2012 12:56 PM
    Expert4800 points

    Hi Cor,

    The most straightforward way to attack this (in my opinion) would be with an RTOS.    You could try FreeRTOS if you have an RM4x or TMS570 and are using the HalCoGen program. 

    Recipe with FreeRTOS would be something like this:

    a) Set the RTOS tick to 1ms

    b) Create two semaphores, one for 'fastLoop' and one for 'slowLoop'.

    c) Implement a 'tick hook' function that simply calls a post to the semaphore for 'fastLoop' every time there is a tick, and a post to 'slowLoop' semaphore every 10th time.

    d) Configure fastLoop to be higher priority than slowLoop  (priority is a parameter in the task create function)

    e) fastLoop and slowLoop should first sleep pending their respective semaphores.  then they should do their function, and finally loop back to the pend.

    The above recipe should create exactly the function that you want,  and you won't have to worry at all about the details of implementing ISRs  (RTOS will handle this for you).

    Best Regards,

    Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 28 2012 14:24 PM
    Intellectual665 points

    Thanks Anthony,

    I have difficulties with the overhead of the RTOS. In my example I talked about 1 mS and 10 mS. But in fact I want this as fast as possible. I need very low overhead.

    I have the feeling that it is not very difficult. But I do not have much expertise on this subject. So I hope someone can tell me if it can be done this way.

    I assume the stack is in a state that new interrupts may occure (The FIQ can ocure too). But maybe some IRQ related information is not stored on the stack but in registers. This info would get corrupted when a nested IRQ occurs. But if I know what this info is, it is probably easy to save/restore it before/after the enabling of the nested IRQ.

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 28 2012 14:54 PM
    Expert4800 points

    Hi Cor,

    Ok, understand. 

    Are you using the TI compiler?  I think it'll do the job for you if you use the interrupt keyword before your ISRs, I gave it a try with the simple test program below.

    2500.main.c

    0825.main.lst

    The "LR" is a usual suspect for trouble when it comes to not being saved.   And function calls inside ISRs are also another trouble case.

    You can see how the test program compiles;  and if you want more detail we should add someone from our compiler team to this thread.  (the compiler manual doesn't go into a whole lot of detail other than saying interrupt makes the compiler pessimisticallys save registers.

    Best Regards,

    -Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 28 2012 15:30 PM
    Intellectual665 points

    Hello Anthony,

    Yes I use the TI compiler.

    I'm not sure what your are trying to tell with the example. Could you please explain a little more?

    I found some information in the Technical Refercence Manual. There seem to be some special irq registers, like:

    • SPST_irq
    • SP_irq
    • R13_irq
    • R14_irq

    But it is not clear to me if these are all seperate registers, or maybe some are different names for the same thing. It is also not clear to me if I need to save/restore these registers for the one level nesting.

    If this is the case, how can I save/restore these registers using C-language? Or do I need to write assembly code for this. If so, can you help me out here?

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Zhaohong Zhang
    Posted by Zhaohong Zhang
    on Aug 28 2012 15:57 PM
    Genius9255 points

    Cor,

    You may consider this option if you do not like RTOS.

    (1) Generate a RTI interrupt every 1 ms. Increment an global variable (Counter, for example) and do the fast loop processing in the ISR.

    (2) Poll global  variable "Counter"  in main loop.When it reaches 10, reset the value to zero and start the slow loop processing.

    This approach has minimum CPU overhead. However, you need to do the following analysis to see if it work for you.

    (1) What is the latency tolerance for the slow loop processing in your application?

    (2) What is the maximum delay caused by other processing or interrupt ISRs in polling the global variable "Counter"?

    The approach only works if the value in (1) is larger than the value in (2).

    Thanks and regards,

    Zhaohong

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 28 2012 16:06 PM
    Intellectual665 points

    I did some more investigations.

    If I understand correctly, the R13_irq, R14_irq and SPST_irq are banked registers valid during IRQ processing. SP_irq seems another name for R13_irq.

    So when I save/restore the R13, R14 and SPST when in IRQ mode, It should be possible to nest one level of IRQ. Right?

    So how can I save/restore these registers? Can I just push/pull them on the stack? If so, how? (I have no experience with ARM assembly:-).

    Or can I easily write C-code to save/store these registers?

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 28 2012 16:09 PM
    Intellectual665 points

    Thanks, Zhaohong,

    The problem with that is that both the slowLoop and fastLoop need to be interrupt based. The main loop is already busy doing other things and uses functions that run relatively long times.

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 28 2012 16:17 PM
    Expert4800 points

    Hi Cor, 

    Yes, for the most part**, when you use R13 in an instruction, it means R13 of the mode you're in.

    Since the IRQ puts you into IRQ mode,  the code that executes in the IRQ routine will be saving R13_irq if it saves R13.

      ** there are some very special instructions that access registers of another mode;  but that's not relevant here.

    The example code I posted had several cases:

      - isr written as a 'c' function without interrupt keyword

      - isr with interrupt keyword

      - isr with interrupt keyword, also calling another function inside the isr.

    The .lst file has the generated assembly so you can see which registers are saved.


    The cases where the interrupt keyword is used clearly show that the compiler is saving registers that it wouldn't ordinarily save for a regular function call.

    For example,  LR is getting saved because if there's another IRQ it would overwrite LR_irq.    You would be able to return back to the first IRQ, but not back to the user mode code that was interrupted in the first place,  unless you save LR_irq onto the stack.     A regular user mode function doesn't need to do this because if it is interrupted, the LR used by the interrupting function will be the banked LR_irq not the user mode LR.

    To me the compiler examples look fine except I don't see SPSR being saved;  I think that it might be saved as a side-effect of one of the instructions that's listed but trying to dig this up.

    -Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 28 2012 16:53 PM
    Expert4800 points

    Hi Cor,

    So the trick I was thinking of might be that if you include R15 in a LDMxx instruction, then this causes SPSR to also be written back to CPSR.

    Not finding a trick that works in the other direction;  but it still might be there.   I'll want to discuss this with our compiler team.

    In the meantime, I think that if you use the interrupt keyword from the compiler but also insert some assembly instructions to push and pop SPSR from the stack in addition to what the compiler saves then you should be OK.   Also you should probably save the floating point status register if you are using floating point in your interrupt routines.

    You can look at the context switch code in the HalCoGen FreeRTOS port to Cortex R4 source code:   <HalCoGen_Instlal>\drivers\FreeRTOS\ports\Cortex-R4.  The file portasm.inc has macros to save and restore context.  You can see SPSR is being saved and restored explicitly  (the mrs instruction moves SPSR to R0 and then the stmfd instruction moves this to the stack.)

    Will let you know what we find out re. the compiler.

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 28 2012 17:21 PM
    Intellectual665 points

    Thanks Tony,

    I will investigate this keyword ussage.

    I still would like to be able to read/write the registers SP (=R13), LR (=R14) and SPSR.

    I was thinking using the inline assembly feature like this:

    uint32_t readRegXXX()
    {
         __asm("  ......"); // assembly instruction to move the register to read to register A1 (result register)
    }

    void writeRegXXX(uint32_t value)
    {
         __asm("  ......"); // assembly instruction to move register A1 (argument register) to the register to write
    }

    Then I only need to know the assembly instructions to move:

    • SP -> A1
    • LR -> A1
    • SPSR -> A1
    • A1 -> SP
    • A1 -> LR
    • A1 -> SPSR

    I do not know where to look for the correct assembly instructions (RM4 processor). Tried several document, whithout success.

    Could you help me with this?



    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 28 2012 18:10 PM
    Expert4800 points

    Hi Cor,

    To get details on the instruction set, you want the ARM document DDI0406 (ARM® Architecture Reference Manual ARM®v7-A and ARM®v7-R edition)    You can get that from ARM's website.  Sorry I can't really put a link here directly to it;  link I copied goes to a login page (for this particular document, you need to be registered on their site and logged in).

    The instruction to copy the SP into A1 is just "MOV".  It's documented on in section A8.6.97 in the version that I've got access to.

    I would probably write your ISR and also your register read/write function completely in assembly or completely in C the more I think about it.

    A good example of assembly functions that you call from C is the 'sys_core.asm' file that contains assembly functions called from the 'sys_startup.c' file; both of these are output by HalCoGen.  You can see how the assembly function '_coreInitRegisters_' is called by the C routine c_int00() for example.

    As an aside, assuming that you write your interrupt handler in assembly also, I noticed in DDI0406 that there are two instructions added to ARMv7  for saving and restoring both LR and SPSR at the same time.   These would be "SRS" (store return state) and "RFE" (return from exception) respectively.   From the psuedo code, it looks like you would need to subtract 4 from LR before the SRS on IRQ entry;  but I haven't tried this myself so might be wrong.   (for IRQ, the value you return to is always LR - #4 to compensate for the pipeline).

    Best Regards,

    Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 29 2012 16:09 PM
    Intellectual665 points

    Now I'm completely confused!

    I did a simple test:

    Just enabling the IRQ (_enable_IRQ();) at the beginning of an IRQ handling function and disabling it at the end (_disable_IRQ();). No register saving/restoring at all.

    Then I generated an IRQ interrupt request during this interrupt handling routine. And it works!!!? The IRQ routine is interruptable by a new IRQ request. No crashes. As far as I can see now: correct code execution!

    How is this possible? As far as I understand the stack and link register en status registers are not saved during IRQ calls because there are sepperate IRQ bank registers. Or does TI (I use TI compiler and HalCoGen) save these registers on the stack enabling nested IRQ request?

    I tried to find out looking at te code, but I can not find the IRQ handling assembly source. Most code is generated by HalCoGen.

    Where can I found the low level IRQ handling code?

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 29 2012 19:16 PM
    Expert4800 points

    Hi Cor,

    Here is an example IRQ handler from HalCoGen.  I added some code (between the /* USER CODE BEGIN */  and END comments... to test nesting.

    /** @fn void rtiCompare0Interrupt(void)
    *   @brief RTI1 Compare 0 Interrupt Handler
    *
    *   RTI1 Compare 0 interrupt handler
    *
    */
    #pragma CODE_STATE(rtiCompare0Interrupt, 32)
    #pragma INTERRUPT(rtiCompare0Interrupt, IRQ)

    void rtiCompare0Interrupt(void)
    {
    /* USER CODE BEGIN (74) */
        static unsigned int x, y;
    /* USER CODE END */

        rtiREG1->INTFLAG = 1U;
        rtiNotification(rtiNOTIFICATION_COMPARE0);

    /* USER CODE BEGIN (75) */
        _enable_IRQ( );
        for (x = 0; x < 1024*1024*1024; x++) {
            y = x;
        }
    /* USER CODE END */
    }

    I had to manipulate the PC a little to jump around the _enable_IRQ() on the second time I hit the ISR and then set the interrupt disable bit again when returning from the first nest, in order to get back to the main loop.

    But what I found confirmed my suspicion,  in this HalCoGen project you enter main() in SVC mode.  But after nesting the RTI interrupt without saving the SPSR register during nesting, when you return to main you are in IRQ mode and not SVC mode.   The original value in CPSR before the interrupt is lost.

    I suspect you might find the same on your setup. 

    Note that this doesn't mean there is a crash.  In fact I think my simple test case that has just while(1) in the main loop after enabling interrupts would run forever and be just fine.   Yet the main loop would be using the IRQ mode stack and not the SVC stack like it is supposed to.

    Since we used the #pragma INTERRUPT()  and declared the RTI handler as an IRQ,  which I believe is functionally equivalent to using the 'interrupt' keyword in the function definition, the LR is being pushed onto the stack and restored on exit.     If the LR were not saved, then an obvious 'crash' would be much more likely.

    The effects of corrupting SPSR are harder to see;   running with the wrong stack and possibly wrong status flags will not create a problem unless some other condition happens that makes use of these resources.    A while(1) loop as I've got doesn't make use of the stack and doesn't make use of status flags so nothing bad happens.

    But you can't build on this because when you start calling functions and doing arithmetic, the SPSR corruption will soon become evident.

    I did open a ticket in our internal compiler support forum regarding the suggestion to support nesting.   I believe all that you need to add to the ISR that has the interrupt keyword is a save & restore of SPSR.   But the best way to accomplish this will probably need some discussion.   I will let you know.   

    In the mean-time, I think you can add in inline assembly these instructions:

        SRSFD SP!, #0x12

        ... enableIRQ() ...

        RFEFD SP!

     The "FD" suffix after the SRS and the RFE is the type of stack, compiler uses FD (full descending).

    The #0x12 is the M value for IRQ mode, SRS needs to know which banked registers to save.

    I haven't tried the above yet myself so there might be a typo... I'll try now and post if this works as expected.

    -Anthony

    Hope this makes sense  & thanks for your perseverance.

    Best Regards,

    Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Anthony F. Seely
    Posted by Anthony F. Seely
    on Aug 29 2012 19:43 PM
    Expert4800 points

    Cor,

    I ran into a different issue with the SRSFD instruction inline assembly that I need to run down, assembler just accepted SRSFD SP!   (no 0x12) and the instruction generated had mode bits set to 0.  Looked like nothing happened when I executed this.  

    So, next best thing below,  adding these lines in RED saves SPSR and it's restored on exit of the ISR.   I don't like having to use R0 in inline assembly because you really don't know if the compiler is using it for some other purpose, but putting these as the first instruction right after entry of the ISR they come after R0 is stored on the stack.
    So for now, I'd try the below.   I confirmed that when the save & restore of SPSR is added, now even after nesting when I return to the main / foreground loop the CPU is back in the correct mode (SVC) with the original CPSR value from before the interrupt.

    void rtiCompare0Interrupt(void)
    {
    /* USER CODE BEGIN (74) */
        static unsigned int x, y;
        asm("   MRS R0, SPSR ");
        asm("   STMFD SP!, {R0} ");
    /* USER CODE END */

        rtiREG1->INTFLAG = 1U;
        rtiNotification(rtiNOTIFICATION_COMPARE0);

    /* USER CODE BEGIN (75) */
        _enable_IRQ( );
        for (x = 0; x < 1024*1024*1024; x++) {
            y = x;
        }
        asm("   LDMFD SP!, {R0} ");
        asm("   MSR SPSR, R0 ");
    /* USER CODE END */
    }

    note: there's some bugs in the above code in the for loop ... I used the 'move to line' option in CCS to jump around the loop to test,  anyway pls. only copy the assembly statements in RED.

    Thanks and Best Regards,

    Anthony

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
  • Cor Jansen
    Posted by Cor Jansen
    on Aug 30 2012 03:45 AM
    Intellectual665 points

    Thanks Anthony.

    You are right!

    I did not much in the main loop at first and the nested IRQ seemed to work. Byt when I start to do more inside the main loop the nested interrupt doesn't work correct anymore.

    So I tried your solution, but the IRQ doesn't seem to do its job anymore (while the main loop keeps running). Even when not using the nested interrupts. So somehow the IRQ routine is corrupted.

    So I tried this:

    uint32_t savedSPSR[10];
    uint32_t savedSPSRptr = 0;

    uint32_t read_SPSR()
    {
        __asm(" MRS A1,SPSR");
    }

    void write_SPSR(uint32_t value)
    {
        __asm(" MSR SPSR,A1");
    }

    void gioNotification(int bit)
    {
        // to make the gioNotification (IRQ) re-entrant, we have to save and restore the SPSR
        savedSPSR[savedSPSRptr++] = read_SPSR();


        ... some interrupt stuff


        _enable_IRQ();


        ... my interruptable interrupt stuff


        _disable_IRQ();


        // restoring the SPSR
        write_SPSR(savedSPSR[--savedSPSRptr]);
    }

    Now it works! The main loop seems to continue and I use nested IRQs. I know it is a dirty implementation but it was just for testing. I prefer to store the SPSR on the stack.

    I will test a bit further to be sure all is ok.



    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
123
TI E2E™ Community
  • Support Forums
  • Blogs
  • Videos
  • Groups
  • Site Support & Feedback
  • Settings
TI E2E™ Community Groups
  • TI University Program
  • Make the Switch
  • Microcontroller Projects
  • Motor Drive & Control
Other Communities
  • Deyisupport
  • Designsomething.org
  • beagleboard.org
  • TI on Element 14
  • TI on TechXchangeSM
Other Technical & Support Resources
  • WEBENCH® Design Center
  • Product Information Centers
  • Technical Documents
  • TI Design Network
  • TI Technical Articles
  • TI Training

All content and materials on this site are provided "as is". TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with regard to these materials, including but not limited to all implied warranties and conditions of merchantability, fitness for a particular purpose, title and non-infringement of any third party intellectual property right. TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with respect to these materials. No license, either express or implied, by estoppel or otherwise, is granted by TI. Use of the information on this site may require a license from a third party, or a license from TI.

Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms of Use of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms of Use of this site. TI, its suppliers and providers of content reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.

Follow Us Texas Instruments on Facebook Texas Instruments on Twitter Texas Instruments on LinkedIn Texas Instruments on Google+
TI Worldwide | Contact Us | my.TI Login | Site Map | Corporate Citizenship | mobile m.ti.com (Mobile Version)

TI is a global semiconductor design and manufacturing company. Innovate with 100,000+ analog ICs and
embedded processors, along with software, tools and the industry’s largest sales/support staff.

© Copyright 1995-2013 Texas Instruments Incorporated. All rights reserved.
Trademarks | Privacy Policy | Terms of Use