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.

Use ISR or EDMA3 to service MCBSP audio?

Hi,

Should CPU interrupt or EDMA3 service MCBSP when it is outputting audio?

Audio with acceptable qualities usually have frequency of more than 8 KHz. I have tested with polling XRDY bit of MCBSP.SPCR and write new data to DXR every time this bit becomes zero, and this was successful.

However, when the playing of audio has to be worked with other multimedia functionalities simultaneously, for example periodically updating LCD buffers for like every 1/60 seconds, I can no longer poll XRDY continuously because this will put the LCD on hold. I am therefore considering two alternatives: EDMA3 or interrupt?

Using interrupt seems easier to implement. However, each interrupt function needs to first save the context, do the work, and then restore the context, and they together consume several dozens of assembly instruction. In addition, both the audio data and the MCBSP configuration instrutions could locate in the external memory (since none of L138’s internal DSP or ARM memories is large enough to hold audio data), each instruction and data fetch might take as long as over ten cycles.

If we assume 8 KHz audio data, and 100 cycles in total for both context switch of ISR plus audio data reading and take this 100 KHz estimation as already included the memory-hierarchy fetch delay, then since MCBSP’s DXR holds only 1 unit of data a time, to play audio we then need eight thousand ISRs per second, which takes together 8×1000×100 = 0.8M cycles.

This rough calculation seems however to suggest that although there can be as many as eight thousand ISRs per second, the actual fraction of CPU time it takes, compare to an assumed typical 300MHz clock rate, is in fact very small, and this seems to suggest that interrupt based MCBSP serving can be a good choice in that it is unlikely to interfere with other routines?

I actually also implemented this ISR based MCBSP servicing and it suggested otherwise. When this executes it seems to cause problems to other routines, in particular USTIMER_delay() which uses the timer to create a specified wait/delay. There are also other ISRs being set at the same time which are periodically triggered or asynchronously triggered due to external events such as a touch, but none of these other interrupts causes appreciable disruption to USTIMER_delay() -- only the MCBSP ISR does. On the other hand, none of the other interrupts has a frequency so high as comparable to 8 KHz of the MCBSP ISR. So it seems to me that it is the high frequency of MCBSP that has caused the problem?

When using MCBSP to output audio data, which is the best and unobtrusive method? Could someone help?

 

Paul

  • Clearly EDMA is the least intrusive method.  Of course engineering is all about tradeoffs.  In this case the tradeoff is between run-time efficiency and programming effort.  Perhaps an "intermediate" solution would be to use interrupts but to utilize the McBSP's integrated FIFO.  That gives you the ease of use of interrupts, but with reduced number of interrupts because you could read many data elements per interrupt.

  • Brad,
     
    We use
     

    EDMA3ClrIntr

    L138 Starterware \ drivers

     

    EDMA3EnableDmaEvt

    L138 Starterware \ drivers

     

    McBSP0

    Transmit Event

    EDMA3_CC0 Event 3

    Synchronization

    Type A

    aCnt = 2

    bCnt = 65000

    cCnt = 1

    srcBIdx = 2

    destBIdx = 2

    srcCIdx = 2

    destCIdx = 2

    cCnt = 1

    Write destination

    McBSP0.DXR

     

     
     

    and configure EDMA3 with the following function:

     
    void MCBSP0_EDMA3()
    {
           EDMA3ClrIntr(SOC_EDMA30CC_0_REGS,7);//"Transfer complete code" TCC  = 7
     
           /* EDMA3 event/channel mapping
            *            Actual mapping is @ SPRS586C <EDMA3 Channel Synchronization Events>
            *                   3 - McBSP0 Transmit
            *            Also refer SPRUH77.18.2.6 <Event, Channel, and PaRAM Mapping> */
          EDMA3CCPaRAMEntry * paramPtr=(EDMA3CCPaRAMEntry *)(SOC_EDMA30CC_0_REGS + EDMA3CC_OPT(3)); //param No. == even No.
     
           paramPtr->opt=0x00107000; //A synchronized, "Transfer complete code" TCC set to 7 paramPtr->srcAddr = (unsigned int)(&my_sound[0]);
           paramPtr->aCnt= 2;
           paramPtr->bCnt= 65000; //Because 16 bit allows maximum 65535
           paramPtr->destAddr= (int)(&(HWREG(SOC_MCBSP_0_CTRL_REGS+MCBSP_DXR))); //DXR of MCBSP0
           paramPtr->srcBIdx= 2;
           paramPtr->destBIdx= 0;
           paramPtr->linkAddr=0xffff
           paramPtr->bCntReload=0;
           paramPtr->srcCIdx=0;
           paramPtr->destCIdx=0;
           paramPtr->cCnt=1;
     
           /*EDMA3EnableDmaEvt() is not necessary for manual triggering, but needed for event triggering*/
           EDMA3EnableDmaEvt(SOC_EDMA30CC_0_REGS,3);
    }
     
     
    Problem:
     
    McBSP0 transmit event DOES NOT trigger EDMA3 action.

    1.    We have verified that McBSP0 worked normally with a multifold of tests. For example, both polling based and interrupt based operation could successfully supply audio data stream to external codec and the correct audio can be heard.

    2.    We either programmatically or manually write to McBSP0.DXR. Each time we write a new word to DXR, on EDMA_CC0 side:

    a)    [Register EER @ 0x0c01020], event enable register, = 0x00000008, means event 3 is enabled. This was configured beforehand.

    b)    [Register ER @ 0x0c01000] would be set to 0x00000008, this is the expected behavior because according to <Table 5-14. EDMA Synchronization Events @SPRS586C>, “McBSP0 Transmit” correspond to EDMA_CC0’s event 3.

    c)    We also tried clearing ER’s 3rd bit by writing 1 to ECR’s 3rd bit, and this always immediately clears the 3rd bit of ER. If we write to McBSP0.DXR again, ER’s 3rd bit will again be set.

    b & c combined should mean that writes to McBSP0.DXR is always detected by EDMA3, and the event is always correctly latched in the ER register.

     

    Despite of this, EDMA3 transmission is never triggered. In the code above bCnt = 65000, and it never decreases; meanwhile, McBSP0.DXR is never updated. EDMA3 behaves like “I hear and I latch the event, but I just refuse to work.

     

    Occasionally, EDMA3 will successfully play the full 65000 words of audio stream for just once; if I want issue commands to repeatedly play the 65000 words stream in a while(1) loop, it will only work for the 1st time but not for all remaining times. All while(1) iterations use the same MCBSP0_EDMA3() function listed above, and they should share identical configurations, but why would it work for the 1st time but not the remaining?

     

    In OPT there is [bit 3 STATIC] controlling whether PaRAM is updated after TR (transfer request) submission. My expectation on the mechanism is:

    1.    First configure EDMA3 with MCBSP0_EDMA3()

    2.    Then I use a line of code to write the first word of the audio stream to McBSP0.DXR, this would trigger EDMA3 action. Since this action actually writes a new word to McBSP0.DXR because destAddr=McBSP0.DXR, this would again trigger a new EDMA3 action. Repeatedly, it will exhaust all bCnt=65000 and the audio could be played in this manner.

    Therefore, bCnt should decrement each time, so I think there is no reason to set the STATIC bit in OPT. In fact, I tested and found that if STATIC is set, even the first iteration of the while(1) loop which previously worked, would also fail.

     

    Debugging this has caused me great frustration yet no progress has been made. Anyway, I tried to document the problem as detailed as I could. I also checked L138’s silicon errata SPRZ301H without finding. Could this actually be another undiscovered silicon bug, or is due to other causes?
     
     
    Paul
  • Please try the "run all" option from this gel file:

    http://processors.wiki.ti.com/index.php/OMAP-L1x_Debug_Gel_Files

    Please attach the corresponding log. 

  • Brad,
     
    I was able to make it work, and the fix is all about shadow region registers.
     
    According to “18.2.7 EDMA3 Channel Controller Regions” of TRM SPRUH77, the benefit of shadow region register is minimizing conflicts especially in hetero multi-core environment.
     
    The L138 Starterware put all EDMA3 functions in edma.c, which includes edma.h, which in turn includes hw_edma3cc.h, and it is hw_edma3cc.h that includes the definitions of the original registers and shadow registers. For example,
     
    Global Registers:
    #define EDMA3CC_ER                   (0x1000)
    #define EDMA3CC_ECR                  (0x1008)
    #define EDMA3CC_ESR                  (0x1010)
     
    Shadow Region 0 registers:
    #define EDMA3CC_S_ER(n)              (0x2000 + (0x200 * n))
    #define EDMA3CC_S_ECR(n)             (0x2008 + (0x200 * n))
    #define EDMA3CC_S_ECRH(n)            (0x200C + (0x200 * n))
    #define EDMA3CC_S_ESR(n)             (0x2010 + (0x200 * n))
    #define EDMA3CC_S_ESRH(n)            (0x2014 + (0x200 * n))
     
    and almost all the functions in edma.c implement register accesses using shadow region 0 and shadow region 1 registers. However, I found that accessing shadow region 0 & 1 registers are does not have the expected effect in many cases, for example, writes to shadow registers don’t actually modify global register, and reads from shadow registers don’t reflect the actual global register value, so I modified the code to access global registers directly, and all the behaviors became normal.
     
    Have you or other TI people ever encountered the same problem? Or did I miss some other configuration steps that are required for shadow region registers to become effective?
     
     
    Paul
  • You have to program the DRAE registers in order to allow a given shadow region to use a given channel.  I don't believe the global EDMA3CC interrupt is hooked up, so unless you use the shadow regions I don't see how you're going to get an interrupt.  Basically the ARM uses Shadow Region 0 and the DSP uses Shadow Region 1.  You should set the bits in DRAE0 to correspond to the channels used by the ARM, and you should set the bits in DRAE1 to correspond to the channels used by the DSP.  You should then doing all the programming using the shadow region registers.

  • Brad,

    Thanks for the clarification on DRAEn registers. I modified all instances of my EDMA3 use with the more proper access via shadow region registers as well as had interrupts successfully enabled.

    Paul