FT245R transmisión de relleno rellenando, repetidos IOCTL_SERIAL_WAIT_ON_MASK mensajes

0

Estoy realizando pruebas de estrés del chip FT245R utilizando un CPLD básico para negociar la lectura y escritura con el chip y la PC como host USB.

Básicamente, he programado el CPLD para leer en palabras de 8 bits del host USB, almacenar estos datos y luego, cuando tengo dos palabras almacenadas, solicito que se las devuelva.

Estoy probando con realterm. Utilizo la pestaña Enviar para enviar algunos caracteres ASCII, es decir, 'abcdef'. Esto funciona bien con 1 repetición, 10 repeticiones pero a 100 repeticiones, obtengo algunos conteos inconsistentes. Si lo presiono todo el camino y uso miles de repeticiones, ocurre algo interesante: la señal TXE del chip FTDI sube y se mantiene alta (de la hoja de datos: "Cuando es baja, los datos se pueden escribir en el FIFO. Cuando es alta, haga no escribir datos en el FIFO "). Nada más sucederá, el indicador RXD en el realterm permanece sólido.

¿El chip FTDI indica que no puedo escribir porque el host USB, es decir, realterm no está leyendo desde el búfer de transmisión FIFO? La única forma de salir de este estado parece ser cerrar la conexión COM ... ¿debería ser tan fácil interrumpir una sesión de comunicación? ¿Cómo puedo mitigar ese riesgo desde el lado CPLD de las cosas? ¿Quizás reiniciar el módulo cuando detecte que la TXE es alta durante demasiado tiempo?

La hoja de datos no está muy clara, pero aquí está como referencia. Estaré realmente agradecido si puede arrojar algo de luz sobre lo que está sucediendo y cómo proceder.

-

Más información sobre lo que está sucediendo, ahora he espiado los mensajes del puerto con Portmon:

IRP_MJ_WRITE Length: 12
IRP_MJ_WRITE Length: 90
IRP_MJ_WRITE Length: 180
IRP_MJ_WRITE Length: 198
IRP_MJ_WRITE Length: 204
IOCTL_SERIAL_WAIT_ON_MASK SUCCESS
IRP_MJ_WRITE Length: 198
IRP_MJ_WRITE Length: 204
...

Entre estas escrituras, como puede ver arriba, obtenemos una solicitud WAIT_ON_MASK. Por lo que entiendo, esto es una instrucción del realterm al controlador FTDI para esperar a que el chip FTDI realice cualquiera de las máscaras de espera establecidas, a saber:

IOCTL_SERIAL_SET_WAIT_MASK Mask: RXCHAR CTS DSR RLSD BRK ERR RING

PERO lo que me está sucediendo es que, después de haber enviado una gran cantidad de ESCRITURAS (entre estos mensajes de espera), entro en un estado en el que simplemente recibo repetidos mensajes IOCTL_SERIAL_WAIT_ON_MASK. Esto sigue y sigue, y no puedo detenerlo a menos que cierre el puerto (después de lo cual los mensajes IOCTL_SERIAL_PURGE se envían correctamente).

Por lo tanto, puedo volver a abrir el puerto y la conexión está bien, es decir, con una ráfaga de escritura más pequeña, recibo todos los datos de vuelta (como he configurado el CPLD que controla el chip FTDI).

Es probable que también valga la pena tener en cuenta que en este estado en el que después de haber enviado LOTES de comandos de escritura, en el momento en que comienza a colgarse, el chip FTDI indica que no hay capacidad para escribir (es decir, TXE sigue siendo alto, la hoja de datos no está clara pero Creo que esto significa que el buffer de escritura FIFO está lleno). Si me desconecto durante la larga secuencia de mensajes de ESPERA, observo un:

IRP_MJ_WRITE CANCELLED

mensaje de longitud generalmente bastante larga.

-

Hoja de datos: enlace

Código CPLD (VHDL):

----------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY main IS
    PORT ( clk : IN STD_LOGIC; -- 8 MHz, 125 ns osc
              rst : IN STD_LOGIC; -- reset (ACTIVE LOW)
              usb0_rxf_led : OUT  STD_LOGIC; -- usb activity, illuminate with logic 0
              usb1_rxf_led : OUT STD_LOGIC; -- unused, illuminate with logic 0
           max_temp_log_led : OUT  STD_LOGIC; -- overtemp, illuminate with logic 0
              one_wire_bus : INOUT STD_LOGIC; -- 1-wire bus with thermometer
              -- usb ctrl
              usb_rd : OUT STD_LOGIC;
              usb_wr : OUT STD_LOGIC;
              usb_rxf : IN STD_LOGIC;
              usb_txe : IN STD_LOGIC;
              -- usb databus ports
              usb_db0 : INOUT STD_LOGIC;
              usb_db1 : INOUT STD_LOGIC;
              usb_db2 : INOUT STD_LOGIC;
              usb_db3 : INOUT STD_LOGIC;
              usb_db4 : INOUT STD_LOGIC;
              usb_db5 : INOUT STD_LOGIC;
              usb_db6 : INOUT STD_LOGIC;
              usb_db7 : INOUT STD_LOGIC;
              -- plel databus ports
              plel_db0 : INOUT STD_LOGIC;
              plel_db1 : INOUT STD_LOGIC;
              plel_db2 : INOUT STD_LOGIC;
              plel_db3 : INOUT STD_LOGIC;
              plel_db4 : INOUT STD_LOGIC;
              plel_db5 : INOUT STD_LOGIC;
              plel_db6 : INOUT STD_LOGIC;
              plel_db7 : INOUT STD_LOGIC;
              plel_db8 : INOUT STD_LOGIC;
              plel_db9 : INOUT STD_LOGIC;
              plel_db10 : INOUT STD_LOGIC;
              plel_db11 : INOUT STD_LOGIC;
              plel_db12 : INOUT STD_LOGIC;
              plel_db13 : INOUT STD_LOGIC;
              plel_db14 : INOUT STD_LOGIC;
              plel_db15 : INOUT STD_LOGIC
      );

END main;

ARCHITECTURE Behavioral OF main IS

    -- define states (in terms of ftdi chip, i.e. reading from usb host, writing to usb host)
    TYPE usb_state_type IS
        (idle, read_ready, reading, read_done, write_req, writing, write_complete, inout_initialise);
    SIGNAL next_state   : usb_state_type;
    -- constants
    CONSTANT fsm_delay_const        : INTEGER := 1; -- 1/const = delay factor
    CONSTANT write_timeout_const    : INTEGER := 80000; -- clock cycles (125 ns) : 80,000 = 10 ms
    --signals
    SIGNAL read_store           : STD_LOGIC_VECTOR (15 DOWNTO 0) := "0000000000000000";
    SIGNAL write_waiting        : STD_LOGIC := '0';
    SIGNAL counter              : INTEGER RANGE fsm_delay_const DOWNTO 0;
    SIGNAL write_wait_counter   : INTEGER RANGE write_timeout_const DOWNTO 0;
    SIGNAL fsm_enable           : STD_LOGIC;
    SIGNAL word_half_rd         : STD_LOGIC;
    SIGNAL word_half_wr         : STD_LOGIC;

    SIGNAL usb_rxf_d0               : STD_LOGIC;
    SIGNAL usb_rxf_d1               : STD_LOGIC;
    SIGNAL usb_txe_d0               : STD_LOGIC;
    SIGNAL usb_txe_d1               : STD_LOGIC;
BEGIN

    run:
    PROCESS(clk)
    BEGIN

        IF RISING_EDGE(clk) THEN

            IF(rst = '0') THEN

                -- Reset states
                next_state          <= idle;
                usb_rd                  <= '1';
                usb_wr                  <= '0';
                usb_db0                 <= 'Z';
                usb_db1                 <= 'Z';
                usb_db2                 <= 'Z';
                usb_db3                 <= 'Z';
                usb_db4                 <= 'Z';
                usb_db5                 <= 'Z';
                usb_db6                 <= 'Z';
                usb_db7                 <= 'Z';
                plel_db0            <= 'Z';
                plel_db1            <= 'Z';
                plel_db2            <= 'Z';
                plel_db3            <= 'Z';
                plel_db4            <= 'Z';
                plel_db5            <= 'Z';
                plel_db6            <= 'Z';
                plel_db7            <= 'Z';
                plel_db8            <= 'Z';
                plel_db9            <= 'Z';
                plel_db10           <= 'Z';
                plel_db11           <= 'Z';
                plel_db12           <= 'Z';
                plel_db13           <= 'Z';
                plel_db14           <= 'Z';
                plel_db15           <= 'Z';
                one_wire_bus        <= 'Z';
                max_temp_log_led    <= '1';
                --usb1_rxf_led      <= '1';
                write_waiting       <= '0';
                word_half_rd        <= '0';
                word_half_wr        <= '0';

            ELSIF fsm_enable = '1' THEN

                usb_rd <= '1';
                usb_wr <= '0';

                CASE next_state IS

                    WHEN idle =>

                        IF (write_waiting = '1') THEN
                            next_state <= write_req;
                        ELSIF (usb_rxf_d1 = '0') THEN
                            next_state <= read_ready;
                        ELSE
                            next_state <= idle;
                        END IF;

                    WHEN read_ready =>

                        -- rxf low, data available. set rd# low
                        usb_rd <= '0';
                        next_state <= reading;

                    WHEN reading => 

                        -- valid data within 20-50 ns. one period is 125 ns so valid data should definitely be
                        -- available by now.
                        -- read and store with valid data. set rd# high again
                        usb_rd <= '1';

                        CASE word_half_rd IS
                            WHEN '0' =>
                                read_store(0) <= usb_db0;
                                read_store(1) <= usb_db1;
                                read_store(2) <= usb_db2;
                                read_store(3) <= usb_db3;
                                read_store(4) <= usb_db4;
                                read_store(5) <= usb_db5;
                                read_store(6) <= usb_db6;
                                read_store(7) <= usb_db7;
                                word_half_rd <= '1';
                            WHEN '1' =>
                                read_store(8) <= usb_db0;
                                read_store(9) <= usb_db1;
                                read_store(10) <= usb_db2;
                                read_store(11) <= usb_db3;
                                read_store(12) <= usb_db4;
                                read_store(13) <= usb_db5;
                                read_store(14) <= usb_db6;
                                read_store(15) <= usb_db7;
                                word_half_rd <= '0';
                                write_waiting <= '1';
                            WHEN OTHERS =>
                        END CASE;       

                        next_state <= read_done;

                    WHEN read_done =>

                        next_state <= idle;

                    WHEN write_req =>
                        IF (usb_txe_d1 = '0') THEN
                            -- pull wr high, with valid data on d0-7
                            usb_wr <= '1';

                            CASE word_half_wr IS
                                WHEN '0' =>
                                    usb_db0 <= read_store(0);
                                    usb_db1 <= read_store(1);
                                    usb_db2 <= read_store(2);
                                    usb_db3 <= read_store(3);
                                    usb_db4 <= read_store(4);
                                    usb_db5 <= read_store(5);
                                    usb_db6 <= read_store(6);
                                    usb_db7 <= read_store(7);
                                    word_half_wr <= '1';
                                WHEN '1' =>
                                    usb_db0 <= read_store(8);
                                    usb_db1 <= read_store(9);
                                    usb_db2 <= read_store(10);
                                    usb_db3 <= read_store(11);
                                    usb_db4 <= read_store(12);
                                    usb_db5 <= read_store(13);
                                    usb_db6 <= read_store(14);
                                    usb_db7 <= read_store(15);
                                    word_half_wr <= '0';
                                    write_waiting <= '0';
                                WHEN OTHERS =>
                            END CASE;
                            next_state <= writing;

                        ELSIF (write_wait_counter > 0) THEN
                            -- cannot write this time! should we timeout?
                            write_wait_counter <= write_wait_counter - 1;
                            next_state <= write_req;

                        ELSE
                            -- cannot write, we have waited long enough. abort the write.
                            write_wait_counter <= write_timeout_const;
                            write_waiting <= '0';
                            next_state <= inout_initialise;
                        END IF;

                    WHEN writing =>

                        -- drop wr low, data is written to FIFO when txe drops
                        usb_wr <= '0';
                        next_state <= write_complete;

                    WHEN write_complete =>

                        -- stay in this state until ft254r has accepted the write into fifo
                        next_state <= inout_initialise;

                    WHEN inout_initialise =>
                        usb_db0 <= 'Z';
                        usb_db1 <= 'Z';
                        usb_db2 <= 'Z';
                        usb_db3 <= 'Z';
                        usb_db4 <= 'Z';
                        usb_db5 <= 'Z';
                        usb_db6 <= 'Z';
                        usb_db7 <= 'Z';
                        next_state <= idle;

                    WHEN OTHERS =>
                        next_state <= idle;

                END CASE;

            END IF; -- enable
        END IF; -- clock
    END PROCESS;


    delay_proc:
    PROCESS (clk)
    BEGIN
        IF RISING_EDGE(clk) THEN

            IF rst = '0' THEN
                --DO RESET LOGIC HERE...
                fsm_enable <= '0';
                counter <= fsm_delay_const;
            ELSE

                fsm_enable <= '0';

                IF counter = 0 THEN
                    fsm_enable <= '1';
                    counter <= fsm_delay_const;
                ELSE
                    counter <= counter - 1;
                END IF;             

            END IF; --ELSE IF rst = '1' THEN

        END IF; --IF RISING_EDGE(clk) THEN

    END PROCESS;


    activity_led:
    PROCESS (usb_rxf)
    BEGIN
        usb0_rxf_led <= usb_rxf;
        usb1_rxf_led <= usb_txe;
    END PROCESS;

    anti_meta:
    PROCESS (clk)
    BEGIN
        IF RISING_EDGE(clk) THEN
            IF rst = '0' THEN
                usb_rxf_d0 <= '1';
                usb_rxf_d1 <= '1';
                usb_txe_d0 <= '1';
                usb_txe_d1 <= '1';
            ELSE
                usb_rxf_d0 <= usb_rxf;
                usb_rxf_d1 <= usb_rxf_d0;
                usb_txe_d0 <= usb_txe;
                usb_txe_d1 <= usb_txe_d0;
            END IF;
        END IF;
    END PROCESS;

END Behavioral;
    
pregunta deed02392

1 respuesta

0

Sí, de hecho está llenando el búfer de transmisión de 256 bytes del FT245R.

Recuerde, el USB es fundamentalmente un protocolo de comunicaciones semidúplex. Si envía cientos de bytes a un dispositivo en una ráfaga, no hay oportunidad de recibir bytes desde el dispositivo hasta que finalice la ráfaga.

Su CPLD no tiene problemas para aceptar los datos del búfer de recepción de 128 bytes, lo que evita que se llene, al menos hasta que se llene el búfer de transmisión. Pero con una capacidad de transmisión total de 256 + 2 bytes de almacenamiento en búfer, es probable que cualquier ráfaga de más de 258 bytes falle.

Hay algo de "aleatoriedad" en los resultados con cientos de bytes debido al hecho de que Windows sondea el estado del dispositivo cada 1-2 ms. Si sucede sondear el dispositivo en medio de una gran ráfaga, antes de que se llene el FIFO, drenará los datos disponibles de la FIFO de transmisión, dejando espacio para otros 258 bytes antes de que falle. Pero a una velocidad de transferencia máxima de 1 MBps, cualquier transferencia de más de unos 500 bytes está garantizada para fallar.

La conclusión es que, si necesita transferir grandes cantidades de datos en ráfagas, debe tener suficiente memoria para almacenar en búfer esas ráfagas fuera del FT245R.

Si puede usar su CPLD para configurar un FIFO de datos de 2 KB (tal vez utilizando un chip externo ), Su prueba de estrés funcionará perfectamente con cualquier cantidad de datos.

    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas