Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Counter question — Parallax Forums

Propeller Counter question

K2K2 Posts: 691
edited 2010-10-29 23:30 in Propeller 1
When using one of the Prop's counters in Duty Cycle mode, is there any way for PHSx to be automatically preloaded with a new value when APIN goes high? Lacking that, is there a way to automatically preload FRQx on a counter event?

What I'm trying to avoid is the indeterminacy involved whenever program control is used to sense a condition and modify a register.

Maybe the key to avoiding indeterminacy is to update PHSx under program control just after an overflow or underflow event? But can a read/modify/write be done to PHSx without slipping or skipping a few clocks? AN001 seems awfully scarce on practical details. And none of the coding examples shown seem anywhere close to a real-world application.

On the other hand, there is a comment line in one piece of code that says:
'back up phsa so that it trips "value" cycles from now.

That sort of wording gives me hope that there's more to the counters than I've discovered so far.

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-18 10:10
    When using one of the Prop's counters in Duty Cycle mode, is there any way for PHSx to be automatically preloaded with a new value when APIN goes high?

    No. APIN in DUTY mode is an output, anyway.

    Lacking that, is there a way to automatically preload FRQx on a counter event?

    No.

    But can a read/modify/write be done to PHSx without slipping or skipping a few clocks?

    No. You can't perform a R/M/W operation on the PHSx register of a running clock.

    What is your overall objective? Perhaps there's another way to achieve it.

    -Phil
  • K2K2 Posts: 691
    edited 2010-10-18 10:40
    When using one of the Prop's counters in Duty Cycle mode, is there any way for PHSx to be automatically preloaded with a new value when APIN goes high?

    No. APIN in DUTY mode is an output, anyway.

    Naturally APIN is an output. But ever since Adam and Eve, outputs have been connected to inputs in order to trigger events.
    What is your overall objective? Perhaps there's another way to achieve it.

    I'd like to create an arbitrary squarewave with 12.5 ns timing resolution and no jitter.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-18 10:49
    What frequency range do you need?

    -Phil
  • JonnyMacJonnyMac Posts: 8,918
    edited 2010-10-18 10:50
    One of my wishes for the Propeller II is a timer mode that allows one to create a set-and-forget PWM signal with a fixed frequency, variable duty cycle. It would be nice to load on-cycles and off-cycles into registers and just let it fly (like one of the modes of the SX48 counter/timer).
  • K2K2 Posts: 691
    edited 2010-10-18 11:21
    "What frequency range do you need?"

    From 2500 Hz to 5000 Hz. I'd also hoped to be able to smoothly vary the frequency, in one Hertz increments, on the fly. It would seem that I ignorantly imbued the Prop counter with capabilities it never posessed.

    "One of my wishes for the Propeller II is a timer mode that allows one to create a set-and-forget PWM signal with a fixed frequency, variable duty cycle. It would be nice to load on-cycles and off-cycles into registers and just let it fly (like one of the modes of the SX48 counter/timer)."

    I very much agree, JonnyMac. Beau has suggested that the new I/O pads contain fabulous new features. Hopefully they include exactly what you describe.

    Meanwhile, I'll probably continue doing just as I have done up to this point in the design - use SX chips.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-18 11:40
    Here's a program that can produce jitter-free square waves up to 1 MHz with 25ns resolution for the whole period (12.5ns/half period). The argument to set_period is in clocks per whole cycle and should be even. It allows you to change the period on the fly. set_period should always be called once just before squarewave is started.
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      OUTPIN        = 6
    
    VAR
    
      long  Period
    
    PUB start
    
      set_period(300)
      cognew(@squarewave, @Period)
    
    PUB set_period(pd)
    
      Period := (pd >> 1) - 12
    
    DAT
    
    squarewave    mov       ctra,ctra0
                  mov       frqa,frqa0
                  mov       dira,dira0
                  
    sqlp          rdlong    per,par
                  waitpne   zero,dira0
    
                  or        per,sign
                  mov       phsa,per
                  waitpeq   zero,dira0
                  
                  andn      per,sign
                  mov       phsa,per
                  jmp       #sqlp
    
    ctra0         long      %00100 << 26 | OUTPIN
    frqa0         long      -1
    dira0         long      1 << OUTPIN
    zero          long      0
    sign          long      $8000_0000
    
    per           res       1
    

    -Phil
  • K2K2 Posts: 691
    edited 2010-10-18 12:09
    Stick a fork in it! Thanks, Phil, this is beautiful! I'll have to study the code more before I can ask even dumb questions about it. Meanwhile, it is MUCH more elegant than my SX approach and it doesn't rely on an obsolete chip.
  • K2K2 Posts: 691
    edited 2010-10-18 12:39
    I'm guessing that the "-12" in the calculation of Period represents twice the fixed overhead in setting up each half cycle?

    How does the divide-by-four nature of the cog clock not interfere with the accuracy of waitpeq and waitpne? I'm guessing that for any particular cog on any particular day, the same number of system clocks (0, 1, 2, or 3) expire before that cog sees the respective condition met and resumes exection.

    Anyway, it's a great lesson for a newbie on how to get accurate timing done on a Prop. Thanks again!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-18 12:53
    I'm guessing that the "-12" in the calculation of Period represents twice the fixed overhead in setting up each half cycle?

    Yes, that's to accommodate the overhead between the waits and the moves to phsa.

    How does the divide-by-four nature of the cog clock not interfere with the accuracy of waitpeq and waitpne?

    The wait instructions have a granularity of one clock for the time that they resume execution once their conditions are met.

    Anyway, it's a great lesson for a newbie on how to get accurate timing done on a Prop. Thanks again!

    You're welcome! :)

    -Phil
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2010-10-18 13:37
    JonnyMac wrote: »
    One of my wishes for the Propeller II is a timer mode that allows one to create a set-and-forget PWM signal with a fixed frequency, variable duty cycle. It would be nice to load on-cycles and off-cycles into registers and just let it fly (like one of the modes of the SX48 counter/timer).
    I use this all the time, I also hope Prop 2 has this capability, but I haven't heard anything about it. At this point, I've written objects that can do single output, 6X outputs, and 31X outputs with PWM at a specific frequency. My object requires the cog be restarted to set new values, but I've been thinking about re-writing them so the outputs can be altered on the fly with the call of a single method. Though resolution would decrease slightly.

    For single output, Phil's program may be modified to do what we need. And dual output using the counters in the way he did would also be easy enough.
  • K2K2 Posts: 691
    edited 2010-10-18 17:59
    Bobb Fwed wrote: »
    For single output, Phil's program may be modified to do what we need. And dual output using the counters in the way he did would also be easy enough.

    The first thing I realized, once I understood Phil's code, was that his approach could easily be extended to two counters to generate two non-overlapping variable-frequency variable-duty-cycle PWM outputs.

    Once I added two resistors, a film capacitor, and two 4th-order switched-capacitor filters, the project was done!
  • K2K2 Posts: 691
    edited 2010-10-20 08:41
    I really can't get over how fantastic the Prop is for frequency synthesis. Phil's little code example has really opened my eyes. I apologize for any disparaging comments I ever made about the Prop (or Phil!).

    My employer has spent a small fortune on development tools from company A (of the great State of Massachusetts).

    For pennies I'm getting the same results with an insanely simple set-up, free tools, and a Prop. I'm putting together a show-and-tell for the big boss. My hope is that it will restore sanity here, and sell a few Props in the process.
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-20 21:02
    @Phil: Nice example (now squirrelled away). But note that your effective period value varies depending on the actual pin/cog combination used. E.g. 0/1 does what you expect but 0/4+ has a slightly longer period.
    movs    ctra, #0
    movi    ctra, #%0_00100_000     ' NCO single-ended
    neg     frqa, #1
    
    mov     phsa, #511
    waitpeq mask, mask              ' low high transition
    mov     temp, phsa
    
    For textbook timing we'd expect -6 in temp. With the 0/4+ combination mentioned above I get -7 on the demo board due to internal delays. Picking an example, I measured the time between two low/high transitions from within a different cog (waitpxx/cnt). With a set period of 532 I get 532 for 0/2 but 534 for 0/4+ (that's running the PASM fragment you posted unchanged).

    That said, it's only likely to be an issue (in terms of accuracy) when you get close to 1MHz. Everyone relax!

    Suggested fix:
    CON
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      OUTPIN        = 0
    
    VAR
      long  Period
    
    PUB start
    
      set_period(300)
      cognew(@squarewave, @Period)
    
    PUB set_period(pd)
    
      Period := [COLOR="Red"](pd >> 1) - 6 | $80000000[/COLOR]
    
    DAT           org       0
    
    squarewave    mov       dira, dira0
                  mov       ctra, ctra0
                  neg       frqa, #1
                  
    sqlp          rdlong    per, par
                  waitpeq   dira0, dira0
    
                  [COLOR="Red"]mov       phsa, phsa[/COLOR]
                  [COLOR="Red"]add       phsa, per[/COLOR]
                  waitpne   dira0, dira0
                  
                  [COLOR="Red"]mov       phsa, phsa[/COLOR]
                  [COLOR="Red"]add       phsa, per[/COLOR]
                  jmp       #sqlp
    
    ctra0         long      %0_00100_000 << 23 | OUTPIN
    dira0         long      |< OUTPIN
    
    per           res       1
    
                  fit
    
  • K2K2 Posts: 691
    edited 2010-10-21 07:16
    You're insane, Marko. If there is ever awarded a Doctor of Propeller degree, it should go to you. Given 20 years in which to do it, I wouldn't have found the least issue with Phil's code. In fact I've been marveling every day at how fantastic the whole concept is, and what can be done with it. It is what was going through my mind as I drifted off to sleep last night.

    It will take some time to digest the nuances you've incorporated. But I can already tell there is real cleverness to it.

    I think it was heater who mentioned a while ago that the book is still being written on the Prop. I questioned that, privately. But I see that it is true, both with what Phil has done and with what you have added. Nice job!
  • BeanBean Posts: 8,129
    edited 2010-10-21 07:38
    Kuroneko,
    What is it that causes the delay ? Is it the output to the pin ? The input from the pin ?

    I'm just trying to understand why it makes a difference.

    Bean
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-21 09:51
    Kuroneko,

    I think I get the internal delay part, although I'm surprised it amounts to an entire clock cycle. And I can understand how adding to phsa rather than just setting phsa each time fixes the problem. What I'm not sure I grasp yet (prior to my morning coffee) is how you get by with a read-modify-write to phsa when the counter is running. The operation is apparently primed by the mov phsa,phsa above it and must have something to do with phsa's shadow register, but I don't yet get what's happening internally, since both the counter and the ALU are contending to add something to phsa in the same machine cycle.

    Can you explain? Whatever the explanation, though, it's a neat trick worth remembering! Up until now, I've been using the textbook approach of jiggering frqa to effect a phase shift. But that's limited to multiple-of-four-clock shifts.

    Thanks,
    -Phil
  • KaosKiddKaosKidd Posts: 296
    edited 2010-10-21 10:15
    Here's a program that can produce jitter-free square waves up to 1 MHz with 25ns resolution for the whole period (12.5ns/half period). The argument to set_period is in clocks per whole cycle and should be even. It allows you to change the period on the fly. set_period should always be called once just before squarewave is started.
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      OUTPIN        = 6
    
    VAR
    
      long  Period
    
    PUB start
    
      set_period(300)
      cognew(@squarewave, @Period)
    
    PUB set_period(pd)
    
      Period := (pd >> 1) - 12
    
    DAT
    
    squarewave    mov       ctra,ctra0
                  mov       frqa,frqa0
                  mov       dira,dira0
                  
    sqlp          rdlong    per,par
                  waitpne   zero,dira0
    
                  or        per,sign
                  mov       phsa,per
                  waitpeq   zero,dira0
                  
                  andn      per,sign
                  mov       phsa,per
                  jmp       #sqlp
    
    ctra0         long      %00100 << 26 | OUTPIN
    frqa0         long      -1
    dira0         long      1 << OUTPIN
    zero          long      0
    sign          long      $8000_0000
    
    per           res       1
    

    -Phil

    Phil:
    Would this be suitable to use as a clock input to other propellers? The idea being this would run on prop, and it's output would be sent to other propeller's to keep them all 100% synchronized? All of the props that have their clock's use the output from this would be synchronized with that counter.
    KK
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-21 10:20
    KK,

    I suppose it could if you used Kuroneko's modification and were satisfied with no more than 1MHz. It would be much easier, though, just to set one of the counters to a "pure" (i.e. power-of-two) frequency and use that.

    -Phil
  • KaosKiddKaosKidd Posts: 296
    edited 2010-10-21 10:25
    KK,

    I suppose it could if you used Kuroneko's modification and were satisfied with no more than 1MHz. It would be much easier, though, just to set one of the counters to a "pure" (i.e. power-of-two) frequency and use that.

    -Phil

    Thanks Phil.
    I got the notion some time ago that doing that wouldn't work. Not sure why, but I just got that idea. Anyway, if what your saying is true, then I can in theory drive a series of props to their max speed with only a few passives to provide for the interconnect. is that about right? I haven't experimented with it yet, but the notion to try something likes this is sitting RIGHT there...
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-21 17:33
    Bean wrote: »
    What is it that causes the delay ? Is it the output to the pin ? The input from the pin ?

    IMO the output delay (OR chain) plays a major part in this. I mean, there isn't anything else in the way and this particular pin is not connected to anything on a demo board. Also, the fact that it affects cog IDs 4+ suggests that distance is involved. OTOH, the pins have to go back to the cogs to be sampled which may have some influence as well.
    I think I get the internal delay part, although I'm surprised it amounts to an entire clock cycle.

    You got me there. In the [thread=118096]Clarity thread[/thread] output propagation and setup time are given with ~2ns so at 80MHz that's still 8.5ns left for not being missed. Most odd. A bit like generating 40MHz with a counter and a waitpxx not seeing it. HA!
    Can you explain? Whatever the explanation, though, it's a neat trick worth remembering!

    No magic involved this time. When data is written to phsx shadow and counter are updated. So the initial mov phsa, phsa will primarily update the shadow register and cause the counter to stutter slightly (it keeps counting but is set back 2 cycles when the result is written) but that's not important in this context. The next op is read-modify-write (add phsa, per). With phsx being special (in the destination slot) the actual op performed is:
    counter[phsx] := shadow[phsx] += per

    From the data sheet:

    Beware that doing a read-modify-write instruction on PHS, like "ADD PHSA, #1", will cause the last-written value to be used as the destination operand input, rather than the current accumulation.
    Our last written value originates from the mov (textbook: -6/POSX-5). We add the period and write it back, affecting both shadow and counter (Any instruction writing to PHS will override any accumulation for that clock cycle.). HTH
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-21 17:58
    Thanks, Kuroneko. The phsa stuff makes sense now. I had forgotten that "last written value" part.

    -Phil
  • K2K2 Posts: 691
    edited 2010-10-22 08:21
    The phsa stuff makes sense now.

    This made me laugh. It wasn't until I reread your post #17 carefully that I even understood the questions.

    Reading both the questions and answers a few times, though, has finally brought a measure of clarity.

    FWIW, because I was using cogs 0,1, I hadn't noticed the slightest timing discrepancy. In fact, commenting out the rdlong, the code does a nice job of generating square waves nearly to 3 MHz. There is also an easy way to control both frequency and duty cycle with just one counter. Toggling DIRA rather than OUTA, and using switched-capacitor filters, one can obtain a nice sine wave with wide-ranging amplitude control. Just what the doctor ordered. Literally. :)
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-29 20:12
    Something irked me about this (triggered by a completely different problem). Given the OR chain delay from the data sheet there is no way that a whole cycle can be lost during round trips. So I eliminated counters from the equation and used a couple of cogs (0/1 and 6/7) to act as sender/receiver. One cog sets a bit in outa and records the time, the other cog waits for the level change (waitpxx) and records its timestamp. Subtract and what we get is a 3 cycle (textbook) delay. Constantly regardless of pin used.

    Below is the timing for cogs 6/7 using the outa approach (pins 0/24).

    attachment.php?attachmentid=74798

    Now back to using counters. We let an NCO drive that particular pin in cog 6 and use the same receiver code in cog 7.

    attachment.php?attachmentid=74799

    The delays for pin 0 and 24 are 5 and 4 respectively (they are not 4 and 3 due to the extra delay of getting frqa set and being used for increments, R:set, S:use). This would suggest that it's not the OR chainA which causes the missing cycle as indicated earlier but something based on how the counter outputs are connected to the I/O pins.

    Using cogs 0/1 reverses the counter timing, 5 cycles for a round trip to pin 24 and 4 to pin 0. Note that the delay is defined by the sender cog and the link pin, i.e. sending from cog 0 to pin 24 and receiving in cog 7 is slow whereas using pin 0 doesn't show any delay (cog 0 >> pin 0 >> cog 7).

    Anyway, maybe a waitcnt based version would have been better in the first place? It certainly avoids all the subtle cycle adjustments.
    CON
      _clkmode = XTAL1 + PLL16X
      _xinfreq = 5_000_000
    
      OUTPIN   = 16
    
    VAR
      long  period
    
    PUB start
    
      set_period(clkfreq)
      cognew(@squarewave, @period)
    
    PUB set_period(pd)
    
      period := pd >> 1
    
    DAT             org     0                       ' waitcnt version
    
    squarewave      mov     dira, mask              '  -4
                    mov     outa, mask              '  +0 =
    
                    mov     cnt, cnt                '  +4
                    add     cnt, #9 + 12            '  +8
                    
    sqlp            rdlong  hper, par               '  -4
                    waitcnt cnt, hper
                    or      outa, mask
                    waitcnt cnt, hper
                    andn    outa, mask
                    jmp     #sqlp
    
    mask            long    |< OUTPIN
    
    hper            res     1
    
                    fit
    

    A it contributes to the delay but not significantly
    604 x 350 - 13K
    604 x 438 - 17K
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-29 20:20
    Kuroneko,

    You're a bulldog! I'm sure all of us appreciate your grit and determination to get to the bottom of this! It might be time for Chip to look at the schematic again. :)

    -Phil
  • Clock LoopClock Loop Posts: 2,069
    edited 2010-10-29 23:30
    KaosKidd wrote: »
    then I can in theory drive a series of props to their max speed with only a few passives to provide for the interconnect.


    Check out my link, i do that with my program and schematic.
    http://forums.parallax.com/showthread.php?t=124343


    It would be nice to be able to see the innards of the various prop chips when it comes to their cog and I/O pad + pathway. I have found some intense die images of the prop, but never shown with cog numbering, pin labels and pathways...
Sign In or Register to comment.