Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPI reads all 1's (0xFF's) off of FT2232H #383

Open
nickhdc33 opened this issue Sep 13, 2024 · 14 comments
Open

SPI reads all 1's (0xFF's) off of FT2232H #383

nickhdc33 opened this issue Sep 13, 2024 · 14 comments

Comments

@nickhdc33
Copy link

nickhdc33 commented Sep 13, 2024

Hi there, we are interfacing the FT2232H with a slave device using pyftdi, using SPI for communication. We can write to the device, and it responds correctly on the logic analyzer. However, the return value from pyftdi is always 0xFFFF, as though it is not reading the MISO. We confirmed that MISO is wired to the correct pin on ADBUS.

The read sequence is <read byte 1> <read byte 2>. We have tried simultaneous read/write as well as separate write/read cycles, reading extra bytes at the end, trying to extend the CS cycle, etc, but always read 1's back, despite correct sequences on the logic analyzer. However, if we turn on loopback, we can see the bytes we're writing out in the read buffer.

Attached is a screen capture of a typical read sequence of register 0:

  • It's SPI mode 0 (write on falling edge, sample on rising edge).
  • The signals shown are /CS, MISO, SCLK, and MOSI from top to bottom.
  • The MOSI commands are all 0's:
    <0x00> (read command) <0x00> (register 0). <reading MSB> <reading LSB>
  • MISO shows the correct return value of 0x0049:
    <writing cmd> <writing register> <0x00> (MSB) <0x49> (LSB)
    But we still see 0xFFFF when pyftdi returns.

image

Any thoughts on what could be going wrong, or other ways to debug why this is happening? Thanks!

Code example:

from pyftdi.ftdi import Ftdi
Ftdi.show_devices()
from pyftdi.spi import SpiController
import time

spi = SpiController(cs_count=1)
spi.configure(f"ftdi://ftdi:2232h/1")
slave = spi.get_port(cs=0,
                     freq=1e4,
                     mode=0)

gpio = spi.get_gpio()
# Enable all GPIO pins not used for SPI for digital output.
gpio.set_direction(0xfff0, 0xfff0)
# Reset chip
reset_gpio_bitfield = 0x0000
gpio.write(reset_gpio_bitfield)
end_reset_gpio_bitfield = 0x1000
gpio.write(end_reset_gpio_bitfield)

# Read addr 0.
cmd_buf = b'\x00\x00'
print(f'sending: {cmd_buf}')
read_buf = slave.exchange(cmd_buf,
                           readlen=2,
                           duplex=False)
print(f'duplex False: {read_buf}')

cmd_buf = b'\x00\x00\x00\x00'
print(f'sending: {cmd_buf}')
read_buf = slave.exchange(cmd_buf,
                           readlen=4,
                           duplex=True)
print(f'duplex True: {read_buf}')
@eblot
Copy link
Owner

eblot commented Sep 14, 2024

We confirmed that MISO is wired to the correct pin on ADBUS.
Do you mean AD2 ?

Things you can do:

  1. check the signals as analog values, to be sure they match the expected levels. What your digital analyzer discretizes may not be what the FTDI chip actually "sees", both in voltage and time domains. The frequency seems quite low, but it is always better to close this door first
  2. simplify your code: you do not need to configure the GPIO pins if you do not plan to use GPIOs for now. See the read_jedec_id example. For debugging this issue, it should be as simple as
    port = SpiController().get_port(0, freq=1e4, mode=0)
    out = port.exchange([0, 0], 2)
    print(out)
  3. try to use alternative SPI modes
  4. Try to use GPIO mode (not SPI mode) to ensure that AD2 reads 0 when this pin is tied to the ground.

@asylv-str
Copy link

asylv-str commented Sep 16, 2024

We are having a similar problem, using similar code, although we must admit we have not yet probed the device. We are using an FT2232 Mini Module to try and communicate with two transceiver devices.

from pyftdi.ftdi import Ftdi
from pyftdi.spi import SpiController, SpiPort

# devices used for configuration
device_a = "ftdi://ftdi:2232/1"
device_b = "ftdi://ftdi:2232/2"
# Documentation for device states 20 MHz is max
# 20 MHz will result in 25% error of 15 MHz for max, so just using 15 MHz
max_freq = 15e6  
freq = 10e6

spi_a_cntrl = SpiController()
spi_b_cntrl = SpiController()
spi_a_cntrl.configure(device_a, frequency=max_freq, debug=True)
spi_b_cntrl.configure(device_b, frequency=max_freq, debug=True)
receiver_a = spi_a_cntrl.get_port(cs=0)
receiver_a.set_frequency(freq)
receiver_a.set_mode(0)
receiver_b = spi_b_cntrl.get_port(cs=0)
receiver_b.set_frequency(freq)
receiver_b.set_mode(0)
 
cmd = b'\x00\x00\x00\x00\x00\x00' # 6 bytes
spi_1_response = receiver_a.exchange(cmd, duplex=True)
spi_2_response = receiver_b.exchange(cmd, duplex=True)

print(spi_1_response)
print(spi_2_response)
  • We are getting a six byte response on each device of all F's.
  • Documentation for these devices state SPI mode 0, and we are not using any GPIO pins.
  • Using a debugger, pyftdi is reporting that the device is configured correctly.

I know this comment does not provide new or helpful information to the original post, but thought we should share as the issue is very similar to our own. Could this potentially be a bug with pyftdi?

@nickhdc33
Copy link
Author

Thanks for the help in brainstorming ideas. It turns out that we needed to initialize the second MPSSE interface in order for the receive buffer to start working. Adding this code (and then doing nothing with the second interface afterward) solved our problem.

# Open up the 2nd MPSSE interface on the FTDI chip.
# We've found experimentally that the 1st MPSSE interface will not 
# work until the 2nd is initialized.
# We won't do anything with this interface after this.
b = Ftdi.create_from_url("ftdi://ftdi:2232h/2")
b.open_mpsse_from_url("ftdi://ftdi:2232h/2", 0xFFFB, 0xFFFB, 1e4, 127)

@asylv-str
Copy link

Hello, as we are still having issues, I am curious as to why this solved your problem. Why did you go the route of using .create_from_url() and .open_mpsse_from_url() methods? Shouldn't configuring another SpiController work just as well, as we have done above, as it calls the .open_mpsse_from_url() command?
We are always configuring both SpiControllers on the device, but still not getting anything but F's back from the receive buffer.
Would appreciate any help on this.

@nickhdc33
Copy link
Author

Unfortunately, we just stumbled across the "solution". After we initially couldn't get pyftdi to work, we reimplemented the protocol in C using the FT2X libraries from FTDI directly. When we tested that, we were getting the same issues (everything else works, but we read all FFs). So we were pretty sure this wasn't an issue with pyftdi (we didn't look under the hood, but assumed they use the same or similar drivers from FTDI), but instead an FTDI initialization issue.

After scouring all the FTDI literature and examples, we were just methodically measuring and initializing as much of the part as we could when we stumbled upon this in the C version. Then we back-ported it to pyftdi, and it worked there as well. There is no significance to the methods that we used to initialize it. I believe that it will work with the initialization method you use, but we haven't tested that version yet to say it conclusively.

We ended up making no modifications to the original pyftdi code above except adding those lines in my later post. We needed the GPIO for some discrete signals for reset, standby, SPI control selection, etc, so they had to stay. Luckily we found this, since we were 1-2 days away from throwing away the FTDI part and instead driving the chip directly from an FGPA, which would have been a bit more of a hassle in writing our own drivers - not terrible on the SPI protocol side, but a bit of a chore to manage all the initialization, buffering, comms with a PC, etc.

As to your problem - I don't have too much more insight, except noting that it could be a problem on the transceiver side. Did you hook up a logic analyzer to see if your system under test is driving the MISO line? If you have a pull-up and the chip is not driving the line, then you would see all 1's, which would be the correct behavior from the FTDI device, and you'd have to start debugging why the SUT is not responding.

@nickhdc33 nickhdc33 changed the title SPI reads all 1's (0xFF's) off of FT232H SPI reads all 1's (0xFF's) off of FT2232H Sep 17, 2024
@nickhdc33
Copy link
Author

Also, thanks again @eblot for your thoughtful debug steps! Great library!

@eblot
Copy link
Owner

eblot commented Sep 19, 2024

Also, thanks again @eblot for your thoughtful debug steps! Great library!

Thanks @nickhdc33

Re-opening this issue for now, as I do not understand why this workaround is needed...

@asylv-str
Copy link

asylv-str commented Sep 19, 2024

Following the same steps as @nickhdc33, I was unable to get a response (other than all F's), so I am not sure it is a universal workaround/fix.

@eblot eblot reopened this Sep 19, 2024
@eblot
Copy link
Owner

eblot commented Sep 19, 2024

BTW, what are the reference(s) of the SPI slave devices?

@asylv-str
Copy link

With the FTDI2232 Mini Module, we are using a 10 MHz reference clock for both the slave devices. Is this what you are asking?

@eblot
Copy link
Owner

eblot commented Sep 19, 2024

Not really, the type/kind of SPI device you're trying to communicate with: FTDI being the master/host, what are the (slave) devices?

@asylv-str
Copy link

A couple of transceiver modules, found here.

@asylv-str
Copy link

It turns out that the device we linked does not have wiring as specified in the pyftdi documentation. We only realized this once the manufacturer finally gave us schematics for the device. To get around this, we had to use GPIO pins to control which slave device to communicate with, and we were finally able to get status back.
Sorry for any confusion on our part. We thought this issue was very similar to our own.

@H3ndrik-Jan
Copy link

Thanks for the help in brainstorming ideas. It turns out that we needed to initialize the second MPSSE interface in order for the receive buffer to start working. Adding this code (and then doing nothing with the second interface afterward) solved our problem.

# Open up the 2nd MPSSE interface on the FTDI chip.
# We've found experimentally that the 1st MPSSE interface will not 
# work until the 2nd is initialized.
# We won't do anything with this interface after this.
b = Ftdi.create_from_url("ftdi://ftdi:2232h/2")
b.open_mpsse_from_url("ftdi://ftdi:2232h/2", 0xFFFB, 0xFFFB, 1e4, 127)

Thanks, I can confirm this also fixed the issue of receiving all 1's for me. I was also using a FT2232H and probing the SPI lines, the MISO line was always actively pulled high by the FTDI device. Using this code to initialize the second interface, I can now also read data from the MISO line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants