I'm stress testing the FT245R chip using a basic CPLD to negotiate reading and writing with the chip and PC as the USB host.
Basically I have programmed up the CPLD to read in 8 bit words from the USB host, store these data and then when I have two words stored, request to write them back.
I am testing with realterm. I use the Send tab to send some ASCII characters i.e. 'abcdef'. This works fine with 1 repeat, 10 repeats but at 100 repeats, I get some inconsistent counts. If I push it all the way and use thousands of repeats, something interesting happens - the TXE signal of the FTDI chip goes high and stays high (from the datasheet: "When low, data can be written into the FIFO. When high, do not write data into the FIFO"). Nothing else will happen, the RXD indicator in realterm stays solid.
Is the FTDI chip indicating I can't write because the USB host i.e. realterm is not reading from the FIFO transmit buffer? The only way to exit this state seems to be to close the COM connection... should it be this easy to crash a communication session? How can I mitigate that risk from the CPLD side of things? Perhaps reset the module when it detects TXE is high for too long?
The datasheet is rather unclear but here it is for reference. I'll be really grateful if you can shed some light on what's happening and how to proceed.
Some more information on what's happening, now I've spied on the port messages with 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 ...
Inbetween these writes as you can see above we get a WAIT_ON_MASK request. From what I understand this is an instructino from realterm to the FTDI driver to wait for the FTDI chip to do any of the set wait masks, namely:
IOCTL_SERIAL_SET_WAIT_MASK Mask: RXCHAR CTS DSR RLSD BRK ERR RING
BUT what is happening to me is, after I have sent a huge number of WRITEs, (in amongst these wait messages), I get into a state where I simply get repeated IOCTL_SERIAL_WAIT_ON_MASK messages. This goes on and on, and I cannot stop this unless I close the port (whereupon IOCTL_SERIAL_PURGE messages are sent successfully).
I can hence reopen the port and the connection is fine, i.e. with a smaller burst of writing I get all the data echoed back (as I have configured the CPLD controlling the FTDI chip to do).
IT is probably worth noting also that in this state where after I have sent LOTS of write commands, at the point it starts to hang the FTDI chip indicates no ability to write (i.e. TXE remains high, the datasheet is not clear but I think this means the FIFO write buffer is full). If I disconnect during the long sequence of WAIT messages I note an:
message of usually quite a long length.
CPLD Code (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;