tinyfpga_bx_usbserial icon indicating copy to clipboard operation
tinyfpga_bx_usbserial copied to clipboard

uart_out_valid lockup when reading bytes slowly

Open Mecrisp opened this issue 5 years ago • 3 comments

I wished to insert this beautiful ttyACM interface into Mecrisp-Ice, which is a Forth processor derived from the J1a and Swapforth by James Bowman. For testing, I tried Host <--> ttyACM logic <--> J1a CPU <--> 115200 baud UART.

The CPU --> ttyACM --> Host direction works fine will full speed. But on the Host --> ttyACM --> CPU direction, I ran into a problem: When I fetched (wait for UART_OUT_VALID going high, read byte and pulse UART_OUT_READY for one clock cycle) the bytes coming into the logic quickly (below 1 us per byte), then everything was fine, and I could receive the bytes correctly. But when I relayed the bytes to the real 115200 baud UART in a loop (read byte from ttyacm logic as before, give byte to 115200, wait for 115200 to finish transmitting, repeat), then single bytes were send fine, but when I send multiple bytes at once ( cat Test.txt > /dev/ttyACM0 ) a few bytes were received and relayed correctly, but then the UART_OUT_VALID line went high forever, further pulsing UART_OUT_READY did not help and UART_OUT_DATA stayed fixed at the last byte correctly received. Also the cat command on the host locks up without further progress of the transfer. I then tried to remove the 115200 baud uart and added a delay instead, also locking up the ttyacm logic.

I expect the ttyACM logic to wait for the bytes to be actually fetched without locking up.

As the whole setup is quite complicated, I tried to reproduce the problem with small snipplets of glue logic.

Good example: When receiving the bytes from host to logic quickly, I can send multiple bytes of data to logic at once without problems:

  assign uart_out_ready = uart_out_valid;

  reg [7:0] LEDs;
  always @(posedge clk)
  begin
    if (uart_out_valid) LEDs <= uart_out_data;
  end

  assign {D8, D7, D6, D5, D4, D3, D2, D1} = LEDs;

Bad example demonstrating lock-up behaviour:

  reg out_ready_buffered = 0;
  assign uart_out_ready = out_ready_buffered;

  reg [23:0] slowdown = 0;
  reg [7:0] LEDs;

  always @(posedge clk)
  begin
    slowdown <= slowdown + 1;

    if (slowdown == 0)
    begin
      if (uart_out_valid)
      begin
        LEDs <= uart_out_data;
        out_ready_buffered <= 1;
      end
    end
    else out_ready_buffered <= 0;
  end

  assign {D6, D5, D4, D3, D2, D1} = LEDs;

  assign D7 = uart_out_valid;
  assign D8 = uart_out_ready;

When using this code, you can see uart_out_valid going high upon key press in terminal, and when slowdown timer reaches 0, a short flash of uart_out_ready visible with oscilloscope only, updating the LEDs, and uart_out_valid going low. When typing slowly, it's fine. But when typing fast or copying data into the terminal, then uart_out_valid keeps high, without further data bytes being transmitted.

Best wishes and many thanks for the great project, Matthias

Mecrisp avatar May 19 '20 11:05 Mecrisp

Hi Matthais,

The code is pretty bratty about being held up. There is no provision to apply back pressure to the USB internals, so the code just gets unhappy when it's stalled. If there's a chance of back pressure, a FIFO is helpful.

Future versions of this code should handle this better.

davidthings avatar Jun 22 '20 16:06 davidthings

Hi David,

my problem is that large transfers into a slow sink would clog and overrun the FIFO, too. I am looking forward to the new code with support for backpressure !

Matthias

Mecrisp avatar Jun 22 '20 16:06 Mecrisp

Got it. When the FPGA logic is slower than the connection that would be a requirement.

davidthings avatar Jun 23 '20 13:06 davidthings