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;