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

Move SPI bit writes to the right clock phase. #31

Merged
merged 1 commit into from
Mar 4, 2024

Conversation

kbsriram
Copy link
Contributor

@kbsriram kbsriram commented Mar 1, 2024

  • Updated all SPI methods that write bits, so they clock the data on the correct clock transition.
  • Added tests with a small logic net simulation to verify bits are read and written correctly.

Technically I think the underlying issue could be resolved without this fix, but I also noticed some issues with the current implementation noted in #20 (comment)

The summary is that all the SPI methods that write bits clock it out on the incorrect clock transition. These examples use the following snippet:

 with adafruit_bitbangio.SPI(clock=self.clock_pin, MOSI=self.copi_pin) as spi:
        spi.try_lock()
        spi.configure(baudrate=100, polarity=polarity, phase=phase, bits=8)
        self._enable_net(1)
        spi.write(b"\x55")
        self._enable_net(0)

Currently this produces this waveform for polarity=0 phase=0 :
write_issue_0_0
The controller clocks out the bit on the rising transition when it's also being read. However the code likely also works most of the time as the sequence in which the pins are updated in the code are just enough to change the data line right before the clock transitions.

With this PR, the waveform now clocks out the bits at the right transition, and hopefully is more reliable.
fixed_0_0

Unfortunately the same situation exists in all modes, and all the write/read-write methods. This is an example when polarity=0 and phase=1 for instance, and data 0x55 and 0xaa:
write_issue_0_1
write_issue_0_1_1

With this PR, the waveform now shows this for polarity=0 and phase=1 for data 0x55 and 0xaa:
fixed_0_1
fixed_0_1_1

(This PR is large-ish though mostly to do with a test setup that seemed useful to sanity check the fiddly state changes, also in the hope it may be handy to check the I2C code later.)

Resolves #20

- Updated all SPI methods that write bits, so they clock the
  data on the correct clock transition.
- Added tests with a small logic net simulation to
  verify bits are read and written correctly.
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool! One naming request. Mind checking the C implementation of bitbangio too? https://github.com/adafruit/circuitpython/blob/main/shared-module/bitbangio/SPI.c I think this code was based on it.

tests/simulator.py Show resolved Hide resolved
@kbsriram
Copy link
Contributor Author

kbsriram commented Mar 2, 2024

Thanks for reviewing @tannewt !

Re: Pin -> DigitalInOut - just wanted to double-check, as the current library code is expecting a Pin in the constructor and internally creates another DigitalInOut from it. So I ended up faking the Pin class implementation so I could pass it into the constructor, and letting it use the real DigitalInOut (which in tests/CPython would come from blinka of course...) Let me know if you had a different idea in mind? Maybe an alternative is when the API changes to passing in a DigitalInOut instead of a Pin, we could update the test fake then?

Re: comparing bitbangio/SPI.c (just for context you're probably aware the native and python implementations started to diverge in terms of how the bits are clocked out in #30)

That said, I think there are some corner cases that appear to need updates in the native code as well, as far as I could tell. For example, with this test function and monitoring the pins:

import board
import bitbangio
import digitalio
import time


def doit():
    time.sleep(3)
    with digitalio.DigitalInOut(board.GP13) as en:
        en.switch_to_output(value=True)
        time.sleep(1)
        with bitbangio.SPI(clock=board.GP15, MOSI=board.GP14) as spi:
            spi.try_lock()
            spi.configure(baudrate=2, polarity=1, phase=0, bits=8)
            en.switch_to_output(value=False)
            spi.write(b"\xaa")
            en.switch_to_output(value=True)

I see this output:
test_1_0_0xaa_full

I think the code implicitly assumes the clock is always in the idle state when the bytes start getting clocked out but for polarity=1 it's not typically the case - and in this situation it ends up essentially missing a clock transition I think, and could cause issues for the first bit. E.g. sigrok correctly identifies the transaction as starting on the enable, as the clock is active at that point. But it doesn't decode the bits.

(The fixed code in this PR for a similar test gives this)
fix_1_0_0xaa

@tannewt
Copy link
Member

tannewt commented Mar 4, 2024

Re: Pin -> DigitalInOut - just wanted to double-check, as the current library code is expecting a Pin in the constructor and internally creates another DigitalInOut from it. So I ended up faking the Pin class implementation so I could pass it into the constructor, and letting it use the real DigitalInOut (which in tests/CPython would come from blinka of course...) Let me know if you had a different idea in mind? Maybe an alternative is when the API changes to passing in a DigitalInOut instead of a Pin, we could update the test fake then?

Ah, right. Let's leave as-is for now then. We'll want to move both over when we do.

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@tannewt tannewt merged commit fb37622 into adafruit:main Mar 4, 2024
1 check passed
@kbsriram kbsriram deleted the fix-phase branch March 5, 2024 03:18
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Mar 22, 2024
Updating https://github.com/adafruit/Adafruit_CircuitPython_AGS02MA to 1.0.8 from 1.0.7:
  > Merge pull request adafruit/Adafruit_CircuitPython_AGS02MA#4 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306 to 2.0.0 from 1.8.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306#39 from makermelissa/master

Updating https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI to 7.1.0 from 7.0.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_ESP32SPI#196 from anecdata/exit
  > Merge pull request adafruit/Adafruit_CircuitPython_ESP32SPI#192 from mMerlin/new-sample
  > Merge pull request adafruit/Adafruit_CircuitPython_ESP32SPI#193 from justmobilize/fix-readme-requirements

Updating https://github.com/adafruit/Adafruit_CircuitPython_FT5336 to 1.0.2 from 1.0.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_FT5336#3 from adafruit/buttons_example

Updating https://github.com/adafruit/Adafruit_CircuitPython_GPS to 3.10.13 from 3.10.12:
  > Merge pull request adafruit/Adafruit_CircuitPython_GPS#102 from jkittner/minutes_fix

Updating https://github.com/adafruit/Adafruit_CircuitPython_HT16K33 to 4.6.9 from 4.6.7:
  > Merge pull request adafruit/Adafruit_CircuitPython_HT16K33#118 from adafruit/matrix_fill_color_swap
  > Merge pull request adafruit/Adafruit_CircuitPython_HT16K33#117 from DJDevon3/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_LTR329_LTR303 to 3.0.6 from 3.0.5:
  > Merge pull request adafruit/Adafruit_CircuitPython_LTR329_LTR303#8 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_MAX31856 to 0.12.0 from 0.11.8:
  > Merge pull request adafruit/Adafruit_CircuitPython_MAX31856#28 from diegosolano/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_MMC56x3 to 1.0.8 from 1.0.7:
  > Merge pull request adafruit/Adafruit_CircuitPython_MMC56x3#4 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_PCA9685 to 3.4.15 from 3.4.14:
  > Merge pull request adafruit/Adafruit_CircuitPython_PCA9685#55 from DJDevon3/WorkingBranch

Updating https://github.com/adafruit/Adafruit_CircuitPython_SI5351 to 1.4.4 from 1.4.3:
  > Merge pull request adafruit/Adafruit_CircuitPython_SI5351#31 from kamocat/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_SSD1306 to 2.12.17 from 2.12.16:
  > Merge pull request adafruit/Adafruit_CircuitPython_SSD1306#85 from waynedyck/fix-pillow-10-errors

Updating https://github.com/adafruit/Adafruit_CircuitPython_TCA8418 to 1.0.11 from 1.0.10:
  > Merge pull request adafruit/Adafruit_CircuitPython_TCA8418#13 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_TSC2007 to 1.1.0 from 1.0.9:
  > Merge pull request adafruit/Adafruit_CircuitPython_TSC2007#6 from makermelissa/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_TT21100 to 1.0.2 from 1.0.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_TT21100#5 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_VL53L4CD to 1.2.2 from 1.2.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_VL53L4CD#15 from FoamyGuy/fix_readme

Updating https://github.com/adafruit/Adafruit_CircuitPython_Wiznet5k to 5.0.9 from 5.0.7:
  > Merge pull request adafruit/Adafruit_CircuitPython_Wiznet5k#148 from pinkavaj/pi-fix-if
  > Merge pull request adafruit/Adafruit_CircuitPython_Wiznet5k#143 from us3r64/fix/socket-recv-timeout

Updating https://github.com/adafruit/Adafruit_CircuitPython_BitbangIO to 1.3.15 from 1.3.14:
  > Merge pull request adafruit/Adafruit_CircuitPython_BitbangIO#32 from kbsriram/i2c-clock-stretching
  > Merge pull request adafruit/Adafruit_CircuitPython_BitbangIO#31 from kbsriram/fix-phase

Updating https://github.com/adafruit/Adafruit_CircuitPython_HID to 6.1.1 from 6.1.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_HID#125 from dhalbert/fix-class-docs

Updating https://github.com/adafruit/Adafruit_CircuitPython_NTP to 3.0.13 from 3.0.12:
  > Merge pull request adafruit/Adafruit_CircuitPython_NTP#31 from anecdata/cm_multi_radio

Updating https://github.com/adafruit/Adafruit_CircuitPython_PyCamera to 1.3.0 from 1.2.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_PyCamera#33 from FoamyGuy/overlay_scale

Updating https://github.com/adafruit/Adafruit_CircuitPython_Requests to 3.2.2 from 3.0.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#170 from DJDevon3/DJDevon3-TwitchAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#169 from DJDevon3/DJDevon3-SteamAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#166 from DJDevon3/GithubAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#165 from DJDevon3/DJDevon3-PremiereLeagueAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#160 from DJDevon3/main
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#164 from DJDevon3/DJDevon3-FitbitAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#163 from DJDevon3/DJDevon3-DiscordAPI
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#162 from DJDevon3/DJDevon3-DiscordActive
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#161 from DJDevon3/MastodonBranch
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#159 from justmobilize/remove-response-close-read-all
  > Merge pull request adafruit/Adafruit_CircuitPython_Requests#156 from bablokb/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_RGBLED to 1.2.0 from 1.1.19:
  > Merge pull request adafruit/Adafruit_CircuitPython_RGBLED#25 from BiffoBear/Error_check_colors

Updating https://github.com/adafruit/Adafruit_CircuitPython_RSA to 1.2.20 from 1.2.19:
  > Merge pull request adafruit/Adafruit_CircuitPython_RSA#40 from FoamyGuy/add_hashlib_req

Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA:
  > Updated download stats for the libraries
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

Successfully merging this pull request may close these issues.

Bitbang SPI phase=1 lowest bit is wrong
2 participants