• Please review our updated Terms and Rules here

Theoretical max serial speeds

MykeLawson

Veteran Member
Joined
Mar 20, 2014
Messages
607
I have this idea floating around in my brain, and I'd like some feedback to decide if this is a path worth pursuing, or just dump in the trash. The scenario would go something like this; two Z-80 processors at 2MHz connect to each other through a serial interface provided by a UART (think 8251ish). No RS-232 conversion, just straight TTL, on a distance less than a foot or two. Assuming no handshaking and just Rx & Tx, and only software overhead being the testing to see if the buffers are empty or full, and grabbing (sending side) or storing (receiving side) , what would you think would be the maximum baud rate? Something that is a standard value and lowest possible risk of missed data. Thanks for any thought or suggestions.
 
The 8251 reference was just to indicate the use of a UART, and was the first type that popped into my head. 85C30, 16x50, etc.
 
According to the data sheet for the 8251 it is 9600 bits/second (asynchronous) and 56,000 bits/second (synchronous).

Synchronous transmission would be much more preferable than asynchronous - due to the higher speeds - if that is what you are aiming for...

Now, whether there are options to circumnavigate the asynchronous limit in the 8251 is another matter.

I have always used a x16 divider. But I see there is a x1 divider mode. I wonder what that will do for you?

According to the data sheet, in x1 mode the highest clock frequency is 56 kHz. A bit is shifted in/out on each clock - so this would suggest 56 kbits per second.

There would be 10 bits /character (in asynchronous mode) = 1 start bit, 8 data bits and at least 1 stop bit.

This would limit us to 56,000 [bits/second] / 10 [bits/byte] = 5,600 [bytes/second].

At x16, the highest frequency is 615 kHz. which would give 615 kHz / 16 = 38.4375 kHz bit shift rate.

38,438 [bits/second] / 10 [bits/byte] = 3,844 [bytes/second].

Therefore the x16 mode transfers fewer data bytes than the x1 mode.

HOWEVER - I can't see where the 9,600 bits/second limitation enters the equation if you are permitted higher clock rates than that...

Dave
 
I think what I'm trying to get out is what is the maximum that the processor could handle before data just can't be transmitted and/or received reliably? The UART is just the device used to facilitate the physical transfer, conversion between parallel and serial data, and flagging the processor that it is ready for more. I'm thinking the theoretical maximum is more centered around the timing of the loop to test if a byte has been received, or the transmit buffer is empty, then get the byte from the buffer, or load the byte into the buffer, then save the byte to memory, or get the byte from memory. Knowing that, I could see what UART would give me the fastest thruput.
 
In that case, it depends upon your hardware configuration and how 'tight' you can write code.

A pure software-only solution will be pretty poor - although that doesn't give you a number.

If you can combine a software solution with the hardware, you can significantly improve the performance. For example, the USART can put the CPU in a wait condition (waiting at an IN instruction for example) until such a time as a byte has been assembled for the CPU to read. The wait condition is then removed, the Z80 can complete the IN instruction and can process the byte.

On a Z80, you could wait the CPU at an INI instruction (waiting for the port read from port C). When the INI instruction completes, the data byte that has been read from port C will be stored into memory pointed to by register pair HL. The HL register pair will be incremented and B decremented. You can ignore register B (unless you want to use it as a byte counter for byte transfers < 256 bytes of course). The only thing to consider then is how you are going to inform the CPU that the message transfer has completed.

We use a pure software software solution on our communications cards. With a 6 MHz 8086 CPU (MAX mode) running with an 8274 (in SDLC/HDLC synchronous mode) we can achieve about 250 kbps. Of course, a Z80 at 2 MHz will never keep up. We use a Z80 at the other end of some of the communications cards, but we use another 8274 with a Z80 DMA chip to keep up...

I will think of a pure software solution using a Z80 CPU. If you count in T states for the instruction loop - you can calculate what the solution will theoretically run at for a given clock speed - but it is getting too late in the UK to do any sort of programming exercise now...

Dave
 
As for code, I'm thinking something very simple along these lines:

TRANSMIT:
Load loop counter
Load data pointer to get byte
Get byte from memory
Check if transmit buffer is empty
If not, keep checking. If empty load transmit buffer.
Inc. data pointer, dec data pointer, check to see if finished.
If finished, then exit. If not, then loop to 'get byte'

RECEIVE:
Load loop counter
Load data pointer to save byte
Check if receive buffer is empty
If empty, keep checking. If not empty get data from receive buffer.
Save data to memory
Inc. data pointer, dec data pointer, check to see if finished.
If finished, then exit. If not, then loop to 'check receive buffer empty'

Unless anyone has any insights on issues this may present, probably my next step would be to create the code and test it at 9600 (already do this on another project), then calculate the number of T states and thus the loop time for sending or receiving one byte, and then see what standard baud rate is closest, and lowest, to that. Of course, from what I'm seeing, the most common baud rate generator chips max out at 19,200 baud, so whatever UART is chosen would need to have the baud rate set internally via software. And the 85C30 can do 76,800.
 
I assume you are talking about data message lengths greater than 256 bytes?

Using register B as a counter (up to 256 bytes) is relatively easy in Z80. Using a register pair is more troublesome (as DEC BC - for example - does not set the flags when the register pair reaches 0. So you have to check yourself in code).

I wrote a music program a while ago using a 4 MHz Z80. Every T state counted - so we optimised the code - using some of the so called 'undocumented' instruction set - that all Z80's implement... This squeezed a little more performance (fewer T states) out of the loop...

This sounds like a similar problem.

I will knock something up...

Dave
 
I have pulled the fastest Z80 instructions out for the task [e.g. AND n is faster (by 1T) than BIT n,A; and JP Z,nnnn is faster than JR Z,nn (if the jump/branch is taken - which will be the majority of cases)].

One question though...

On the transmitter, I assume the machine knows where the transmitter buffer starts and how long the message is going to be?

On the receiver, is the same thing true - or does the communications protocol be required to incorporate the length of the message (sent from the transmitter to the receiver)? This will add some slight overhead to the protocol that needs to be accounted for.

I assume the receiver will allocate its own buffer for the message to be stored into, or does this also need to be communicated from the transmitter to the receiver?

Dave
 
Yes, both the loop counter & the data pointer are 16bit, and both sender and receiver know where to start and how long the data is. I have no issue writing the code, I'm just interested to see what thoughts anyone has as to the maximum 'standard' transfer rates that could be assumed.
 
In order to do the maths, you need to write some code to identify the time to iterate the reading or writing loop itself...

Code:
; Start of the transmit routine!
TX:
LD HL, tx buffer start ; Outside of transmit code.
LD DE, tx buffer length ; Outside of transmit code.
TXLP:
IN A,(status port) - [11T].
AND tx status bit - [7T].
JP NZ,TXLP ; Transmitter still busy. - [10T] (irrespective of taken or not).
LD A,(HL) ; Acquire data byte from buffer. - [7T].
OUT (tx data port),A ; Transmit byte to UART. Use the transmit time to execute some Z80 instructions concurrently. - [11T].
INC HL ; Bump the data pointer. - [6T].
DEC DE ; One less data byte to transmit. - [6T].
; Is the byte counter exhausted?
LD A,D ; High byte of byte counter. - [4T].
OR E ; Low byte of byte counter. - [4T].
JP NZ,TXLP ; More bytes to transmit. - [10T].
; All done!

Assuming the best case:

= 11T + 7T + 10T + 7T + 11T + {6T + 6T + 4T + 4T + 10T}

= 46T + {30T}.

Some of the bit in {} will be executed concurrently with the actual byte transmission.

Let's assume worst case for that - in that the 46T and 30T are sequential = 76T.

Assuming a 2 MHz Z80, this gives 1T = 0.5 us.

Therefore, 76T = 76 * 0.5 us = 38 us per iteration of the loop.

38 us is approximately 26,315 bytes per second.

This is on the basis that the test for the transmitter being available is satisfied when tested (i.e. the actual data byte itself has physically been transmitted). There is still (potentially) some margin in the code as we have assumed no concurrent byte transmission and Z80 code execution (which is not the case).

Need to do a similar exercise for the receiver. If that loop is longer, it becomes the limiting factor.

Dave
 
Last edited:
Dang, thanks Dave. That is starting to look like 115,200 may be the max standard baud rate (assuming receive has similar numbers). 115,200 isn't too bad either.
 
Don't know anything about the requirements, but you could get significantly higher throughput by replacing the UARTs with a FIFO (e.g., Am7201, which is of course obsolete but can be found in a 28-pin DIP, in keeping with the vintage vibe). This would let you transfer a byte at a time in parallel (with the cost of extra wires/traces going between the processors).
 
It depends upon the depth of the FIFO and the size of the message.

If you can buffer the full message, you can pull it out as fast as you can.

However, the CPU may still be the bottleneck and if the FIFO can't buffer the full message, you are in trouble...

Dave
 
Parallel 8-bit transfers, like LapLink? Okay, so long as the receiver and transmitter aren't too distant from on another and are properly terminated. You could even extend the parallel idea to dedicated RAM (skip the FIFO).
FIFOs in general are pretty expensive, for what you get. (I've got a pile of SMT 7201s here, that never made it into a project). Off-the shelf async FIFOs can still be had in capacities of 8Kx9, for example, but are pretty pricey.

The bottom line is "how fast can you process the data?" An 8 bit parallel transfer running at 50 MHz does you no good if you can't handle the data that quickly.
 
Skew gets to be problem with parallel transfers. The longer the distance the greater the skew.
It's one of the reasons disks moved to serial transfer.
 
OP did state "less than a foot or two" for distance. That won't introduce any significant skew given the speed everything is running at.

Of course the CPU is still the bottle neck, but there is no problem if the FIFO can't buffer the whole message - it has empty/full status lines, so the transmitter would just have to wait. Yes, it would be nice if the transmitter could just blast out the message without having to check if the buffer is full. That would simplify and speed up the transmit side, but I suspect the receive side overhead is the limiting factor in any case (but that's just a guess because we don't have any specs). So practically speaking, the depth of the FIFO may not matter much unless it is large enough to hold an entire message. But we're talking close to an order of magnitude speedup just by going from serial to 8 bits in parallel unless the CPU is really, really slow :)

Dual-port RAM is, of course, another option, but likely more expensive than a FIFO (which, from parts prices I've seen, and depending on a lot of other factors, would be roughly the same as a UART solution).
 
Back to my question of "how fast (on an 8 bit system) can you process the data?"
Write it to hard disk? There's limiting factor there.
 
Back
Top