Shop OBEX P1 Docs P2 Docs Learn Events
_clkmode and _clkfreq — Parallax Forums

_clkmode and _clkfreq

Does anyone remember how one goes about setting the clock mode in PropGCC? I know you can set it at runtime by using a macro in propeller.h but I want to set the initial clock settings in the binary file header. I know there is a way to do it but I haven't been able to find the documentation. Does anyone remember? Should this work?
#include <propeller.h>
  
#define RCFAST      0x00
#define RCSLOW      0x01
#define XINPUT      0x22
#define XTAL1       0x2a
#define XTAL2       0x32
#define XTAL3       0x3a
#define PLL1X       0x41
#define PLL2X       0x42
#define PLL4X       0x43
#define PLL8X       0x44
#define PLL16X      0x45

unsigned char _clkmode = XTAL1+PLL8X;

Comments

  • Also, I'm wondering if we should add the defines I show above to propeller.h?
  • In fact, the above code does not work. There is also a symbol _clkmodeval but I'm not sure the correct way to set it.
  • I think I have it figured out. I need to use this code:
    __asm__ ( "    .global __clkmodeval\n" );
    __asm__ ( "    __clkmodeval = 0x6e\n" );
    
    Seems like it would be good to wrap this in a macro in propeller.h! :smile:
  • David BetzDavid Betz Posts: 14,511
    edited 2018-11-19 03:33
    I tried this macro:
    #define INITIAL_CLKMODE(x)                      \
    __asm__ ( "    .global __clkmodeval\n" );       \
    __asm__ ( "    __clkmodeval = " #x "\n" );
    
    That works with this code:
    INITIAL_CLKMODE(0x6e)
    
    but not with this code:
    #define RCFAST      0x00
    #define RCSLOW      0x01
    #define XINPUT      0x22
    #define XTAL1       0x2a
    #define XTAL2       0x32
    #define XTAL3       0x3a
    #define PLL1X       0x41
    #define PLL2X       0x42
    #define PLL4X       0x43
    #define PLL8X       0x44
    #define PLL16X      0x45
    
    INITIAL_CLKMODE(XTAL1+PLL8X)
    
    I guess C macro substitution is not done inside of __asm__ blocks?
  • Cluso99Cluso99 Posts: 18,066
    edited 2018-11-19 09:56
    The Propeller Manual, pages 28-29 & 65.
    The table is a bit misleading though.
    Scroll down to the bottom of this code ;)
    RCFAST          0_0_0_00_000
    RCSLOW          0_0_0_00_001
    XINPUT (no PLL) 0_0_1_00_010
    
    XINPUT PLL1X    0_1_1_00_011
    XINPUT PLL2X    0_1_1_00_100
    XINPUT PLL4X    0_1_1_00_101
    XINPUT PLL8X    0_1_1_00_110
    XINPUT PLL16X   0_1_1_00_111
    
    XTAL1  PLL1X    0_1_1_01_011 
    XTAL1  PLL2X    0_1_1_01_100 
    XTAL1  PLL4X    0_1_1_01_101 
    XTAL1  PLL8X    0_1_1_01_110 
    XTAL1  PLL16X   0_1_1_01_111 
    
    XTAL2  PLL1X    0_1_1_10_011
    XTAL2  PLL2X    0_1_1_10_100
    XTAL2  PLL4X    0_1_1_10_101
    XTAL2  PLL8X    0_1_1_10_110
    XTAL2  PLL16X   0_1_1_10_111
    
    XTAL3  PLL1X    0_1_1_11_011                            
    XTAL3  PLL2X    0_1_1_11_100
    XTAL3  PLL4X    0_1_1_11_101
    XTAL3  PLL8X    0_1_1_11_110
    XTAL3  PLL16X   0_1_1_11_111
    
                    ^---------------0 = always 0 (1 resets the chip)
                      ^-------------0 = pll disabled
                                    1 = pll enabled
                        ^-----------0 = rcfast/rcslow (internal)
                                    1 = oscillator/xtal (external) 
                          ^^-------00 = rcfast/rcslow/xinput
                                   01 = xtal1  2Kohm  36pF  4-16MHz xtal
                                   10 = xtal2  1Kohm  26pF  8-32MHz xtal
                                   11 = xtal3  500ohm 16pF 20-60MHz xtal
                             ^^^--000 = rcfast
                                  001 = rcslow
                                  010 = xinput (no pll) (oscillator input)
                                  011 = pllx1
                                  100 = pllx2
                                  101 = pllx4
                                  110 = pllx8
                                  111 = pllx16
    
  • Thanks but I'm looking for a way to set the value at the start of a C program using symbolic constants not he values. I understand the bit encoding I just don't know how to get PropGCC to stuff the right value into the binary file header.
  • David Betz wrote: »
    I guess C macro substitution is not done inside of __asm__ blocks?
    Its not done inside strings, and asm uses strings... I guess its assumed you'd use native assembler
    macros inside assembler...


  • Mark_T wrote: »
    David Betz wrote: »
    I guess C macro substitution is not done inside of __asm__ blocks?
    Its not done inside strings, and asm uses strings... I guess its assumed you'd use native assembler
    macros inside assembler...

    Yes, that is true. I thought maybe there was a way to use the # operator to force evaluation of the expression but I guess not. This can probably be done by making all of the constants like XTAL1 and PLL16X actual symbols instead of just macros. I'll have to look at that.

  • Typing on my phone, so I can't pull up an example very readily, but you should be able to pass in x as a read only variable rather than doing strong concatenation. I haven't done that with numeric literals before, but I bet it works the same as how I did a bunch of the functions in PropWare/gpio/port.h
  • I'll try the scheme used in port.h but I'm not sure what to specify as the parameter type. I don't want the value loaded into a register. I just want it used to set the value of _clkmodeval.
  • David Betz wrote: »
    I tried this macro:
    #define INITIAL_CLKMODE(x)                      \
    __asm__ ( "    .global __clkmodeval\n" );       \
    __asm__ ( "    __clkmodeval = " #x "\n" );
    
    ...
    I guess C macro substitution is not done inside of __asm__ blocks?

    Getting strings out of the C macro preprocessor is always a challenge. The C preprocessor is very dumb, and it just literally does string substitution, whereas most of us (even seasoned programmers) think in terms of semantic replacement.

    Here's a sample program that shows (I think) how to do it. Try1 is the naive approach, which only does one level of expansion. Try2 does a deeper substitution:
    #include <stdio.h>
    
    #define stringize_(x) #x
    #define string_(x) stringize_(x)
    
    #define RCSLOW 0x01
    #define CLOCKMODE1(x) "__clkmodeval = " #x ";"
    #define CLOCKMODE2(x) "__clkmodeval = " string_(x) ";"
    
    int main()
    {
        printf("try 1: %s\n", CLOCKMODE1(RCSLOW));
        printf("try 2: %s\n", CLOCKMODE2(RCSLOW));
        return 0;
    }
    

    The output is:
    try 1: __clkmodeval = RCSLOW;
    try 2: __clkmodeval = 0x01;
    

    Note that the macro replacement, if done, is recursive, e.g. if you replace the #define RCSLOW with:
    #define RCSLOW_ORIG 0x01
    #define RCSLOW RCSLOW_ORIG
    
    the output is still the same. The trick is just getting past that first level of indirection.
  • Incidentally, if you're looking at getting the clock settings from a binary, I think the easiest approach is to extract them from locations 0 (clkfreq) and 4 (clkmode). That should work for every language, at least for P1.
  • Thanks, Eric. Will this work if I pass "XTAL1+PLL16X" as the value for "x"? In other words, a constant integer expression?
  • David Betz wrote: »
    Thanks, Eric. Will this work if I pass "XTAL1+PLL16X" as the value for "x"? In other words, a constant integer expression?

    You'll get a string something like "0x08+0x0400" substituted (remember, the preprocessor doesn't understand any syntax, it just blindly replaces strings). I think the assembler will probably be OK with that, it should be able to evaluate the expression.
  • ersmith wrote: »
    David Betz wrote: »
    Thanks, Eric. Will this work if I pass "XTAL1+PLL16X" as the value for "x"? In other words, a constant integer expression?

    You'll get a string something like "0x08+0x0400" substituted (remember, the preprocessor doesn't understand any syntax, it just blindly replaces strings). I think the assembler will probably be OK with that, it should be able to evaluate the expression.
    That sounds fine. I'll try that later when I get home. Thanks!

  • ersmith wrote: »
    Incidentally, if you're looking at getting the clock settings from a binary, I think the easiest approach is to extract them from locations 0 (clkfreq) and 4 (clkmode). That should work for every language, at least for P1.
    That's what I'm doing. My problem was not getting them out of the binary file. It was getting them in! :smile:

  • David,

    What happens when there are multiple clock settings, one of them has a typo, or there is nothing?
  • Genetix wrote: »
    David,

    What happens when there are multiple clock settings, one of them has a typo, or there is nothing?
    I assume you get a duplicate symbol error if you have more than one setting. If there is no setting then it defaults to 80mhz and XTAL1+PLL16X.

  • David BetzDavid Betz Posts: 14,511
    edited 2018-11-19 22:09
    I tried this and it seems to work. Should this be added to propeller.h so other people don't have to go through this hassle if they want non-standard clock settings?
    #define RCFAST      0x00
    #define RCSLOW      0x01
    #define XINPUT      0x22
    #define XTAL1       0x2a
    #define XTAL2       0x32
    #define XTAL3       0x3a
    #define PLL1X       0x41
    #define PLL2X       0x42
    #define PLL4X       0x43
    #define PLL8X       0x44
    #define PLL16X      0x45
    
    #define stringize_(x) #x
    #define string_(x) stringize_(x)
    
    #define INITIAL_CLKMODE(x)			        \
    __asm__ ( ".global __clkmodeval\n\t"			\
    	  "__clkmodeval = " string_(x) ";");
    
    INITIAL_CLKMODE(XTAL1+PLL8X)
    
  • David Betz wrote: »
    I tried this and it seems to work. Should this be added to propeller.h so other people don't have to go through this hassle if they want non-standard clock settings?
    #define RCFAST      0x00
    #define RCSLOW      0x01
    #define XINPUT      0x22
    #define XTAL1       0x2a
    #define XTAL2       0x32
    #define XTAL3       0x3a
    #define PLL1X       0x41
    #define PLL2X       0x42
    #define PLL4X       0x43
    #define PLL8X       0x44
    #define PLL16X      0x45
    
    #define stringize_(x) #x
    #define string_(x) stringize_(x)
    
    #define INITIAL_CLKMODE(x)			        \
    __asm__ ( ".global __clkmodeval\n\t"			\
    	  "__clkmodeval = " string_(x) ";");
    
    INITIAL_CLKMODE(XTAL1+PLL8X)
    

    I think so. The only concern I have is polluting the preprocessor macro space. I can't think of any reason off the top of my head why someone would complain about `stringize_` and `string_`.... but maybe something a little more cryptic to be sure? Maybe `__propgcc_stringize_` and `__propgcc_string_` to be sure?
  • I figured Eric appended the underscore as an indication that it is a system symbol. Is that the convention?
  • Actually system symbols should start with an underscore. In the example I gave I appended the underscore so I could compile that code with any compiler without fear of conflict (I don't know if Linux/Windows might already have a __stringize macro, and symbols starting with two underscores are reserved for the compiler and/or OS). But for a compiler/library that "we" (the Prop community) control, the symbols should start with an underscore rather than end with one.
  • That makes sense. I assume this should apply to the clock setting constant macros as well. However, I'm not quite sure how to add these definitions to propeller.h anyway since there are multiple repositories with the sources now and Parallax is unlikely to update the version of PropGCC that goes with SimpleIDE anyway. The problem is that any update would have to be verified with all of the Simple Libraries and no one has time to do that.
  • David,
    I've used changes in clock settings in SimpleIDE to reduce battery power and for long wait times. For example with a set up that measures temperature, bar pressure, and humidity every 10 minutes and stores them to an SD card for later evaluation.

    I have cog 0 enter a wait state after saving the data, setting pin 0 to 0, which causes two other cogs to sleep (waitpeq), but I can't sleep the SimpleIDE SD and ADC cogs, so I rely on lowering the clock to reduce power. After 10 minutes, cog 0 wakes, sets the clock back to 80 Meg, and sets P0 = 1, waking the waitpeq cogs.

    Would your method allow this?

    Tom
  • twm47099 wrote: »
    David,
    I've used changes in clock settings in SimpleIDE to reduce battery power and for long wait times. For example with a set up that measures temperature, bar pressure, and humidity every 10 minutes and stores them to an SD card for later evaluation.

    I have cog 0 enter a wait state after saving the data, setting pin 0 to 0, which causes two other cogs to sleep (waitpeq), but I can't sleep the SimpleIDE SD and ADC cogs, so I rely on lowering the clock to reduce power. After 10 minutes, cog 0 wakes, sets the clock back to 80 Meg, and sets P0 = 1, waking the waitpeq cogs.

    Would your method allow this?

    Tom

    What David was showing was a way to set the initial clock mode and frequency. To change this at run time, use the clkset(mode, frequency) macro from propeller.h.
Sign In or Register to comment.