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.

HWI ISR return context lost!

Hi, I'm using the DM6437 here.

I'm setting up a simple ISR to handle I2C interrupts. I've used DSP/BIOS to set up the interrupt using the dispatcher:


bios.HWI.instance("HWI_INT6").interruptSelectNumber = 83;
bios.HWI.instance("HWI_INT6").fxn = prog.extern("I2CIsr__Fv");
bios.HWI.instance("HWI_INT6").useDispatcher = 1;
bios.HWI.instance("HWI_INT6").interruptMask = "all";


My ISR (does nothing at the moment except check the I2C interrupt type):

void I2CIsr(void)

{

    unsigned int    I2CIntVec;            // I2C interrupt vector register


    // get I2C interrupt status
    g_RegRead = ICSTR_R;
    I2CIntVec = ICIVR_R;

}

I enable the interrupt a thread that loops, doing the main work of the program:

int MainLoop()

{

    //setup ...

    // enable interrupt 6
    C64_enableIER(C64_EINT6);

    // main loop

    while (1)
        m_Manager.RuntimeUpdates();

}

The I2C interrupt fires, the ISR runs OK, but when it exits the CPU either:

* resets to c_int00 through HWI_Obj HWI_RESET (I stepped through)

* stops at the end of the ISR

* jumps somewhere seemingly random with corrupted context (this has only happened once so far)

 

A possible problem: I'm using NDK 2.00 which is running in the background. NDK has it's own running thread (s?) and uses a CLK_F_isr on INT14.

 

I'm not sure where to start here to narrow this down. I'd love to get your ideas about the probable cause. I've read that the BIOS dispatcher is supposed to handle HWI_enter / exit in order to save and restore the ISR calling context, but it looks like something is going wrong with that.

 

Thanks for considering my problem. Please let me know if I can supply added information that would clarify.

- Craig

 

Some system details if they help:

CCS 3.3.82.13 SR12

BIOS 5.41.11.38

CGTools 7.0.4

Coding in C++

 

  • Craig,

    We will have this moved to the BIOS forum since it is more likely some of the BIOS experts will have better answers than what I can give you here.

    My recommendation is to remove the direct lines in the .tcf file that you have written above, and use the DSP/BIOS GUI to setup your interrupt. Be sure to insert _ in front of the function name where you list it in the GUI.

    If you are getting to your ISR, then you have the interrupt setup correctly. Something must be missing for what the BIOS HWI_Dispatcher is doing for you. I do not see if from the lines above, so my recommendation comes from my trust that the GUI will insert all the commands you need to make this work. The HWI_Dispatcher does exactly as you said, and it should handle all entry/exit issues.

    Please let us know how this turns out if you try the GUI.

    Regards,
    RandyP

     

    If you need more help, please reply back. If this answers the question, please click  Verify Answer  , below.

  • Craig,

    Is it possible that the stack being used by the loop is overflowing when the interrupt occurs? Try increasing the task stack size ans see if the problem goes away.

    Also, it could be that the HWI stack is so small that the ISR thread overflows the HWI stack. Try increasing the HWI stack size if increasing the task stack size doesn't work.

    Alan

  • Thank you both for your responses and for moving my post to the right forum. Randy, I am actually using the DSP/BIOS GUI. I was just copying the relevant lines from the .tcf file into my post.

    In my debugging today, I've seen a number of odd behaviors. I started by removing my main loop and replacing it with just an incrementing variable and some register reads I can step through.

        unsigned int i = 0;
        while (1)
        {
            g_RegRead = EVTFLAG2_R;
            g_RegRead = INTMUX1_R;
            g_RegRead = IER;
            g_RegRead = CSR;
            i++;
        }

    NDK is still running in the background (NC_NetStart has been called), but it's not doing anything at this point except using the Daemon to monitor for socket traffic (at which point it would launch my server task thread).

    Here's what I found:

    • With the I2C HWI's int mask set to self (IE 6 or 0x0040), the ISR does not exit if I place a breakpoint in the ISR, hit it, and 'run' from there.
    • However, if there is no breakpoint, the ISR runs and exits (I can have it increment a counter), but upon returning to the main loop, IER.IE 6 is disabled.
    • If I did put a breakpoint at the end of the ISR, the IRP pointer is set to the last breakpoint I stopped at. If I hit "step into" it loops to this address. If I "run" it halts at the end of the ISR. Occasionally, attempting to halt this infinite run causes the emulator to crash.
    • With the int mask at bitmask (0x4040), disabling both 'self', my interrupt (IE6) and the CLK_F_isr period clock (IE14), the ISR exits. But this effectively disables the DSP/BIOS heartbeat, right? When it reenters the main loop, IE6 and IE14 are still disabled.

    Is there something I need to actively do to return properly from a HWI dispatched ISR to a running TSK thread?

    Alan, my main loop TSK size was 4096 MAUs (does MAU = 1-byte or 4 here?). My MEM global stack size was 5120 MAUs. I increased these to 8192 MAUs each with no noticeable change.

    I found the article: http://processors.wiki.ti.com/index.php/DSP_BIOS_Debugging_Tips. I tried using the Kernel Object Viewer to see if any stacks were overflowing. None appeared to be even close. The DM6437 doesn't have AET so I couldn't check the stack that way.

    You mention increasing the 'HWI stack' size. Forgive me, I'm not sure how to do that. I don't see a stack size option in the BIOS editor for a HWI. Could you explain please?

     

    Thanks for the help,

    - Craig

  • Craig,

    The HWI stack I referred to is the same as the MEM global stack size. After main(), the C stack is used as the HWI stack. So your experiment likely ruled out a HWI stack overflow.

    By design, you should not have to actively do anything in your ISR to properly return to the interrupted TSK thread. As long as your ISR is written in C (or C++) the dispatcher should handle all of the register context save and restore requirements.

    I haven't concluded anything yet but I think you may have another interrupt firing in your application that is NOT managed by the BIOS interrupt dispatcher.

    "If I did put a breakpoint at the end of the ISR, the IRP pointer is set to the last breakpoint I stopped at. If I hit "step into" it loops to this address. If I "run" it halts at the end of the ISR"

    I think this can only happen if another interrupt occurs between the two breakpoints and that the other interrupt is NOT restoring the IRP properly, which leads me to suspect it is NOT being handled by the BIOS interrupt dispatcher. However, your initial post indicated that you configured your interrupt to disable ALL other interrupts while your ISR is running. After halting at the first breakpoint in your ISR, what value does the IER register have?

    Alan

  • Hi Alan,

    I made an IER read the first statment in my ISR. The value was:

    0x0000 582B, so IE 14, 12, 11, 5, 3 (reserved), NMIE, and RST are on. This is with my IE6 int mask set to 'self'.

    By the way, this conflicts with the C64x CPU Guide (SPRU732) which says that reserved bit [3] will always read as 0.

    IE14 is CLK_F_isr, assigned in INTMUX3 to EVT4 "Timer 0 - TINT12". I think this is required for DSP/BIOS.

    IE 12 is _HSRTDX_rec, assigned in INTMUX2 to EVT12 EMU_RTDXTX.

    IE 11 is _HSRTDX_xmt, assigned in INTMUX3 to EVT11 EMU_RTDXRX.

    I don't know if RTDX is being used, it came enabled in the BIOS and I didn't alter it. I'm connecting to the DM6437 using a SD XDS560R on 14-pin JTAG.

    IE 5 is undefined in the BIOS, assigned in INTMUX1 to EVT43, or EMACINT "EMAC Memory Controller". So it appears that NDK uses this interrupt. The NDK HelloWorld example project (which my code grew from really) doesn't have IE 5 registered in BIOS either.

    IE 3 is _RTDX_Poll in BIOS.

    BIOS also has a NDK heartbeat PRD object calling _llTimerTick every 100ms. I don't know if that counts as an interrupt.

    A little background on my application: The DM6437 is running image acquisition and processing for our company's custom image sensor. We've had great success using it as an Ethernet-based device. Our customers have asked for an I2C comm link as well for embedded systems, so we're trying to add that now.

    I tried setting my ISR's bit mask to 0x0060, which should disable 'self' and IE5. No change in behavior I can see. It still exits (once) if there's no breakpoint with IE6 disabled. Now IE5 is disabled as well, so whatever interrupts are disabled by HWI, stay disabled afterwards. Still also does the crazy 'step in' vs. 'run' behavior.

    Thanks!

    - Craig

  • Craig,

    I'm a little troubled by IE 5 not being managed by BIOS. I'll check into this locally and get back with you.

    Alan

  • Hi Craig,

    I'm curious about your observation:

    Craig Neely said:
    So it appears that NDK uses this interrupt. The NDK HelloWorld example project (which my code grew from really) doesn't have IE 5 registered in BIOS either

    Could you please elaborate on this?  I'm just wondering how you determined this?

    The Ethernet driver for the dm6437 in NDK 2.0.0 registers interrupt vector ID 5 for transmit and receive as follows:

    a) First in: ndk_2_0_0\packages\ti\ndk\src\hal\evmdm6437\eth_dm6437\ethdriver.c:

    line 48:

    static      Uint32                      IntVector = 5u;     /* Interrupt vector to use */

    then at line 865 - 873:

        /* Setup the Rx/Tx Int using NDK's Interrupt Manager */
        hwi_intSetup.intVectId = IntVector;
        hwi_intSetup.sysEvtCount = 1;
        hwi_intSetup.sysEvtId[0] = 43;
        hwi_intSetup.pCallbackFxn = &HwInt;          // <-- note that the ISR is set to HwInt
        hwi_intSetup.pCallbackArg = 0;
        hwi_intSetup.bEnable = 1;

        retVal = Interrupt_add(&hwi_intSetup);

     

    b) Then in: ndk_2_0_0\packages\ti\ndk\src\os\intmgmt.c

    Uint32  Interrupt_add(IntSetup* myIntSetup)
    {
        Uint32      i, vectid;
        HWI_Attrs   hwi_attrs;
        ECM_Attrs   ecm_attrs;

        if(!myIntSetup || !myIntSetup->sysEvtCount || !myIntSetup->pCallbackFxn)
        {
            /* Invalid Interrupt setup arguments. Return error */
            return 1;
        }

        if(myIntSetup->sysEvtCount == 1)
        {
            /* One to One mapping between the system event and the
             * interrupt vector. We can use HWI module of the BIOS
             * to configure this interrupt.
             */
            if( myIntSetup->intVectId > MIN_INTVECT_ID && myIntSetup->intVectId < MAX_INTVECT_ID )
            {
                HWI_eventMap(myIntSetup->intVectId, myIntSetup->sysEvtId[0]);

                if(myIntSetup->pCallbackArg)
                {
                    hwi_attrs.intrMask = 1; /* default value */
                    hwi_attrs.ccMask   = 1; /* default value */
                    hwi_attrs.arg = (Arg)myIntSetup->pCallbackArg;
                    HWI_dispatchPlug(myIntSetup->intVectId, (Fxn)myIntSetup->pCallbackFxn, -1, &hwi_attrs);
                }
                else
                    HWI_dispatchPlug(myIntSetup->intVectId, (Fxn)myIntSetup->pCallbackFxn, -1, 0);
            }
            else
            {
                /* The interrupt vector configured is invalid. Only
                 * MAX_NUM_INTERRUPTS (128) are defined by C64x+ devices.
                 * Return error.
                 */
                return 1;
            }

    Can you please verify that you're hitting one of the above calls to HWI_dispatchPlug()?

    If you are hitting those, then the interrupt should be registered in BIOS.

    Steve

  • Hi Steve,

    Thanks for your reply. Sorry, that was a bad assumption on my part! I saw that the interrupt wasn't in the static BIOS config, but I didn't check that it wasn't being assigned dynamically.

    I added intmgmt.c to my project and ran it. It hit my breakpoint after NC_NetStart was called at:

    HWI_dispatchPlug(myIntSetup->intVectId, (Fxn)myIntSetup->pCallbackFxn, -1, 0);

    with intVectId = 5, and pCallbackFxn set to the address of HwInt(). So the interrupt should be registered fine.

    I also added ethdriver.c, put a breakpoint in HwInt(), and it hit once at startup, then nothing until my client sent the DM6437 data (as I would expect).

     

    I think I'm going to make a new project with nothing but NDK and the my I2C interrupt. Hopefully that will help narrow this down.

     

    Would you mind giving me your opinion on proper thread usage with NDK? Here's what I've been doing:

    • main(): does nothing, exits.
    • TSK EthernetSetup: calls NC_SystemOpen, configures the NDK, calls NC_NetStart, which sets up 2 daemons looking for activity on 1 socket each, blocks.
    • TSK Setup: waits for NC_NetStart, performs various PCB config and class setup tasks, exits.
    • One of my daemon-launched server threads is an infinite loop which:

      - checks for new Ethernet data

      - sends data to client

      - runs a function which does the 'work' for that frame (image processing, etc.). This needs to run regularly.

    • The other I use as an alternate "command socket" which receives short messages, replies, and returns with the socket still open (thread is launched again the next time there is new data).

     

    This scheme has been working fine for Ethernet, but I doubt my infinite loop the most elegant way to do things. Do you see any potential conflicts here with adding a I2C HWI interrupt?

     

    Thanks again,

    - Craig

  • Craig,

    Craig Neely said:
    I think I'm going to make a new project with nothing but NDK and the my I2C interrupt. Hopefully that will help narrow this down.

    I think this is a good idea.  Start with a basic project that has the networking component working, then add in your I2C component and see if it works.

    One thing I can think of is that in the past I've seen people having problems when using the PSP drivers (not sure if you are using the PSP drivers for your I2C or not).  The problem was the drivers need to use the event combiner (ECM) module and had assigned an ECM group to the same interrupt that the Ethernet driver had used, so there was a conflict.  The behavior/symptom was basically that the program would work with just the NDK part, then it would work with just the driver part, however not with both (due to the interrupt conflict).

     

    Craig Neely said:
    I doubt my infinite loop the most elegant way to do things.

    The infinite loop may not be too bad.  I'm guessing that you are calling recv() inside of that daemon/loop?  If your socket is configured to be blocking, then the recv() call should wait until data comes in.  If it is non-blocking, then the loop would be polling constantly until data is actually received, which may not be desirable.  So, I'd make sure that your socket isn't non-blocking.

    Steve

  • Hi Steve, Alan,

    Well, I created a new project and found the solution. I had read the BIOS debugging guide and I was sure that I hadn't used the 'interrupt' keyword to declare my ISR.

    And I hadn't ... in one of the two places it was defined. I had relocated my ISR code and had left a prototype declaration behind that used the interrupt keyword. The compiler didn't complain about the dual definition and I didn't catch it until now.

    http://processors.wiki.ti.com/index.php/DSP_BIOS_Debugging_Tips#Cause:_Improper_use_of_the_interrupt_keyword

    Anyways, my thanks for your help. Sorry for bothering you guys about this ridiculous error! At least now I can get back to work. :-)

    - Craig