2007-05-30 08:35 AM
Temporarily locking of Interrupts on STR71x/STR73x/STR75x
2007-04-25 03:18 AM
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 Andreas2007-04-25 03:41 AM
One idea from absolutely another dimension - it is also possible to disable FIQ and IRQ at ARM core level by writing CPSR register. Not regarding the ARM7TDMI for STR71x and STR73x nor ARM7TDMI-S for STR75x, the procedure is always the same for all STR7xx CPU versions - read, modify and write the CPSR register. You can find more info in ARM7TDMI(-S) Technical Reference Manual available at arm's website...
Handicap - this enables/disables all FIQ and/or IRQ interrupts (two independent locking bits), not only one specific interrupt. [ This message was edited by: opijozko on 25-04-2007 16:15 ]2007-04-25 04:09 AM
Mike also mentioned something in that way. But disabling the global interrupt requires this procedure:
http://www.arm.com/support/faqip/3677.html So, I would prefer adding NOPs in my code, instead this stuff mentioned in that document. But of course if it is the only correct method, I might have no option and have to use it. BTW: Also I do not like to disable all interrupts if it is possible. Regards Andreas2007-04-25 09:23 PM
Update:
I just tried, why my former tests (STR71 and STR73 code from me exists since some time) did not detect the problem. Implementation: The disabling of the interrupt is done in a separate function. When the function returns the access to my 'protected' variable is made. In case I move now my instruction from disabling the CAN interrupt: #if (TAR_CHECK_CAN_IRQ_LOCKING == 1) DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter; #endif to the caller of this function. Like: Function_xyz() { CanIntDisable(); #if (TAR_CHECK_CAN_IRQ_LOCKING == 1) DBG_CANIRQ_CounterCpy = DBG_CANIRQ_Counter; #endif .... } I do not see the problem on none of the STR7xx devices (including the STR710). This is either caused, because I get now a different timing or what I suppose is, that due to the jump back to the caller, the instruction pipeline of the ARM is flushed and needs to be first reloaded (this is similar to the 'NOPs' ?). But if it is really the jump back and not the timing, it may be anyway dangerous -- because some compilers might optimize such 'small' functions and make them in a kind of 'inline code'. In this case, there will be no function call and so also no flush of the pipeline. Regards Andreas2007-04-25 09:23 PM
So... my opinion is as follows:
There is 3-stage pipeline in ARM7TDMI core. So, if actually the instruction disabling the interrupt is loaded, yet two next instructions must be loaded while it takes effect. Therefore you need two nops, if EIC clock (I think it is APB2 clock in STR710 case) is equal to core clock. If EIC is clocked in slower rate, these two instructions must be multiplied by factor of clock ratio (e.g. by factor of 2 if EIC is clocked in half of core, therefore you need 2x2=4 nops). On the other hand: There are from 2 up to 4 clock cycles needed to catch the interrupt by EIC. EIC generates the IRQ request to ARM core by setting the nIRQ line. ARM core needs additional from 5 up to 29 (in case of single IRQ request, without FIQ intervention) clock cycles to start handle the interrupt (it depends on actual instruction executed, see ARM7TDMI TRM rev4p1 at page 2-23 for more info). So, even if you already have interrupt disabled, it seems that some oldier interrupt request (rising more than 30 clock cycles before) may be still waiting at ARM core level and executed later.2007-04-25 10:24 PM
Sounds interesting. So either I had with the 'jump/flush pipeline' so far good luck and the ARM core required only the minimum latency. Or maybe the EIC has removed the 'Last interrupt' before the ARM core has read it out (don't know if EIC does this).
So questions over questions ?????? I wrote today again to support of ST -- so far (since 2007-04-16) I even did not get a direct contact partner, only an automatic reply --, maybe they can finally clarify this topic. Thank you for your reply. Regards Andreas2007-05-10 02:16 AM
Hi Andreas, your support should be managed by your local ST FAE, not by online support - it works, except MCUs :D The ST FAE has access to European MCU support directly.
I recommend to you using SWI for your non-interruptable processing, because SWI does disable IRQs automatically. In User mode you can't access I & F bits. Another approach - you use SWI as a service provider, that modifies SPSR by updating I & F flags, and returns. It might update I & F bits in User mode, then. Tomas2007-05-14 12:24 AM
Hi Edison,
you are right -- FAE has answered within one day. I am currently checking the proposals (proposals: Try with SWI or alternatively try to disable Interrupt Control register in EIC->ICR register). As soon as we find a solution I will post the result (but this may take some days/week(s)). BTW: What did you mean with ''service provider''. I can roughly think what you want to do, but do you think you can explain it in more detail. Regards Andreas2007-05-30 08:35 AM
''Service provider'' means to me that you have some underlaying layer of services (eg. your system routines or those handling your protected code) and the ''provider'' is a way (instruction) to enter these services. In my opinion the SWI is a way, how to access the service provider.
Nevertheless, opijozko, I think that you don't need additional 29 NOP cycles in ARM code because the core proceeds with only one long instruction, but just 2 (IRQ entrance)+2 (pipeline ahead of eg. multiply or STMFD).