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.

The Notes about DINT and EINT in User's Guide

Other Parts Discussed in Thread: MSP430F5529

User's Guide of various series of MSP430 all has the Note below in the Section that describes the DINT instruction.

NOTE: Disable interrupt

If any code sequence needs to be protected from interruption, DINT should be executed at least one instruction before the beginning of the uninterruptible sequence, or it should be followed by a NOP instruction.

I tried to verify this and my preliminary finding contradicts to this Note. Furthermore, my finding contradicts to a similar Note for EINT.

I wonder if anyone else has ever tried to verify these Notes?

  • The msp430 is a pipelined processor.  I believe it takes 6 cycles for the DINT to happen ... they want you to add some time between the DINT and the code you don't want to disrupt I believe.

  • The User's Guide says "should", not "must".

    It's possible that the note describes the behaviour of another implementation, but that the NOP stays because of this erratum:

    CPU39

    Function: PC is corrupted when single-stepping through an instruction that clears the GIE bit

    Description: Single-stepping over an instruction that clears the General Interrupt Enable bit (for example DINT or BIC #GIE,SR) when the GIE bit was previously set may corrupt the PC. For example, the DINT or BIC #GIE,SR is a 2-byte instruction. Single stepping through this instruction increments the PC by a value of 4 instead of 2 thus corrupting the next PC value.
    Note: This erratum applies to debug mode only.

    Workaround: Insert a NOP or __no_operation() intrinsic immediately after the line of code that clears the GIE bit.

  • Phil,
    Thank you for your reply.
    I agree that the behavior described in those Notes could happen and can be explained by the fact that msp430 is a pipelined processor.
    However, it is also possible that msp430 (or some of them) may not behave that way.
    I am double checking my test program and will post updates of my findings.
    --OCY
  • Clemens,
    Thank you for your reply.
    I did not use single-stepping or break-point to debug my tests. I use the debugger only to download the object coder or to display the Flash and RAM contents while the target CUP is not running. I could have done the same with BSL.
    -- OCY
  • Attached is one of my test programs. (It is in IAR assembly.) And here is the fragment of machine code (for MSP4305529) under my scrutiny.
    004470 4303 nop
    004472 4303 nop
    004474 C232 dint
    004476 4305 clr.w R5
    004478 4316 mov.w #0x1,R6
    00447A 4327 mov.w #0x2,R7
    00447C D232 eint
    00447E 4228 mov.w #0x4,R8
    004480 5238 add.w #0x8,R8
    004482 5338 add.w #0xFFFF,R8
    004484 4303 nop
    004486 4303 nop
    004488 4303 nop
    00448A 4303 nop
    00448C 4303 nop
    00448E 4303 nop
    004490 4303 nop
    Note that this fragment consists of 17 single word single MCLk cycle instructions. Preceding this fragment, TA0CTL is set up in continuous mode using SMCLK=MCLK. TA0CCTL is set up in compare mode to generate an interrupt at the count preloaded in TA0CCR0. R5, 6, 7, and 8 are loaded with junk (DEAD BEEF BAAD F00D). This entire sequence (TimerA0 set up, R5-8 loading, and the fragment shown) is repeated 16 times. Each time with a different setting in TA0CCR0 aimed to generate one single interrupt at one of the 16 slots between those 17 instructions in the fragment shown.
    The ISR for Timer0_A0 will write 8 words in consecutive RAM and disable TA0CTL. After 16 sets of 8 words are collected in RAM by ISR, the ISR enters an infinity loop. Here is what was collected by MSP430F5529.
    (1) (2) (3) (4) (5) (6) (7) (8)
    0008 0011 0009 4472 dead beef baad f00d
    0009 0012 0009 4474 dead beef baad f00d
    000a 0013 0009 4476 dead beef baad f00d
    000b 0019 000e 4482 0000 0001 0002 000c
    000c 0019 000d 4482 0000 0001 0002 000c
    000d 0019 000c 4482 0000 0001 0002 000c
    000e 0019 000b 4482 0000 0001 0002 000c
    000f 0019 000a 4482 0000 0001 0002 000c
    0010 0019 0009 4482 0000 0001 0002 000c
    0011 001a 0009 4484 0000 0001 0002 000b
    0012 001b 0009 4486 0000 0001 0002 000b
    0013 001c 0009 4488 0000 0001 0002 000b
    0014 001d 0009 448a 0000 0001 0002 000b
    0015 001e 0009 448c 0000 0001 0002 000b
    0016 001f 0009 448e 0000 0001 0002 000b
    0017 0020 0009 4490 0000 0001 0002 000b
    Column (1) is the content of TA0CCR0 as seen by the ISR. Column (2) is the current content of TA0CR at the beginning of the ISR. Column (3) is the difference and thus the “latency” of that interrupt. It should be 9 plus the effect of interrupt being disabled at the time. Column (4) is the retune address pushed in the stack. It should be the address of the 2nd through 17th instruction in the fragment of code shown earlier -- except it can be higher if interrupt was disabled at the time. Columns (5) through (8) are the contents of R5 through R8 during the ISR. At most one of them may change after each instruction, except the changes may be hidden by interrupt being disabled.
    My interpretation is:
    (a) The DINT instruction disables interrupt after the current MCLK ends and before the next MCLK ends.
    (b) The EINT instruction enables interrupt after two more MCLK cycles beyond the current MCLK.
    There are three independent evidences left in RAM by MSP430F5529 presented above.
    This contradicts to the Notes in the User’
  • Source code of the test program described in my previous post is as follows. Foe MAP430F5529, instruction in blue will not be interrupted.

    #include <MSP430.h>

    #define SIZE   16

            RSEG    DATA16_N

    log:    ds      16*SIZE

            RSEG    CSTACK

            RSEG    CODE

    ta0isr: MOV     &TA0R, R9

            MOV     #0, &TA0CTL

            MOV     &TA0CCR0, log(R4)

            MOV     R9, log+2(R4)

            SUB     &TA0CCR0, R9

            MOV     R9, log+4(R4)

            MOV     2(SP), log+6(R4)

            MOV     R5, log+8(R4)

            MOV     R6, log+10(R4)

            MOV     R7, log+12(R4)

            MOV     R8, log+14(R4)

            ADD     #16, R4

            CMP     #16*SIZE, R4

            JEQ     $

            RETI

    main:   MOV     #SFE(CSTACK), SP

            EINT

            MOV     #WDTPW+WDTHOLD,&WDTCTL

            MOV     #0, R4

            MOV     #8, R15

    loop:   MOV     R15, &TA0CCR0

            MOV     #CCIE, &TA0CCTL0

            MOV     #TASSEL_2|MC_2|TACLR, &TA0CTL

            MOV     #0xDEAD, R5

            MOV     #0xBEEF, R6

            MOV     #0xBAAD, R7

            MOV     #0xF00D, R8

            NOP

            NOP

            NOP

            NOP;             R5   R6   R7   R8

            DINT;           DEED BEEF BAAD F00D

            MOV     #0, R5; 0000 BEEF BAAD F00D

            MOV     #1, R6; 0000 0001 BAAD F00D

            MOV     #2, R7; 0000 0001 0002 F00D

            EINT;           0000 0001 0002 F00D

            MOV     #4, R8; 0000 0001 0002 0004

            ADD     #8, R8; 0000 0001 0002 000C

            ADD     #-1, R8;0000 0001 0002 000B

            NOP

            NOP

            NOP

            NOP

            NOP

            NOP

            NOP

            ADD     #1, R15

            JMP     loop

            NOP

            COMMON  INTVEC

            ORG     TIMER0_A0_VECTOR

            DW      ta0isr

            COMMON  RESET

            DW      main

            END

     

  • old_cow_yellow said:
    Clemens,
    Thank you for your reply.
    I did not use single-stepping or break-point to debug my tests. I use the debugger only to download the object coder or to display the Flash and RAM contents while the target CUP is not running. I could have done the same with BSL.

    I think Clemens is making the point that the note could be there to ensure people don't fall foul of CPU39 if they do use the debugger. In other words the NOP might be unnecessary for ordinary execution, but required to guarantee correct behaviour when single-stepping.

  • Due to the pipelining, when the DINT instruction (which has the status register as target) is executed which doesn't require a memory access), the next instruction is already loaded and will be executed. However, an interrupt request may also have been granted and will be executed after the instruction following the DINT.
    The time window for the interrupt is the very clock cycle while GIE is cleared and the next instruction is loaded. Maybe less than a clock cycle.
    Looking at your test code, it is possible that it can only happen if the interrupt is asynchronous to MCLK (which makes it virtually impossible to simulate without extreme effort and external circuitry). But if the chance is greater than zero, don't take a risk.
    If the interrupt happens before the clock cycle in which GIE is cleared, it will be granted too, but then the next instruction won't be fetched, and all is well. Doe sit happen after, it won't be granted. The problem is the timespan in which the next instruction fetch can't be stopped anymore (which likely is at the beginning of the MCLK cycle, to get the memory answer on the next clock edge) and the moment where GIE is cleared, after reading the status register and performing the OR operation.

    The problem with interrupts granted inside an uninterruptible sequence is as old as the 1x family. The problem with the single stepping, however, appeared on newer MSPs (X or X2 core, I think, but that's just a guess).

    I guess, saving one clock cycle on every operation that has a register as target, is worth the effort of wasting one instruction and clock cycle after each DINT :)
  • Robert,

    In addition to JMG's comments, that Note says: "... DINT should be executed at least one instruction before the beginning of the uninterruptible sequence, or it should be followed by a NOP instruction.". The focus is not that NOP, but the fact that the "no-interrupt" will not take effect until one instruction later -- and I am questioning that point.

    -- OCY
  • JMG,

    I modified my test code to use 4MHz XT2 as MCLK and 25MHz DCO as SMCLK for TA0_A0 to generate interrupts to "machine gun" at the following 16 instructions.

    The isr counts how many times each of those instructions is hit by interrupt. The first 6 (with green lines) and the last 5 (green) were Interrupted equally often. There are 5 in the middle (with red lines) never got interrupted, and the green one next to these 5 got interrupted 6 times as often. Here are the long-long counts in one run.

    0000000008c366f6 00000000098c13c8

    00000000098c160e 00000000098c225d

    00000000098c900f 00000000098c0d5a

    0000000000000000 0000000000000000

    0000000000000000 0000000000000000

    0000000000000000 0000000039492c42

    00000000098c1a90 00000000098c403b

    00000000098c569b 0000000009a4cd0f

    0000000008c366f6 00000000098c13c8

    00000000098c160e 00000000098c225d

    00000000098c900f 00000000098c0d5a

    0000000000000000 0000000000000000

    0000000000000000 0000000000000000

    0000000000000000 0000000039492c42

    00000000098c1a90 00000000098c403b

    00000000098c569b 0000000009a4cd0f

    The results are the same as last time. They contradict what the Notes in the User's Guide about the effects of DINT and EINT. 

    Here is the modified source code:

    #include <MSP430.h>

    #define SIZE    500

            RSEG    DATA16_N

    log:    ds      16*SIZE

            RSEG    CSTACK

            RSEG    CODE

    main:   MOV     #SFE(CSTACK), SP

            MOV     #WDTPW+WDTHOLD,&WDTCTL

            MOV     #SELA_2+SELM_2+SELS_2, &UCSCTL4

            MOV     #SELREF_2, &UCSCTL3

            MOV.B   #BIT3+BIT2, &P5SEL

            MOV     #XT1OFF, &UCSCTL6

            MOV     #DCORSEL_7, &UCSCTL1

            MOV     #763-1, &UCSCTL2       

            MOV.B   #0, R15

    delay:  SUB.B   #1, R15

            JNZ     delay

            BIT     #OFIFG, &SFRIFG1

            JZ      go

            MOV     #0, &UCSCTL7

            bic     #OFIFG, &SFRIFG1

    go:     MOV     #SELA_2+SELS_3+SELM_5, &UCSCTL4

            EINT

            MOV     #0, R4

    min:    MOV     #40, R15

    loop:   MOV     R15, &TA0CCR0

            MOV     #CCIE, &TA0CCTL0

            MOV     #TASSEL_2|MC_2|TACLR, &TA0CTL

            MOV     #0xDEAD, R5

            MOV     #0xBEEF, R6

            MOV     #0xBAAD, R7

            MOV     #0xF00D, R8

    first:  NOP

            NOP

            NOP

            NOP

            DINT

            MOV     #0, R5

            MOV     #1, R6

            MOV     #2, R7

            EINT

            MOV     #4, R8

            ADD     #8, R8

            ADD     #-1, R8

            NOP

            NOP

            NOP

    last:   NOP

            NOP

            NOP

            NOP

            MOV     #0, &TA0CTL

            ADD     #1, R15

            CMP     #140, R15

            JZ      min

            JMP     loop

            NOP

    ta0isr: MOV     #0, &TA0CTL

            MOV     2(SP), R9

            CMP     #last, R9

            JNC     h1

            MOV     #last, R9

    h1:     CMP     #first, r9

            JGE     h2

            MOV     #first, R9

    h2:     SUB     #first, R9

            RLA     R9

            RLA     R9

            AND     #0x01F8, R9

            ADD     #1, log+0(R9)

            JNC     h3

            ADD     #1, log+2(R9)

            JNC     h3

            ADD     #1, log+4(R9)

            JNC     h3

            ADD     #1, log+6(R9)

    h3:     RETI

            COMMON  INTVEC

            ORG     TIMER0_A0_VECTOR

            DW      ta0isr

            COMMON  RESET

            DW      main

            END

  • OCY, I don't question your result. However, it doesn't prove that it can't be.

    Often, an erratum is a theoretical possibility, or something that has been observed under difficult to reproduce situations. I sometimes have things in my code where I say: normally it can't happen, but with an unlucky combination of events there can be a racing situation that causes malfunction.
    The possible problem with the DINT is not how often an instruction is hit by an interrupt but likely _when_ during the instruction the interrupt happens. Perhaps the critical time window is only a few nanoseconds long. If the interrupt happens sooner or later, all is well. But within this window, it will do unexpected things.
    Without analyzing the internal logic circuits and calculating signal runtimes in the interrupt logic part of the CPU, I can neither confirm nor deny the problem, but I can easily imagine that it is possible. And the fact that it has been added to the errata list indicates that I'm not the only one, but also someone with likely more information has confirmed it.

    So better safe than sorry, even if your test has shown no anomaly.
    Even if all is well in 99.9999% of all situations, there is a chance of 1ppm of a fatal error. Which might be acceptable for a typical PC program but maybe not for an embedded system, depending on application.

**Attention** This is a public forum