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.

TMS320F28379D: Event Driven Application Architecture

Part Number: TMS320F28379D
Other Parts Discussed in Thread: C2000WARE

Hi,

I'm currently working on my first C2000 application. Toolchain is up and running, eval board is blinking and is sending CAN bus messages periodically. So far so good. Now I have to think about how to structure the whole application (firmware). All examples I've seen so far, where busy polling / looping in the main loop and thus (probably) consuming much more energy than necessary. I came across the IDLE() CPU instruction, which places the CPU into a power saving mode.

Now I wonder, how I could utilize this CPU instruction (IDLE) to place the CPU to sleep until some external event (aka Interrupt) happened to then look for the event source and to handle that. Unless I've missed something (and I hope that this is the case), I can't find a way to setup some interrupt source and place the CPU to idle without a race condition.

If I would for example setup a timer to timeout in X. The timer would be configured to cause an interrupt and thus to get the CPU out of idle mode:

setup_timer(X);
IDLE();

But what happens if the interrupt handler (serving the timeout set by setup_timer()) is served before IDLE() is called? Is it possible to structure this race free? I thought of disabling all interrupts, then calling IDLE(), but of cause this would then block for ever...

Any readings / recommendations / coments regarding general application structures using C2000?

best regards,

Torsten

  • Torsten,

             Not sure I understand your concern. In the IDLE mode, all peripherals can be kept alive i.e. the peripherals can be doing their thing. I make this distinction because both STANDBY & HALT modes shut down the peripheral clock domain. Any configured interrupt can wake up the device from the IDLE condition. For example, data received through a serial port. 

    In the CPU timer case, the timer is going to keep running and generating interrupts. So, I don’t quite understand your concern about a “race” condition.

     

  • Hi,

    then let us switch to a serial port example. Let us assume, I have configured the serial port for reception and enabled the interrupt for that port. After that, I switch the CPU to IDLE mode:

    1) configure serial port
    2) IDLE()

    Now, what can happen is, that the interrupt fires between 1) and 2), leaving the CPU in IDLE mode forever (if the interrupt fires after 2, everything is fine). The only use for the IDLE() mode, I could think of, would be to have everything being handled in interrupt handlers, which would result in an empty main loop, which then could be safely converted into a loop calling IDLE().

    The ARM (Cortex-M) architecture solves this problem by having the interrupt setting a flag that will be consumed be the corresponding instruction (WFE).

    regards,

    Torsten

  • If interrupt fires between 1 & 2, it will be serviced normally, since the CPU is active. After ISR execution, CPU is put into IDLE. It would then wake up the next time the serial port receives the data and hence triggers an interrupt.

  • Yes, that is exactly how I understand the documentation too! But what I don't understand, is how I could utilize the IDLE() instruction other then handling all my application logic in the interrupt service routines, having an empty main() function that just calls IDLE(). Right?

  • An other example: In the C2000 Ware 3.02, there is an example called lpm_ex1_idleware.c, which configures a GPIO pin as output and set it initial to 1 and configures one GPIO pin as input and as an interrupt source. The interrupt handler of that input pin, sets the output pin to 0. The main loop of the example is setting the output pin back to 1, once the ISR returns and then goes into IDLE state.

    So basically, I would expect that the intended behavior of this example is that one could trigger the external input and that the output goes shortly to 0 and then back to 1:

    main()
    {
    ... while(1) { GPIO_writePin(1, 1); // Line A SysCtl_enterIdleMode(); // Line B } } __interrupt void xint1ISR(void) { GPIO_writePin(1, 0); Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1); }

    Now, what would happen if the xint1ISR() would be served after line A, but before line B? The output would be switched to 0 and would stay there, as the CPU would go to IDLE().

    Is there something I'm missing? How would a typical C2000 application look like, that makes use of the IDLE mode to conserve power?

  • how I could utilize the IDLE() instruction other then handling all my application logic in the interrupt service routines, having an empty main() function that just calls IDLE(). Right?

    Sorry I don’t understand what you are asking here. Could you please rephrase/clarify? If I have understood your requirement correctly, your application will normally be in IDLE. When a wakeup signal arrives, the device comes out of IDLE, executes the ISR and go back to IDLE and waits for the next wake up signal. Your main() will not have much code, except for the one putting the device into IDLE. All the "action" happens in the ISR.

    So basically, I would expect that the intended behavior of this example is that one could trigger the external input and that the output goes shortly to 0 and then back to 1:

    Correct. 

    Now, what would happen if the xint1ISR() would be served after line A, but before line B? The output would be switched to 0 and would stay there, as the CPU would go to IDLE().

    Correct. Since XINT is asynchronous, it can happen anytime. Now, while the CPU is in IDLE, the next time XINT happens, the CPU would come out of IDLE , execute the ISR and go back to main() and again go to IDLE. The desired sequence is determined by the application. From the device point of view, while it is in IDLE, a wakeup signal would wake it up and after ISR execution, control would go back to main(). 

    How would a typical C2000 application look like, that makes use of the IDLE mode to conserve power?

    The C2000ware example is all we have. 

  • Hi Haressh,

    Hareesh Janakiraman said:

    Sorry I don’t understand what you are asking here. Could you please rephrase/clarify? If I have understood your requirement correctly, your application will normally be in IDLE.

    The requirements to my application is, to handle multiple asynchronous events (ADC, CAN, Timer and probably more). Handling them all in the corresponding interrupt service routines might become complicated, as data structures would have to be accessed from different ISRs. So one common pattern (or call it architecture) in embedded software development is, to have the ISRs being very short (basically trying to just do some atomic operations in them) and to do all the heavy lifting inside the main loop. This has the benefit, that more complicated operations on data structure would take place in the main loop and would not require any synchronization with ISRs. An other benefit would be, that very high priority things still could be done in the ISRs, which would then not be blocked by lower priority ISRs.

    This pattern (or architecture) is implementable with C2000. But: I would require the main loop to not contain a call to IDLE()!

    If my application would for example just receive incoming data from an UART and would defer the handling of the incoming data to the main loop, adding an IDLE() statement to the main loop would result in a race condition:

    for ( ;; )
    {
       start_asyncronouse_receiving_data();
       IDLE();
       handle_data_if_there_is_data();
    }

    Without that IDLE() statement, there is no race condition but the CPU would burn unnecessary power. I wonder, what would the usual use of the IDLE() instruction be. I guess the CPU designers would have talked to some software developers at TI and have discussed with, how to the software developer could make use of that instruction.

    Hareesh Janakiraman said:

    Correct. Since XINT is asynchronous, it can happen anytime.

    So, you agree with me, that the intended behavior of the example will most probably be that the output goes low for a very short period of time, if an edge was detected. And most likely the example is doing this 99.9% of the time. But if the load of the CPU get's a little bit higher (maybe due to other interrupts that have to be handled), once in a while the example is doing something different. That's what I call a bug (in this case a race condition). This bug can be fixed, by either removing the IDLE() statement (if consuming much more power is acceptable), or by handling the detected edge entirely in the ISR, so that the main loop only contains the IDLE() statement.

    Anyway, I'm going to put my main - loop code into the DLOGINT ISR, just call IDLE() from the main loop and have every ISR triggering a DLOGINT interrupt.

    best regards,

    Torsten