EmbDev.net

Forum: µC & Digital Electronics AVR clock prescaler (CLKPR) and gcc code optimization


von Peter-Jan P. (pjp)


Rate this post
useful
not useful
Since I spend the last two days trying to get the clock prescaler to 
work I wanted to share my results. Everything I write here has been 
tested with an attiny2313. Anyway this problem exists with other AVR 
microcontrollers as well.

To set for clock prescaling factor it is necessary to access the CLKPR 
twice within 4 clock cycles. First you set the highest bit (CLKPCE) to 1 
and all others to 0. Then you write the actual value for CLKPR. avr-gcc 
code could look like this:
1
  CLKPR = (1<<CLKPCE);
2
  CLKPR = 1;

As long as code optimizatin is used (gcc option -Os) everything works as 
expected. If no optimization is used (-O0), the above code has no 
effect. The clock prescaling factor isn't changed. The reason for this 
can be found in the respective assembler code.
With optimization you get:
1
  ldi r24,lo8(-128)   ;  tmp44,
2
  out 70-32,r24   ; ,, tmp44
3
  ldi r24,lo8(1)   ;  tmp46,
4
  out 70-32,r24   ; ,, tmp46
without optimization this becomes
1
  ldi r24,lo8(70)   ;  D.2172,
2
  ldi r25,hi8(70)   ;  D.2172,
3
  ldi r18,lo8(-128)   ;  tmp54,
4
  movw r30,r24   ; , D.2172
5
  st Z,r18   ; , tmp54
6
  ldi r24,lo8(70)   ;  D.2173,
7
  ldi r25,hi8(70)   ;  D.2173,
8
  ldi r18,lo8(1)   ;  tmp55,
9
  movw r30,r24   ; , D.2173
10
  st Z,r18   ; , tmp55

Obviously the register access (the lines with "st Z,r18") is not done 
within 4 clock cycles.

If you do not want to use code optimization, you can circumvent the 
problem by using an inline assembler statement like this one:
1
  asm volatile (
2
    "st Z,%1" "\n\t"
3
    "st Z,%2"
4
    : :
5
    "z" (&CLKPR),
6
    "r" ((uint8_t) (1<<CLKPCE)),
7
    "r" ((uint8_t) 1)  // new CLKPR value
8
  );

von MWS (Guest)


Rate this post
useful
not useful
> Since I spend the last two days trying to get the clock prescaler to
> work I wanted to share my results.

Since you've spent two days senselessly, as you either forgot to, or you 
don't know how to read the documentation, there's a certain chance 
you'll do it next time more clever.

Anyway, it doesn't make you look smarter, if you post the same proof 
that you had blinders on, in English too.

Beitrag "AVR clock prescaler (CLKPR) und gcc-Codeoptimierung"

The solution is simple: use clock_prescale_set(x) from power.h

von DFA (Guest)


Rate this post
useful
not useful
PJP,

I am new to ATMEL and found your posting here very helpful.  I do not 
bash people for not reading documentation or missing some particular 
part of the documentation.  I would not have known to go to a seemingly 
unrelated file like "power.h" to find out about setting the clock 
prescaler.

Keep up your good work here. I look forward to seeing what you discover.

DFA

von RT (Guest)


Rate this post
useful
not useful
PJP,

Thank you for this. I had also the same problem, as I was in debug mode 
and the prescaler would not change.

@MWS I am gratefull he posted it in English as wel. Also not everyone 
uses the libraries provided by Arduino.

von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
RT wrote:
> Also not everyone uses the libraries provided by Arduino.

The header file mentioned is part of avr-libc (and thus in no relation 
to "Arduino libraries"), and this particular inline function has been 
provided there for exactly the purpose the TO tried to solve with his 
own inline asm macro: being independent of the compiler optimization.

Of course, everyone is free to roll their own … and making their own 
mistakes (like, in this case, not thinking about whether interrupts need 
to be disabled - the inline function in avr/power.h takes care about 
that).

: Edited by Moderator
von c-hater (Guest)


Rate this post
useful
not useful
Jörg W. wrote:

> The header file mentioned is part of avr-libc (and thus in no relation
> to "Arduino libraries"), and this particular inline function has been
> provided there for exactly the purpose the TO tried to solve with his
> own inline asm macro: being independent of the compiler optimization.

Yes, that's the truth. But it is NOT PORTABLE.

> Of course, everyone is free to roll their own

Those guys are the guys, who're REALLY know, what they're doing. The 
advantage is: they drop any dependencies to any specific compiler. 
That's the only way to get really free. Gain own knowledge regarding the 
target. Don't become a stupid slave of one specific compiler.

> and making their own
> mistakes

Of course. Making the own mistakes is the only way to generate foundated 
knowledge.

This is a very widely accepted fact.

von Johann L. (gjlayde)


Rate this post
useful
not useful
Jörg W. wrote:
> and this particular inline function has been provided there
> for exactly the purpose the TO tried to solve with his
> own inline asm macro:

In avr/power.h I find at line 1600:
1
void timer_clock_prescale_set(timer_clock_div_t __x)
2
{
3
    uint8_t __t;
4
    __asm__ __volatile__ (
5
        "in __tmp_reg__,__SREG__" "\n\t"
6
        "cli" "\n\t"
7
        "in %[temp],%[clkpr]" "\n\t"
8
        "out %[clkpr],%[enable]" "\n\t"
9
        "andi %[temp],%[not_CLTPS]" "\n\t"
10
        "or %[temp], %[set_value]" "\n\t"
11
        "out %[clkpr],%[temp]" "\n\t"
12
        "sei" "\n\t"
13
        "out __SREG__,__tmp_reg__" "\n\t"
14
        : /* no outputs */
15
        : [temp] "r" (__t),
16
          [clkpr] "I" (_SFR_IO_ADDR(CLKPR)),
17
          [enable] "r" (_BV(CLKPCE)),
18
          [not_CLTPS] "M" (0xFF & (~ ((1 << CLTPS2) | (1 << CLTPS1) | (1 << CLTPS0)))),
19
          [set_value] "r" ((__x & 7) << 3)
20
        : "r0");
21
}

http://svn.savannah.nongnu.org/viewvc/avr-libc/trunk/avr-libc/include/avr/power.h?revision=2525&view=markup#l1600

What's the rationale behind the SEI just prior to the final OUT?

von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
Johann L. wrote:
> What's the rationale behind the SEI just prior to the final OUT?

SEI takes effect just after the next instruction only.

von Peter D. (Guest)


Rate this post
useful
not useful
Jörg W. wrote:
> SEI takes effect just after the next instruction only.

But it was overwritten by the following access to SREG. So it may be 
omitted.

von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
OK ;-)

von Programmierer (Guest)


Rate this post
useful
not useful
c-hater wrote:
> Those guys are the guys, who're REALLY know, what they're doing. The
> advantage is: they drop any dependencies to any specific compiler.

Inline assembly is compiler specific and not portable at all. For 
example, the original code contains "asm volatile (...". This is, in 
fact, invalid C syntax, and will not compile with a standard-compliant 
compiler (e.g. GCC when specifying -std=c99). The compiler will think 
that "asm" is a function or type name and will indicate a syntax error. 
By changing it to "__asm__ __volatile__", as in avr/power.h, it becomes 
correct C, as now the compiler-specific keyword "__asm__" is used, which 
the standard allows. Of course, it's still not portable, but at least 
not invalid C.

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.