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.

DSP BIOS 5.40.02.22 - Question about getting two interrupts\tasks to coexist .

Other Parts Discussed in Thread: TMS320C5505

Hi,

 

I have a problem getting two tasks to co-exist properly on a VC5505 patform, using the CCSV4 tools with the following environment…

 

Processor: 320VC5505 @ 100MHz.

Tools: CCSV4 –  version 4.0.2.01003.

Target: Spectrum Digital TMS320C5505 EVM, revision E.

DSP BIOS version 5.4.02.02.

 

Basically I need to run two tasks.....

Task 1 - is an audio compression algorithm encode+ decode, which is running at priority level 1.

Task 2 - is an HDLC Rx state machine, and is running at priority level 2.

 

Task 1 is DMA-driven: as soon as the DMA has transferred 48 input audio samples from the I2S3 port, a DMA interrupt occurs, and the ISR for this interrupt posts a mailbox message for an encode+decode TSK to run. This encode\decode task takes about 850 microseconds to run to completion. The DMA uses HWI_INT8, as specified in the BIOS Configuration Tool GUI.

 

Task 2 is interrupt-driven, via /INT1, from an external RF transceiver chip connected to the daughterboard interface J22 of the EVM. This interrupt has a higher priority than the DMA interrupt, it uses HWI_INT3. The ISR for /INT1 also posts a mailbox message for a HDLC Rx task, which must run within 80uS of /INT1 interrupt occurring so that incoming data can be read without loss. The /INT1 interrupt is generated every 80 microseconds from an incoming RF signal.

 

Task 1 will run fine if it is the only active task: Task 2 will also run fine if it is the only task - but with both tasks running at the same time, Task 2 (the HDLC Rx task) gets to run just once, immediately after main() has handed over control to the BIOS - and it never gets to run again after that. Task1, though, will run OK.

 

It was my understanding that since Task 2 has the higher priority, in both interrupt and task priority, that it would pre-empt Task 1 - but that doesn't appear to be happening in this instance.

 

So, I have a number of questions..

 

1. How often, by default, does the BIOS scheduler switch between tasks - and can we control that switch rate via the .tcf file ?

2. In the BIOS manual (document number SPRU423H, section 4.1.5), it states that the highest priority thread is a task that is blocked - could it be that my Task 1 is blocked and is not preemptable by Task 2 ?

3. Also, I’m not using the BIOS interrupt dispatcher for either of the two interrupts associated with the above task, but I am using the ‘ interrupt’ keyword for the C routines that define the two ISRs  - would it be better to not use the interrupt keyword and use the dispatcher instead? What is the difference between these two approaches?

 

I should point out that I'm new to using this BIOS, and am pretty sure that the problem is something I'm doing wrong !!

 

Any help would be greatly appreciated.


Cheers

 

Michael

 

  • Hi Micheal,

    I have a similar(ish) scheme and had similar issues.  I can not remember all the details but have looked back at the code and found the following which may offer some clues.  I should add that I am no DSP BIOS expert.

    I tried various schemes with varying success.  Some worked but stopped the RTDX (cpu load graph, message log etc) from working and other ran TSKs just once, some worked but always reported 100% CPU load.

    The following two schemes allowed everythng to work and may provide a clue....

    Scheme 1:

    A DMA HWI interrupt firing every 2ms which posts a SWI_data interrupt to handle a block of data which must complete before the next DMA interrupt.

    A TSK_encode carrying out encoding which has 38ms to complete.

    DMA HWI is highest priority
    SWI_data is next priority
    TSK_encode is next priority
    Idle Functions lowest priority

    The BIOS Interrupt Dispatcher was used and, I believe, was essential as it handles all the context switching, particularly BIOS related.  My code makes no use of the interrupt keyword.

    The TSK was arranged as follows:

    void encode(void)
    {
        while(1)
        {
             if(encode_flag)
             {
                     // Encode
              }
             
              TSK_sleep(5);

              // TSK_disable();
              // IDL_run()
              // TSK_enable();
        }
    }

    Without the while(1) loop the TSK ran just once.

    SWI_data sets encode_flag when required.

    The TSK_sleep(5) allows other TSKs to run including TSK_idl and the idle functions which allows the CPU load graph, message logs etc to run.

    The TSK_sleep(5) argument '5' sets the time that the TSK sleeps (blocks?) and is the number of PRD ticks.

    The commented out lines (BIOS functions) are an alternative attempt which was not, I believe, entirely successful.

    Configuring the data handling function as a SWI had (I think) instrumentation advantages - providing statistics on function duration that TSK did not.

    Although this works for me I have always assumed there must be a better way of doing this and it would be great to get confirmation.

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

    Scheme 2:

    The BIOS Interrupt Dispatcher was used and, I believe, was essential as it handles all the context switching, particularly BIOS related.  My code makes no use of the interrupt keyword.

    A DMA HWI interrupt firing every 2ms which posts a SWI_data interrupt to handle a block of data which must complete before the next DMA interrupt.

    A second SWI_encode (posted by SWI_data when required) carrying out encoding which has 38ms to complete.

    DMA HWI is highest priority
    SWI_data is next priority
    SWI_encode is next priority
    Idle Functions lowest priority

    The TSK - Task Manager is not enabled at all in this scheme.

    Regards, Andrew.

  • Hi Andrew,

    Firstly, thanks for the reply - the really useful bit was the comment about running the task in a while() loop - I wasn't doing that, so with my HDLC_Rx_Tsk  read task encapsulated in a while loop, it does get called every time, so that's good progress.

    I stripped the project down to a "minimal criminal" project build that had just  the /INT1 interrupt and the TSK that responds to the message post made in INT1's interrupt service routine, and found by observing it (INT1) on the scope that the interrupt frequency was far from regular - in fact it was almost random, varying from being every 80 uS up to every 30uS - plus there was no guarantee that reading the data FIFO over the SPI would actually clear the interrupt signal. The reason for this was that the RF tranceiver wasn't receiving a valid signal, and so was not in a deterministic state as far as interrupts were concerned - but with a valid signal, things settled down and  a regular 80uS interrupt was generated and serviced OK. Again, more progress :-)

    There's one issue that I haven't got to the bottom of yet - if I drop the 'interrupt' keyword from the ISR source code, and then in the Configuration Tool GUI configure the HWI object associated with /INT1 to use the BIOS interrupt dispatcher, then the interrupt will run just twice. On the second time through the ISR,  the debugger halts in a routine called HWI_F_dispatch, and after a few run - halt cycles, I eventually get an error message " Can't Run Target CPU..... Processor communication timeout, .....Power Cycle the target board before continuing".

    I assume that the  HWI_F_dispatch function is part of the BIOS dispatcher code. I don't know why the code halts here, unless there's some sort of exception handling being invoked  ??

    The code runs OK when I don't use the BIOS dispatcher, but it would be more desirable to use the BIOS, for consistency.

    I'm confused as to what is the recommended approach - the BIOS User Guide (document reference SPRU423h) states in section 4.2  (Hardware Interrrupts), that " To allow an HWI objects function to be written completely in C, the system HWI dispatcher should be used".  In the C/C++ Compiler User Guide (document reference SPRU281F), section 6.6.3 (Using C/C++ Interrupt Routines ), it states that the interrupts can be handled directly using  the 'interrupt' keyword  - and at the minute that seems to the only method that works for me.

    Presummably the 'interrupt' keyword allows the compiler to add what ever code is necessary for the context switch - similar to the HWI_enter and HWI_exitcode in the BIOS dispatcher   ???

    Below is my code...

    interrupt void AX5042_Rx_Isr(void)
    {

      uint16_t temp;
      MsgObj1 HDLC_msg;
    // must read the AX5042 FIFO to de-assert this interrupt pin.

            
            g_count++; // increment global count to check how often the ISR is hit.
            MBX_post (&MBX1, &HDLC_msg, 0);
            //AxSpiRead(AX5042_REG_FIFODATA,0x00);
            temp = IFR0;
            //temp &=0xFFF7;  //disable any further INT1 interrupts until this one is serviced by the associated task.
               IFR0 = temp; //clear /INT1 interrupt ??  - does the CPU not do this for you ?

    }

     

    . and here is the Task that processes the Mailbox post made in the ISR..

     

    #include "stdint.h"
    #include "stdint.h"
    #include <bios.h>
    #include <mbx.h>
    #include "appData.h"
    #include "spi5042.h"
    #include "register_cpu.h"


    extern MBX_Obj MBX1;

    void HDLC_Rx_Tsk(void)
        {
            MsgObj1 msg1;  // dummy message for now.
            uint16_t temp;
            Bool status;
           
            while(1){
            status = MBX_pend(&MBX1,&msg1,10); // wait one system clock tick before returning. (was 0).
           
            if (status != 0)  // i.e., return value from MBX_pend is true
                {
                  temp = AxSpiRead(AX5042_REG_FIFOCONTROL,0x00);
                  AxSpiRead(AX5042_REG_FIFODATA,0x00);
                      //temp = IFR0;
                    //temp |=0x0008;  //enable  further INT1 interrupts since this one is serviced
                       //IFR0 = temp; //enable /INT1 interrupt ?
                  //AxSpiRead(AX5042_REG_FIFODATA,0x00);
                  //AxSpiRead(AX5042_REG_FIFODATA,0x00);
                  //temp = (Uint8)(AxSpiRead(AX5042_REG_IRQREQUEST,0x00)& 0x00FF);
                 
                }
            }   
        } 

     Once again, thanks for your help and posting the example code - much appreciated !!

    Best regards

    Michael

     

  • You should only call MBX_post when using the BIOS dispatcher.  Otherwise BIOS will not realize that a hardware interrupt has occurred and it could inadvertently perform a context switch thinking it was still in some other task.  Your procedure of enabling the dispatcher and removing the interrupt keyword was the correct thing to do.  I'm scratching my head a bit at what could be the issue.  One other thing you might try is in the BIOS HWI settings to set the interrupt mask as "all".  That will mask all interrupts during the ISR such that your ISR cannot be pre-empted.  I don't know why that would be causing an issue, but it would make it behave more like your case where you use the interrupt keyword.

  • Suggestions to consider and try:

    One thing that confused me initially with DSP/BIOS was that you had to exit main() - not clear from your previous posts if you are definitely doing this.  I think you must be to have got as far as you have - but just in case....

    To help debug things try simplifying your TSKs by removing MBX code.

    Consider switching your functions from TSKs to SWIs - a relatively easy thing to try and conceptually closer to traditional interrupt driven code.  In particular changing your highest priority function to SWI will guarantee it prempts the TSK because SWIs are higher priority than TSKs

    Regards, Andrew

  • Hi Brad,

    I tried your suggestion of enabling the dispatcher, and changing the mask from ‘self’ to ‘all’ on both IER0 and IER1 - however it made no difference to the behavior of the code – it still stuck at the HWI_F_dispatch routine when I ran it from the debugger.

     

    However the root of the problem was that I had removed the interrupt keyword from the function definition in the C source file, but not from the prototype in the associated header file - doh!  With that rectified, the interrupt is getting dispatched OK and getting serviced by the associated task :-)

     

    So I probably was using interrupt keyword with the dispatcher, as recommended not to do yourself and the documentation !

     


    One thing I noticed in the Configuration tool GUI was that  when I tried to reverse the setting for IER1 back from 'all' to ‘self’ that the Configuration tool would freeze, requiring it to be shut down. This was consistently repeatable, and if I try to change IER1 to anything other than ‘all’, it also results in a program hang - is that a known issue ?  The version of the Configuration Tool is 5.00. I can verify that it is set to 'all' by looking at the resultant *.cdb file.

     

    Many thanks for your help on this problem

     

    Best regards

     

    Michael.

  • Hi Andrew,

    I am exiting from main() OK - I think we have got to the bottom of the dispatcher issue too - I hadn't deleted the 'interrupt'  qualifier in the header file where the isr function was declared.

    The /INT1 Task now runs OK, and the DMA Task also runs OK, both using the dispatcher, so I will now try and get them to run together - if that doesn't work, I'll definitely try the SWI approach that you suggest for /INT1, as it is of high enough priority to warrant such an method, especially if I need to increase the bitrate of the link at some point in the future.

    Once again, thanks for your help, it has been very useful !

    Cheers

    Michael.

     

  • Michael Feerick said:
    One thing I noticed in the Configuration tool GUI was that  when I tried to reverse the setting for IER1 back from 'all' to ‘self’ that the Configuration tool would freeze, requiring it to be shut down. This was consistently repeatable, and if I try to change IER1 to anything other than ‘all’, it also results in a program hang - is that a known issue ?  The version of the Configuration Tool is 5.00. I can verify that it is set to 'all' by looking at the resultant *.cdb file.

    Strange one.  Instead you can simply open up the tcf file in a text editor to delete the line that was added to make that change to begin with.  However, I would recommend that you set ALL of your interrupts to mask "all" unless for some reason you really want/need pre-emption.  As long as you're keeping your ISRs concise there's not generally a need for pre-emption of interrupts.  Furthermore it will result in greater stack requirements since each interrupt will cause the stack to go deeper potentially overflowing it.

  • H Michael,

    Could you communicate using AX5042 chips?

    Thank you.

  • Michael Feerick said:
    plus there was no guarantee that reading the data FIFO over the SPI would actually clear the interrupt signal. The reason for this was that the RF tranceiver wasn't receiving a valid signal, and so was not in a deterministic state as far as interrupts were concerned - but with a valid signal, things settled down and  a regular 80uS interrupt was generated and serviced OK.

    Hi Michael,

    Sorry to bother you but I am having a similar issue with the AX5042. I'm in a perpetual interrupt state because the AX5042 FIFO always has random content in the buffer thus triggering the interrupt. I empty the FIFO by reading this content and this clears the interrupt but then the interrupt is immediately triggered again with the same random garbage 0xF0, 0x90, 0xC0 ... You talk about a deterministic state and a valid RF signal, could you please elaborate. I'm new to working with RF chips and I'm not sure where this "noise" might be coming from? Any help/suggestion would be greatly appreciated.

    Kind Regards,

    Jaco