Close

Receiver Overruns (as expected)

A project log for 3-Chip Z80 Design

Combining a Z80 retro design with a modern PSoC CPU.

land-boardscomland-boards.com 10/14/2019 at 17:310 Comments

The USB to Serial into the card is much faster than the Z80 can handle. If you are typing keys on the keyboard things are fine. But if you want to upload code to the card then things go bad quickly since the Z80 can't keep up. In the real thing that is handled by toggling the RTS line back to the host. In the case of this emulation of the Serial interface there is no RTS line to toggle.

There isn't an equivalent problem for serial output since the Z80 waits on the Transmit Empty before sending another character. Send packets of one byte on USB isn't particularly efficient but it works just fine in practice. The USB-Serial connection to the PC is very fast and any bytes just get put into single byte packets and sent out the USB to the Host. I will check the speed of the interface later to see how efficient this is and whether improvements should be made or left as-is.

The PSoC interface to the USB is called USBUART in their documentation. Double clicking on the UART in the GUI pulls up this screen.

Clicking on the Datasheet button in the bottom left pulls up the USBUART datasheet.


There are a few PSoC functions which are used when receiving data from the USB interface. In the USBUART example the code uses the following functions:

It should be possible to use these functions to determine if the packet size is greater than 1 and then get one byte at a time to feed to the SIO interface. If the packet size is 1 and there's room for data then it can be sent to the SIO interface.

To determine if there's room for another character in the SIO interface look at the SIO Status Register 0 for Port A. The relevant bit is the D0 bit which 1 if there's still a character in the receive buffer.

The SIO routine is:

It's easy to see that there's a difference between a routine which sends one character to the Z80 and routines which receive a packet of up to 64-bytes.

Let's make the assumption that if the GetAll routine isn't called the PSoC and/or USB interface waits on the data to be read before accepting further USB packets. That has to be the case otherwise there'd always be overruns.

Further, GetAll(buffer) is called with a pointer to a buffer and it returns the byte count from that call. To deal with this difference, the byte count could be decremented every time a character is send to the Z80. The USB receive function would be inhibited until the count has reached zero.

This is a fairly simple change to the current code. In main( ):

if (0u != USBUART_DataIsReady())
...

Change to:

/* Check for input data from host. */
/* Only do the check if the buffer is already empty */
if ((0u != USBUART_DataIsReady()) & (USB_To_Z80_RxBytes_count == 0))

Then, read the buffer and check if it's a single character and the SIO receiver buffer is not busy.

USB_To_Z80_RxBytes_count = USBUART_GetAll(buffer);
if ((USB_To_Z80_RxBytes_count == 1) & (checkSIOReceiverBusy() == 0))     // Input 1 character immediately
{
     sendCharToZ80(buffer[0]);
     USB_To_Z80_RxBytes_count = 0;
}

 Add the function checkSIOReceiverBusy( ) to Z80_SIO_Emul.c as:

///////////////////////////////////////////////////////////////////////////////
// uint8 checkSIOReceiverBusy(void) - Check the SIO port A receiver status
// Returns: 
//  0 if the port can take another character
//  1 if the port is busy and can't take another character

uint8 checkSIOReceiverBusy(void)
{
    return (SIO_A_RD0 & SIOA_CHAR_RDY);
}

Back in main( ), add in this code to the main loop:

        if (USB_To_Z80_RxBytes_count > 0)           // There are chars in the input buffer (USB -> Z80)
        {
            if (checkSIOReceiverBusy() == 0)        // Check if receive buffer can take another character
            {
                sendCharToZ80(buffer[bufferOff]);   // Send received character to Z80 SIO interface
                bufferOff++;                        // ready for next character
                USB_To_Z80_RxBytes_count--;         // worked off one character
                if (USB_To_Z80_RxBytes_count == 0)  // Sent last character to Z80
                {
                    bufferOff = 0;                  // point back to start of character in buffer
                }
            }
        }

Those changes made it a lot better but not yet perfect. If I sent in smallish packets it works. If I sent in large packets it goes bad. Might have something to do with end points? There's something odd noted about packets that are 64 bytes. Will need to investigate because that's about where the problem happens.

Discussions