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.

SEM_post from an ISR

Other Parts Discussed in Thread: OMAP-L138

I'm running on an OMAP-L138.  I'm trying to do QDMA transfers, following the LLD example code.  I have almost everything working, but I've run into a puzzling problem.

I have a callback set up to set a SEM when the transfer is complete.  The call back is being called and calling HWI_isHWI shows that I am in HWI context.  Most of the time when I call SEM_post(), the semaphore is posted and the ISR completes normally and once I am out of HWI context, the SEM_pend() completes in my task.  However, sometimes (the same time every time in my particular application), the SEM_post() causes BIOS to switch to the SEM_pend() which completes before the ISR task exits.  I'm then in non-HWI context, but I never finished my callback.  This doesn't seem right to me, but I can't see anything I'm doing to cause it.  I can't see why a SEM_post from and ISR should ever cause a task switch before completing the ISR.  I believe that is what the old SEM_ipost() attempted to avoid, but that is no longer an option.

I'm running BIOS version 5.41.03.17.  Is this a known bug?  Am I doing something wrong?

Dave

 

  • By the way, I've actually stepped through the assembly code -- from the SEM_post it drops into the kernel to do the SEM_post processing, does some logging, does a task switch, exits the kernel and drops into the return from the  SEM_pend.  No other tasks run in the meantime -- in fact no user code executes between the SEM_pend and SEM_post completion.

    Dave

  • Dave,

    It sounds like the DSP/BIOS scheduler is not being correctly disabled while ISR is executing.  How did you implement your ISR?  Is it written in assembly ot in C?  Are you using the Interrupt Dispatcher, or explicitly calling HWI_enter()/exit()?

     

  • I'm using the EDMA3 LLD interrupt handler:

        hwi_attrs = HWI_ATTRS;
        HWI_dispatchPlug(8, (Fxn)lisrEdma3ComplHandler0, -1, &hwi_attrs);
        C62_enableIER(1<<8);
        
    I'm then passing my callback into the EDMA3 LLD.  I have Use Interrupt Dispatcher checked on the BIOS settings for INT 8, which is where the EDMA is hooked in.

    By the way, after the task switch on SEM_post from with my callback, I no longer get interrupts from the EDMA (and maybe not any interrupts -- I don't have any others I'm checking), which is probably what you'd expect if the iret isn't being executed.

    Dave

  • Can you explain what your callback function looks like?

    Are you making any DSP/BIOS API calls within the callback, besides the call to SEM_post() ?

     

  • Here's my entire handler:

    void
    tcDspQDMA::QDMA_Channel_Handler(int anTcc)
    {
        int channel;
        bool inHWI;   

        assert((anTcc >= 0) && (anTcc < EDMA_CHA_CNT));

        inHWI = HWI_isHWI();

        // look up channel
        channel = gpDspQDMA->mnChannel[anTcc];

        // if registered, process channel
        if (channel >= 0)
        {
            if (gpDspQDMA->ghDMADone[channel])
            {
    #ifdef MITYDSP_L138
                SEM_post(*gpDspQDMA->ghDMADone[channel]);
    #else
                SEM_ipost(*gpDspQDMA->ghDMADone[channel]);
    #endif
            }
            gpDspQDMA->maDMAActive[channel] = false;
        }
    }

    The HWI_isHWI() call is just for debug to verify that the software thinks it's in the HWI context.  It does.

    The SEM_ipost() was used in a previous version of this code for the C6711 and C6455.

    Dave

  • Dave --

     

    Can you add an assert() to make sure that inHWI is always true in your code above?

     

        inHWI = HWI_isHWI();
        assert(inHWI != 0);


    Just want to make sure that it is not possible for this callback to be called from another context.  If inHWI is always non-zero, then the SEM_post() call should never result in a context switch until the ISR completes and returns back intothe  HWI_dispatch code.

     

    -Karl-

  • I could do that, but I don't think it's necessary.  I have a breakpoint in the ISR and I can see EVERY time that it breaks, inHWI is always 1, both when it works correctly and when if fails.  I have stepped in assembler through the kernal and the task switch while inHWI is set to 1.   The failure is repeatable enough for me to catch the failure and walk into it.

    I don't understand how the task switch occurs if the inHWI is 1.  This seems like a BIOS bug, but it's certainly possible I'm doing something wrong. 

    Dave

  • Dave --

    The conditions you are describing should never happen and I'm not aware of any known issues that could explain it.

    Have you used Kernel/Object View (KOV) in CCSv3 (or ROV for CCSv4) to look at kernel state and stack sizes to make sure everything looks OK?  These tools will flag stack overflows and other internal problems.

    Is this part of a big application or is it possible for you to make a cut-down test case for us to try?   If you can zip up a small test case we can take a look.

    -Karl-

  • Interestingly, if I change the SEM_post() to SEM_ipost(), it seems to work.  I thought those two functions were now the same, but maybe not.  I've always used ipost from an ISR in the past, but the current BIOS documentation says to use post()...

    I'm working on paring this down to a small app so you can try it out.

    Dave

  • I'm wondering if I'm not setting up the HWI dispatching correctly.  I'm installing the LLD library dispatcher using either  HWI_dispatchPlug(8, (Fxn)lisrEdma3ComplHandler0, -1, &hwi_attrs); or by putting it in the INT8 interrupt dialog in the TCF editor.  Both seem to do the same thing.  I've got the code down to a pretty small set, but you'll need the LLD driver installed.  Do you have that?

    Dave

  • Set breakpoints on and after the SEM_post() in the ISR toward the end of DspQDMA.cpp.  Then point breakpoints on each of the lines containing GetInstance() in MonitorDma() in FFTDataAcq.cpp.  You'll see that it hits the first breakpoint in the ISR, then hits a breakpoint in MonitorDma(), then hits the second breakpoint in the ISR.  I would expect it to hit both ISR breakpoints before hitting a breakpoint in the task.

    Dave

     

  • Another interesting fact: when I use SEM_ipost, the task switch doesn't occur, but this may be because the semaphore is not actually set by SEM_ipost.  When I watch the semaphore and step (or run to breakpoints) over the SEM_ipost, the semaphore doesn't increment like I expect.

    Dave

  • Can you please specify which version of EDMA LLD you are using?  And also which CCS and compiler versions you are using?

    I will try to reproduce and get back to you.

    SEM_ipost() is same as SEM_post() except that SEM_post() has one additional check to see code is in the kernel (a flag we set in HWI_dispatcher and also within other system calls).  SEM_ipost() assumes this is true and skips this one if test.    If in the kernel, then SEM_post/ipost, queue up the job and the work gets taken care of when we leave the kernel (back in HWI_dispatch return leg).   One way that I can see this fail is if the data structure at 'SWI_data' is somehow getting corrupted or out of synch.  Can you look at SWI_data in a memory window and see if anything unusual happens to it at the time of the failure?  If you could send a screen shot of the memory window for SWI_data for the good case and the bad case, that would help.

    /*
     * Structure for SWI global data.  This definition is used by
     * assembly code (using .cdecls support) as well as C.
     */
    typedef struct SWI_DataObj {
        Fxn         runfxn;
        Fxn         execfxn;
        Uns         curmask;
        Uns         curset;
        Int         lock;
        Uns         curmbox;
        Ptr         curfxn;
        Ptr         rdybeg;
        Uns         inswi;
    } SWI_DataObj;


    -Karl-

  • The .pjt in the .zip file you sent is missing a bunch of files (see list below).    Can you please make another .zip file with the files below?  Also please make sure that I have all necessary .h files so I can rebuild.  If your code runs on an EVM, then please include the .out file.

    Thanks,
    -Karl-

    C:\MityDSP\DEV\sw\src\SigProc\BFFT.cpp

    C:\MityDSP\DEV\sw\src\SigProc\BITREV.cpp

    DspError.cpp

    DspTimer.cpp

    C:\MityDSP\DEV\sw\src\SigProc\FFFT.cpp

    C:\MityDSP\DEV\sw\src\SigProc\FFFT2.cpp

    FFTProcessing.cpp

    C:\MityDSP\DEV\sw\src\SigProc\FFTRECRS.cpp

    FSIR2.TCF

    C:\MityDSP\DEV\sw\src\SigProc\RDFT.cpp

    C:\MityDSP\DEV\sw\src\SigProc\SigProcFFT.cpp

  • Hi Karl,

    I included an old version of the .prj file.  Sorry about that.  Here's the current one.  I deleted all those files to simplify the test case.

    4578.QDMA Test prj.zip

    I'm using:

    BIOS v. 5.41.03.17

    EDMA3 LLD 01.10.00.01

    Code Gen Tools: 6.1.15

    CCS: 3.3.82.13

    I'll look at the SWI object and report back shortly...

    Dave

     

     

     

  • Hi Karl,

    Attached are screen shots from the debugger.  I displayed both SWI data and HWI data -- SWI didn't look very interesting and I'm not using SWI's so I thought maybe you actually meant HWI...  I've enclosed a shot when the app is first initialized (first line of main), when the ISR is first called (break on the SEM_post), and after it erroneously returns to the task.  I noticed that the HWI_D_inhwi flag is still set when it returns to the task, which makes sense since it never completed the ISR.

    Dave

     

  • Ooops!  Here's the attached file:

     

    0042.QDMA screenshots.zip

  • Dave --

    The new project still has a missing .h file, but the code and screenshots were helpful.

    I think the problem is that your 'intialize' task is calling SWI_enable and TSK_enable.   You should not be calling these APIs when TSK or SWI schedulers are already enabled.  These are simple counters that are used to keep track of disable depth (for nested ISRs and nested calls).  If you call them by themselves (w/o first calling disable), then the counter gets out of synch.  BIOS will enable TSK and SWI and HWI between the end of main() and the first task switch.  When main() returns, BIOS gets control and starts the TSK and SWI schedulers and enables interrupts. 

    Simple solution is to remove the HWI/TSK/SWI_enable calls from your 'initialize' function.

    The SWI_enable API man page has a bullet about this in the constraints section (Do not call SWI_enable when SWIs are already enabled ...).

    The SWI_D_lock variable, which is used to lock the SWI scheduler is out of synch and SEM_post() call thinks that SWIs are enabled, and hence the task switch.  I've attached a few slides that shows the value of this lock variable in a typical app.  I think you'll find different values at similar points in your code.

    6170.swi-lock.zip

    On a side now, I notice that you have some code that uses local TSK_Attrs variable w/o first initializing it to TSK_ATTRS.  You do it correctly in one place, and potentially badly in another place.  It is important to always assign attrs to MOD_Attrs before overriding the particular default values.  These MOD_Attrs structures allow us to extend the MOD_create() parameters in a compatible way.  We can add new parameters in the future (with backwards-compatible default behavior) and your code will continue to work after you rebuild it.  If you don't do the 'attrs = MOD_ATTRS', then your attrs structure might become out of date with future additions and uninitialized fields will cause problems.  We don't add new parameters very often, but it is safer just  in case.

    Regards,
    -Karl-

  • You are exactly correct -- that was the source of the problem.  This code has evolved from an earlier project where the disable was called and then enable was called later.  I had removed the disable call but the enable was still there.  I have removed the enable and it now works correctly.  Thanks for your help!!

    Dave