Shop OBEX P1 Docs P2 Docs Learn Events
efficient high speed serial output — Parallax Forums

efficient high speed serial output

mike56mike56 Posts: 22
edited 2009-08-18 09:07 in Propeller 1
I've been learning the Prop Assembly and my first project was write a good high speed serial routine.

I looked a number of code that claim high data rate routines like FT232R-BlockSerialTransmit v2.0 by Stephen Moraco but the issue that I see with all of them seems to be that there seems to be a minor timing issue that isn't as efficient as I would like.

Consider the following code:

:sendLoop········· or····· cTxByte,#$100
······················· shl···· cTxByte,#1············· ' add startbit
······················· mov···· nBits2xmit,#10········· ' start bit, 8 data bits, stop bit
······················· mov···· nDeltaTime,cnt········· ' capture our starting time
······················· add···· nDeltaTime,nTicksPerBit ' add in our first bit-window
:sendbit·············shr···· cTxByte,#1····· wc····· 'test LSBit
······················· muxc··· outa,nMaskTxD
······················· waitcnt nDeltaTime,nTicksPerBit 'wait 1 bit, then add bit time for next wait
······················· djnz··· nBits2xmit,#:sendbit··· '10 times

Let's consider 2mbps. 80mhz/2e6=40clk cycles = 10 instructions.

In the above code when the first bit is processed, I see:
· nTicksPerBit := clkfreq / baudrate_p· = 80e6/2e6=40

t=0,· muxc··· outa,nMaskTxD
t=4,· waitcnt nDeltaTime,nTicksPerBit
t=44, djnz··· nBits2xmit,#:sendbit···
t=48, shr···· cTxByte,#1····· wc·····
t=52, muxc··· outa,nMaskTxD
So instead of 40clk cycle pulse, it is actually 52 clock cycles.· That's a pretty big error for the start bit.

The next bit is:
t=52, muxc··· outa,nMaskTxD
t=56, waitcnt nDeltaTime,nTicksPerBit
t=84,· djnz··· nBits2xmit,#:sendbit···
t=88,· shr···· cTxByte,#1····· wc·····
t=92,· muxc··· outa,nMaskTxD
so delta_t=40 clks which is back on track.

Also, in all the serial routines I've seen, every one does a whole bunch of operations before sending the start bit which requires:
······················· mov···· nBits2xmit,#10········· ' start bit, 8 data bits, stop bit
······················· mov···· nDeltaTime,cnt········· ' capture our starting time

So the cnt is synced every byte iteration.

This doesn't seem very efficient to me and I have noticed when I "stream" data continuously i.e. no pause in the data, then I get a bunch of errors at high speed.· If I send data only once in a while then it works fine.

Instead, I rewrote my own routine:
transmitlong··········· mov···· txbytecount,#4·················· ' 4 bytes per long
······················· mov···· txtimer, bitticks·············· ' load bit timing
······················· add···· txtimer, cnt··················· ' sync with system counter
:txbyte················ mov···· txbitcount,#8·················· ' 8 bits per byte
······················· waitcnt txtimer, bitticks·············
······················· mov···· outa, #0······················· ' send start bit start bit low·······················
:txbit················· shr···· txwork, #1············· wc····· ' move bit0 to C
······················· waitcnt txtimer, bitticks·············· ' let timer expire, reload
······················· muxc··· outa, txmask··················· ' output the bit
······················· djnz··· txbitcount, #:txbit············ ' update bit count
······················· waitcnt txtimer, bitticks··············
······················· mov···· outa,txmask···················· 'send stop bit
······················· djnz··· txbytecount,#:txbyte
······················· waitcnt txtimer, bitticks·············· 'wait 1 extra bit to sync
······················· waitcnt txtimer, bitticks·············· 'wait 1 extra bit to sync
transmitlong_ret······· ret


This ensures exact timing as far as I can see.

They key is always·follow a waitcnt with an immediate outa.· Then as long as there aren't more than 6 instructions in between, it is good for constant data streaming.

The above example is for streaming a long (4 bytes) but can be extended to any number of bytes (except rdlong takes 22 clock cycles so you need to do something special to get the data in cog memory without running extra instructions).

Any comments or tips how to do this better?

I wrote my own C# program using the FTDI dll drivers because I heard the VCP interface doesn't stream very well at high speed.

I did an Xmodem like packet of 4 bytes of init string + 32*4=128 bytes of data + 1 byte checksum and it works pretty well.· I can stream continuously but above ~1.5mbps I start to get a lot·checksum errors from missing bytes in the buffer.· Note this is streaming data, the prop is sending this as fast as it can in a very tight asm loop.· There's no waiting around.

I'm not sure if this is my C# program or my propeller program but the timing looks ok to me.

Can anyone comment on my serial output loop above?

I'll try cleaning up and documenting the rest of my code and release it but I'm still a newbie at Prop Asm so it's not that good yet.





Post Edited (mike56) : 8/16/2009 4:49:24 AM GMT

Comments

  • DurbanFlyBoyDurbanFlyBoy Posts: 7
    edited 2009-08-18 09:07
    Hi Mike

    I cant comment on your assembly code, but regarding the FTDI combined-mode driver (can connect to an FTDI device through a virtual comport (VCP) or through the more direct API route using the FTD2XX.DLL, the latter is MUCH faster, up to 2MBs, whereas in the VCP mode, you are looking at maxing out at about 300KBS.

    I have used the FTDI chips in quite a few projects using the SX chip, where the bottleneck is the SX chip itself. The propeller is a newbie for me, but the potential is tremedous.

    By the way, the object you discuss above, "FT232R-BlockSerialTransmit v2.0 by Stephen Moraco" Where did you find it, I cannot locate it in the OBEX. Please foward to me so I can play around with it and can comment on your project.

    Kind regards
Sign In or Register to comment.