Shop OBEX P1 Docs P2 Docs Learn Events
Using cog counter as a serial I/O — Parallax Forums

Using cog counter as a serial I/O

yisiguroyisiguro Posts: 52
edited 2019-11-23 04:00 in Propeller 1
Is there someone who tried using a cog counter as a shift register something like below ?
  MOV FRQx, #1 ' can be any of power of 2
  ADD PHSx, PHSx
  ...
  ADD PHSx, PHSx
  MOV somewhere, PHSx

Comments

  • Cluso99Cluso99 Posts: 18,069
    You could try the video counters to generate a serial sequence.
  • You can't do a read-modify-write on PHSA or PHSB. Per the manual:

    " For Propeller Assembly, only readable as a source register (i.e., mov dest, source); read-modify-write not possible as a destination register."

    -Phil
  • Not exactly like that, and not for async serial, but @"Timothy D. Swieter" wrote a nice driver for the W5100 ethernet chip that makes use of the counters to shift out SPI data. I also ripped out what I could to try to make a more general purpose SPI driver and put it in my fork of the spin-standard-library (it's in the testing branch as com.spi.fast.spin). Both operate at a fixed 20MHz (!) write and 10MHz read.
    Maybe you could adapt it for async somehow? I'm not experienced enough to know how, or how to alter it to change speeds, but it could perhaps be a starting point.

    Cheers,
    Jesse
  • You can't do a read-modify-write on PHSA or PHSB. Per the manual:

    " For Propeller Assembly, only readable as a source register (i.e., mov dest, source); read-modify-write not possible as a destination register."

    -Phil

    Actually, you can, but the destination read will come from the shadow register, which contains whatever was last written to that register.
  • yisiguroyisiguro Posts: 52
    edited 2019-11-25 13:50
    This idea is not so useful nor casual to receive serial stream, I know.

    To use cog counter as a serial input, it is to be assumed that the counter is updated only ONCE ( or always TWICE through receiving entire bit stream ) between previous instruction's Write cycle and Dest cycle of following "ADD PHSx,PHSx" instruction. This means sampling window is 2 sysclk wide.

    "ADD PHSx,PHSx" causes updating shadow ram by previous value of itself plus incremented value of PHS.
    That is, new value is sum of twice of ( i.e. left-shifted ) old value, and, if any, 1 ( or 2 ).

    Some code added.
      MOV CTRx, somevalue  ' ex. LOGIC A and APIN, etc.
      MOV FRQx, #1
      MOV PHSx, #0 ' start sampling here
      ADD PHSx, PHSx  ' shift in first bit
      ADD PHSx, PHSx  ' and second bit
      ADD PHSx, PHSx  ' third, and so on ...
      (abbrev)
      ADD PHSx, PHSx
      MOV FRQx, #0  ' stop sampling, if needed
      ADD PHSx, PHSx ' last bit
      MOV somewhere, PHSx  ' save value
      REV somewhere, #revvalue
    

    (edit)
    Instead of using "ADD PHSx,PHSx" instruction, using "SHL FRQx,#1" can be more flexible ?
    (but sampling window becomes wider and contiguous)

  • Wuerfel_21Wuerfel_21 Posts: 5,052
    edited 2019-11-24 14:10
    "SHL FRQx,#something" is what Kuneko's 20MHz SPI read routine from FSRW uses. (#something to fix the byte order)

    I've used that one and it works great.

    Infact, here it is, copy-pasted straight from one of my own source files, so this should work
    sd_DImask long 1<<spiDI
    sd_DOmask long 1<<spiDO
    sd_CLKmask long 1<<spiCLK
    ctr_clock long (%00110 << 26) | (spiCLK << 0) ' DUTY, 25% duty cycle
    ctr_read long (%11000 << 26) | (spiDO << 0) | (spiCLK << 9)
    ctr_write long (%00100 << 26) | (spiDI << 0) 
    serbuffer long 0
    
    fastread      mov vm_junk,#128
                  or outa,sd_DImask
                  mov ctra,ctr_read
                  movi phsb,#%11_0000000
    :readloop     ' Start my clock
                  mov frqa,#1<<7
                  mov phsa,#0
                  movi frqb,#%01_0000000
                  ' keep reading in my value, one bit at a time! (Kuneko - "Wh)
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shl frqa,#15
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shl frqa,#15
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shl frqa,#15
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  shr frqa,#1
                  mov frqb,#0 ' stop the clock
                  mov serbuffer,phsa
                  wrlong serbuffer,vm_par2
                  add vm_par2,#4
                  djnz vm_junk,#:readloop
                  mov frqa,#0
    fastread_ret  ret
    
    fastwrite     mov vm_junk,#128
                  andn outa,sd_DImask
                  mov ctra,ctr_write
    :writeloop
                  rdlong phsa,vm_par2
                  add vm_par2,#4              
                  movi phsb,#%11_0000000 ''TODO: can this be moved out of the loop?
                  ' a long in LE order is DCBA
                  rol phsa,#24            ' move A7 into position, so I can do the swizzled version
                  movi frqb,#%01_0000000  ' start the clock (remember A7 is already in place)
                  rol phsa,#1             ' A7 is going out, at the end of this instr, A6 is in place
                  rol phsa,#1             ' A5
                  rol phsa,#1             ' A4
                  rol phsa,#1             ' A3
                  rol phsa,#1             ' A2
                  rol phsa,#1             ' A1
                  rol phsa,#1             ' A0
                  rol phsa,#17            ' B7
                  rol phsa,#1             ' B6
                  rol phsa,#1             ' B5
                  rol phsa,#1             ' B4
                  rol phsa,#1             ' B3
                  rol phsa,#1             ' B2
                  rol phsa,#1             ' B1
                  rol phsa,#1             ' B0
                  rol phsa,#17            ' C7
                  rol phsa,#1             ' C6
                  rol phsa,#1             ' C5
                  rol phsa,#1             ' C4
                  rol phsa,#1             ' C3
                  rol phsa,#1             ' C2
                  rol phsa,#1             ' C1
                  rol phsa,#1             ' C0
                  rol phsa,#17            ' D7
                  rol phsa,#1             ' D6
                  rol phsa,#1             ' D5
                  rol phsa,#1             ' D4
                  rol phsa,#1             ' D3
                  rol phsa,#1             ' D2
                  rol phsa,#1             ' D1
                  rol phsa,#1             ' D0 will be in place _after_ this instruction
                  mov frqb,#0             ' shuts the clock off, _after_ this instruction
                  djnz vm_junk,#:writeloop
    fastwrite_ret ret
    
  • @lonesock's Fast Full-Duplex Serial, 1-cog uses ctra and ctrb to establish timing:
    FDS_entry     ' Counter A's job is to output data on the TX pin
                  ' Counter B's job is to count how many clocks the RX pin is low
                  ' (specifically for aligning the read to the start bit).  
    

    Source thread:
    forum: https://forums.parallax.com/discussion/143514/fast-full-duplex-serial-1-cog-a-k-a-ffds1
    FFDS1        
      (Fast Full-Duplex Serial, 1 cog)
      version 0.9
      Jonathan Dummer (lonesock)
      FFDS1 provides a fast and stable serial interface
      using a single cog.
      Max baudrate = clkfreq / (86 * 2)
      Clock  | MaxBaud | Standard
      -------+---------+---------
      96 MHz | 558_139 | 500_000    <- 6MHz XTAL at 16x PLL
      80 MHz | 465_116 | 460_800    <- 5MHz XTAL at 16x PLL (most common)
      12 MHz |  69_767 |  57_600    <- approx RCFAST
      20 kHz |     116 | hah hah    <- approx RCSLOW
      Bit period is calculated to the nearest 2 clocks.
      So, the bit period should be within 1 clock, or
      12.5 ns at 80 MHz.
    
  • My serial drivers in Tachyon are bit-bashed and work up to 3Mbd although I really only receive and buffer data in the cog since it is more efficient to bit-bash transmit data than it is to buffer it at higher speeds. So what's the advantage of using the counters then?

    Of note too is that while a link may be full duplex, very often the protocol is essentially half duplex anyway.
  • yisiguroyisiguro Posts: 52
    edited 2019-12-01 03:07
    Every comment help me to make my thought clear. Thank you guys.

    By the way, there's an experimental code to try detecting baudrate automatically.
    I implemented my concept straight forward, so this code is not so optimised, I think.
    Acceptable format is 8bit-nonparity.
    Only lower 7 bit characters ( $00 thru $7F ) are detectable, so do not set MSB to high.
    First character must be sent with trailing 68 bit or more idle state ( logic high ) in worst case ( i.e. on receiving NUL character).
    In this code, both cog counters are used for measuring positive and negative pulse width precisely.
Sign In or Register to comment.