Skip to main content
Visitor II
May 30, 2007
Question

Temporarily locking of Interrupts on STR71x/STR73x/STR75x

  • May 30, 2007
  • 18 replies
  • 2511 views
Posted on May 30, 2007 at 17:35

Temporarily locking of Interrupts on STR71x/STR73x/STR75x

    This topic has been closed for replies.

    18 replies

    neunerAuthor
    Visitor II
    April 18, 2007
    Posted on April 18, 2007 at 07:49

    Hello,

    I have to port a software to STR750. Thereby a question came up. How can I temporarily disable the CAN interrupt to protect the read/write to a variable which is accessed as well in my 'normal' program execution (no interrupt), as well as in the CAN interrupt. Checking the STR library, it should be possible to disable the interrupt by writing to EIC->IER and deleting the corresponding bit of the CAN.

    As my STR750 evaluation board is broken (new one I will receive in the next days), I tried this on two other boards, one the STR710 (STR710FZ2T6) and the other the STR730 (STR730FZ2T7).

    On the STR730 it worked like expected, when I disabled the CAN interrupt in the EIC->IER, the interrupt is immediatelly locked. When I do the same on STR710, it requires some additional asm(''nop'') instructions until the CAN interrupt is really locked. This is very dangerous because the access of my variable might not be locked properly. The different behaviour I explain myself that there is a slightly different EIC inside the STR730/STR710.

    BTW: The EIC is an external component in relation the the ARM7 core, is it possible, that it is on the STR710 to slow ? What happens if the clocks of the peripherals (EIC) are more slowed down -- is it then getting worse.

    Should be first the ARM7 interrupt be disabled instead ?

    I also checked on my LPC21xx evaluation board (also ARM7 core and an VIC interrupt controller), there it also works properly.

    I found no document nor an ERRATA of STR7xx series. Further I found no application note dealing with this.

    As soon I get my STR750 board, I will try this here as well. The STR750 EIC seems to be more like the STR710.

    Questions:

    1) What is the correct way to temporarily lock the interrupt ?

    2) If exits: Where can the erratas be found

    3) What are the latencies of the EIC (also maybe in relation to the peripheral clock of EIC)

    Any help is welcome. Thank you in advance.

    Best Regards

    Andreas

    Test code basic:

    Immediatelly after disabling the interrupt I set a volatile defined variable to 1. Before reenabling the interrupt I set it back to 0. Inside my interrupt I check if the variable is set => what should never occur, if the interrupt is disabled immediatelly (but on STR710 I have seen that it was set in the interrupt).

    P.S: Unfortunatelly I cannot provide my test code to you.

    P.P.S: Also that I currently port to STR750, the other processors STR710, 730 we have also running code on it and therefore I have to crosscheck the proper behaviour also on this processors.

    Visitor II
    April 18, 2007
    Posted on April 18, 2007 at 16:17

    Disabling ALL interrupts seems a bit extreme when you only need to disable CAN interrupts. There must be an interrupt masking bit somewhere among CAN control registers.

    Since the EIC is external to the ARM CPU and clocked by the APB2 clock, there probably is some latency associated with accesses to its registers. Unfortunately, I am not aware of any document describing such latency. A few NOPs should fix it, but it would be nice to know how many NOPs is enough.

    Regards,

    - mike

    neunerAuthor
    Visitor II
    April 19, 2007
    Posted on April 19, 2007 at 05:17

    Hi Mike,

    disabling a flag in the CAN controller itself usually leads to loose interrupts. So, disabling the CAN IRQ in the EIC seems to be the correct way. I know from other mirco controllers that there is exactly stated, that you need to wait e.g. 5 cycles until it is really locked (e.g. seen on TMS320F2808 device).

    But like you mentioned, it would be good (respectively it is required) to know exactly how many NOPs have to be inserted (the number of NOPs might also change depending on ratio of clock frequency of CPU core and the clock frequency of the peripherals). And simply try and check if it works is not applicable.

    BTW: In the application note for TMS320F2808 is a complete sequence explained with 6 single steps which are necessary to disable a peripheral interrupt. Maybe here are also some additional steps necessary for a proper sequence ?

    Hopefully somebody can provide these information.

    Best Regards

    Andreas

    Visitor II
    April 19, 2007
    Posted on April 19, 2007 at 08:20

    There is a downside to disabling the interrupts globally: the overall interrupt latency of the system increases. This is not necessarily a problem in your application. If not, you could disable interrupts in the ARM CPU. It can be tricky too, for details see here:

    http://www.arm.com/support/faqip/3677.html

    Regards,

    - mike

    neunerAuthor
    Visitor II
    April 19, 2007
    Posted on April 19, 2007 at 09:24

    Thank you for the link. I had a look on the document. Like you mentioned, this looks also very tricky. On a first view, I think I would prefer the 'nop' method if it is possible (but here it is necessary to know how to calculate the number of required 'nops' and not just to try).

    I have also written to the offical support of ST, but so far I received no reply to my inquiry.

    I think I will wait one/two more days and then try to find a solution (but in my opinion there must be something provided by ST).

    Thank for your help.

    Best Regards

    Andreas

    Visitor II
    April 19, 2007
    Posted on April 19, 2007 at 19:09

    Dear Andreas,

    Just one point very important the STR71x, STR73x and STR75x EIC implementation are quite different since in STR71x the EIC is on APB2 and so assuming the CPU and memory (MCLK) have the same frequency , we have 5 added wait-states of sync between AHB and APB2 and I let you compute if the dividers is not the same. 10 if APB2 is twice slower than AHB (CPU).

    For the STR75x, the newest one from ST (the EIC is moved to AHB) the fastest Bus rather than APB. so normally there is no added wait-states between EIC and CPU buses, but of course we if we count the path between the CAN peripheral ( which is on APB ) the interrupt signal should have teh same latency of 5 cycles to go to the EIC.

    Fianlly for the STR73x, the EIC is on APB but the APB and the CPU are running at the same fresquency all time so it is like on AHB on STR75x.

    I hope This may help you. :-]

    With best regards,

    Rave

    neunerAuthor
    Visitor II
    April 20, 2007
    Posted on April 20, 2007 at 06:18

    Hi Rave,

    thank you for this detailed answer, I will spend next week some more time to go deeper into this topic.

    Your answer explains, why I have not seen this problem on STR730. Hopefully my STR750 replacement board will be soon delivered.

    Also I will then check again the code on the STR710 board.

    Thanks again.

    BTW: The value of 5 / 10 clocks, where they calculated or did you just try until it worked ?

    Best Regards

    Andreas

    Visitor II
    April 22, 2007
    Posted on April 22, 2007 at 16:44

    Hi Andi_99,

    Yes I get these results based on my own tests with some nops. Looking forward to your analysis so that we can consolidate both results ;) Thank You.

    Rave

    neunerAuthor
    Visitor II
    April 23, 2007
    Posted on April 23, 2007 at 07:26

    Hi Rave,

    I have now first to finish an other job. Then I will continue on the STR7xx topic.

    As soon I have new information I will post them here (may take some days).

    Best Regards

    Andreas

    neunerAuthor
    Visitor II
    April 25, 2007
    Posted on April 25, 2007 at 12:18

    Hi Rave,

    I spend some more time to check the delay by locking the CAN interrupt. Here is what I found out:

    BTW: So far I received no reply to my support request from ST. So all answers are without guarantee.

    First of all I changed my test routine. This made it an instruction faster.

    a) On STR750 I have not seen the problem. The interrupt seems to be locked immediatelly.

    Following code was used to initialize the PLL and the pheripheral clocks:

    /* Wait for OSC4M start-up */

    t_error = MRCC_WaitForOSC4MStartUp();

    if(t_error == SUCCESS)

    {

    /* Set HCLK to 60MHz */

    MRCC_HCLKConfig(MRCC_CKSYS_Div1);

    /* Set CKTIM to 60MHz */

    MRCC_CKTIMConfig(MRCC_HCLK_Div2);

    /* Set PCLK to 30MHz */

    MRCC_PCLKConfig(MRCC_CKTIM_Div1);

    /* Enable Flash Burst mode */

    CFG_FLASHBurstConfig(CFG_FLASHBurst_Enable);

    /* Set CK_SYS to 60 MHz */

    MRCC_CKSYSConfig(MRCC_CKSYS_OSC4MPLL, MRCC_PLL_Mul_15);

    }

    ....

    /* enable clock of peripherals required for this application */

    MRCC_PeripheralClockConfig((MRCC_Peripheral_GPIO |

    MRCC_Peripheral_CAN |

    MRCC_Peripheral_TIM1)

    ,ENABLE);

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

    Global Variables used:

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    volatile UINT8 DBG_CANIRQ_Counter;

    volatile UINT8 DBG_CANIRQ_CounterCpy;

    volatile UINT8 DBG_CANIRQ_Error;

    #endif

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

    The CAN interrupt handler:

    void CAN_IRQHandler(void)

    {

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    DBG_CANIRQ_Counter++;

    #endif

    .... code for handling interrupt

    }

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

    Disabling of CAN interrupt:

    /* disable CAN IRQ */

    EIC->IER &=~ (1 << CAN_IRQChannel);

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter;

    #endif

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

    Enabling of CAN interrupt:

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    if (DBG_CANIRQ_CounterCpy != DBG_CANIRQ_Counter)

    {

    DBG_CANIRQ_Error = TRUE;

    }

    #endif

    /* enable CAN IRQ */

    EIC->IER |= (1 << CAN_IRQChannel);

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

    b) On STR710 I have the problem that the interrupt is locked some instructions later.

    I did two tests:

    b1) Peripheral Clocks are equal to clock of ARM core (48Mhz on my system)

    RCCU_Div2Config ( ENABLE);

    RCCU_FCLKConfig ( RCCU_DEFAULT );

    RCCU_PCLKConfig (RCCU_DEFAULT);

    RCCU_MCLKConfig (RCCU_DEFAULT);

    RCCU_PLL1Config (RCCU_PLL1_Mul_12, RCCU_Div_2) ;

    while(RCCU_FlagStatus(RCCU_PLL1_LOCK)==RESET);

    RCCU_RCLKSourceConfig (RCCU_PLL1_Output) ;

    For disabling/enabling I used

    EIC->IER &= ~(1 << CAN_IRQChannel); /* disable */

    EIC->IER |= (1 << CAN_IRQChannel); /* enable */

    My test reported a failure. First after adding two NOPs it worked.

    EIC->IER &= ~(1 << CAN_IRQChannel); /* disable */

    asm(''nop'');

    asm(''nop'');

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter;

    #endif

    b2) Peripheral Clock is half of clock of ARM core:

    RCCU_Div2Config ( ENABLE);

    RCCU_FCLKConfig ( RCCU_RCLK_2 );

    RCCU_PCLKConfig (RCCU_RCLK_2);

    RCCU_MCLKConfig (RCCU_DEFAULT);

    RCCU_PLL1Config (RCCU_PLL1_Mul_12, RCCU_Div_2) ;

    while(RCCU_FlagStatus(RCCU_PLL1_LOCK)==RESET);

    RCCU_RCLKSourceConfig (RCCU_PLL1_Output) ;

    I had to add four NOPs:

    EIC->IER &= ~(1 << CAN_IRQChannel); /* disable */

    asm(''nop'');

    asm(''nop'');

    asm(''nop'');

    asm(''nop'');

    #if (TAR_CHECK_CAN_IRQ_LOCKING == 1)

    DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter;

    #endif

    c) Contrary to my first test, I have seen the problem also now on the STR730 (maybe because I optimized my test)

    CFG_PeripheralClockConfig(CFG_CLK_EIC , ENABLE);

    CFG_PeripheralClockConfig(CFG_CLK_GPIO1, ENABLE);

    CFG_PeripheralClockConfig(CFG_CLK_GPIO2, ENABLE);

    CFG_PeripheralClockConfig(CFG_CLK_GPIO4, ENABLE);

    CFG_PeripheralClockConfig (CFG_CLK_CAN0, ENABLE);

    CFG_PeripheralClockConfig (CFG_CLK_CAN1, ENABLE);

    CFG_PeripheralClockConfig(CFG_CLK_TIM1 , ENABLE);

    CMU_StructInit(&s_cmu_init);

    CMU_DeInit( );

    s_cmu_init.CMU_CKSEL0 = CMU_CKSEL0_CKOSC;

    CMU_Init (&s_cmu_init);

    PRCCU_DeInit();

    PRCCU_StructInit(&s_prccu_init);

    s_prccu_init.PRCCU_DIV2 = ENABLE;

    s_prccu_init.PRCCU_MCLKSRC_SRC = PRCCU_MCLKSRC_PLL;

    s_prccu_init.PRCCU_PLLDIV = 2;

    s_prccu_init.PRCCU_PLLMUL = PRCCU_PLLMUL_16;

    s_prccu_init.PRCCU_FREEN = DISABLE;

    PRCCU_Init(&s_prccu_init);

    --------

    EIC->IER1 &= ~(0x0001 << (CAN0_IRQChannel-32)); /* disable */

    ----

    EIC->IER1 |= 0x0001 << (CAN0_IRQChannel-32); /* enable */

    ----

    Here I had to add one NOP instruction that it worked.

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

    Looking at assembler code, for instruction:

    DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter;

    It starts with a fetch for indirect addressing, before loading the content.

    376: DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter;

    377: #endif

    0x800024EC E59F300C LDR R3,[PC,#0x000C] <===== indirect addressing preparation

    0x800024F0 E5D32000 LDRB R2,[R3] <===== load address

    0x800024F4 E59F3008 LDR R3,[PC,#0x0008]

    0x800024F8 E5C32000 STRB R2,[R3]

    378: }

    So, this is a kind of 'additional NOP'. So for security it might be better to add everywhere at least one NOP more.

    Result:

    Nevertheless, this is just 'trying'. What I require is a statement from ST. Maybe it is completely wrong how we disable/enable the interrupt.

    BTW: On NXP (Philips) LPC2129 it also worked without any NOPs.

    Hopefully I will soon get an reply from ST.

    Best Regards

    Andreas