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.

Interrupt handling with mixed C and Assembly



I am writing C code as a separate module to be used by a bigger project written in assembly.  Currently I have interrupts triggered in assembly which calls a function in C to do necessary tasks.  But how does one get assembly to trigger an ISR in C directly using IAR Workbench?

Here is what I tried and didn't work...

(in assembly file)

EXTERN   SOME_ISR        ;C interrupt routine

RSEG  INTVEC

DW            SOME_ISR        ;isr at FFF0

RSEG  MCODE

-----------------------------------------------------------------

(in C file)

#pragma  vector=SOME_HARDWARE_VECTOR         //this caused a duplicate vector error with assembly

extern      "C"

__interrupt  void  SOME_ISR(void)

{

   Do some stuff

}

-----------------------------------------------------------------

  • Simon Yuen said:
    how does one get assembly to trigger an ISR in C

    You don't!

    The whole point of interrupts is that they are not triggered by any code at all - they are triggered directly by the hardware itself.

    But the assembler code you show is not triggering the interrupt;  it is just creating the entry in the vector table,  which is exactly what the #pragma vector in the 'C' source does - so you will obviously get a "duplicate vector" error!

    Perhaps if you explained why you're trying to do this - ie, what are you trying to achieve by it - people may be able to sugges appropriate options...

    http://bit.ly/ZXjUM3

     

     

  • Thank you for your reply Andy,

    I realized what I wrote was not clear.  What I meant to say is that I am trying to write a module in C that will be used by the main code in assembly.  What I would like to do is to have a C interrupt service routine execute directly when the associated hardware interrupt triggers.  I was only able to achieve this in a roundabout way by making a call to the C function from assembly like below.  Instead of this roundabout way, how can I get my C interrupt routine, in the previous post, linked directly to the hardware interrupt without having to CALL it from assembly? I suppose I could replace DW  SOME_HWRE_ISR with an ORG  #FFF2 and use the vector declaration in C... just thought of it. 

     

    (in assembly file)

    EXTERN   SOME_ISR        ;C interrupt routine

    RSEG  INTVEC

    DW            OTHER_ISR                   ;isr at FFEE

    DW            SOME_HWRE_ISR        ;isr at FFF0

    DW            OTHER2_ISR                 ;isr at FFF2

    RSEG  CODE

    SOME_HWRE_ISR:  CALLA  #SOME_ISR

                                       RETI

    RSEG  MCODE

    -----------------------------------------------------------------

    (in C file)

    #pragma  location=MCODE       

    extern      "C"

    void  SOME_ISR(void)

    {

       Do some stuff

    }

  • In other words, you want to run your roundabout ISR in a non-roundabout way.

    Here is way to do that. Rename you main assembly code as something_else and write a simple main in c which just calls the something_else.

  • Simon,

    All you need to do is to remove the

    EXTERN   SOME_ISR        ;C interrupt routine

    RSEG  INTVEC

    DW            SOME_ISR        ;isr at FFF0


    lines from the assembly file.

    Your error comes from the linker, which is saying "Hey, there are two vectors that go into the same location. I don't know which one is correct. I'm stopping."

  • Thanks for your reply Brian.  I understand the duplicated vector declaration and put it there on purpose to illustrate what I liked to do.  Doing what you suggested would mean too much work involved in a short time because the assembly progam is big and there are other hardware interrupt routines under INTVEC.

  • Thanks for your input OCY.  I hadn't thought of that, but I'll keep that in mind.  It may be what I should do as we move away from doing most things in assembly.  I tried to do an ORG 0FFF2h under INTVEC but the assembler gave an out of range error which made sense.  I might just keep what I have if there is no easy two or three  line code change.

  • Hi Simon,

    You had the right idea to use the ORG statement.  But because INTVEC is a relocatable segment, use the *offset* in the ORG statement, as in 0x0012 instead 0xFFF2 (assuming INTVEC is linked at 0xFFE0).

    OLD:

    COMMON  INTVEC
    DW      ISR_FOR_IRQ_AT_OFFSET_00
    DW      ISR_FOR_IRQ_AT_OFFSET_02
    DW      ISR_FOR_IRQ_AT_OFFSET_04

    NEW:

    COMMON  INTVEC
    DW      ISR_FOR_IRQ_AT_OFFSET_00
    ORG     0x0004
    DW      ISR_FOR_IRQ_AT_OFFSET_04

    Now you can declare your ISR for the 0x0002 vector in C.

    Also note that I use "COMMON" instead of "RSEG" because INTVEC is a "common" segment where all contributions to the segment start from a common point (the linker-located address).  The best formatting for INTVEC in assembly language is actually to use a simple entry immediately above each ISR, like this:

    BEST PRACTICE:

    COMMON  INTVEC
    ORG     VECTOR_NAME_FROM_HEADER_FILE
    DW      HANDLER_NAME

    RSEG    ISR_CODE
    HANDLER_NAME  RETI

    Jeff

  • Great answer Jeff.  You definitely know what you are doing.  I totally agree with what you said about the "best practice."  Unfortunately the code was written using offsets from INTVEC and I dont want to go through the trouble to change it since it works fine.  I changed the code according to your response and it was indeed what I was looking to do.  Thank you!

  • Simon Yuen said:
    Great answer Jeff.  You definitely know what you are doing. 

    Indeed, he does. And apparently he has the tiem I'm lacking, so he can make tests and provide code.

    btw, Jeff, Did I miss the moment you crossed the 10k border? Congratulations for your hard-earned guru badge. :)

  • Thanks JMG.  At my pace it took a very long time to reach 10K.  I really don't have much time for the forum, but I try to check in once in a while.  When I find a topic that really interests me, I usually make time to experiment if necessary so I can really understand the MCU.

    Simon, I'm glad I could help you.  It just so happens that several of my projects still have a few ISRs written in assembly language, and I use IAR too.

    Jeff

  • So, anytime I'm in an RSEG, the ORG directive will treat the address as an offset rather than an absolute address?

  • Jon Clark said:

    So, anytime I'm in an RSEG, the ORG directive will treat the address as an offset rather than an absolute address?

    Yes. That's correct.

    In an RSEG, the offset is from the beginning of the current contribution to that segment.

    In a COMMON, the offset is the from the linker-located beginning of that segment.

  • Jon Clark said:
    So, anytime I'm in an RSEG, the ORG directive will treat the address as an offset rather than an absolute address?

    Not only in RSEG. Also in COMMON. However, in COMMON, all code blocks into the same COMMON segment will overlap each other., so ORG is relative to the segment start
    In RSEG, all code blocks are placed one after another, and ORG is it is relative to the current RSEG code block.

    That's why interrupt vectors are in a COMMON segment: in each module, the offsets of vector contents are relative to the start of the intvec segment.

    To have ORG referring to an absolute address, it must be in an ASEG absolute segment (which is the default at the beginning of an assembly module)

    The 2.x IAR assembler documentation also states:

    "The result of the expression [after an ORG directive] must be of the same type as the current segment, i.e. it is not valid to use ORG 10 during RSEG, since the expression is absolute; use ORG $+10 instead.The expression must not contain any forward or external references."

    So in RSEG, you can use ORG only to give an address relative to the current location, not even relative to the start of the RSEG. Which makes ORG pretty much useless in RSEG (except for creating gaps)

  • okay, it's starting to make more sense now. I still need to do some reading on what the COMMON directive does. In IAR 5.6 I built just fine with no errors without using the $ and just the defined offset in the header. Do you think that it assumest the $ in the newer versions?

  • Jon Clark said:
    Do you think that it assumest the $ in the newer versions?

    No, it does not assume "$ + ".  It assumes the location of the beginning of the contribution, like this:

            RSEG    ASM_CODE
    START   EQU     $
    DELAY   MOV     #8, R15
    DEC     SUB     #1, R15
            JNE     DEC
            RET

            ORG     10   ; EQUIVALENT TO "ORG  START+10" in IAR 3 and newer

    Again, the final linker-located address of "START" might not be the beginning of the ASM_CODE segment because there can be many contributions to the ASM_CODE segment, and this contribution might not get linked in first.  As JMG concludes, ORG in RSEG is not very useful practically speaking.

    Jeff

  • I see. Thank you both very much.  If I want to add specific vectors to the vector table, using INTVEC, I would use ORG offset. This would work as long as there were no other contributions to INTVEC?

  • Jon Clark said:
    This would work as long as there were no other contributions to INTVEC?

    No other *conflicting* contributions, i.e. two of the same vector.

  • Is the term contribution referring to:

    RSEG CODE
    contribution.

    RSEG Somethingelse
    stuff

    RSEG CODE
    another contribution?

    and the idea is that we don't know where either of the contribution will reside in code space? therefor we can't use anything other than a relative address?

  • I thought we were discussing the placement of the interrupt vectors that point to the ISRs.

    The vector table is a finite length of elements starting at a fixed location, those elements being just the 16-bit addresses of where the ISR is located *elsewhere* in code space.

    So if you COMMON INTVEC and use an ORG following that, you are choosing a specific peripheral interrupt vector. And you cannot have multiples of the same interrupt vector. That was my comment.

  • Thank you. I see what you meant now.

  • Jon it seems you have come around to recognizing why we would use "COMMON" for placing vectors instead of RSEG.

    It can still work with RSEG if you place all of the vectors in one RSEG "section" or contribution and then make no other contributions to that segment.  But that's not a very modular approach.

    A COMMON segment lets you contribute to it in a modular way while still ensuring proper final addresses.  It is the right solution, where RSEG is the wrong solution.

    Jeff

  • Jeff Tenney said:
    A COMMON segment lets you contribute to it in a modular way while still ensuring proper final addresses

    We could say, in a COMMON segment, ORG means "I don't know where in memory this COMMON segment is placed, but the following information shall go to the position N bytes after start of the segment."
    Multiple contributions will overlap, so one may fill the gap left in another contribution. However, the same location may not be filled by more than one contribution. It is an overlap, not an overlay.

    in an RSEG, ORG means "I don't know where this RSEG will be placed in memory and where this contribution will be placed inside this RSEG, but the following data shall be placed N bytes from the start of this contribution (or N bytes after the last content in this contribution, creating a gap)"

    In an ASEG, ORG means "the following bytes will be placed on the given absolute address (or relative to the previous absolute content)"
    Actually, ASEG spans the full addressing range, but gaps left in it will be filled with COMMON and RSEG content.

    Since the COMMON segment intvec is been given a fixed absolute position in the linker script, the interrupt vectors relatively placed to the INTVEC start will end up at the correct absolute position.

**Attention** This is a public forum