summaryrefslogtreecommitdiffstatshomepage
path: root/tests/extmod/machine_spi_rate.py
blob: c65095f22a1a567384896f274027f29f69c714bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Test machine.SPI data transfer rates
import sys


try:
    import time
    from machine import Pin, SPI
except ImportError:
    print("SKIP")
    raise SystemExit

MAX_DELTA_MS = 8

# Configure pins based on the port/board details.
# Values are tuples of (spi_id, sck, mosi, miso)
if "alif" in sys.platform:
    MAX_DELTA_MS = 20
    spi_instances = ((0, None, None, None),)
elif "pyboard" in sys.platform:
    spi_instances = (
        (1, None, None, None),  # "explicit choice of sck/mosi/miso is not implemented"
        (2, None, None, None),
    )
elif "rp2" in sys.platform:
    spi_instances = ((0, Pin(18), Pin(19), Pin(16)),)
elif "esp32" in sys.platform:
    impl = str(sys.implementation)
    if "ESP32C3" in impl or "ESP32C6" in impl:
        spi_instances = ((1, Pin(4), Pin(5), Pin(6)),)
    else:
        spi_instances = ((1, Pin(18), Pin(19), Pin(21)), (2, Pin(18), Pin(19), Pin(21)))
elif "esp8266" in sys.platform:
    MAX_DELTA_MS = 50  # port requires much looser timing requirements
    spi_instances = ((1, None, None, None),)  # explicit pin choice not allowed
else:
    print("Please add support for this test on this platform.")
    raise SystemExit


def get_real_baudrate(spi):
    # Return the 'real' baudrate for a SPI object, from parsing 'print' output
    # i.e.
    # SPI(id=1, baudrate=500000, polarity=0, phase=0, bits=8, firstbit=0, sck=14, mosi=13, miso=12)
    #
    # Note the 'real' rate may be quite different to the requested rate, i.e.
    # on ports where the SPI hardware only supports power of 2 clock dividers.
    #
    # Implementation is somewhat fiddly and inefficient to avoid dependency on
    # 're' module,
    s = str(spi)
    s = s.split("baudrate=")[1].split(",")[0]
    return int(s)


def test_instances():
    print_results = True
    for spi_id, sck, mosi, miso in spi_instances:
        for baudrate in (
            100_000,
            250_000,
            800_000,
            1_000_000,
            2_000_000,
            4_000_000,
            8_000_000,
        ):
            test_spi(
                spi_id,
                sck,
                mosi,
                miso,
                baudrate,
                0,
                0,
                print_results,
            )

        for baudrate in (100_000, 4_000_000):
            # Test the other polarity and phase settings
            for polarity, phase in ((0, 1), (1, 0), (1, 1)):
                test_spi(
                    spi_id,
                    sck,
                    mosi,
                    miso,
                    baudrate,
                    polarity,
                    phase,
                    print_results,
                )

        # Ensure the same test output regardless of how many SPI instances are tested
        print_results = False


wr_short = b"abcdefghijklmnop" * 10
rd_short = bytearray(len(wr_short))

wr_long = wr_short * 20
rd_long = bytearray(len(wr_long))


def test_spi(spi_id, sck, mosi, miso, baudrate, polarity, phase, print_results):
    if sck:
        s = SPI(
            spi_id,
            sck=sck,
            mosi=mosi,
            miso=miso,
            baudrate=baudrate,
            polarity=polarity,
            phase=phase,
        )
    else:
        s = SPI(spi_id, baudrate=baudrate, polarity=polarity, phase=phase)

    # to keep the test runtime down, use shorter buffer for lower baud rate
    wr_buf = wr_long if baudrate > 500_000 else wr_short
    rd_buf = rd_long if baudrate > 500_000 else rd_short

    real_baudrate = get_real_baudrate(s)
    assert real_baudrate
    transfer_time_ms = len(wr_buf) * 8 * 1000 // real_baudrate

    def test_write_readinto():
        s.write_readinto(wr_buf, rd_buf)

    def test_write():
        s.write(wr_buf)

    def test_readinto():
        s.readinto(rd_buf)

    for test_func, name in (
        (test_write_readinto, "write_readinto"),
        (test_write, "write"),
        (test_readinto, "readinto"),
    ):
        t0 = time.ticks_ms()
        test_func()
        transfer_time = time.ticks_diff(time.ticks_ms(), t0)
        t_delta = abs(transfer_time - transfer_time_ms)
        t_ok = t_delta < MAX_DELTA_MS

        if print_results or not t_ok:
            print(
                None if print_results else spi_id,
                baudrate,
                polarity,
                phase,
                name,
                t_ok or t_delta,
            )

    s.deinit()


test_instances()