Shop OBEX P1 Docs P2 Docs Learn Events
Extra bit time in FullDuplexSerial? — Parallax Forums

Extra bit time in FullDuplexSerial?

David BetzDavid Betz Posts: 14,516
edited 2015-02-16 00:10 in Propeller 1
I was just looking through some code that I derived from the FullDuplexSerial driver and noticed the following:
                        or      txdata,#$100          'or in a stop bit
                        shl     txdata,#2
                        or      txdata,#1             'or in a idle line state and a start bit
                        mov     txbits,#11
What's this about "idle line state"? Why is it necessary to add an extra bit time between two adjacent bytes during transmission? Shouldn't it be possible to send to bytes back to back?

Comments

  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-02-15 12:52
    That's actually the way the original FDS does things. In a way, this is using two stop (line at idle state) bits when bytes art tx'd back-to-back (i.e., that idle state bit acts as a second stop bit for the previous byte).

    This is how I do it, but it assumes the TX line is properly setup to begin with.
    transmit                or      txwork, STOP_BITS               ' set stop bit(s)
                            shl     txwork, #1                      ' add start bit
                            mov     txcount, #11                    ' start + 8 data + 2 stop
    


    [Edit] Having thought about it for a minute, Chip probably did this because he couldn't add two stop bits without creating a constant (like I did) -- #$300 would be an illegal constant in PASM as it exceeds 511.
  • David BetzDavid Betz Posts: 14,516
    edited 2015-02-15 12:54
    JonnyMac wrote: »
    That's been modified -- seems unnecessarily. Normally, you would see something like this:
    transmit                or      txwork, STOP_BITS               ' set stop bit(s)
                            shl     txwork, #1                      ' add start bit
                            mov     txcount, #11                    ' start + 8 data + 2 stop
    


    The SHL 1 takes care of adding the start bit. The code you cite is shifting left by two and then returning bit0 to the TX idle state. Again, this seems unnecessary if the TX line was properly setup to begin with.
    Okay but is it really necessary to have two stop bits? I thought that was only required at 110 baud with old teletypes.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-02-15 13:06
    Sorry, David, as sometimes happens I get a new thought as soon as I post a response. As you can see, I've heavily edited my initial response.

    At low baud rates you're right: two stop bits is not required. But at faster baud rates with a software UART, the extra stop bit gives the processor time to handle what just came in (clean-up, buffer management, etc). FDS may be assuming that the other side is software too and is being kind.

    It would not be very difficult to write a version of FDS that allowed one to set the number of stop bits. In most apps it's one or two (though I've seen more in a couple instances), and if one is okay, two will work. It seems to be the lowest [practical] common denominator.
  • David BetzDavid Betz Posts: 14,516
    edited 2015-02-15 13:13
    JonnyMac wrote: »
    Sorry, David, as sometimes happens I get a new thought as soon as I post a response. As you can see, I've heavily edited my initial response.

    At low baud rates you're right: two stop bits is not required. But at faster baud rates with a software UART, the extra stop bit gives the processor time to handle what just came in (clean-up, buffer management, etc). FDS may be assuming that the other side is software too and is being kind.

    It would not be very difficult to write a version of FDS that allowed one to set the number of stop bits. In most apps it's one or two (though I've seen more in a couple instances), and if one is okay, two will work. It seems to be the lowest [practical] common denominator.
    Thanks Jon! And now I have an even dumber question. Can you tell I'm not a PASM wizard yet? :-)

    Is there a way to set the carry flag with a single instruction?

    Is there a way to clear the carry flag with a single instruction?

    I'd like to write a function that use the state of the carry to indicate success or failure and it would be nice to be able to load up the value of C with a single instruction.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-02-15 13:14
    Apparently the two stop bits (one is often at the beginning of the byte) is very common among the various serial drivers. I doubt it's really needed.

    There's a thread where I discuss my attempt to add even parity to a serial driver. Tracy Allen kindly educated me about start bits and stop bits. There was mention of the two stop bits in this thread. I'm sure I could find it if you're interested in reading it.

    Edit: Here's the link:
    http://forums.parallax.com/showthread.php/155390-Adding-Even-Parity-to-One-Port-in-Tracy-Allen-s-4-Port-Serial-Object
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-02-15 13:19
    Is there a way to set the carry flag with a single instruction?

    Is there a way to clear the carry flag with a single instruction?


    I think there are ways to do what you want, but I'm not a PASM wizard yet either, just a guy who slogs it out trying to write simple code.

    Phil Pilgrim once has a list of instruction tricks that would be helpful -- but they were lost during the forums migration. Perhaps he or on of the other PASM wizards will chime in. I'd like to know, too!
  • David BetzDavid Betz Posts: 14,516
    edited 2015-02-15 13:21
    Duane Degn wrote: »
    Apparently the two stop bits (one is often at the beginning of the byte) is very common among the various serial drivers. I doubt it's really needed.

    There's a thread where I discuss my attempt to add even parity to a serial driver. Tracy Allen kindly educated me about start bits and stop bits. There was mention of the two stop bits in this thread. I'm sure I could find it if you're interested in reading it.

    Edit: Here's the link:
    http://forums.parallax.com/showthread.php/155390-Adding-Even-Parity-to-One-Port-in-Tracy-Allen-s-4-Port-Serial-Object
    Thanks Duane. That was very helpful.
  • kuronekokuroneko Posts: 3,623
    edited 2015-02-15 13:47
    flag manipulation
    PUB null
    DAT
    
    set_z                   test    $, #0 wz
    set_nz                  test    $, #1 wz
    set_c                   test    $, #1 wc
    set_nc                  test    $, #0 wc
    
    set_z_c                 shr     $, #%11111 wz,wc,nr
    set_z_nc                shr     $, #%11110 wz,wc,nr
    set_nz_c                shr     $, #%01111 wz,wc,nr
    set_nz_nc               shr     $, #%01110 wz,wc,nr
    
    mov_z_c                 muxnc   $, $ wz,nr
    mov_z_nc                muxc    $, $ wz,nr
    mov_c_z                 muxz    $, $ wc,nr
    mov_c_nz    if_z_or_nc  muxnz   $, $ wc,nr              ' condition flips parity to odd (required)
                                                            ' this nop's for z = 0 && c = 1 (c == nz)
    swap_z_c    if_z_ne_c   sumc    $, $ wc,wz,nr
    
    not_z                   muxz    $, $ wz,nr
    not_c                   muxnc   $, $ wc,nr
    
    save_z                  muxnz   restore_z, #1
    restore_z               test    $, #1 wz
    
    save_c                  muxc    restore_c,#1
    restore_c               test    $, #1 wc
    
    save_z_c                muxc    restore_z_c, #%00001
                            muxz    restore_z_c, #%10000
    restore_z_c             shr     $, #%01110 wz,wc,nr     ' %Z---C
    
    DAT
    
  • jmgjmg Posts: 15,173
    edited 2015-02-15 13:51
    David Betz wrote: »
    What's this about "idle line state"? Why is it necessary to add an extra bit time between two adjacent bytes during transmission? Shouldn't it be possible to send to bytes back to back?

    It can be a good idea to have a second stop bit as optional.(or even more choice, if that is easy to do )

    You are right that at the byte-level, and with very fast receivers, there is no strict need, and it could be removed.

    There is however a somewhat subtle system level use case where an extra idle bit helps, and that is with Baud skew on long packets.
    If you receive 100 bytes, and echo on each one, you can get a TX overflow effect with slight baud differences.
    (just 1% here is a full byte of creep)
    Adding a second stop bit, allows the Baud to skew and ensures a slightly slower TX, can always pace continual RX.

    If you push up the BAUD speed on FTDI/CP210x you will see they add extra stop bits, from the firmware not being able to keep up. They can add 1-2-3-4+ extra idle bit times.
    The HS parts of FT232H/FT2232H are able to send higher speeds without added idles/stop bits..
  • David BetzDavid Betz Posts: 14,516
    edited 2015-02-15 13:54
    kuroneko wrote: »
    flag manipulation
    PUB null
    DAT
    
    set_z                   test    $, #0 wz
    set_nz                  test    $, #1 wz
    set_c                   test    $, #1 wc
    set_nc                  test    $, #0 wc
    
    set_z_c                 shr     $, #%11111 wz,wc,nr
    set_z_nc                shr     $, #%11110 wz,wc,nr
    set_nz_c                shr     $, #%01111 wz,wc,nr
    set_nz_nc               shr     $, #%01110 wz,wc,nr
    
    mov_z_c                 muxnc   $, $ wz,nr
    mov_z_nc                muxc    $, $ wz,nr
    mov_c_z                 muxz    $, $ wc,nr
    mov_c_nz    if_z_or_nc  muxnz   $, $ wc,nr              ' condition flips parity to odd (required)
                                                            ' this nop's for z = 0 && c = 1 (c == nz)
    swap_z_c    if_z_ne_c   sumc    $, $ wc,wz,nr
    
    not_z                   muxz    $, $ wz,nr
    not_c                   muxnc   $, $ wc,nr
    
    save_z                  muxnz   restore_z, #1
    restore_z               test    $, #1 wz
    
    save_c                  muxc    restore_c,#1
    restore_c               test    $, #1 wc
    
    save_z_c                muxc    restore_z_c, #%00001
                            muxz    restore_z_c, #%10000
    restore_z_c             shr     $, #%01110 wz,wc,nr     ' %Z---C
    
    DAT
    
    Thanks! That is very helpful.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-02-15 13:56
    David Betz wrote: »
    Thanks! That is very helpful.

    +1

    Thanks kuroneko.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-02-15 14:08
    Whether to add a second stop bit depends on the receiver at the other end and when it starts looking for the next start-bit edge. If it waits a full stop bit before it starts looking, and if the transmit and receive baud rates are a little off (i.e. the receive a little slower), the receiver could lose sync after an uninterrupted string of bytes. I always add the second stop bit in my serial transmit code, just to be on the safe side. For that matter, an extra half stop bit would also suffice, and even some hardware UARTs provide that capability.

    -Phil
  • David BetzDavid Betz Posts: 14,516
    edited 2015-02-15 14:11
    Whether to add a second stop bit depends on the receiver at the other end and when it starts looking for the next start-bit edge. If it waits a full stop bit before it starts looking, and if the transmit and receive baud rates are a little off (i.e. the receive a little slower), the receiver could lose sync after an uninterrupted string of bytes. I always add the second stop bit in my serial transmit code, just to be on the safe side. For that matter, an extra half stop bit would also suffice, and even some hardware UARTs provide that capability.

    -Phil
    Okay, I guess I'd better leave the FDS code as it is. Sounds like it will be safer and only a little bit slower. Thanks everyone for your advice and comments!
  • jmgjmg Posts: 15,173
    edited 2015-02-15 14:51
    David Betz wrote: »
    Okay, I guess I'd better leave the FDS code as it is. Sounds like it will be safer and only a little bit slower. Thanks everyone for your advice and comments!

    Keep in mind that 'standard baud rates' are no longer a constraint. (but keep them as a default/fallback)

    Most modern USB-UART devices can accept any baud value, and deliver the nearest baud they support, which is mostly from a 12MHz virtual Baud Clock.
  • KyeKye Posts: 2,200
    edited 2015-02-15 15:51
    It has the feature built-in and tested. I implemented the feature for the CMUcam4.

    http://obex.parallax.com/object/246
  • jmgjmg Posts: 15,173
    edited 2015-02-15 16:22
    Kye wrote: »
    It has the feature built-in and tested. I implemented the feature for the CMUcam4.

    http://obex.parallax.com/object/246

    If there a 2 COG version of this, that can go (a lot?) faster ? ie one COG for RX and One COG for TX
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-02-15 16:22
    If a receiver ever loses synch the only real way to correct that is to have the line idle for at least 10 bit times and this is good practice to implement this breather between transmissions. However that extra stop bit at the start of the character is totally redundant and might have served a purpose at one time but it wouldn't help with synching anything anyway because if the transmit line was not initialized then it would still need a 10-bit idle beforehand to allow the remote receiver to cycle through and wait for a start bit again. Any kind of UART, hardware or otherwise would normally only sample the stop bit like any other data bit, in the middle, assuming timing is good, so there should be around half a bit time before it needs to be ready for the start bit again. This means that the FDS receiver running at higher baud rates of say 115.2k might not be ready in time in which case it will then start later in the character and progressively become later and later for each character until it becomes corrupted.

    My dedicated fast serial receive routine works up to 3M baud with only one stop bit between characters so to make sure it can write the received data to the hub and be ready in time it prepares the hub write and index update while it is still receiving data so that all it has to do between characters is to write the data to the hub. btw, the TX part of this is a simple bit-bang without the need for buffering because at these high baud rates it's faster to bang out the bits rather than slow hub buffering and unbuffering.

    I sometimes wish that asynch data would always have a start bit (0) followed by a mark (1) and then the data, so that start bits and timing could always be confirmed, that is detect the start bit and/or measure its width, then synchronize on the leading edge of the sync pulse. Then UARTs could automatically adapt their baud rates on the fly. All for the sake of 1 bit we are stuck with fixed baud rates and timing mismatches.
  • jmgjmg Posts: 15,173
    edited 2015-02-15 17:55
    My dedicated fast serial receive routine works up to 3M baud with only one stop bit between characters

    Is that 3.00Mbd with what Crystal / System Clock ? What margin is there for other clocks ?

    Suppose a design used a USB related Clock, the two candidates could be
    (16*48M/10)/3M = 25.6 - a simply alternating 25/26 would be ~0.4% off ?
    (16*48M/8)/3M = 32
    or a Std Xtal gives
    (16*5M)/3M = 26.6666 => /27/27/26 repeating
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-02-15 20:26
    jmg wrote: »
    Is that 3.00Mbd with what Crystal / System Clock ? What margin is there for other clocks ?

    Suppose a design used a USB related Clock, the two candidates could be
    (16*48M/10)/3M = 25.6 - a simply alternating 25/26 would be ~0.4% off ?
    (16*48M/8)/3M = 32
    or a Std Xtal gives
    (16*5M)/3M = 26.6666 => /27/27/26 repeating

    I've tested this on a standard 80MHz Prop and checked the this with a scope as well. I know the timing would be off a little but it seems to work fine because I used it at this baudrate for quite some time early in the Tachyon development. 2M baud does not have this timing error at 80Mhz but my newer boards are designed to run from a 6MHz oscillator at 96MHz so there is no timing error at this frequency. btw, my start bit timing is pre-calculated to half a bit time less 4 cycles to compensate for latencies at higher speeds and I always check the stop bit for framing error as part of break detection.
  • jmgjmg Posts: 15,173
    edited 2015-02-15 20:44
    Thanks, is there a link to this code somewhere ?
    Does it have enough headroom to run 3M at 25.5 cycles per bit average (vs 26 2/3 at 80Mhz )

    If it uses an unrolled loop, it may be possible to support fractional baud ideas to get more Xtal choice tolerance.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-02-15 20:55
    jmg wrote: »
    Thanks, is there a link to this code somewhere ?
    Does it have enough headroom to run 3M at 25.5 cycles per bit average (vs 26 2/3 at 80Mhz )

    If it uses an unrolled loop, it may be possible to support fractional baud ideas to get more Xtal choice tolerance.


    Check my sig for the Dropbox links where you will find the serial receive right at the end of the Tachyon2.4.spin source or look in the "trouble with serial code" thread where I pasted this code in. It is more than just unrolled, it is interwoven and writes immediate fields to reconstruct the data so that saves a right justification operation as well.
  • pjvpjv Posts: 1,903
    edited 2015-02-15 21:05
    Here are two separate PASM code snippets for 5 Megabit/sec receive and transmit data transfers to/from hub addresses. It assumes other code does the initialization of number of the hub address, number of bytes and number of search samples on receive before giving up (returning) to the calling program.. It was from a long time ago, but as I recall, it wil receive correctly with just a single stop bit
    'Receive Routine =======================================
    HubRx5Mbs     rdbyte    Shifter,#0                      'sync hub
                  sub       HubAddr,#1                      'pre-dec address
                  nop                                       'set hub sweet spot
    StartSearch   jmp       #SrchForStart                   'entry point
    OneBit        mov       SearchCtr,#RxSearchShort        'load number of loops to search for        nop
    FirstBit      and       RxLine0,ina             wc,nr   'parity sample into carry              
                  rcr       Shifter,#1                      'pull sample into shifter
                  djnz      BitCtr,#OneBit                  'test for 7 bits done
                  and       RxLine0,ina             wc,nr   'parity sample bit 8 into carry              
                  rcr       Shifter,#25                     'pull sample into shifter and bit0 justify
                  wrbyte    Shifter,HubAddr                 'save byte in main ram buffer
                  djnz      ByteCtr,#SrchForStart           'track received bytes
                  jmp       #HubRx5Mbs_ret                  'exit on all received            '
    SrchForStart  and       RxLine0,ina             wc,nr   'test for low start condition
            if_c  djnz      SearchCtr,#SrchForStart wz      '2x oversample start bit... zero on time-out
                  mov       BitCtr,#7                       'how many bits... 8th bit is unrolled
                  add       HubAddr,#1                      'next address
            if_nz jmp       #FirstBit                       'look to receive a byte
    HubRx5Mbs_ret ret                                       'exit on search timed out
    
    
    'Transmit Streamer Routine ============================
    TxOneBit      xor       outa,#TxBitTime6                'optional scope bit time indicator.. replace with nop
                  shr       Shifter,#1              wc      'get bit
                  muxc      outa,#TxLine4                   'output it
                  djnz      BitCtr,#TxOneBit                'test for 8 bits done
                  sub       ByteCtr,#1              wz      'keep track of how many done
    HubTx5Mbs     or        outa,#TxLine4                   'stop condition
                  rdbyte    Shifter,HubAddr                 'get value from Hub RAM
    'optionally   rdbyte    Shifter,HubAddr          wz      'get value from Hub RAM and exit on zero terminated string
                  add       HubAddr,#1                      'next byte address
    OneByte if_nz andn      outa,#TxLine4                   'start condition
                  mov       BitCtr,#8                       'how many bits
            if_nz jmp       #TxOneBit +1                    'loop for another byte
    HubTx5Mbs_ret ret                                       'done
    
    RxLine0       long      1                               'receive pin mask... Pin0
    HubAddr       long      0                               'location in main RAM
    ByteCtr       long      0                               'how many bytes to receive/transmit
    SearchCtr     long      0                               'keep track of search time for START condition
    BitCtr        long      0                               '
    Shifter       long      0                               '
    

    Cheers,

    Peter (pjv)
  • jmgjmg Posts: 15,173
    edited 2015-02-16 00:10
    It is more than just unrolled, it is interwoven and writes immediate fields to reconstruct the data so that saves a right justification operation as well.
    hehe :) expected nothing less.
    The 1/3 tick error can be centrally positioned, to be -1.5 tick at start and +1.5 tick at Stop bit.

    I think there is room to gain another 15% bit timing margin, if you swap the Startbit code to immediately after Stop/save (removes one JMP, and inserts it in a less critical place).

    With some low cost Micros having 1.5% Oscillator tolerance, every bit of margin helps.
    Of course, if the Micro also provides the 4.8MHz or 6Mhz CLK, then there is no relative drift to allow for.
Sign In or Register to comment.