Shop OBEX P1 Docs P2 Docs Learn Events
Beauty of _clkfreq — Parallax Forums

Beauty of _clkfreq

ErlendErlend Posts: 612
edited 2014-12-21 10:50 in Propeller 1
I am wondering - the example I have been following so far, to set the clocking frequency is this:

CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

-but it is so convenient to define time constants based on _clkfreq, so I like this format better:

CON
_clkmode = xtal1 + pll16x
_clkfreq = 80_000_000

mSec = _clkfreq / 1000
uSec = mSec / 1000

CintBitDurVM2 = _clkfreq / 9600

Is there a nuance I am not aware of? - if not, why is this format not used more frequntly?

Erlend

Comments

  • msrobotsmsrobots Posts: 3,709
    edited 2014-11-25 00:50
    Isn't this the same?

    even if you define:

    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000

    you can still do:

    mSec = _clkfreq / 1000
    uSec = mSec / 1000

    CintBitDurVM2 = _clkfreq / 9600

    the compiler will do that for you. Where is the problem?

    Enjoy!

    Mike
  • ErlendErlend Posts: 612
    edited 2014-11-25 02:27
    msrobots wrote: »
    Isn't this the same?

    even if you define:

    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000

    you can still do:

    mSec = _clkfreq / 1000
    uSec = mSec / 1000

    CintBitDurVM2 = _clkfreq / 9600

    the compiler will do that for you. Where is the problem?

    Enjoy!

    Mike

    So I thought, but when I tried it the compiler complained that _clkfreq was an undefined sybol.

    Erlend
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-11-25 09:58
    I'd asked Jeff Martin about that a while back,
    I've noticed that if I define _xinfreq and _clkmode, then I can't
    reference _clkfreq in my program, for example, as

    rate := CONSTANT(_clkfreq/2000)

    Why is that, given the fixed relationship between those three
    constants at compile time?

    Instead I do
    rate := clkfreq/2000 ' referring to the variable, not the constant

    Even as _xinfreq is more convenient at the top, _clkfreq would be
    more useful in the code.

    his reply
    Hi Tracy,

    I've noticed that too but don't have an explanation other than it's a
    special kind of constant. I'll ask Chip about this.

    Jeff Martin
    Sr. Software Engineer

    I think that is where it ended, still a mystery. It really isn't a big deal. If you need the startup clock frequency, store it in ram or eeprom during initialization.
  • ErlendErlend Posts: 612
    edited 2014-11-25 10:52
    Unsolved mystery then. While waitng for someone to solve this mystery I will change my Spin template to:
    CON
    _clkmode = xtal1 + pll16x
    _clkfreq = 80_000_000
    
    mSec = _clkfreq / 1000
    uSec = mSec / 1000
    

    -it really is handy.

    Erlend
  • ErlendErlend Posts: 612
    edited 2014-11-25 11:07
    By the way - I now remember (I believe) it was JonnyMac who posted the mSec = _clkfreq / 1000 idea in someone's thread, but I do not remember if this 'mystery' was mentioned.

    Erlend
  • edited 2014-11-25 17:13
    Constants preceeded by an underscore are 'one time settable'. The symbol is defined but the value is determined in the top object file. Have a look at _CLKFREQ in the manual.

    Sandy
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-11-25 18:49
    That wasn't my original idea -- I'm sorry I cannot remember whom I liberated it from. This is my timing header:
    con { timing }
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000                                          ' use 5MHz crystal
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq               ' system freq as a constant
      MS_001   = CLK_FREQ / 1_000                                   ' ticks in 1ms
      US_001   = CLK_FREQ / 1_000_000                               ' ticks in 1us
    


    I do projects with different crystals (using 6.5 in one now), so I like this form.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-11-25 19:49
    Jon,

    I like your way of doing things, too. You don't need to subtract xtal1 from _clkmode, though, since that value gets shifted out.

    -Phil
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-11-25 20:46
    I "liberated" that blurb without checking the details. Thanks for the heads up, Phil.

    Jon,

    I like your way of doing things, too. You don't need to subtract xtal1 from _clkmode, though, since that value gets shifted out.

    -Phil
  • pik33pik33 Posts: 2,366
    edited 2014-11-25 23:07
    _clkfreq is only for setting the frequency. If you want to get the clock frequency use clkfreq instead. Without this _


    CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
    
    
    pub start |msec
    
    msec:=clkfreq/1000
    
    
  • msrobotsmsrobots Posts: 3,709
    edited 2014-11-26 00:46
    ouch.

    Yes. I looked up my code where I did this and pik33 is absolutely right!

    So the _clkfreq is not available but clkfreq is. Funny.

    Enjoy!

    Mike
  • ErlendErlend Posts: 612
    edited 2014-11-26 07:07
    A constant is a constant, and a variable is a variable. I like to think of a mSec as a constant (although I know Einstein would disagree).
    I will adopt this method:
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000                                          ' use 5MHz crystal
    
      CLK_FREQ = (_clkmode >> 6) * _xinfreq               ' system freq as a constant
      mSec     = CLK_FREQ / 1_000                                   ' ticks in 1ms
      uSec     = CLK_FREQ / 1_000_000                               ' ticks in 1us
    

    Thank you for the discussion :)

    Erlend
  • MagIO2MagIO2 Posts: 2,243
    edited 2014-11-26 07:36
    You forget about the fact that the frequency can be changed during runtime. That's a nice feature to save battery. In this case the clockfrequency is a variable. So, especially in objects added to the object exchange it is better to use the variable-solution.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-11-26 08:45
    I first come across this issue when changing the clock frequency in real time. The command to do this is,
    CLKSET(mode,frequency)
    It mysteriously does not digest _clkfreq when the project has been started using _clkmode and _xinfreq. The goal is to switch modes and then at times switch back to the original mode declared at the top. I like the approach using
    __clkfreq = (_clkmode >> 6) * _xinfreq ' double underscore for __clkfreq

    So, the mode declared at the top can still be changed for the project as a whole. However, time constants based on that such as MSEC and USEC would then apply only when that particular frequency is active.

    I still prefer to read the actual clkfreq as the program starts up and store that in a variable or even in user eeprom. If an object that becomes subsumed under another top object, that parent object's declarations prevail and the constants in the child version are ignored. Better to track the real time clkfreq variable.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-11-26 12:01
    We went through this with the DEFCON badge code with is designed to allow speed switching. In that case, I created a global variables called ms001 and us001 that could be used with waitcnt.
    pub set_speed(mhz)
    
    '' Sets badge clock speed
    '' -- sets timing variables ms001 and us001
    '' -- note: objects may require restart after speed change
    
      case mhz
         0: clkset(RC_SLOW,     20_000)                             ' super low power -- sleep mode only!
         5: clkset(XT1_PL1,  5_000_000)
        10: clkset(XT1_PL2, 10_000_000)
        20: clkset(XT1_PL4, 20_000_000)
        40: clkset(XT1_PL8, 40_000_000) 
        80: clkset(XT1_P16, 80_000_000)
    
      waitcnt(cnt + (clkfreq / 100))                                ' wait ~10ms
    
      ms001 := clkfreq / 1_000                                      ' set ticks per millisecond for waitcnt
      us001 := clkfreq / 1_000_000                                  ' set ticks per microsecond for waitcnt
    
  • David CarrierDavid Carrier Posts: 294
    edited 2014-11-26 13:15
    Using _xinfreq is most useful when you plan to switch frequencies during run time, using _clkfreq is more useful when hard setting a frequency at compile time. Historically, example code from Parallax has used _xinfreq in all cases, but over the last few years we've been working _clkfreq into code that is expected to run at a set frequency.

    You can see this in the Eddie firmware which defines:
    _CLKFREQ = 80_000_000
    
    …and uses _clkfreq in constants, for example using this line to wait half a second:
    waitcnt(constant(_clkfreq / 2) + cnt)
    

    — David Carrier
    Parallax Inc.
  • average joeaverage joe Posts: 795
    edited 2014-11-26 23:30
    I think I'm bumping into this problem now but I don't quite understand it. My application uses a TXCO that is disabled by default. The top object contains no clock settings (so the program starts off in RCFast. The TXCO is enabled and then clock source is switched. The clock selection code is contained in a child object and looks something like this:
    PUB SetClock(mode)
      if mode == "e" or mode == "E"
        result := ReadExpander(cons#_olat)
        result |=  cons#_v_sd
        WriteExpander(cons#_olat, @result)              ' turn on osc
        CLKSET(%0_1_1_00_110, 80_000_000)        ' XIN + PLLX8 @80mhz
          
      elseif mode == "f" or mode == "F"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn off osc   
        CLKSET (%0_0_0_00_000, 12_000_000)
        
      elseif mode == "s" or mode == "S"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn off osc   
        CLKSET (%0_0_0_00_001, 20_000)
    
      else
        return 
      waitcnt(cnt + clkfreq)
    

    The child object that contains the above code contains another child object that has the actual wait routine:
    PUB pause1ms(period)  | clkcycles
      clkcycles := ((clkfreq / 1_000 * period) - 4296) #> 381                        ' Calculate 1 ms time unit
      waitcnt(clkcycles + cnt)                                                      ' and wait
    
    This is called from just about every object in the project. Things seemed fine for the most part but occasionally it's almost as if there is no wait at all (I have a 5 second wait in the top object that doesn't seem like it waits at all and previously had an issue where the wait is less than expected. Am I missing something? If clkfreq is a variable (should be global?) then why would I be having problems. And why wouldn't this be popping up all over the place?

    (edit)
    I was able to fix my issue by changing this in the child object containing the wait routine:
    DAT
    ms long
    PUB updateTicks
        ms := clkfreq / 1_000
        
    PUB pause1ms(period)  | clkcycles
      clkcycles := ((ms * period) - 4296) #> 381                        ' Calculate 1 ms time unit
      waitcnt(clkcycles + cnt)                                                      ' and wait
    

    And then changing the parent object code to:
    PUB SetClock(mode)
      if mode == "e" or mode == "E"
        result := ReadExpander(cons#_olat)
        result |=  cons#_v_sd
        WriteExpander(cons#_olat, @result)              ' turn on osc
        CLKSET(%0_1_1_00_110, 80_000_000)        ' XIN + PLLX8 @80mhz
      elseif mode == "f" or mode == "F"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn on osc   
        CLKSET (%0_0_0_00_000, 12_000_000)
      elseif mode == "s" or mode == "S"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn on osc   
        CLKSET (%0_0_0_00_001, 20_000)   
      else
        return 
      waitcnt(cnt + clkfreq)
      cons.updateTicks
    

    I still don't understand why this is an issue. Is it that the clkfreq variable propagates from parent to child but not from child to parent?
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-11-28 10:04
    Average Joe,
    Hard to say without more clues. clkfreq is a system variable accessible anywhere, so the parent-child propagation is unlikely an issue. More likely a timing issue. When operating at 20kHz, it can take longer than expected to execute spin code. And when switching down in frequency, data left unsent or received in serial buffers can cause insidious problems. Do your ReadExpander and WriteExpander methods by any chance rely on asynchronous serial?
  • average joeaverage joe Posts: 795
    edited 2014-11-28 14:25
    Tracy,

    Thanks for your response. The expander methods rely on I2C (spin) so this isn't the issue. I've been having difficulty determining the issue since obvious bug really doesn't make sense to me. The clock has been running for quite some time when the program reaches the offending code. I wish I could post the entire code but am unable to at this time. Here's the offending portion of code:
      LCDString(String("DHCP cog      =")) '6   
      LCDdec(byte[n][3]-1)
      cons.pause1ms(5000)      '
      Clearscreen
    

    The issue was that the 5 second pause never paused. (Or if it did, it was less than a second) I should have noted the other bugs as I went. If memory serves correct, the waitcnt right after switching clock frequency was formatted using the pause1ms and didn't pause either. I have not changed the formatting back yet since I have other issues bumping around. The one thing I can say is I'm pretty sure it's NOT stack issues. Other than that, I'm not sure what to do in order to track these issues further.

    (edit)
    One more piece of info, at this point the program never switches back to an internal clock source. Still just trying to get everything working. Hopefully I won't have too many issues when switching to internal clock since this is just done for power saving when the system will be idle for extended periods of time.
  • edited 2014-12-01 23:14
    Try replacing your wait statement with waitcnt( clkfreq * 5 + cnt ) and see if that works. I'm betting it will.

    clkfreq returns the current system clock frequency while _clkfreq returns to system clock frequency at startup. Page 64 in the Propeller Manual v1.1.

    Sandy
  • average joeaverage joe Posts: 795
    edited 2014-12-02 01:43
    Try replacing your wait statement with waitcnt( clkfreq * 5 + cnt ) and see if that works. I'm betting it will.

    clkfreq returns the current system clock frequency while _clkfreq returns to system clock frequency at startup. Page 64 in the Propeller Manual v1.1.

    Sandy

    That was the way I originally fixed that problem (as well as a few other instances of this bug). The update ticks method works and now the bug has completely disappeared. Now I'm going back and replacing the instances of waitcnt with pause1ms. This is my preferred method as I can use named constants (in MS) and it's quite a bit easier to read. I just thought it was a very strange problem to have and still don't understand what was causing it. I should have run it through a few different compilers to see if it was PropTool specific.
  • average joeaverage joe Posts: 795
    edited 2014-12-20 22:51
    So. The bug strikes again.... I don't get it!
    PUB rxtime(port,ms) : rxbyte | t
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
      t := cnt
      repeat until (rxbyte := rxcheck(port)) => 0 or (cnt - t) / (clkfreq / 1000) > ms
    

    For some reason, this is not evaluated properly. This is the same program as before, that starts on RCfast, then enables Xin + PLLx8 @80Mhz.
    PUB SetClock(mode)
      if mode == "e" or mode == "E"
        result := ReadExpander(cons#_olat)
        result |=  cons#_v_sd
        WriteExpander(cons#_olat, @result)              ' turn on osc
        CLKSET(%0_1_1_00_110, 80_000_000)        ' XIN + PLLX8 @80mhz
      elseif mode == "f" or mode == "F"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn on osc   
        CLKSET (%0_0_0_00_000, 12_000_000)
      elseif mode == "s" or mode == "S"
        result := ReadExpander(cons#_olat)
        result &=  (!cons#_v_sd)
        WriteExpander(cons#_olat, @result)              ' turn on osc   
        CLKSET (%0_0_0_00_001, 20_000)   
      else
        return 
      waitcnt(cnt + clkfreq)
      cons.updateTicks
    
    DAT
    ms long
    
    PUB getMS
        return ms
        
    PUB updateTicks
        ms := clkfreq / 1_000
        
    PUB pause1ms(period)  | clkcycles
      clkcycles := ((ms * period) - 4296) #> 381                        ' Calculate 1 ms time unit
      waitcnt(clkcycles + cnt)                                       
    
    But I bet
    repeat until (rxbyte := rxcheck(port)) => 0 or (cnt - t) / (cons.getMS) > ms will work fine!

    edit
    ...
    Nope, that waits forever as well... hmmm?
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-12-21 09:30
    Are you starting the serial object before or after the speed change?
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-12-21 09:40
    Here's another way to write RxTime(ms).
    [font=courier]PUB rxtime(port,ms) : rxbyte | t, ticks
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
      ticksPerMs := clkfreq/1000   ' better if this is pre-evaluated in Start method
      t := cnt
      repeat until (rxbyte := rxcheck(port)) => 0
        if ((cnt - t) > ticksPerMs * ms)       ' or until time elapsed
           quit[/font]
    

    The advantage is that it can respond faster in case there is already a char waiting in the receive buffer when the RxTime method first executes. It doesn't have to evaluate the time condition unless the buffer is empty, so it can keep up with a faster stream and avoid dropping characters. It is better still if ticksPerMs can be pre-evaluated in a Start method and with each clockset change of clock frequency. The variable ticksPerMs will not be a global variable like clkfreq though.

    You show accessing your timeout value via a method in another object, cons.updateTicks. Does that value propagate to the object (fds4port) that contains the RxTime method? That should be done along with restarting the serial port driver.
  • average joeaverage joe Posts: 795
    edited 2014-12-21 10:50
    Thanks guys!

    @JM. I am starting serial driver AFTER the clock change.

    @TA. I was just thinking about something similar. But I'll get to that in a minute...
                BaseStation.spin
                  │
                  ├──NetHost.spin
                  │    │
                  │    ├──MemWrapper.spin
                  │    │    │
                  │    │    ├──SPI_Asm.spin
                  │    │    │
                  │    │    ├──Basic_I2C_Driver.spin
                  │    │    │
                  │    │    └──Settings.spin
                  │    │
                  │    ├──PacketDriverHost.spin
                  │    │    │
                  │    │    ├──XBee_ObjectC.spin
                  │    │    │    │
                  │    │    │    └──Settings.spin
                  │    │    │
                  │    │    ├──Numbers.spin
                  │    │    │
                  │    │    └──Settings.spin
                  │    │
                  │    ├──Numbers.spin
                  │    │
                  │    ├──Settings.spin
                  │    │
                  │    └──Stack Length.spin
                  │         │
                  │         └──Parallax Serial Terminal.spin
                  │
                  ├──Numbers.spin
                  │
                  ├──FullDuplexSerial4portPlus_0v3.spin
                  │
                  ├──MemWrapper.spin
                  │    │
                  │    ├──SPI_Asm.spin
                  │    │
                  │    ├──Basic_I2C_Driver.spin
                  │    │
                  │    └──Settings.spin
                  │
                  └──Settings.spin
    

    XBee_ObjectC.spin contains RXtime. Settings.spin is cons.xxx The PUB SetClock(mode) method is contained in MemWrapper.spin.

    This is Settings.spin
    DAT
    ms long
    
    PUB getMS
        return ms
        
    PUB updateTicks
        ms := clkfreq / 1_000
        
    PUB pause1ms(period)  | clkcycles
      clkcycles := ((ms * period) - 4296) #> 381                        ' Calculate 1 ms time unit
      waitcnt(clkcycles + cnt)
    

    So the answer to " Does that value propagate to the object (fds4port) that contains the RxTime method? " should be yes? (Since ms is declared as dat?)

    I'm wondering if I should move the PUB SetClock(mode) method into Settings.Spin

    The way I was thinking about evaluating the timeout was:
    PUB rxtime(port,ms) : rxbyte | t, ticks
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
     
      ticks := (cons.getMs) * ms    ' better if this is pre-evaluated in Start method 
    '  ticks := (clkfreq/1000) * ms    ' better if this is pre-evaluated in Start method
      t := cnt
      repeat until (rxbyte := rxcheck(port)) => 0
        if ((cnt - t) > ticks)       ' or until time elapsed
          return    ' still don't understand the semantics of quit / return  ??
    

    I did find an error.. I was running my clockset at the wrong entry point, so ABORT ?could? mess with clock setting? IE.
    PUB ErrorHandler | e            '' the return point for an abort. displays a message on the LCD
      mem.InitI2C   ' do init HERE!!
      repeat
        if e := \Start              ' Run start, trap abort, 
          Clearscreen
          LCDString(e)              ' display the error on LCD
          repeat until GetKeypress == false
          repeat while GetKeypress == false
          Clearscreen
    
     Start  |  n              '' Starting point for main program 
     ' mem.InitI2C   ' not HERE!!
      result :=  false              ' initialize result to false
      num.Init                      ' initialize numbers object
      ser.init
      ser.AddPort(cons#_lcdPort,cons#_lcdRX,cons#_lcdTX,-1,-1,ser#DEFAULTTHRESHOLD,ser#NOMODE,cons#_lcdBaud) ' add LCD port 
      ser.Start
      ClearScreen               ' clear LCD screen
      LCDString(String("Booting System",13,10)) '             
      LCDString(String(13,10,"Serial cog    =")) '1   
      LCDdec(ser.getCOGid)
      n := host.Start(tlong, @status, @Sflag, cons#_dch, cons#_tch)                      ' this method aborts frequently!!!                 
      LCDString(String(13,10,"PktSerial cog =")) '2   
      LCDdec(byte[n][0] -1)                        
    
    
    Don't know what this would have to do with it...
    But, for some reason.
    PUB rxtime(port,ms) : rxbyte | t, ticks
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
     
      ticks := (clkfreq / 1000 ) * ms    ' better if this is pre-evaluated in Start method 
    '  ticks := (clkfreq/1000) * ms    ' better if this is pre-evaluated in Start method
      t := cnt
      repeat until (rxbyte := rxcheck(port)) => 0
        if ((cnt - t) > ticks)       ' or until time elapsed, always evaluates true?
          quit    
    
Sign In or Register to comment.