Shop OBEX P1 Docs P2 Docs Learn Events
Structure of @rx_head in FullDuplexSerialPlus — Parallax Forums

Structure of @rx_head in FullDuplexSerialPlus

ke4pjwke4pjw Posts: 1,173
edited 2010-06-27 02:44 in Propeller 1
I need to receive data with the prop via serial => 512kbps and find that spin appears to top out at about 115.2kbps. I want to rewrite my code in PASM, but understanding FullDuplexSerialPlus has me scratching my head a bit. It looks like all of the intercog communication happens through @rx_head. (But I could be way off here)

Does anyone know the structure of @rx_head in FullDuplexSerialPlus? Or maybe point me to a tutorial on FullDuplexSerialPlus that will explain it to me like I was a 4year old [noparse];)[/noparse]

Thanks in advance.

--Terry

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-- Terry

Comments

  • KyeKye Posts: 2,200
    edited 2010-06-19 13:24
    The driver uses a circular FIFO buffer.

    There are head and tail pointers into the buffer which is 16 bytes long.

    The system works like this. Both the head and tail pointer start off at zero equal to each other. When a byte comes in off the serial line the byte is put into the serial buffer like this "buffer[noparse][[/noparse]headIndex] := byteOffSerialLine" Then the head pointer (which is actual and index in the serial buffer - it goes from 0 to 15) is incremented and then anded wtih $F to make sure it nevers gets above 15.

    When the spin code then wants to get a byte from the serial buffer it grabs the byte pointed to by the tail index in the serial buffer and then increments the tail pointer by 1 and ANDs it with $F.

    So, by doing this you have a First In First Out (FIFO) buffer in which the head and tail pointer tell you where valid data is and isn't.

    The amount of stuff in the FIFO buffer is equal to ((head - tail) & $F). So this means the buffer can hold 15 bytes or space at maximum and when the head and tail pointer are equal to each other at any point the buffer is empty.

    I hope this helps. "Google circular FIFO buffer otherwise"

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-19 13:49
    I am not familiar with FullDuplexSerialPlus.· Is it in the OBEX?· I mostly use FullDuplexSerial, and I did run a Spin timing test back in April when I was trying some YModem code.· I modified FDS by adding an RxFill routine so I could·pre-fill the receive buffer for my test.· I ran the following code:

      ser.rxfill("A", 10)
      repeat j from 0 to 9
        i := ser.rx
        pdata[noparse][[/noparse]j] := i
    
    


    This loop took·about 80 usec per byte, which is 125 kbps.· It would have been slightly faster if I used pdata[noparse][[/noparse]j] := i instead of doing this in two lines.· However, it wouldn't have achieved 512 kbps.· There is a significant amount of overhead in calling ser.rx.· You might be able to achieve 512 kbps if you create a block read function in FDS+ as follows:

    PUB rxblock(ptr, num)
      result := (rx_head - rx_tail) & $f
      if num < result
        result := num
      ifnot result
        return
      repeat result
        byte[noparse][[/noparse]ptr++] := rx_buffer[noparse][[/noparse]rx_tail]
        rx_tail := (rx_tail + 1) & $F
    
    

    This method will copy up to "num" bytes from the RX serial buffer to the buffer pointed to by "ptr".· It will return the number of bytes transferred.· If I get a chance I'll run a timing test on this to see how fast it is.

    Dave

    ·
  • ke4pjwke4pjw Posts: 1,173
    edited 2010-06-19 15:18
    Thanks guys! So what I am seeing is the actual bit banging is done in a separate cog, using PASM, but all of the book keeping for the FIFO is handled in SPIN. If that is the case, I can see why it's a bit pokey and falls over at high speeds. Does my understanding sound correct?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -- Terry
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-19 21:37
    I measured the cycle times and I couldn't get more than about 386 kbps with the rxblock routine I posted above.· This version uses bytemove, and get's over 1 mbps.· BTW, I changed the rx buffer size to 128 in my version of FDS.· I haven't tested whether rxblock is correct, but I think it should work.

    Dave

    PUB rxblock(ptr, num) | num1, num2
      if rx_head => rx_tail
        num1 := rx_head - rx_tail
        num2 := 0
      else
        num1 := 128 - rx_tail
        num2 := rx_head
      if num1 > num
        num1 := num
        num2 := 0
      elseif num1 + num2 > num
        num2 := num - num1
      bytemove(ptr, rx_buffer + rx_tail, num1)
      bytemove(ptr + num1, rx_buffer, num2)
      result := num1 + num2
      rx_tail := (rx_tail + result) & 127
    
    

    ·Edit: I forgot to update rx_tail.· I added the necessary line above.

    Post Edited (Dave Hein) : 6/19/2010 11:20:30 PM GMT
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-20 03:08
    I looked at the rxblock code one more time and realized that there is a race condition on rx_head.· Here is the new "bullet-proof" code that first makes a local copy of rx_head.

    Dave

    PUB rxblock(ptr, num) | num1, num2, rxhead
      rxhead := rx_head
      if rxhead => rx_tail
        num1 := rxhead - rx_tail
        num2 := 0
      else
        num1 := 128 - rx_tail
        num2 := rxhead
      if num1 > num
        num1 := num
        num2 := 0
      elseif num1 + num2 > num
        num2 := num - num1
      bytemove(ptr, rx_buffer + rx_tail, num1)
      bytemove(ptr + num1, rx_buffer, num2)
      result := num1 + num2
      rx_tail := (rx_tail + result) & 127
     
    

    ·
  • ke4pjwke4pjw Posts: 1,173
    edited 2010-06-26 17:55
    Dave, I appreciate your work! I know that is some code I can use in the future! I really want to do my code in PASM.

    I have now figured out how and why the pointer that is passed off to the cog running the UART interfaces with the locations in hub memory. I have written some PASM to pull a byte from the buffer and compare it with the ASCII value of the capitol letter "D". It runs in a separate cog from the UART, though the pointer to rx_head is passed to this code.

    The code works 100% up to 250kbps. Does not decode at 460.8kbps or 921.6kbps. It will decode properly about 10% of the time at 1Mbps. Since I am only sending one character at a time, by hand, I do not believe this to be a buffer over run issue. I suspect that either I have a hardware issue (RF needs to be decoupled or standing waves on the transmission line) or I am running against the limits of a cog's processing power that is running FullDuplexSerialPlus. I am thinking of removing the ping-pong multitasking and separating the UART into two cogs. One for RX and one for TX. I do have some doubt if this is a software issue, as Dave was able to reliably receive data at 1Mbps.

    I am not sure what direction to head to next. Hardware or software.

    Thanks guys!
    --Terry

    ' My serial rx test harness
                            org  
    rxentry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     PinsOut,#$0000       'Set PinsOut variable so that pins 0 - 15 are low
                            or      PinsOut,Pin15        'Set bitmask for Pin15
                            mov     dira,PinsOut         'Set direction of Output pins
    
    :loop                   call    #getabyte              ' Go get a byte 
                            cmp     rxdata,#68      wz     ' Compare received byte with ASCII "D"
    
            if_z            or      PinsOut, Pin15         ' Prepare to turn on LED if compare was equal
            if_nz           mov     PinsOut,#$0000         ' Prepare to turn off LED if compare was not equal
                            mov     outa, PinsOut          ' Turn LED on or off
                       
                            jmp     #:loop                 ' Do it *all*  *over* *again*! :)
    
                                                          'check for head <> tail
    getabyte                mov     t1,par                ' Get address of rx_head
                            rdlong  t2,t1                 ' Assign the value of rx_head to t2
                            add     t1,#1 << 2            ' Move address up 4 bytes to get address of rx_tail
                            rdlong  t3,t1                 ' Assign the value of rx_tail to t3
                            cmp     t2,t3           wz    ' Compare values of rx_heads and rx_tails
            if_z            jmp     #getabyte             ' Jump to getabyte is result of compare was equal
    
                                                          'get byte and inc tail
                            add     t3,rxbuff             ' Increment the value of rx_tails by the address of rxbuff 
                            rdbyte  rxdata,t3             ' Assign the value at the buffer tail to rxdata
                            sub     t3,rxbuff             ' Decrement t3 by the value of the address of rxbuff. Result is rx_tail
                            add     t3,#1                 ' Increment rx_tail by 1
                            and     t3,#$0F               ' Perform AND operation on rx_tail with $0F (if > %1111 then rollover)
                            wrlong  t3,t1                 ' Update value of rx_tail in hub memory
    getabyte_ret            ret
    
    ' Uninitialized data
    '
    Pin15                   LONG    0-0                   'Pointer to variable in Spin code 
    
    PinsOut                 res     1                     'Pin bitmask
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -- Terry
  • ke4pjwke4pjw Posts: 1,173
    edited 2010-06-27 02:44
    Ok, well I removed the ping-pong multitasking used by FullDuplexSerialPlus. I commented out anything that would jump to the transmit label. I am able to achieve 100% reliable reception of data at 460.8kbps. I am starting to suspect this is a software issue. After looking at how many instructions are used by the software UART, and knowing that a cog is capable of 20MIPS, I don't think reliable asynchronous communication is possible much beyond 500kbps with a single cog running FullDuplexSerialPlus. There are 27 instructions used to receive a byte and place it in the buffer.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -- Terry
Sign In or Register to comment.