digsim-logic-simulator 0.7.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of digsim-logic-simulator might be problematic. Click here for more details.
- digsim/app/gui/_circuit_area.py +2 -2
- digsim/app/gui_objects/_image_objects.py +4 -2
- digsim/circuit/_circuit.py +60 -41
- digsim/circuit/_waves_writer.py +19 -8
- digsim/circuit/components/_label_wire.py +3 -3
- digsim/circuit/components/atoms/__init__.py +2 -1
- digsim/circuit/components/atoms/_component.py +40 -37
- digsim/circuit/components/atoms/_port.py +70 -65
- digsim/circuit/components/ic/74162.json +86 -85
- digsim/circuit/components/ic/7448.json +62 -61
- digsim/utils/_yosys_netlist.py +2 -0
- {digsim_logic_simulator-0.7.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/METADATA +5 -38
- {digsim_logic_simulator-0.7.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/RECORD +16 -16
- {digsim_logic_simulator-0.7.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/WHEEL +1 -1
- {digsim_logic_simulator-0.7.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/licenses/LICENSE.md +0 -0
- {digsim_logic_simulator-0.7.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/top_level.txt +0 -0
digsim/app/gui/_circuit_area.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) Fredrik Andersson, 2023-
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
2
|
# All rights reserved
|
|
3
3
|
|
|
4
4
|
"""The circuit area and component widget"""
|
|
@@ -275,7 +275,7 @@ class _CircuitAreaScene(QGraphicsScene):
|
|
|
275
275
|
component_objects = self._app_model.objects.components.get_object_list()
|
|
276
276
|
for src_comp_item in component_objects:
|
|
277
277
|
for src_port in src_comp_item.component.outports():
|
|
278
|
-
for dst_port in src_port.
|
|
278
|
+
for dst_port in src_port.wired_ports:
|
|
279
279
|
dst_comp_item = self._app_model.objects.components.get_object(
|
|
280
280
|
dst_port.parent()
|
|
281
281
|
)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
"""A component with an image as symbol the GUI"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
8
10
|
from PySide6.QtCore import QPoint, Qt
|
|
@@ -14,8 +16,8 @@ from ._component_object import ComponentObject
|
|
|
14
16
|
class ImageObject(ComponentObject):
|
|
15
17
|
"""The class for a image component placed in the GUI"""
|
|
16
18
|
|
|
17
|
-
IMAGE_FILENAME = None
|
|
18
|
-
ACTIVE_IMAGE_FILENAME = None
|
|
19
|
+
IMAGE_FILENAME: str | None = None
|
|
20
|
+
ACTIVE_IMAGE_FILENAME: str | None = None
|
|
19
21
|
_pixmap = None
|
|
20
22
|
_pixmap_active = None
|
|
21
23
|
|
digsim/circuit/_circuit.py
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
# Copyright (c) Fredrik Andersson, 2023-
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
2
|
# All rights reserved
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
5
|
Module that handles the circuit simulation of components
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import os
|
|
11
|
+
from typing import Tuple
|
|
9
12
|
|
|
10
13
|
from digsim.storage_model import CircuitDataClass, CircuitFileDataClass
|
|
11
14
|
|
|
12
15
|
from ._waves_writer import WavesWriter
|
|
13
|
-
from .components.atoms import DigsimException
|
|
16
|
+
from .components.atoms import Component, DigsimException, PortOutDelta
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class CircuitError(DigsimException):
|
|
@@ -23,90 +26,89 @@ class CircuitEvent:
|
|
|
23
26
|
delta events in the simulation.
|
|
24
27
|
"""
|
|
25
28
|
|
|
26
|
-
def __init__(self, time_ns, port, value):
|
|
27
|
-
self._time_ns = time_ns
|
|
28
|
-
self._port = port
|
|
29
|
-
self._value = value
|
|
29
|
+
def __init__(self, time_ns: int, port: PortOutDelta, value: int | str | None):
|
|
30
|
+
self._time_ns: int = time_ns
|
|
31
|
+
self._port: PortOutDelta = port
|
|
32
|
+
self._value: int | str | None = value
|
|
30
33
|
|
|
31
34
|
@property
|
|
32
|
-
def time_ns(self):
|
|
35
|
+
def time_ns(self) -> int:
|
|
33
36
|
"""Get the simulation time (ns) of this event"""
|
|
34
37
|
return self._time_ns
|
|
35
38
|
|
|
36
39
|
@property
|
|
37
|
-
def port(self):
|
|
40
|
+
def port(self) -> PortOutDelta:
|
|
38
41
|
"""Get the port of this event"""
|
|
39
42
|
return self._port
|
|
40
43
|
|
|
41
44
|
@property
|
|
42
|
-
def value(self):
|
|
45
|
+
def value(self) -> int | str | None:
|
|
43
46
|
"""Get the delta cycle value of this event"""
|
|
44
47
|
return self._value
|
|
45
48
|
|
|
46
|
-
def is_same_event(self, port):
|
|
49
|
+
def is_same_event(self, port: PortOutDelta):
|
|
47
50
|
"""Return True if the in the event is the same as"""
|
|
48
51
|
return port == self._port
|
|
49
52
|
|
|
50
|
-
def update(self, time_ns, value):
|
|
53
|
+
def update(self, time_ns: int, value: int | str | None):
|
|
51
54
|
"""Update the event with a new time (ns) and a new value"""
|
|
52
55
|
self._time_ns = time_ns
|
|
53
56
|
self._value = value
|
|
54
57
|
|
|
55
|
-
def __lt__(self, other):
|
|
58
|
+
def __lt__(self, other) -> bool:
|
|
56
59
|
return other.time_ns > self.time_ns
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
class Circuit:
|
|
60
63
|
"""Class thay handles the circuit simulation"""
|
|
61
64
|
|
|
62
|
-
def __init__(self, name=None, vcd=None):
|
|
63
|
-
self._components = {}
|
|
64
|
-
self._circuit_events = []
|
|
65
|
-
self._name = name
|
|
66
|
-
self._time_ns = 0
|
|
67
|
-
self._folder = None
|
|
65
|
+
def __init__(self, name: str | None = None, vcd: str | None = None):
|
|
66
|
+
self._components: dict[str, Component] = {}
|
|
67
|
+
self._circuit_events: list[CircuitEvent] = []
|
|
68
|
+
self._name: str | None = name
|
|
69
|
+
self._time_ns: int = 0
|
|
70
|
+
self._folder: str | None = None
|
|
71
|
+
self._vcd: WavesWriter | None = None
|
|
68
72
|
|
|
69
73
|
if vcd is not None:
|
|
70
74
|
self._vcd = WavesWriter(filename=vcd)
|
|
71
|
-
else:
|
|
72
|
-
self._vcd = None
|
|
73
75
|
|
|
74
76
|
@property
|
|
75
|
-
def name(self):
|
|
77
|
+
def name(self) -> str | None:
|
|
76
78
|
"""Get the circuit name"""
|
|
77
79
|
return self._name
|
|
78
80
|
|
|
79
81
|
@property
|
|
80
|
-
def time_ns(self):
|
|
82
|
+
def time_ns(self) -> int:
|
|
81
83
|
"""Get the current simulation time (ns)"""
|
|
82
84
|
return self._time_ns
|
|
83
85
|
|
|
84
86
|
@property
|
|
85
|
-
def components(self):
|
|
87
|
+
def components(self) -> list[Component]:
|
|
86
88
|
"""Get the components in this circuit"""
|
|
87
89
|
comp_array = []
|
|
88
90
|
for _, comp in self._components.items():
|
|
89
91
|
comp_array.append(comp)
|
|
90
92
|
return comp_array
|
|
91
93
|
|
|
92
|
-
def load_path(self, path):
|
|
94
|
+
def load_path(self, path) -> str:
|
|
93
95
|
"""Get the load path relative to the circuit path"""
|
|
94
96
|
if self._folder is not None:
|
|
95
97
|
return self._folder + "/" + path
|
|
96
98
|
return path
|
|
97
99
|
|
|
98
|
-
def store_path(self, path):
|
|
100
|
+
def store_path(self, path) -> str:
|
|
99
101
|
"""Get the store path relative to the circuit path"""
|
|
100
102
|
if self._folder is not None:
|
|
101
103
|
return os.path.relpath(path, self._folder)
|
|
102
104
|
return path
|
|
103
105
|
|
|
104
|
-
def delete_component(self, component):
|
|
106
|
+
def delete_component(self, component: Component):
|
|
105
107
|
"""Delete a component from the circuit"""
|
|
106
108
|
del self._components[component.name()]
|
|
107
109
|
component.remove_connections()
|
|
108
110
|
|
|
109
|
-
def get_toplevel_components(self):
|
|
111
|
+
def get_toplevel_components(self) -> list[Component]:
|
|
110
112
|
"""Get toplevel components in the circuit"""
|
|
111
113
|
toplevel_components = []
|
|
112
114
|
for _, comp in self._components.items():
|
|
@@ -157,7 +159,7 @@ class Circuit:
|
|
|
157
159
|
for port in comp.ports:
|
|
158
160
|
self._vcd.write(port, self._time_ns)
|
|
159
161
|
|
|
160
|
-
def _time_to_ns(self, s=None, ms=None, us=None, ns=None):
|
|
162
|
+
def _time_to_ns(self, s=None, ms=None, us=None, ns=None) -> int:
|
|
161
163
|
time_ns = 0
|
|
162
164
|
time_ns += s * 1e9 if s is not None else 0
|
|
163
165
|
time_ns += ms * 1e6 if ms is not None else 0
|
|
@@ -168,7 +170,7 @@ class Circuit:
|
|
|
168
170
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
169
171
|
self._vcd.close()
|
|
170
172
|
|
|
171
|
-
def process_single_event(self, stop_time_ns=None):
|
|
173
|
+
def process_single_event(self, stop_time_ns=None) -> Tuple[bool, bool]:
|
|
172
174
|
"""
|
|
173
175
|
Process one simulation event
|
|
174
176
|
Return False if ther are now events of if the stop_time has passed
|
|
@@ -190,13 +192,20 @@ class Circuit:
|
|
|
190
192
|
self._vcd.write(event.port, self._time_ns)
|
|
191
193
|
return True, toplevel
|
|
192
194
|
|
|
193
|
-
def _is_toplevel_event(self):
|
|
195
|
+
def _is_toplevel_event(self) -> bool:
|
|
194
196
|
if len(self._circuit_events) == 0:
|
|
195
197
|
return False
|
|
196
198
|
event = self._circuit_events[0]
|
|
197
199
|
return event.port.parent().is_toplevel()
|
|
198
200
|
|
|
199
|
-
def run(
|
|
201
|
+
def run(
|
|
202
|
+
self,
|
|
203
|
+
s: int | None = None,
|
|
204
|
+
ms: int | None = None,
|
|
205
|
+
us: int | None = None,
|
|
206
|
+
ns: int | None = None,
|
|
207
|
+
single_step: bool = False,
|
|
208
|
+
) -> bool:
|
|
200
209
|
"""Run simulation for a period of time"""
|
|
201
210
|
stop_time_ns = self._time_ns + self._time_to_ns(s=s, ms=ms, us=us, ns=ns)
|
|
202
211
|
single_step_stop = False
|
|
@@ -210,13 +219,19 @@ class Circuit:
|
|
|
210
219
|
self._time_ns = max(self._time_ns, stop_time_ns)
|
|
211
220
|
return single_step_stop
|
|
212
221
|
|
|
213
|
-
def run_until(
|
|
222
|
+
def run_until(
|
|
223
|
+
self,
|
|
224
|
+
s: int | None = None,
|
|
225
|
+
ms: int | None = None,
|
|
226
|
+
us: int | None = None,
|
|
227
|
+
ns: int | None = None,
|
|
228
|
+
):
|
|
214
229
|
"""Run simulation until a specified time"""
|
|
215
230
|
stop_time_ns = self._time_to_ns(s=s, ms=ms, us=us, ns=ns)
|
|
216
231
|
if stop_time_ns >= self._time_ns:
|
|
217
232
|
self.run(ns=stop_time_ns - self._time_ns)
|
|
218
233
|
|
|
219
|
-
def add_event(self, port, value, propagation_delay_ns):
|
|
234
|
+
def add_event(self, port: PortOutDelta, value: int | str | None, propagation_delay_ns: int):
|
|
220
235
|
"""Add delta cycle event, this will also write values to .vcd file"""
|
|
221
236
|
event_time_ns = self._time_ns + propagation_delay_ns
|
|
222
237
|
# print(f"Add event {port.parent().name()}:{port.name()} => {value}")
|
|
@@ -226,7 +241,7 @@ class Circuit:
|
|
|
226
241
|
return
|
|
227
242
|
self._circuit_events.append(CircuitEvent(event_time_ns, port, value))
|
|
228
243
|
|
|
229
|
-
def add_component(self, component):
|
|
244
|
+
def add_component(self, component: Component):
|
|
230
245
|
"""Add component to circuit"""
|
|
231
246
|
name_id = 1
|
|
232
247
|
namebase = component.name()
|
|
@@ -235,21 +250,21 @@ class Circuit:
|
|
|
235
250
|
name_id += 1
|
|
236
251
|
self._components[component.name()] = component
|
|
237
252
|
|
|
238
|
-
def change_component_name(self, component, name):
|
|
253
|
+
def change_component_name(self, component: Component, name: str):
|
|
239
254
|
"""Change component name"""
|
|
240
255
|
comp = self._components[component.name()]
|
|
241
256
|
del self._components[component.name()]
|
|
242
257
|
comp.set_name(name, update_circuit=False)
|
|
243
258
|
self.add_component(comp)
|
|
244
259
|
|
|
245
|
-
def get_component(self, component_name):
|
|
260
|
+
def get_component(self, component_name: str) -> Component:
|
|
246
261
|
"""Get component witgh 'component_name'"""
|
|
247
262
|
comp = self._components.get(component_name)
|
|
248
263
|
if comp is not None:
|
|
249
264
|
return comp
|
|
250
265
|
raise CircuitError(f"Component '{component_name}' not found")
|
|
251
266
|
|
|
252
|
-
def to_dataclass(self, folder=None):
|
|
267
|
+
def to_dataclass(self, folder: str | None = None) -> CircuitDataClass:
|
|
253
268
|
"""Generate dict from circuit, used when storing circuit"""
|
|
254
269
|
if self._name is None:
|
|
255
270
|
raise CircuitError("Circuit must have a name")
|
|
@@ -257,8 +272,12 @@ class Circuit:
|
|
|
257
272
|
return CircuitDataClass.from_circuit(self)
|
|
258
273
|
|
|
259
274
|
def from_dataclass(
|
|
260
|
-
self,
|
|
261
|
-
|
|
275
|
+
self,
|
|
276
|
+
circuit_dc: CircuitDataClass,
|
|
277
|
+
folder: str | None = None,
|
|
278
|
+
component_exceptions: bool = True,
|
|
279
|
+
connect_exceptions: bool = True,
|
|
280
|
+
) -> list[str]:
|
|
262
281
|
"""Clear circuit and add components from dict"""
|
|
263
282
|
self._folder = folder
|
|
264
283
|
self.clear()
|
|
@@ -285,12 +304,12 @@ class Circuit:
|
|
|
285
304
|
|
|
286
305
|
return exception_str_list
|
|
287
306
|
|
|
288
|
-
def to_json_file(self, filename):
|
|
307
|
+
def to_json_file(self, filename: str):
|
|
289
308
|
"""Store circuit in json file"""
|
|
290
309
|
circuitfile_dc = CircuitFileDataClass(circuit=self.to_dataclass())
|
|
291
310
|
circuitfile_dc.save(filename)
|
|
292
311
|
|
|
293
|
-
def from_json_file(self, filename, folder=None):
|
|
312
|
+
def from_json_file(self, filename: str, folder: str | None = None):
|
|
294
313
|
"""Load circuit from json file"""
|
|
295
314
|
file_dc = CircuitFileDataClass.load(filename)
|
|
296
315
|
self.from_dataclass(file_dc.circuit, folder)
|
digsim/circuit/_waves_writer.py
CHANGED
|
@@ -1,35 +1,46 @@
|
|
|
1
|
-
# Copyright (c) Fredrik Andersson, 2023-
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
2
|
# All rights reserved
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
5
|
Module that handles the creation of vcd files
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import io
|
|
9
|
+
from typing import Any, Tuple
|
|
10
|
+
|
|
8
11
|
from vcd import VCDWriter
|
|
9
12
|
|
|
13
|
+
from .components.atoms import Port
|
|
14
|
+
|
|
10
15
|
|
|
11
16
|
class WavesWriter:
|
|
12
17
|
"""Class that handles the creation of vcd files"""
|
|
13
18
|
|
|
14
|
-
def __init__(self, filename):
|
|
15
|
-
self._vcd_name = filename
|
|
16
|
-
self._vcd_file = None
|
|
17
|
-
self._vcd_writer = None
|
|
18
|
-
self._vcd_dict = {}
|
|
19
|
+
def __init__(self, filename: str):
|
|
20
|
+
self._vcd_name: str = filename
|
|
21
|
+
self._vcd_file: io.TextIOWrapper | None = None
|
|
22
|
+
self._vcd_writer: VCDWriter | None = None
|
|
23
|
+
self._vcd_dict: dict[str, Any] = {}
|
|
19
24
|
|
|
20
|
-
def init(self, port_info):
|
|
25
|
+
def init(self, port_info: list[Tuple[str, str, int]]):
|
|
21
26
|
"""Initialize vcd writer"""
|
|
22
27
|
if self._vcd_file is not None or self._vcd_writer is not None:
|
|
23
28
|
self.close()
|
|
24
29
|
self._vcd_file = open(self._vcd_name, mode="w", encoding="utf-8")
|
|
30
|
+
if self._vcd_file is None:
|
|
31
|
+
raise RuntimeError("VCD file is None")
|
|
25
32
|
self._vcd_writer = VCDWriter(self._vcd_file, timescale="1 ns", date="today")
|
|
26
33
|
for port_path, port_name, port_width in port_info:
|
|
27
34
|
var = self._vcd_writer.register_var(port_path, port_name, "wire", size=port_width)
|
|
28
35
|
self._vcd_dict[f"{port_path}.{port_name}"] = var
|
|
29
36
|
self._vcd_file.flush()
|
|
30
37
|
|
|
31
|
-
def write(self, port, time_ns):
|
|
38
|
+
def write(self, port: Port, time_ns: int):
|
|
32
39
|
"""Write port value to vcd file"""
|
|
40
|
+
if self._vcd_file is None:
|
|
41
|
+
raise RuntimeError("VCD file is None")
|
|
42
|
+
if self._vcd_writer is None:
|
|
43
|
+
raise RuntimeError("VCD Writer is None")
|
|
33
44
|
for wired_port in port.get_wired_ports_recursive():
|
|
34
45
|
var = self._vcd_dict.get(f"{wired_port.path()}.{wired_port.name()}")
|
|
35
46
|
if var is None:
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
"""Label Wire components module"""
|
|
5
5
|
|
|
6
|
-
from .atoms import Component, DigsimException, PortWire
|
|
6
|
+
from .atoms import Component, DigsimException, PortIn, PortWire
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class _LabelWireStorage:
|
|
10
10
|
"""Singleton class with label wires"""
|
|
11
11
|
|
|
12
12
|
_instance = None
|
|
13
|
-
_wire_drivers = {}
|
|
14
|
-
_wire_sinks = {}
|
|
13
|
+
_wire_drivers: dict[str, PortWire] = {}
|
|
14
|
+
_wire_sinks: dict[str, PortIn] = {}
|
|
15
15
|
|
|
16
16
|
def __new__(cls):
|
|
17
17
|
if cls._instance is None:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) Fredrik Andersson, 2023
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
2
|
# All rights reserved
|
|
3
3
|
|
|
4
4
|
"""All classes within digsim.circuit.components.atoms namespace"""
|
|
@@ -11,6 +11,7 @@ from ._component import ( # noqa: F401
|
|
|
11
11
|
)
|
|
12
12
|
from ._digsim_exception import DigsimException # noqa: F401
|
|
13
13
|
from ._port import ( # noqa: F401
|
|
14
|
+
Port,
|
|
14
15
|
PortConnectionError,
|
|
15
16
|
PortIn,
|
|
16
17
|
PortMultiBitWire,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
# Copyright (c) Fredrik Andersson, 2023-
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
2
|
# All rights reserved
|
|
3
3
|
|
|
4
4
|
"""This module contains the base classes for all component types"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import abc
|
|
7
9
|
import copy
|
|
10
|
+
from typing import Callable
|
|
8
11
|
|
|
9
12
|
from ._digsim_exception import DigsimException
|
|
13
|
+
from ._port import Port
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
class ComponentException(DigsimException):
|
|
@@ -16,15 +20,14 @@ class ComponentException(DigsimException):
|
|
|
16
20
|
class Component(abc.ABC):
|
|
17
21
|
"""The component base class"""
|
|
18
22
|
|
|
19
|
-
def __init__(self, circuit, name=None, display_name=None):
|
|
23
|
+
def __init__(self, circuit, name: str | None = None, display_name: str | None = None):
|
|
20
24
|
self._circuit = circuit
|
|
21
|
-
self._name = name or self.__class__.__name__
|
|
22
|
-
self._parent = None
|
|
23
|
-
self._ports = []
|
|
24
|
-
self._edge_detect_dict = {}
|
|
25
|
+
self._name: str = name or self.__class__.__name__
|
|
26
|
+
self._parent: Component | None = None
|
|
27
|
+
self._ports: list[Port] = []
|
|
25
28
|
self._circuit.add_component(self)
|
|
26
|
-
self._display_name = display_name or self.__class__.__name__
|
|
27
|
-
self._parameters = {}
|
|
29
|
+
self._display_name: str = display_name or self.__class__.__name__
|
|
30
|
+
self._parameters: dict[str, int | str | bool] = {}
|
|
28
31
|
|
|
29
32
|
def init(self):
|
|
30
33
|
"""Initialize port, will be called when circuit is initialized"""
|
|
@@ -37,15 +40,15 @@ class Component(abc.ABC):
|
|
|
37
40
|
def clear(self):
|
|
38
41
|
"""Remove static state within the component class"""
|
|
39
42
|
|
|
40
|
-
def parameter_set(self, parameter, value):
|
|
43
|
+
def parameter_set(self, parameter: str, value: int | str | bool):
|
|
41
44
|
"""Set component parameter"""
|
|
42
45
|
self._parameters[parameter] = value
|
|
43
46
|
|
|
44
|
-
def parameter_get(self, parameter):
|
|
47
|
+
def parameter_get(self, parameter: str) -> int | str | bool:
|
|
45
48
|
"""Get component parameter"""
|
|
46
49
|
return self._parameters[parameter]
|
|
47
50
|
|
|
48
|
-
def add_port(self, port):
|
|
51
|
+
def add_port(self, port: Port):
|
|
49
52
|
"""
|
|
50
53
|
Add port to component,
|
|
51
54
|
also add a 'portname' variable to the component with help of the 'self.__dict__'
|
|
@@ -59,7 +62,7 @@ class Component(abc.ABC):
|
|
|
59
62
|
"""
|
|
60
63
|
self._ports = []
|
|
61
64
|
|
|
62
|
-
def path(self):
|
|
65
|
+
def path(self) -> str:
|
|
63
66
|
"""Get component path"""
|
|
64
67
|
if self._parent is not None:
|
|
65
68
|
return f"{self._parent.path()}.{self.name()}"
|
|
@@ -70,22 +73,22 @@ class Component(abc.ABC):
|
|
|
70
73
|
"""Get component ports"""
|
|
71
74
|
return self._ports
|
|
72
75
|
|
|
73
|
-
def _get_ports(self, output):
|
|
76
|
+
def _get_ports(self, output) -> list[Port]:
|
|
74
77
|
sel_ports = []
|
|
75
78
|
for port in self._ports:
|
|
76
79
|
if port.is_output() == output:
|
|
77
80
|
sel_ports.append(port)
|
|
78
81
|
return sel_ports
|
|
79
82
|
|
|
80
|
-
def inports(self):
|
|
83
|
+
def inports(self) -> list[Port]:
|
|
81
84
|
"""Get component input ports"""
|
|
82
85
|
return self._get_ports(False)
|
|
83
86
|
|
|
84
|
-
def outports(self):
|
|
87
|
+
def outports(self) -> list[Port]:
|
|
85
88
|
"""Get component output ports"""
|
|
86
89
|
return self._get_ports(True)
|
|
87
90
|
|
|
88
|
-
def port(self, portname):
|
|
91
|
+
def port(self, portname: str) -> Port:
|
|
89
92
|
"""Get port with name 'portname'"""
|
|
90
93
|
for port in self._ports:
|
|
91
94
|
if port.name() == portname:
|
|
@@ -97,46 +100,46 @@ class Component(abc.ABC):
|
|
|
97
100
|
"""Get the circuit for the current component"""
|
|
98
101
|
return self._circuit
|
|
99
102
|
|
|
100
|
-
def name(self):
|
|
103
|
+
def name(self) -> str:
|
|
101
104
|
"""Get the component name"""
|
|
102
105
|
return self._name
|
|
103
106
|
|
|
104
|
-
def set_name(self, name, update_circuit=True):
|
|
107
|
+
def set_name(self, name: str, update_circuit: bool = True):
|
|
105
108
|
"""Set the component name"""
|
|
106
109
|
if update_circuit:
|
|
107
110
|
self.circuit.change_component_name(self, name)
|
|
108
111
|
else:
|
|
109
112
|
self._name = name
|
|
110
113
|
|
|
111
|
-
def display_name(self):
|
|
114
|
+
def display_name(self) -> str:
|
|
112
115
|
"""Get the component display name"""
|
|
113
116
|
return self._display_name
|
|
114
117
|
|
|
115
|
-
def set_display_name(self, display_name):
|
|
118
|
+
def set_display_name(self, display_name: str):
|
|
116
119
|
"""Set the component display name"""
|
|
117
120
|
self._display_name = display_name
|
|
118
121
|
|
|
119
122
|
@property
|
|
120
|
-
def parent(self):
|
|
123
|
+
def parent(self) -> Component | None:
|
|
121
124
|
"""Get parent component"""
|
|
122
125
|
return self._parent
|
|
123
126
|
|
|
124
|
-
def is_toplevel(self):
|
|
125
|
-
"""Return True if this component is a toplevel component"""
|
|
126
|
-
return self._parent is None
|
|
127
|
-
|
|
128
127
|
@parent.setter
|
|
129
|
-
def parent(self, parent):
|
|
128
|
+
def parent(self, parent: Component):
|
|
130
129
|
"""Set component parent"""
|
|
131
130
|
self._parent = parent
|
|
132
131
|
|
|
132
|
+
def is_toplevel(self) -> bool:
|
|
133
|
+
"""Return True if this component is a toplevel component"""
|
|
134
|
+
return self._parent is None
|
|
135
|
+
|
|
133
136
|
@property
|
|
134
137
|
def wire(self):
|
|
135
138
|
"""Property needed to be able to have a setter"""
|
|
136
139
|
raise ComponentException(f"Cannot get wire for component '{self.display_name}'")
|
|
137
140
|
|
|
138
141
|
@wire.setter
|
|
139
|
-
def wire(self, port):
|
|
142
|
+
def wire(self, port: Port):
|
|
140
143
|
"""Some components have a single output port, they can wired at component level"""
|
|
141
144
|
self.outports()[0].wire = port
|
|
142
145
|
|
|
@@ -146,14 +149,14 @@ class Component(abc.ABC):
|
|
|
146
149
|
def remove_connections(self):
|
|
147
150
|
"""Remove component connections"""
|
|
148
151
|
for src_port in self.outports():
|
|
149
|
-
for dst_port in src_port.
|
|
152
|
+
for dst_port in src_port.wired_ports:
|
|
150
153
|
dst_port.set_driver(None)
|
|
151
154
|
|
|
152
155
|
for dst_port in self.inports():
|
|
153
156
|
if dst_port.has_driver():
|
|
154
157
|
dst_port.get_driver().disconnect(dst_port)
|
|
155
158
|
|
|
156
|
-
def add_event(self, port, value, delay_ns):
|
|
159
|
+
def add_event(self, port: Port, value: int, delay_ns: int):
|
|
157
160
|
"""Add delta cycle event"""
|
|
158
161
|
self.circuit.add_event(port, value, delay_ns)
|
|
159
162
|
|
|
@@ -166,12 +169,12 @@ class Component(abc.ABC):
|
|
|
166
169
|
return comp_str
|
|
167
170
|
|
|
168
171
|
@property
|
|
169
|
-
def has_action(self):
|
|
172
|
+
def has_action(self) -> bool:
|
|
170
173
|
"""Return True if this component is interactive"""
|
|
171
174
|
return False
|
|
172
175
|
|
|
173
176
|
@property
|
|
174
|
-
def active(self):
|
|
177
|
+
def active(self) -> bool:
|
|
175
178
|
"""Return True if this component is active/activated ('on' for a switch for example)"""
|
|
176
179
|
return False
|
|
177
180
|
|
|
@@ -185,7 +188,7 @@ class Component(abc.ABC):
|
|
|
185
188
|
"""Get component settings from dict"""
|
|
186
189
|
raise ComponentException(f"No setup for component '{self.display_name}'")
|
|
187
190
|
|
|
188
|
-
def settings_to_dict(self):
|
|
191
|
+
def settings_to_dict(self) -> dict[str, int | str | bool]:
|
|
189
192
|
"""Return component settings as a dict"""
|
|
190
193
|
return copy.deepcopy(self._parameters)
|
|
191
194
|
|
|
@@ -194,7 +197,7 @@ class Component(abc.ABC):
|
|
|
194
197
|
"""Return parameters"""
|
|
195
198
|
return {}
|
|
196
199
|
|
|
197
|
-
def update_settings(self, settings):
|
|
200
|
+
def update_settings(self, settings: dict[str, int | str | bool]):
|
|
198
201
|
"""Update parameters from settings dict"""
|
|
199
202
|
for setting, value in settings.items():
|
|
200
203
|
self.parameter_set(setting, value)
|
|
@@ -222,9 +225,9 @@ class Component(abc.ABC):
|
|
|
222
225
|
class MultiComponent(Component):
|
|
223
226
|
"""A component that holds one or several sub components"""
|
|
224
227
|
|
|
225
|
-
def __init__(self, circuit, name):
|
|
228
|
+
def __init__(self, circuit, name: str):
|
|
226
229
|
super().__init__(circuit, name)
|
|
227
|
-
self._components = []
|
|
230
|
+
self._components: list[Component] = []
|
|
228
231
|
|
|
229
232
|
def init(self):
|
|
230
233
|
super().init()
|
|
@@ -253,11 +256,11 @@ class CallbackComponent(Component):
|
|
|
253
256
|
objects when the component change value.
|
|
254
257
|
"""
|
|
255
258
|
|
|
256
|
-
def __init__(self, circuit, name, callback=None):
|
|
259
|
+
def __init__(self, circuit, name: str, callback: Callable[[Component], None] | None = None):
|
|
257
260
|
super().__init__(circuit, name)
|
|
258
261
|
self._callback = callback
|
|
259
262
|
|
|
260
|
-
def set_callback(self, callback):
|
|
263
|
+
def set_callback(self, callback: Callable[[Component], None]):
|
|
261
264
|
"""Set CallbackComponent callback function"""
|
|
262
265
|
self._callback = callback
|
|
263
266
|
|