emdbg.io.digilent

  1# Copyright (c) 2023, Auterion AG
  2# SPDX-License-Identifier: BSD-3-Clause
  3
  4from __future__ import annotations
  5from pydwf import DwfLibrary
  6from pydwf.utilities import openDwfDevice
  7from contextlib import contextmanager
  8from functools import lru_cache
  9import enum
 10import logging
 11
 12LOGGER = logging.getLogger("io:dwf")
 13_DWF = None
 14
 15@contextmanager
 16def analog_discovery(identifier: str):
 17    """
 18    Starts the DWF library, opens and resets the device and yields it.
 19
 20    :param identifier: serial number of the device to connect to.
 21    """
 22    global _DWF
 23    if _DWF is None:
 24        _DWF = DwfLibrary()
 25    with openDwfDevice(_DWF, serial_number_filter=identifier) as device:
 26        try:
 27            LOGGER.info("Starting...")
 28            device.reset()
 29            yield Digilent(device)
 30        finally:
 31            device.reset()
 32            LOGGER.debug("Stopping.")
 33
 34# -----------------------------------------------------------------------------
 35class DigilentPin:
 36    """
 37    Describes a single pin of the Digilent device. Useful only for low-speed
 38    sampling or driving.
 39    """
 40    class Config(enum.Enum):
 41        """
 42        Specifies the hardware configuration of the pin.
 43        """
 44        Input = enum.auto()
 45        """Input state"""
 46        PushPull = enum.auto()
 47        """Output state with strong high and low driver"""
 48        OpenDrain = enum.auto()
 49        """Output state with only a strong low driver"""
 50        OpenSource = enum.auto()
 51        """Output state with only a strong high driver"""
 52
 53    class Level(enum.IntEnum):
 54        """
 55        Logic level of the pin, for input and output.
 56        """
 57        Low = 0,
 58        High = 1
 59
 60    def __init__(self, dio, mask: int):
 61        """
 62        :param dio: digitalIO driver of the dwf module.
 63        :param mask: the single bit mask of the pin.
 64        """
 65        self.dio = dio
 66        self.mask = mask
 67        self.config = self.Config.Input
 68
 69    def _set_input(self):
 70        self.dio.outputEnableSet(self.dio.outputEnableGet() & ~self.mask)
 71
 72    def _set_output(self):
 73        self.dio.outputEnableSet(self.dio.outputEnableGet() | self.mask)
 74
 75    def _set(self):
 76        self.dio.outputSet(self.dio.outputGet() | self.mask)
 77
 78    def _reset(self):
 79        self.dio.outputSet(self.dio.outputGet() & ~self.mask)
 80
 81    def is_high(self) -> bool:
 82        """
 83        .. warning::
 84            This function does not return the actual pin state, only the set
 85            output state. Use `read()` for that instead.
 86        :return: `True` if the pin output state is set high.
 87        """
 88        return self.dio.outputGet() & self.mask
 89
 90    def is_output(self) -> bool:
 91        """
 92        :return: `True` if pin is configured as output.
 93        """
 94        return self.dio.outputEnableGet() & self.mask
 95
 96    def set_output(self, config: Config = None, level: Level = None):
 97        """
 98        Configures the pin as output with an initial level.
 99        :param config: pin hardware configuration. Default: `Config.PushPull`
100        :param level: initial output level. Default: `Level.High`.
101        """
102        self.config = self.Config.PushPull if config is None else config
103        self.set(level)
104
105    def set(self, level: Level = None):
106        """
107        Sets an output level. Note that depending on pin configuration, the
108        output level may not be achieved.
109
110        :param level: output level. Default: `Level.High`.
111        """
112        if level is None: level = self.Level.High
113        if self.config == self.Config.PushPull:
114            if level: self._set()
115            else: self._reset()
116            self._set_output()
117        elif self.config == self.Config.OpenDrain:
118            if level:
119                self._set_input()
120            else:
121                self._reset()
122                self._set_output()
123        elif self.config == self.Config.OpenSource:
124            if level:
125                self._set()
126                self._set_output()
127            else:
128                self._set_input()
129
130    def high(self):
131        """
132        Sets the output level to `Level.High`.
133        """
134        self.set(self.Level.High)
135
136    def low(self):
137        """
138        Sets the output level to `Level.Low`.
139        """
140        self.set(self.Level.Low)
141
142    def set_input(self):
143        """
144        Configures the pin as `Config.Input`.
145        """
146        self.config = self.Config.Input
147        self._set_input()
148
149    def read(self) -> bool:
150        """
151        :return: the actual input state of the pin.
152        """
153        return self.dio.inputStatus() & self.mask
154
155# -----------------------------------------------------------------------------
156class Digilent:
157    """
158    Wrapper class of the Digilent device.
159    The [DWF Python API][dwf] is already very good and should be used directly
160    to configure more advanced use-cases, so this class is very light-weight.
161    """
162    def __init__(self, device):
163        """
164        :param device: DWF device
165        """
166        self.dwf = device
167
168    @lru_cache(100)
169    def gpio(self, position: int) -> DigilentPin:
170        """
171        Creates a pin abstraction.
172        :param position: Pin number
173        """
174        return DigilentPin(self.dwf.digitalIO, 1 << position)
175
176    def _info_digital_io(self):
177        info = ["",
178            f"Enable Support  = {self.dwf.digitalIO.outputEnableInfo():032b}",
179            f"Enabled         = {self.dwf.digitalIO.outputEnableGet():032b}",
180            f"Output Support  = {self.dwf.digitalIO.outputInfo():032b}",
181            f"Output High/Low = {self.dwf.digitalIO.outputGet():032b}",
182            f"Input Support   = {self.dwf.digitalIO.inputInfo():032b}",
183            f"Input Read      = {self.dwf.digitalIO.inputStatus():032b}",
184        ]
185        LOGGER.info("\n".join(info))
LOGGER = <Logger io:dwf (WARNING)>
@contextmanager
def analog_discovery(identifier: str):
16@contextmanager
17def analog_discovery(identifier: str):
18    """
19    Starts the DWF library, opens and resets the device and yields it.
20
21    :param identifier: serial number of the device to connect to.
22    """
23    global _DWF
24    if _DWF is None:
25        _DWF = DwfLibrary()
26    with openDwfDevice(_DWF, serial_number_filter=identifier) as device:
27        try:
28            LOGGER.info("Starting...")
29            device.reset()
30            yield Digilent(device)
31        finally:
32            device.reset()
33            LOGGER.debug("Stopping.")

Starts the DWF library, opens and resets the device and yields it.

Parameters
  • identifier: serial number of the device to connect to.
class DigilentPin:
 36class DigilentPin:
 37    """
 38    Describes a single pin of the Digilent device. Useful only for low-speed
 39    sampling or driving.
 40    """
 41    class Config(enum.Enum):
 42        """
 43        Specifies the hardware configuration of the pin.
 44        """
 45        Input = enum.auto()
 46        """Input state"""
 47        PushPull = enum.auto()
 48        """Output state with strong high and low driver"""
 49        OpenDrain = enum.auto()
 50        """Output state with only a strong low driver"""
 51        OpenSource = enum.auto()
 52        """Output state with only a strong high driver"""
 53
 54    class Level(enum.IntEnum):
 55        """
 56        Logic level of the pin, for input and output.
 57        """
 58        Low = 0,
 59        High = 1
 60
 61    def __init__(self, dio, mask: int):
 62        """
 63        :param dio: digitalIO driver of the dwf module.
 64        :param mask: the single bit mask of the pin.
 65        """
 66        self.dio = dio
 67        self.mask = mask
 68        self.config = self.Config.Input
 69
 70    def _set_input(self):
 71        self.dio.outputEnableSet(self.dio.outputEnableGet() & ~self.mask)
 72
 73    def _set_output(self):
 74        self.dio.outputEnableSet(self.dio.outputEnableGet() | self.mask)
 75
 76    def _set(self):
 77        self.dio.outputSet(self.dio.outputGet() | self.mask)
 78
 79    def _reset(self):
 80        self.dio.outputSet(self.dio.outputGet() & ~self.mask)
 81
 82    def is_high(self) -> bool:
 83        """
 84        .. warning::
 85            This function does not return the actual pin state, only the set
 86            output state. Use `read()` for that instead.
 87        :return: `True` if the pin output state is set high.
 88        """
 89        return self.dio.outputGet() & self.mask
 90
 91    def is_output(self) -> bool:
 92        """
 93        :return: `True` if pin is configured as output.
 94        """
 95        return self.dio.outputEnableGet() & self.mask
 96
 97    def set_output(self, config: Config = None, level: Level = None):
 98        """
 99        Configures the pin as output with an initial level.
100        :param config: pin hardware configuration. Default: `Config.PushPull`
101        :param level: initial output level. Default: `Level.High`.
102        """
103        self.config = self.Config.PushPull if config is None else config
104        self.set(level)
105
106    def set(self, level: Level = None):
107        """
108        Sets an output level. Note that depending on pin configuration, the
109        output level may not be achieved.
110
111        :param level: output level. Default: `Level.High`.
112        """
113        if level is None: level = self.Level.High
114        if self.config == self.Config.PushPull:
115            if level: self._set()
116            else: self._reset()
117            self._set_output()
118        elif self.config == self.Config.OpenDrain:
119            if level:
120                self._set_input()
121            else:
122                self._reset()
123                self._set_output()
124        elif self.config == self.Config.OpenSource:
125            if level:
126                self._set()
127                self._set_output()
128            else:
129                self._set_input()
130
131    def high(self):
132        """
133        Sets the output level to `Level.High`.
134        """
135        self.set(self.Level.High)
136
137    def low(self):
138        """
139        Sets the output level to `Level.Low`.
140        """
141        self.set(self.Level.Low)
142
143    def set_input(self):
144        """
145        Configures the pin as `Config.Input`.
146        """
147        self.config = self.Config.Input
148        self._set_input()
149
150    def read(self) -> bool:
151        """
152        :return: the actual input state of the pin.
153        """
154        return self.dio.inputStatus() & self.mask

Describes a single pin of the Digilent device. Useful only for low-speed sampling or driving.

DigilentPin(dio, mask: int)
61    def __init__(self, dio, mask: int):
62        """
63        :param dio: digitalIO driver of the dwf module.
64        :param mask: the single bit mask of the pin.
65        """
66        self.dio = dio
67        self.mask = mask
68        self.config = self.Config.Input
Parameters
  • dio: digitalIO driver of the dwf module.
  • mask: the single bit mask of the pin.
dio
mask
config
def is_high(self) -> bool:
82    def is_high(self) -> bool:
83        """
84        .. warning::
85            This function does not return the actual pin state, only the set
86            output state. Use `read()` for that instead.
87        :return: `True` if the pin output state is set high.
88        """
89        return self.dio.outputGet() & self.mask

This function does not return the actual pin state, only the set output state. Use read() for that instead.

Returns

True if the pin output state is set high.

def is_output(self) -> bool:
91    def is_output(self) -> bool:
92        """
93        :return: `True` if pin is configured as output.
94        """
95        return self.dio.outputEnableGet() & self.mask
Returns

True if pin is configured as output.

def set_output( self, config: DigilentPin.Config = None, level: DigilentPin.Level = None):
 97    def set_output(self, config: Config = None, level: Level = None):
 98        """
 99        Configures the pin as output with an initial level.
100        :param config: pin hardware configuration. Default: `Config.PushPull`
101        :param level: initial output level. Default: `Level.High`.
102        """
103        self.config = self.Config.PushPull if config is None else config
104        self.set(level)

Configures the pin as output with an initial level.

Parameters
def set(self, level: DigilentPin.Level = None):
106    def set(self, level: Level = None):
107        """
108        Sets an output level. Note that depending on pin configuration, the
109        output level may not be achieved.
110
111        :param level: output level. Default: `Level.High`.
112        """
113        if level is None: level = self.Level.High
114        if self.config == self.Config.PushPull:
115            if level: self._set()
116            else: self._reset()
117            self._set_output()
118        elif self.config == self.Config.OpenDrain:
119            if level:
120                self._set_input()
121            else:
122                self._reset()
123                self._set_output()
124        elif self.config == self.Config.OpenSource:
125            if level:
126                self._set()
127                self._set_output()
128            else:
129                self._set_input()

Sets an output level. Note that depending on pin configuration, the output level may not be achieved.

Parameters
def high(self):
131    def high(self):
132        """
133        Sets the output level to `Level.High`.
134        """
135        self.set(self.Level.High)

Sets the output level to Level.High.

def low(self):
137    def low(self):
138        """
139        Sets the output level to `Level.Low`.
140        """
141        self.set(self.Level.Low)

Sets the output level to Level.Low.

def set_input(self):
143    def set_input(self):
144        """
145        Configures the pin as `Config.Input`.
146        """
147        self.config = self.Config.Input
148        self._set_input()

Configures the pin as Config.Input.

def read(self) -> bool:
150    def read(self) -> bool:
151        """
152        :return: the actual input state of the pin.
153        """
154        return self.dio.inputStatus() & self.mask
Returns

the actual input state of the pin.

class DigilentPin.Config(enum.Enum):
41    class Config(enum.Enum):
42        """
43        Specifies the hardware configuration of the pin.
44        """
45        Input = enum.auto()
46        """Input state"""
47        PushPull = enum.auto()
48        """Output state with strong high and low driver"""
49        OpenDrain = enum.auto()
50        """Output state with only a strong low driver"""
51        OpenSource = enum.auto()
52        """Output state with only a strong high driver"""

Specifies the hardware configuration of the pin.

Input = <Config.Input: 1>

Input state

PushPull = <Config.PushPull: 2>

Output state with strong high and low driver

OpenDrain = <Config.OpenDrain: 3>

Output state with only a strong low driver

OpenSource = <Config.OpenSource: 4>

Output state with only a strong high driver

class DigilentPin.Level(enum.IntEnum):
54    class Level(enum.IntEnum):
55        """
56        Logic level of the pin, for input and output.
57        """
58        Low = 0,
59        High = 1

Logic level of the pin, for input and output.

Low = <Level.Low: 0>
High = <Level.High: 1>
class Digilent:
157class Digilent:
158    """
159    Wrapper class of the Digilent device.
160    The [DWF Python API][dwf] is already very good and should be used directly
161    to configure more advanced use-cases, so this class is very light-weight.
162    """
163    def __init__(self, device):
164        """
165        :param device: DWF device
166        """
167        self.dwf = device
168
169    @lru_cache(100)
170    def gpio(self, position: int) -> DigilentPin:
171        """
172        Creates a pin abstraction.
173        :param position: Pin number
174        """
175        return DigilentPin(self.dwf.digitalIO, 1 << position)
176
177    def _info_digital_io(self):
178        info = ["",
179            f"Enable Support  = {self.dwf.digitalIO.outputEnableInfo():032b}",
180            f"Enabled         = {self.dwf.digitalIO.outputEnableGet():032b}",
181            f"Output Support  = {self.dwf.digitalIO.outputInfo():032b}",
182            f"Output High/Low = {self.dwf.digitalIO.outputGet():032b}",
183            f"Input Support   = {self.dwf.digitalIO.inputInfo():032b}",
184            f"Input Read      = {self.dwf.digitalIO.inputStatus():032b}",
185        ]
186        LOGGER.info("\n".join(info))

Wrapper class of the Digilent device. The [DWF Python API][dwf] is already very good and should be used directly to configure more advanced use-cases, so this class is very light-weight.

Digilent(device)
163    def __init__(self, device):
164        """
165        :param device: DWF device
166        """
167        self.dwf = device
Parameters
  • device: DWF device
dwf
@lru_cache(100)
def gpio(self, position: int) -> DigilentPin:
169    @lru_cache(100)
170    def gpio(self, position: int) -> DigilentPin:
171        """
172        Creates a pin abstraction.
173        :param position: Pin number
174        """
175        return DigilentPin(self.dwf.digitalIO, 1 << position)

Creates a pin abstraction.

Parameters
  • position: Pin number