Skip to content

Commit 6a028e7

Browse files
nkpro2000srbessman
authored andcommitted
Add test_spi
1 parent 6518484 commit 6a028e7

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

tests/test_spi.py

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
"""Tests for pslab.bus.spi.
2+
3+
When integration testing, the PSLab's logic analyzer and PWM output are used to
4+
verify the function of the SPI bus. Before running the integration tests, connect:
5+
SCK -> LA1
6+
SDO -> LA2
7+
SDI -> SQ1 and LA4
8+
SPI.CS -> LA3
9+
"""
10+
11+
import pytest
12+
import re
13+
from numpy import ndarray
14+
15+
from pslab.bus.spi import SPIMaster, SPISlave
16+
from pslab.instrument.logic_analyzer import LogicAnalyzer
17+
from pslab.instrument.waveform_generator import PWMGenerator
18+
from pslab.serial_handler import SerialHandler, MockHandler
19+
20+
SPI_SUPPORTED_DEVICES = [
21+
# "PSLab vMOCK", # Uncomment after adding recording json files.
22+
"PSLab V6",
23+
]
24+
25+
WRITE_DATA8 = 0b10100101
26+
WRITE_DATA16 = 0xAA55
27+
SCK = "LA1"
28+
SDO = "LA2"
29+
SDI = ["LA4", "SQ1"]
30+
CS = "LA3"
31+
SPIMaster._primary_prescaler = PPRE = 0
32+
SPIMaster._secondary_prescaler = SPRE = 0
33+
PWM_FERQUENCY = SPIMaster._frequency * 2 / 3
34+
MICROSECONDS = 1e-6
35+
RELTOL = 0.05
36+
# Number of expected logic level changes.
37+
CS_START = 1
38+
CS_STOP = 1
39+
SCK_WRITE8 = 16
40+
SCK_WRITE16 = 2 * 16
41+
SDO_WRITE_DATA8 = 8
42+
SDO_WRITE_DATA16 = 16
43+
44+
45+
@pytest.fixture
46+
def master(handler: SerialHandler) -> SPIMaster:
47+
if handler.version not in SPI_SUPPORTED_DEVICES:
48+
pytest.skip("SPI not supported by this device.")
49+
handler._logging = True
50+
spi_master = SPIMaster(device=handler)
51+
yield spi_master
52+
spi_master.set_parameters()
53+
54+
55+
@pytest.fixture
56+
def slave(handler: SerialHandler) -> SPISlave:
57+
if handler.version not in SPI_SUPPORTED_DEVICES:
58+
pytest.skip("SPI not supported by this device.")
59+
handler._logging = True
60+
return SPISlave(device=handler)
61+
62+
63+
@pytest.fixture
64+
def la(handler: SerialHandler) -> LogicAnalyzer:
65+
if not isinstance(handler, MockHandler):
66+
pwm = PWMGenerator(handler)
67+
pwm.generate(SDI[1], PWM_FERQUENCY, 0.5)
68+
handler._logging = True
69+
return LogicAnalyzer(handler)
70+
71+
72+
def verify_value(
73+
value: int,
74+
sck_timestamps: ndarray,
75+
sdi_initstate: int,
76+
sdi_timestamps: ndarray,
77+
smp: int = 0,
78+
):
79+
sck_ts = sck_timestamps[smp::2]
80+
pwm_half_period = ((1 / PWM_FERQUENCY) * 1e6) / 2 # microsecond
81+
82+
pattern = ""
83+
for t in sck_ts:
84+
d, m = divmod(t - sdi_timestamps[0], pwm_half_period)
85+
if m == pytest.approx(0, abs=0.1) or m == pytest.approx(pwm_half_period, abs=0.1):
86+
pattern += "[0,1]"
87+
elif d % 2:
88+
pattern += "1" if sdi_initstate else "0"
89+
else:
90+
pattern += "0" if sdi_initstate else "1"
91+
92+
pattern = re.compile(pattern)
93+
bits = len(sck_ts)
94+
value = bin(value)[2:].zfill(bits)
95+
96+
return bool(pattern.match(value))
97+
98+
99+
def test_set_parameter_frequency(la: LogicAnalyzer, master: SPIMaster, slave: SPISlave):
100+
# frequency 166666.66666666666
101+
ppre = 0
102+
spre = 2
103+
master.set_parameters(primary_prescaler=ppre, secondary_prescaler=spre)
104+
la.capture(1, block=False)
105+
slave.write8(0)
106+
la.stop()
107+
(sck,) = la.fetch_data()
108+
write_start = sck[0]
109+
write_stop = sck[-2] # Output data on rising edge only (in mode 0)
110+
start_to_stop = 7
111+
period = (write_stop - write_start) / start_to_stop
112+
assert (period * MICROSECONDS) ** -1 == pytest.approx(master._frequency, rel=RELTOL)
113+
114+
115+
@pytest.mark.parametrize("ckp", [0, 1])
116+
def test_set_parameter_clock_polarity(
117+
la: LogicAnalyzer, master: SPIMaster, slave: SPISlave, ckp: int
118+
):
119+
master.set_parameters(CKP=ckp)
120+
assert la.get_states()[SCK] == bool(ckp)
121+
122+
123+
@pytest.mark.parametrize("cke", [0, 1])
124+
def test_set_parameter_clock_edge(
125+
la: LogicAnalyzer, master: SPIMaster, slave: SPISlave, cke: int
126+
):
127+
master.set_parameters(CKE=cke)
128+
la.capture(2, block=False)
129+
slave.write8(WRITE_DATA8)
130+
la.stop()
131+
(sck, sdo) = la.fetch_data()
132+
idle_to_active = sck[0]
133+
first_bit = sdo[0]
134+
# Serial output data changes on transition
135+
# {0: from Idle clock state to active state (first event before data change),
136+
# 1: from active clock state to Idle state (data change before first event)}.
137+
assert first_bit < idle_to_active == bool(cke)
138+
139+
140+
@pytest.mark.parametrize("smp", [0, 1])
141+
def test_set_parameter_smp(
142+
la: LogicAnalyzer, master: SPIMaster, slave: SPISlave, pwm: PWMGenerator, smp: int
143+
):
144+
master.set_parameters(SMP=smp)
145+
la.capture([SCK, SDI[0]], block=False)
146+
value = slave.read8()
147+
la.stop()
148+
(sck, sdi) = la.fetch_data()
149+
sdi_initstate = la.get_initial_states()[SDI[0]]
150+
151+
assert verify_value(value, sck, sdi_initstate, sdi, smp)
152+
153+
154+
def test_chip_select(la: LogicAnalyzer, slave: SPISlave):
155+
assert la.get_states()[CS]
156+
157+
la.capture(CS, block=False)
158+
slave._start()
159+
slave._stop()
160+
la.stop()
161+
(cs,) = la.fetch_data()
162+
assert len(cs) == (CS_START + CS_STOP)
163+
164+
165+
def test_write8(la: LogicAnalyzer, slave: SPISlave):
166+
la.capture(3, block=False)
167+
slave.write8(WRITE_DATA8)
168+
la.stop()
169+
(sck, sdo, cs) = la.fetch_data()
170+
171+
assert len(cs) == (CS_START + CS_STOP)
172+
assert len(sck) == SCK_WRITE8
173+
assert len(sdo) == SDO_WRITE_DATA8
174+
175+
176+
def test_write16(la: LogicAnalyzer, slave: SPISlave):
177+
la.capture(3, block=False)
178+
slave.write16(WRITE_DATA16)
179+
la.stop()
180+
(sck, sdo, cs) = la.fetch_data()
181+
182+
assert len(cs) == (CS_START + CS_STOP)
183+
assert len(sck) == SCK_WRITE16
184+
assert len(sdo) == SDO_WRITE_DATA16
185+
186+
187+
def test_read8(la: LogicAnalyzer, slave: SPISlave):
188+
la.capture(4, block=False)
189+
value = slave.read8()
190+
la.stop()
191+
(sck, sdo, cs, sdi) = la.fetch_data()
192+
sdi_initstate = la.get_initial_states()[SDI[0]]
193+
194+
assert len(cs) == (CS_START + CS_STOP)
195+
assert len(sck) == SCK_WRITE8
196+
assert len(sdo) == 0
197+
assert verify_value(value, sck, sdi_initstate, sdi)
198+
199+
200+
def test_read16(la: LogicAnalyzer, slave: SPISlave):
201+
la.capture(4, block=False)
202+
value = slave.read16()
203+
la.stop()
204+
(sck, sdo, cs, sdi) = la.fetch_data()
205+
sdi_initstate = la.get_initial_states()[SDI[0]]
206+
207+
assert len(cs) == (CS_START + CS_STOP)
208+
assert len(sck) == SCK_WRITE16
209+
assert len(sdo) == 0
210+
assert verify_value(value, sck, sdi_initstate, sdi)
211+
212+
213+
def test_transfer8(la: LogicAnalyzer, slave: SPISlave):
214+
la.capture(4, block=False)
215+
value = slave.transfer8(WRITE_DATA8)
216+
la.stop()
217+
(sck, sdo, cs, sdi) = la.fetch_data()
218+
sdi_initstate = la.get_initial_states()[SDI[0]]
219+
220+
assert len(cs) == (CS_START + CS_STOP)
221+
assert len(sck) == SCK_WRITE8
222+
assert len(sdo) == SDO_WRITE_DATA8
223+
assert verify_value(value, sck, sdi_initstate, sdi)
224+
225+
226+
def test_transfer16(la: LogicAnalyzer, slave: SPISlave):
227+
la.capture(4, block=False)
228+
value = slave.transfer16(WRITE_DATA16)
229+
la.stop()
230+
(sck, sdo, cs, sdi) = la.fetch_data()
231+
sdi_initstate = la.get_initial_states()[SDI[0]]
232+
233+
assert len(cs) == (CS_START + CS_STOP)
234+
assert len(sck) == SCK_WRITE16
235+
assert len(sdo) == SDO_WRITE_DATA16
236+
assert verify_value(value, sck, sdi_initstate, sdi)
237+
238+
239+
def test_write8_bulk(la: LogicAnalyzer, slave: SPISlave):
240+
la.capture(3, block=False)
241+
slave.write8_bulk([WRITE_DATA8, WRITE_DATA8])
242+
la.stop()
243+
(sck, sdo, cs) = la.fetch_data()
244+
245+
assert len(cs) == (CS_START + CS_STOP)
246+
assert len(sck) == SCK_WRITE8 + SCK_WRITE8
247+
assert len(sdo) == SDO_WRITE_DATA8 + SDO_WRITE_DATA16
248+
249+
250+
def test_write16_bulk(la: LogicAnalyzer, slave: SPISlave):
251+
la.capture(3, block=False)
252+
slave.write16_bulk([WRITE_DATA16, WRITE_DATA16])
253+
la.stop()
254+
(sck, sdo, cs) = la.fetch_data()
255+
256+
assert len(cs) == (CS_START + CS_STOP)
257+
assert len(sck) == SCK_WRITE16 + SCK_WRITE16
258+
assert len(sdo) == SDO_WRITE_DATA16 + SDO_WRITE_DATA16
259+
260+
261+
def test_read8_bulk(la: LogicAnalyzer, slave: SPISlave):
262+
la.capture(4, block=False)
263+
value = slave.read8_bulk(2)
264+
la.stop()
265+
(sck, sdo, cs, sdi) = la.fetch_data()
266+
sdi_initstate = la.get_initial_states()[SDI[0]]
267+
268+
assert len(cs) == (CS_START + CS_STOP)
269+
assert len(sck) == SCK_WRITE8 + SCK_WRITE8
270+
assert len(sdo) == 0
271+
assert verify_value(value[0], sck, sdi_initstate, sdi[:16])
272+
assert verify_value(value[1], sck, sdi_initstate, sdi[16:])
273+
274+
275+
def test_read16_bulk(la: LogicAnalyzer, slave: SPISlave):
276+
la.capture(4, block=False)
277+
value = slave.read16_bulk(2)
278+
la.stop()
279+
(sck, sdo, cs, sdi) = la.fetch_data()
280+
sdi_initstate = la.get_initial_states()[SDI[0]]
281+
282+
assert len(cs) == (CS_START + CS_STOP)
283+
assert len(sck) == SCK_WRITE16 + SCK_WRITE16
284+
assert len(sdo) == 0
285+
assert verify_value(value[0], sck, sdi_initstate, sdi[:32])
286+
assert verify_value(value[1], sck, sdi_initstate, sdi[32:])
287+
288+
289+
def test_transfer8_bulk(la: LogicAnalyzer, slave: SPISlave):
290+
la.capture(4, block=False)
291+
value = slave.transfer8_bulk([WRITE_DATA8, WRITE_DATA8])
292+
la.stop()
293+
(sck, sdo, cs, sdi) = la.fetch_data()
294+
sdi_initstate = la.get_initial_states()[SDI[0]]
295+
296+
assert len(cs) == (CS_START + CS_STOP)
297+
assert len(sck) == SCK_WRITE8 + SCK_WRITE8
298+
assert len(sdo) == 0
299+
assert verify_value(value[0], sck, sdi_initstate, sdi[:16])
300+
assert verify_value(value[1], sck, sdi_initstate, sdi[16:])
301+
302+
303+
def test_transfer16_bulk(la: LogicAnalyzer, slave: SPISlave):
304+
la.capture(4, block=False)
305+
value = slave.transfer16_bulk([WRITE_DATA16, WRITE_DATA16])
306+
la.stop()
307+
(sck, sdo, cs, sdi) = la.fetch_data()
308+
sdi_initstate = la.get_initial_states()[SDI[0]]
309+
310+
assert len(cs) == (CS_START + CS_STOP)
311+
assert len(sck) == SCK_WRITE16 + SCK_WRITE16
312+
assert len(sdo) == 0
313+
assert verify_value(value[0], sck, sdi_initstate, sdi[:32])
314+
assert verify_value(value[1], sck, sdi_initstate, sdi[32:])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy