## How big of numbers can I divide/multiply on an ATMega?

16 posts / 0 new
Author
Message

Hi all.  I'm using an ATMega328p, and am trying to convert clock ticks into microseconds to check the timing on certain events.  I was hoping to do it using this function:

```#define F_CPU 16000000L
#define PRESCALER 8

int ticks_to_microseconds(int ticks) {
return (ticks / (F_CPU / PRESCALER)) * 1000000; //microsecond is a millionth of  one second
}```

Is it possible to divide/multiple numbers as big as 16,000,000 on this microcontroller?  Will there be a major performance penalty?

Tylerlee12 wrote:

Is it possible to divide/multiple numbers as big as 16,000,000 on this microcontroller?  Will there be a major performance penalty?

What does the data sheet say, about native opcodes ?

Anything larger than that needs to be done in software, and that is of course slower.  16M is close to 24bits, the most natural Type for that, is 32b.

Keep in mind you might want to do a 32b*32b=64b/32b operation

'major performance penalty' depends on your value for major

Try it, and measure the time required in the Simulator.

The C preprocessor precalculates the #defined constants and produces (ticks / ( 2000000 )) * 1000000;
but that will evaluate to 0 until you have more than 2000000 ticks.
(ticks is an int which means that integer arithmetic will be used)

Some rearranging to use an integer division produces ticks / ( F_CPU / ( PRESCALER * 1000000 ) )
which ends-up as ticks / 2

```    int ret = (ticks / (F_CPU / PRESCALER)) * 1000000; //microsecond is a millionth of  one second
int ret = (ticks / 2000000L) * 1000000; //microsecond is a millionth of  one second
int ret = (0) * 1000000; //microsecond is a millionth of  one second```

Integer division will always give you 0 (unless you really are calculating for 2000000 ticks.

If you know that F_CPU is in whole MHz,   you can use a more sensible expression:

```    int ret = (ticks / (F_CPU / 1000000) / PRESCALER)); //microsecond is a millionth of  one second
```

Since you know all the scaling numbers at compile time,  you might just as well pre-compute with floating point e.g.

```    int ret = (ticks / ((F_CPU / 10000000.0) / PRESCALER)); //microsecond is a millionth of  one second
```

Untested.   I bet the Compiler will end up with a simple shift when you have easy numbers like 16MHz

And a simple f-p multiplication when you have 7.3728MHz that is automatically cast back to int.

David.

david.prentice wrote:
you can use a more sensible expression:
david.prentice wrote:
(ticks / ...

Tylerlee12 wrote:
I was hoping to do it using this function:
Tylerlee12 wrote:
int ticks_to_microseconds(int ticks) {

The fragment posted is supposedly code for an AVR target, right?  And given the mention of F_CPU, the toolchain is GCC?  [note to OP:  Tell target CPU model as well as toolchain and related information when posting a query]

What I'm getting at is that "int" is going to be 16-bits, signed, on all mainstream C toolchains with an AVR8 target.

[why is signed being used anyway?]

Have you consulted standard C texts about "short" and "long" and "unsigned"?  Are you aware of the platform-agnostic type names in stdint.h ?  And how these compare to the entries in limits.h ?  And what the documentation on your toolchain might have to say?

http://www.nongnu.org/avr-libc/u...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

When you write expressions like:

`(ticks / (F_CPU / PRESCALER)) * 1000000`

you should always try to make life easy for the MCU and the compiler. Avoid divisions.

`(ticks * (PRESCALER / F_CPU )) * 1000000`

Another problem: these number are integers, not floating point. When you do

`(PRESCALER / F_CPU )`

the result is an integer and will be truncated to zero. Then, the whole expression will be zero. So, another change is needed:

`(ticks * (PRESCALER * 1000000 / F_CPU ))`

Now the compiler can pre-calculate

`(PRESCALER * 1000000 / F_CPU )`

and everything will work fine.

As I said earlier. Untested.
But if you alter the parentheses, the Compiler will probably give you what you want.
As it stands. (ticks/2000000L) will be evaluated with long integer maths. Any value of ticks < 2000000L will evaluate to 0.
.
People may be horrified by f-p scaling. But in practice it does not take many cycles or Flash memory. And in your case, with proper parentheses will probably optimise the f-p to an integer shift.
.
Easy enough to test in the Simulator. I am not at a PC.
I have used similar scaling macros to time sequences with different F_CPU values e.g. baud friendly but math nasty.
.
David.

david.prentice wrote:
As I said earlier. Untested.

So you did. I should have refreshed the page

Thanks to you and david (and everyone else)!  To answer some questions posed to me earlier in the thread, my target is an ATMega328p and I am indeed using the GCC toolchain.  Follow up questions of my own:

1. Why is division more difficult for the MCU and compiler?
2. Testing things in a simulator has been brought up several times in this thread, and I think that's a great idea.  What simulator do you all generally use?  simavr on Github looks nice but if there's a better one I'd love to know.

As you probably remember from school, the algorithm for division is more complicated than the one for multiplication. So it's harder for a machine, the same way it's harder for a human. And in turn, for the ATMega, multiplication is harder than logic/addition/subtraction/shift by 1 bit.

So, for example:

`(a+b) * (a-b)`

is usually faster than

`a*a - b*b`

While you are programming, you should do these simple optimizations when possible, it takes little effort. Sometimes, the compiler will optimize for you, sometimes it won't, so I like to give it an hand.

Regarding a simulator, Atmel Studio comes with a good one, but it only runs on windows, so if you have a Mac or Linux system that is a problem.

Ahhh...`(PRESCALER * 1000000 / F_CPU )` evaluates to zero.

Yes, any result between 0 and 1 will be truncated to zero.

So the parenthesis need to be removed.

`(ticks * PRESCALER * 1000000 / F_CPU )`

Last Edited: Mon. Aug 14, 2017 - 01:00 AM

Constant expressions are evaluated at compile time, therefore 8 * 1000000 / 16000000 becomes 0.5 which when converted to an integer becomes 0

When in doubt, test.

```#include <avr/io.h>

int main(){
return (PORTD * 8 * 1000000 / F_CPU );
}```

compiles to:

```#include <avr/io.h>

int main(){
return (PORTD * 8 * 1000000 / F_CPU );
7a:   8b b1           in      r24, 0x0b       ; 11
7c:   90 e0           ldi     r25, 0x00       ; 0
}
7e:   96 95           lsr     r25
80:   87 95           ror     r24
82:   08 95           ret```

Therefore, a division by 2 is being executed.

With parenthesis

```#include <avr/io.h>

int main(){
return (PORTD * (8 * 1000000 / F_CPU) );
}```

compiles to:

```#include <avr/io.h>

int main(){
return (PORTD * (8 * 1000000 / F_CPU) );
7a:   8b b1           in      r24, 0x0b       ; 11
}
7c:   90 e0           ldi     r25, 0x00       ; 0
7e:   80 e0           ldi     r24, 0x00       ; 0
80:   08 95           ret```

The compiler must keep the order of operations according to precedence. Multiply and division have the same precedence, so the constant expression can't be evaluated first just because. That's why the result without parenthesis isn't zero, the multiplies are executed first, so a fractional result is never formed.