What makes a program very slowly?

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

A program runs very slowly. Is it possible to locate which codes cause this problem? As I/O I have UART and CAN. Do they play a role in speed?

Thank you
Senmeis

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Executing opcodes is what plays a role in speed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

senmeis wrote:
A program runs very slowly. Is it possible to locate which codes cause this problem?
:twisted: Nope, you are stuck. :evil:

Of course there are ways to locate the problem code. :P

You haven't really told us much about what "running slowly" means. Nor have you told us which compiler you are using, nor anything about the application. But, before we go there, check the following:

1 - Have you checked the processor to make sure it is clocking at the rate you expect? A common problem is for the processor to run at the default rate (usually 1 MHz) when the programmer is expecting something much faster (8 MHz or 16 MHz or 20 MHz). Have you run a simple timer and checked the resulting frequency to be sure it's what you expect?

2 - Are you using compiler optimization? Although this usually makes small to moderate difference, it may be enough for you. You should always turn on compiler optimization unless there are really good reasons for turning it off, and even then it should be turned off only temporarily.

3 - Are you using floating point math? Do you need to use it (i.e., use logs, exponential, trig functions extensively)? Look at your computations and convert as much as possible to fixed point math. There is not floating point hardware in AVRs, so all floating point math must be done in software, which is lots slower (more than 10-100 times) than using the built-in integer math. Keep in mind that a long (32-bit) signed variable has a range of -2.1 billion to +2.1 billion. If you want to have 6 places past the decimal point, that still gives a range of -2,147 to +2,147.

4 - Are you keeping your Interrupt Service Routines as short and sweet as possible? An ISR should only do the minimum necessary and then return. If you are running USART or CAN interfaces, have circular buffers that you can load with incoming or outgoing data and let a non-interrupt routine process them. Sitting in the ISR can cause you to lose interrupts. You absolutely should not call printf or scanf (or related functions) in an ISR!

Okay, now that you looked at that, tell us a bit more about what "running slowly" means. What is the purpose of the program? What should it be doing that it is not? What are the effects of the program "running slowly"?

Our answers can only be as good as the information you give us.

Other than this, there are some "heroic" measure that can pinpoint problem code. If you JTAG (or if you a DebugWire-equipped AVR and have an ISP that can handle it), try breaking in the code occasionally to see where the program is spending most of its time.

A variation of this idea is to set up a timer to interrupt at a "good rate" (maybe once every millesecond?) and record the return address in a circular buffer in RAM. Run your program for a while, then dump the contents of the circular buffer. You now know where, on average, the program is spending its time. Use the map you generated from compiling (you are generating a map, aren't you?) to figure out the code.

Those are the ideas off of the top of my head.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

senmeis wrote:
As I/O I have UART and CAN.
This is a know possible condition when using CAN.

Because CAN has multiple interrupt sources (AERG, AERR, BERR, BOFFIT, BXOK, CERG, CERR, FERG, FERR, SERG, SERR, RXOK, TXOK interrupt flags) all using the same CANIT interrupt vector, its not possible to automatically clear these interrupt flags when responding to the CANIT interrupt vector. If they were automatically cleared, then the interrupt response code would not be able to tell which interrupt flag caused the interrupt and therefore which one or ones need servicing. Any enabled CANIT interrupt flags (data sheet Figure 19-14 CAN Controller Interrupt Structure) must be cleared by the interrupt response program code manually.

If any enabled CANIT interrupt flag is not cleared by the time the interrupt return RETI instruction is executed, the interrupt flag will still be enabled when global interrupts are enabled. Since RETI automatically re-enables global interrupts after the next instruction is executed, the program will execute one instruction from the main code after the RETI and immediately re-enter the CANIT interrupt vector again. Since the CANIT vector response code failed to clear that interrupt flag, its a safe bet this behavior will repeat in an endless loop. This means every main program instruction executed will also run the CANIT vector interrupt code as overhead. This makes the AVR run very slowly.

If this is your problem, all you have to do is carefully examine your CANIT interrupt vector program code for enabled interrupt flags and ensure all of them are cleared before you exit the interrupt response code.

Check the data sheet for clearing the interrupt flags. The CANSTMOB register is cleared by software read-modify-write code (i.e. writing a zero to clear the interrupt flag bit or bits) and the CANGIT register is cleared by software the usual way ( i.e. writing a one to clear the interrupt flag bit or bits).

You may only ignore clearing any interrupt flag that is not enabled because it will not cause a CANIT interrupt response.

Actually a similar problem could occur with the USART interrupt code. If you respond to a RXC interrupt flag and exit the interrupt without ever reading the UDR register, the RXC flag will not be cleared. This is because the RXC flag is only cleared when the USART read buffer is empty and reading UDR is the only way to empty the USART read buffer. This is not a common problem because it is obvious the USART interrupt response code is not working. With CAN it is not so obvious.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, looks like you did find someone with a post count of 44.

Quote:
Joined: Feb 11, 2007
Posts: 44
(I only mention it to show the non-specificity of that post count.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have some additional clarification. When I said to ensure all the enabled CANIT interrupt flags are cleared before returning from the CANIT interrupt vector code, I didn't mean to simply clear them before returning. I meant you should ensure your CANIT interrupt response code clears them during normal execution. Another CANIT interrupt flag event could occur and set an interrupt flag again, before you are done and have returned from the CANIT interrupt vector code. When manually clearing an interrupt flag, the best practice is to check an interrupt flag, clear that interrupt flag and then remembering that interrupt flag had been set, run the code for that interrupt flag. If you manually clear a CAN hardware interrupt flag after the code for that flag has already run, you might accidentally clear another brand new interrupt flag event and never respond to it (i.e. accidentally create a bug and drop an interrupt response). The CANSTMOB register RXOK and TXOK interrupt flags are generally an exception to this clear before running code rule because the MOb must be re-enabled before any additional activity will occur for that MOb (generally means you do not re-enable the MOb from inside the CANIT interrupt response code). If a brand new CANIT interrupt flag is set during the CANIT interrupt response, leaving it set when the RETI is executed is alright. This will simply cause a new CANIT interrupt response for the new interrupt just like it should. The correctly working CANIT interrupt response code will still clear the new flag event to prevent an infinite CANIT response loop from a flag that is never cleared problem.

If you use the BXOK interrupt flag see the data sheet. There is a special sequence that must be followed in order to successfully clear this interrupt flag.

Just for the record, if a CANIT interrupt flag is stuck on, the re-entered CANIT interrupt response will be priority arbitrated against any other pending interrupt vectors. Any vector that is higher priority will run instead of CANIT, except that the CANIT interrupt will remain enabled for the next RETI global interrupt re-enable. Lower priority interrupts than CANIT will never run and higher priority interrupts will run instead of CANIT for that particular AVR single main program instruction.

It is also possible to see similar behavior when polling using the CANGIT register bit 7 (with the CANGIE register ENIT bit cleared). The main difference is instead of executing CAN interrupt response code on every single AVR main program instruction, you would only execute CAN polling response code on every single polling cycle. Still bad, but not quite as slow.

I didn't mean to imply reading UDR is the only way to clear the USART read buffer. For example, disabling the USART receiver should also clear the read buffer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For testing time of interrupt execution simple method can be used if exist any not used port which can be used as output. Setting the port to 1 at begin of interrupt and clearing it at end of it allow to time needed to single interrupt execution measure by oscilloscope and, by measuring average value of output voltage, how many percent of whole program execution time is used to service of this interrupt.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry I forgot to inform you of some details.

I'm using GCC compiler V1.43 and there are no floating point operations.

The application is to receive data from UART and transmit them with CAN. I made a test like this: Send 8 bytes => wait for an interval => send the next 8 bytes and so on.

The problem is, if this interval is shorter than 10 ms, some mistakes occur like false data in CAN. I use 9600 bps and I think the problem is not CAN interrupts.

Best regards,
Senmeis

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At a guess, your ISR code is being called continually. i.e. the interrupt flag is not being cleared.

A simple test for this is to place a long counter in each ISR and compare the call count with the expected number of characters.

Of course, posting your skeleton code that exhibits the problem would reduce the wear and tear on members' crystal balls.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'm using GCC compiler V1.43 and there are no floating point operations.

Do you really mean 1.43 ?

The current version (well the main WinAVR distribution anyway) is:

C:\>avr-gcc -dumpversion
4.3.0

C:\>which avr-gcc.exe
C:\WinAVR-20080610\bin\avr-gcc.exe

Cliff