Hi Jean-Marc,
do you have any new information for me?
If you could fill in the XLS that I sent you, I could get a deeper understanding of what's going on in the RM46.
Best regards,
Andy
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.
Other Parts Discussed in Thread: HALCOGEN
Hi,
we are using RM46 @80MHz and we have set up MPU with peripherals as DEVICE_NONSHAREABLE with HalCoGen.
Now, we have a problem:
- in some functions I want to disable the RTI compare interrupt
- then do some action
- and re-enable the RTI compare interrupt
Looks like this
// disable 1ms IRQ
rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT; // 'RTI_COMP0_IRQ_BIT' is defines as '0x01')
// Read flag (and reset, if elapsed)
if(T_timer.at_periodtimer[u8_timer_idx].u8_flag) // This global VAR is also read and written in 1ms IRQ
{
T_timer.at_periodtimer[u8_timer_idx].u8_flag = 0;
u8_retval = 1;
}
// enable 1ms IRQ
rtiREG1->SETINT = RTI_COMP0_IRQ_BIT;
In the case, that the '1ms IRQ' routine breaks normal programm execution
around the time, when I try to clear the enable flag (rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT, see above),
magic things happen:
The '1ms IRQ' is called twice!!!! (Directly after leaving the IRQ routine, it is called again!
(But it should not be called until another 1 millisecond has passed).
What I have tested:
- put one "NOP" after 'rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT;'
-> IRQ Routine is called twice -> ERROR!
- put 2 "NOP" after 'rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT;'
-> IRQ Routine is called only once -> works!!!
- switch Memorytype for peripherals to "STRONGLY ORDERED"
-> IRQ Routine is called only once -> works!!! (... with no "NOP"s at all...)
Do you have any explanation for this behavior?
Best regards,
Andy
Andy,
Is this sequence exactly what you are running?
// disable 1ms IRQ
rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT; // 'RTI_COMP0_IRQ_BIT' is defines as '0x01')
// Read flag (and reset, if elapsed)
if(T_timer.at_periodtimer[u8_timer_idx].u8_flag) // This global VAR is also read and written in 1ms IRQ
{
T_timer.at_periodtimer[u8_timer_idx].u8_flag = 0;
u8_retval = 1;
}
Can you post the dis-assembly code for this sequence? It will be helpful to analyze what is going on.
Hello Andy,
Pls post the dis-assembly as Jean-Marc mentioned that will give us a better idea.
But with the behavior you are mentioning here, I would suggest you give a quick try of the following as well and let us know.
1.
// disable 1ms IRQ
rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT; // 'RTI_COMP0_IRQ_BIT' is defines as '0x01')
while( rtiREG1->CLEARINT & RTI_COMP0_IRQ_BIT); //Make sure the write has happened successfully before you move forward.
2.
I assume you are mentioning the IRQ is called twice based on the Timer flag global variable read/written ? or is there some other qualifier ?
Pls let us know if this works.
Best Regards,
Karthik.
Hi there,
sorry for the late answer - now I'm back in the office again.
Since then I have slightly changed the code, so "magic things" happen at another code sequence, but it's the same error...
this is the code sequence:
uint32 timer_get_milliseconds (void)
{
uint32 u32_milliseconds;
#pragma DEBUGGING!
LED_YEL_SET(1); // This Debugging LED-command has no influence on the irq-error...
// disable 1ms IRQ
rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT;
// read time
u32_milliseconds = T_timer.u32_milliseconds;
// enable 1ms IRQ
rtiREG1->SETINT = RTI_COMP0_IRQ_BIT;
#pragma DEBUGGING!
LED_YEL_SET(0); // This Debugging LED-command has no influence on the irq-error...
// return value
return u32_milliseconds;
}
Here is the disassembly:
00008188: E59F0484 LDR R0, $C$CON1
0000818c: E3A01001 MOV R1, #1
00008190: E3A02001 MOV R2, #1
00008194: EB001449 BL gioSetBit
00008198: E59F04AC LDR R0, $C$CON15
0000819c: E3A0C001 MOV R12, #1
000081a0: E580C000 STR R12, [R0]
000081a4: E59FC46C LDR R12, $C$CON2
000081a8: E59CC000 LDR R12, [R12]
000081ac: E58DC000 STR R12, [R13]
000081b0: E59F0498 LDR R0, $C$CON16
000081b4: E3A0C001 MOV R12, #1
000081b8: E580C000 STR R12, [R0]
000081bc: E59F0450 LDR R0, $C$CON1
000081c0: E3A01001 MOV R1, #1
000081c4: E3A02000 MOV R2, #0
000081c8: EB00143C BL gioSetBit
This is what happens:
What helps in this case:
At the moment, I have no idea what's the reason for double calling the IRQ.
For debugging purpose, I have tested the INTFLAG at the end of the IRQ routine, but it was always cleared....
Best regards, Andy
Hello Andy,
The primary difference between Device and Strongly ordered is Posted and Non-Posted writes respectively.
Answering few of your questions,
a. Setting Strongly ordered takes more cycles - Yes since its NOT posted, every write needs to be acknowledged by slave before the next access.
b. Device Mode - Writes are posted which takes more time to reach the slave based on the existing entries in write buffer.
Doing a read to makes sure the write has completed is a way to make sure the write is completed before moving further.
c. Adding 8 NOP's - Yes this gives enough time for the write to complete staying in device mode(certainly not a good method).
Why you get IRQ twice -
Inside the ISR, you clear the flag and get out of ISR (Assuming the flag is cleared), but in reality this write is posted which has not yet reached the slave which has some latency).
Since the flag is not cleared the interrupt is still pending in VIM, which ends up as the second IRQ.
Hope this clarifies all your queries.
Best Regards,
Karthik.
Andy,
From the history of this post, I assume that you sometimes (not always) see RTI ISR runs twice after you re-enable RTI interrupt. If this is the case, you need to follow the following sequence in disabling and re-enabling RTI interrupt.
(1) Disable RTI interrupt by writing "1" to the related bit in the RTICLEARINT register.
(2) Do whatever processing.
(3) Clear the related bit in the RTIINTFLAG register. The name of this register is misleading. Those flags are set when the compare/overflow event occurs regardless if the interrupt is enabled. If the flag is set, CPU will enter ISR right after the bit in RTISETINT register is set.
(4) Re-enable RTI interrupt by setting the related bit in RTISETINT register.
We normally recommend users to access the peripheral space as device memory. There is no access issue in device mode.
Thanks and regards,
Zhaohong
Andy,
Some more information about access time for device and strongly ordered memory.
(1) LTR instruction takes 12 VCLK cycles in both modes.
(2) STR takes 12 VCLK cycles in strongly ordered mode. In device mode, CPU writes to a buffer. It takes 2 VCLK cycles. It take another 7 cycles for the buffer to be drained.in the mean time CPU can perform other tasks.
Due to this delay, if you write to the RTIINTFLAG register to clear the flag at the end of ISR, the RTIflag could stay set when CPU exit the ISR. Therefore, CPU will enter interrupt mode again. If the RTI flag become cleared when CPU enters IRQ mode, it will become a phantom interrupt (no source). When the interrupt is cleared by writing to a register, a good practice is to read the register back so that we are sure that the flag is truly cleared at source when CPU exits ISR.
Thanks and regards,
Zhaohong
Andy,
I have additional questions for you.
In your application code, are you using Halcogen to generate your code and notification routine or are you writing the code by yourself?
What is the ratio between the HCLK and VCLK used by your application?
Hello,
thanks for all the answers and the infos....
@Karthik
I can not confirm your explanation:
I tried the following (make sure that the flag is cleared):
(memtype for 0xFC000000 - 16_MB, 0xFE000000 - 512BYTES, 0xFF000000 - 16_MB
is set to DEVICE_NONSHAREABLE)
void RTI_1ms_IRQ(void)
{
/* USER CODE BEGIN (74) */
/* USER CODE END */
rtiREG1->INTFLAG = 1U;
rtiNotification(rtiNOTIFICATION_COMPARE0);
/* USER CODE BEGIN (75) */
#pragma DEBUG TIMER
while(rtiREG1->INTFLAG & 0x01);
timer_1ms_IRQ();
/* USER CODE END */
}
-> So INTFLAG shall be cleared when entering IRQ routine.
-> I have also tested INTFLAG at the END of IRQ -> it is always CLEARED
But the error still happens. IRQ is called twice.
---
@Zhaohong
- At the moment, I don't know exacly when the IRQ is executed twice.
(after reenabling IRQs, or before disabling IRQs).
- I don't want to clear the RTIINFLAG during the (very short) time, when
the RTI IRQ is disabled. If I would do so, IRQs might get lost.
Example:
- I disable the RTI IRQ
- Right after disabling th RTI IRQ, INTFLAG is set by system, because 1ms has run up
- I do some processing, then i clear the INTFLAG (as you proposed)
- I reenable the RTI IRQ
-> No IRQ would be handled -> 1ms IRQ is lost, right?
About clearing the INTFLAG at the end of IRQ:
I tried this at the end of the IRQ routine:
#pragma DEBUG TIMER
rtiREG1->INTFLAG = 0x01;
while(rtiREG1->INTFLAG & 0x01);
LED_RED_SET(0); // marks the end of IRQ
}
-> error still happens. IRQ is called twice
---
@Jean-Marc
RTI IRQ:
#pragma CODE_STATE(RTI_1ms_IRQ, 32)
#pragma INTERRUPT(RTI_1ms_IRQ, IRQ)
void RTI_1ms_IRQ(void)
{
/* USER CODE BEGIN (74) */
/* USER CODE END */
rtiREG1->INTFLAG = 1U;
rtiNotification(rtiNOTIFICATION_COMPARE0);
/* USER CODE BEGIN (75) */
#pragma DEBUG TIMER
while(rtiREG1->INTFLAG & 0x01); // test for TI forum
timer_1ms_IRQ(); // function call to my IRQ code
/* USER CODE END */
}
Maybe this is another hint:
Best regards,
Andy
Andy,
Will it be possible to share your project so I can debug or simulate what is going on.
I've tried to duplicate your configuration and can't reproduce what you are seeing.
If you don't want to make your project public, send me a friend request so we can exchange private email.
Andy,
What does timer_1ms_IRQ(); // function call to my IRQ code
Is it the code that disable RTI compare 0?
Hi Jean-Marc,
timer_1ms_IRQ()
is the interrupt routine which handels some timer variables.
It does not disable RTI compare 0. RTI compare 0 is disabled at 'main-loop-level',
not at 'IRQ'-level.
I have sent you my project in conversation 'Project for IRQ double calling error'.
Regards,
Andy
Andy,
I have your project and I can reproduce the problem.
Now I have to simplify your code in order to run a simulation. In the actual state, I see the problem after 23 RTI_COMP0 interrupts.
This is a long time for a simulation.
It will take me few days to setup and analyze this problem.
In the mean time, is it possible to use the NOP workaround or the Strongly Order option?
Hi Jean-Marc,
maybe you can play with the line:
'motor.c', Line 499:
Try different values for this variable (2130, 2160, 5010, 1111, etc...).
Maybe the problem occurrs earlier than after 23 RTI_COMP0 IRQs.
At the moment I'm back on the slower STRONGLY ORDERED access for savety reasons.
Regards,
Andy
Andy,
I have an explanation for what is happening in your code/application.
The problem is coming from the following code:
rtiREG1->CLEARINT = RTI_COMP0_IRQ_BIT;
// read time
u32_milliseconds = T_timer.u32_milliseconds;
// enable 1ms IRQ
rtiREG1->SETINT = RTI_COMP0_IRQ_BIT;
This code is executed while the RTI is running.
Let assume that the RTI is about to fire a COMP0 interrupt. At the same time you are disabling COMP0 interrupt.
If this happens just after the COMP0 flag is already set, the VIM and CPU will be informed of this interrupt.
The disable COMP0 is a simple AND gate. The flag is still set, but the request to the VIM is forced to 0.
In device mode, the CPU will then read your timer and issue the following write to re-enable interrupt.
While the CPU jump in IRQ mode to process your ISR, the RTI COMP0 is re-enabled, while the flag still set.
This create another request to the VIM. The VIM is sensitive to edge on the request.
Because you are already in IRQ mode the CPU cannot acknowledge the request.
Once you are done with your ISR and exit IRQ mode, the CPU will be able to acknowledge the pending request and so re-enter IRQ.
I think the fact that this code works in Strongly order is by chance.
The best way to proceed will be to change your code with:
vimREG->REQMASKCLR0 = RTI_COMP0_IRQ_BIT;
// read time
u32_milliseconds = T_timer.u32_milliseconds;
// enable 1ms IRQ
vimREG->REQMASKSET0 = RTI_COMP0_IRQ_BIT;
(You have to include vim.h in your file)
In this case, you force the VIM to MASK the corresponding request while you execute your critical code.
I did run your code with this modification and I don't see the problem anymore.
Hi Jean-Marc,
do you have any new information for me?
If you could fill in the XLS that I sent you, I could get a deeper understanding of what's going on in the RM46.
Best regards,
Andy
Andy,
I did not forget about your question. I've done a lot of simulation to understand your problem.
Like I've explained in a previous post the problem is the way you disable the RTI Compare 0 interrupt.
Using the VIM Request Mask is the way to go.
Anyway, I will send you some waveform so you can see what is going on.
Apparently, there is a critical window of 1 HCLK cycle when the CPU acknowledge the RTI compare and the at the same time the RTI Interrupt enable is cleared.
Only under these circumstances the CPU will see another RTI interrupt.
Because of the pipeline structure, the speculative access and branch prediction, it is extremely difficult to evaluate from a software point of view when these condition are met.
I will try to prepare this report in the next few days and will be back to you.
Hi,
thanks for providing the detailled timing chart of the different signals in the RM46.
I also tested your suggestion to disable the interrupt by disabling the according channel (vimREG->REQMASKCLR0 = RTI_COMP0_IRQ_BIT;).
This works fine for my actual application.
But, I have also made some other "disable a single interrupt source"-tests:
disable_RTI_interrupt();
AU32_test_array[0] = u32_counter;
AU32_test_array[1] = u32_counter;
AU32_test_array[2] = u32_counter;
AU32_test_array[3] = u32_counter;
AU32_test_array[4] = u32_counter;
AU32_test_array[5] = u32_counter;
AU32_test_array[6] = u32_counter;
AU32_test_array[7] = u32_counter;
AU32_test_array[8] = u32_counter;
enable_RTI_interrupt();
Results:
If I use memory access with type = DEVICE (not STRONGLY_ORDERED), then Interrupts are NOT(!) safely disabled at all.
Even if I disable the RTI interrupt by disabling the according interrupt channel.
Here are my test - results:
DisableRTI_interrupt by (rtiREG1->CLEARINT), Memory Access = DEVICE
disable_RTI_interrupt();
AU32_test_array[0] = u32_counter;
AU32_test_array[1] = u32_counter;
AU32_test_array[2] = u32_counter;
AU32_test_array[3] = u32_counter;
AU32_test_array[4] = u32_counter;
AU32_test_array[5] = u32_counter;
AU32_test_array[6] = u32_counter;
AU32_test_array[7] = u32_counter;
!!!!!!!!!! RTI interrupt routine breaks here (latest) !!!!!!!!!!
AU32_test_array[8] = u32_counter;
enable_RTI_interrupt();
DisableRTI_interrupt by (vimREG->REQMASKCLR0), Memory Access = DEVICE
disable_RTI_interrupt();
AU32_test_array[0] = u32_counter;
AU32_test_array[1] = u32_counter;
AU32_test_array[2] = u32_counter;
AU32_test_array[3] = u32_counter;
AU32_test_array[4] = u32_counter;
!!!!!!!!!! RTI interrupt routine breaks here (latest) !!!!!!!!!!
AU32_test_array[5] = u32_counter;
AU32_test_array[6] = u32_counter;
AU32_test_array[7] = u32_counter;
AU32_test_array[8] = u32_counter;
enable_RTI_interrupt();
------------------------------------------------------------
------------------------------------------------------------
If I set memory access to STRONGLY ORDERED, RTI interrupts are savely disabled:
DisableRTI_interrupt by (vimREG->REQMASKCLR0 OR rtiREG1->CLEARINT), Memory Access = STRONGLY_ORDERED
disable_RTI_interrupt();
--- FINE! There are no interrupts between disable and enable ---
AU32_test_array[0] = u32_counter;
AU32_test_array[1] = u32_counter;
AU32_test_array[2] = u32_counter;
AU32_test_array[3] = u32_counter;
AU32_test_array[4] = u32_counter;
AU32_test_array[5] = u32_counter;
AU32_test_array[6] = u32_counter;
AU32_test_array[7] = u32_counter;
AU32_test_array[8] = u32_counter;
--- FINE! There are no interrupts between disable and enable ---
enable_RTI_interrupt();
------------------------------------------------------------
------------------------------------------------------------
One way to disable interrupts savely with memory access = DEVICE seems to be: reading back the enable-flag:
DisableRTI_interrupt by: (vimREG->REQMASKCLR0 OR rtiREG1->CLEARINT,
Readback the flag (while(rtiREG1->SETINT) OR while(vimREG->REQMASKSET0 & 0x04)),
Memory Access = DEVICE
disable_RTI_interrupt();
--- FINE! There are no interrupts between disable and enable ---
AU32_test_array[0] = u32_counter;
AU32_test_array[1] = u32_counter;
AU32_test_array[2] = u32_counter;
AU32_test_array[3] = u32_counter;
AU32_test_array[4] = u32_counter;
AU32_test_array[5] = u32_counter;
AU32_test_array[6] = u32_counter;
AU32_test_array[7] = u32_counter;
AU32_test_array[8] = u32_counter;
--- FINE! There are no interrupts between disable and enable ---
enable_RTI_interrupt();
It would have been nice to have detailed information about how to disable interrupts safely.
Maybe there are some data sheets, user manuals or application notes which you can recommend?
Best regards,
Andy
Andy,
All these results make sense.
In Strongly Ordered, the CPU waits for all write to be performed before executing the next instruction.
This method as a side effect in term of performance.
Reading back the flag or status after a write has the same effect. You decide to move on when the write is done.
In Device, the CPU takes advantage of his write buffer and does not wait for the write(s) to be performed, but the access order is maintained.
The RTI interrupt is the only interrupt that you want/have to disable or other interrupts could affect your application?
ARMv7 Architecture offers the following instructions:
In your case, in Device mode, after vimREG->REQMASKCLR0 you can add a DSB instruction. In this case the CPU will act as in Strongly Order mode.
These 3 instructions are assembly instruction so the following syntax has to be used in your c code.
asm(" DMB");
asm(" DSB");
asm(" ISB");
Please check this and let me know.