Coverage for tropicsquare / transports / ftdi_mpsse.py: 88%

33 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-27 21:24 +0000

1"""FTDI MPSSE SPI transport.""" 

2 

3from tropicsquare.transports import L1Transport 

4from tropicsquare.exceptions import TropicSquareError 

5 

6 

7class FtdiMpsseTransport(L1Transport): 

8 """L1 transport for FTDI MPSSE SPI bridges. 

9 

10 :param spi: Configured PyFtdi ``SpiPort`` instance 

11 """ 

12 

13 def __init__(self, spi) -> None: 

14 self._selected = False 

15 self._started = False 

16 self._spi = spi 

17 

18 

19 def _transfer(self, tx_data: bytes) -> bytes: 

20 start = self._consume_start_flag() 

21 return self._spi.exchange( 

22 tx_data, 

23 start=start, 

24 stop=False, 

25 duplex=True, 

26 ) 

27 

28 

29 def _read(self, length: int) -> bytes: 

30 if not self._selected: 

31 raise RuntimeError("SPI read attempted with chip select inactive") 

32 

33 start = self._consume_start_flag() 

34 return self._spi.read( 

35 length, 

36 start=start, 

37 stop=False, 

38 ) 

39 

40 

41 def _cs_low(self) -> None: 

42 if self._selected: 

43 return 

44 

45 self._selected = True 

46 self._started = False 

47 

48 

49 def _cs_high(self) -> None: 

50 if not self._selected: 

51 return 

52 

53 if self._started: 

54 self._spi.force_select(True) 

55 

56 self._selected = False 

57 self._started = False 

58 

59 

60 def _consume_start_flag(self) -> bool: 

61 if not self._selected: 

62 raise RuntimeError("SPI transfer attempted with chip select inactive") 

63 

64 start = not self._started 

65 self._started = True 

66 return start