digsim-logic-simulator 0.22.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.
- digsim/__init__.py +6 -0
- digsim/app/__main__.py +12 -0
- digsim/app/cli.py +68 -0
- digsim/app/gui/__init__.py +6 -0
- digsim/app/gui/_circuit_area.py +468 -0
- digsim/app/gui/_component_selection.py +154 -0
- digsim/app/gui/_main_window.py +163 -0
- digsim/app/gui/_top_bar.py +339 -0
- digsim/app/gui/_utils.py +26 -0
- digsim/app/gui/_warning_dialog.py +46 -0
- digsim/app/gui_objects/__init__.py +7 -0
- digsim/app/gui_objects/_bus_bit_object.py +94 -0
- digsim/app/gui_objects/_buzzer_object.py +97 -0
- digsim/app/gui_objects/_component_context_menu.py +79 -0
- digsim/app/gui_objects/_component_object.py +374 -0
- digsim/app/gui_objects/_component_port_item.py +63 -0
- digsim/app/gui_objects/_dip_switch_object.py +104 -0
- digsim/app/gui_objects/_gui_note_object.py +80 -0
- digsim/app/gui_objects/_gui_object_factory.py +80 -0
- digsim/app/gui_objects/_hexdigit_object.py +53 -0
- digsim/app/gui_objects/_image_objects.py +239 -0
- digsim/app/gui_objects/_label_object.py +97 -0
- digsim/app/gui_objects/_logic_analyzer_object.py +86 -0
- digsim/app/gui_objects/_seven_segment_object.py +131 -0
- digsim/app/gui_objects/_shortcut_objects.py +82 -0
- digsim/app/gui_objects/_yosys_object.py +32 -0
- digsim/app/gui_objects/images/AND.png +0 -0
- digsim/app/gui_objects/images/Analyzer.png +0 -0
- digsim/app/gui_objects/images/BUF.png +0 -0
- digsim/app/gui_objects/images/Buzzer.png +0 -0
- digsim/app/gui_objects/images/Clock.png +0 -0
- digsim/app/gui_objects/images/DFF.png +0 -0
- digsim/app/gui_objects/images/DIP_SWITCH.png +0 -0
- digsim/app/gui_objects/images/FlipFlop.png +0 -0
- digsim/app/gui_objects/images/IC.png +0 -0
- digsim/app/gui_objects/images/LED_OFF.png +0 -0
- digsim/app/gui_objects/images/LED_ON.png +0 -0
- digsim/app/gui_objects/images/MUX.png +0 -0
- digsim/app/gui_objects/images/NAND.png +0 -0
- digsim/app/gui_objects/images/NOR.png +0 -0
- digsim/app/gui_objects/images/NOT.png +0 -0
- digsim/app/gui_objects/images/ONE.png +0 -0
- digsim/app/gui_objects/images/OR.png +0 -0
- digsim/app/gui_objects/images/PB.png +0 -0
- digsim/app/gui_objects/images/Switch_OFF.png +0 -0
- digsim/app/gui_objects/images/Switch_ON.png +0 -0
- digsim/app/gui_objects/images/XNOR.png +0 -0
- digsim/app/gui_objects/images/XOR.png +0 -0
- digsim/app/gui_objects/images/YOSYS.png +0 -0
- digsim/app/gui_objects/images/ZERO.png +0 -0
- digsim/app/images/app_icon.png +0 -0
- digsim/app/model/__init__.py +6 -0
- digsim/app/model/_model.py +210 -0
- digsim/app/model/_model_components.py +162 -0
- digsim/app/model/_model_new_wire.py +57 -0
- digsim/app/model/_model_objects.py +155 -0
- digsim/app/model/_model_settings.py +35 -0
- digsim/app/model/_model_shortcuts.py +72 -0
- digsim/app/settings/__init__.py +8 -0
- digsim/app/settings/_component_settings.py +415 -0
- digsim/app/settings/_gui_settings.py +71 -0
- digsim/app/settings/_shortcut_dialog.py +39 -0
- digsim/circuit/__init__.py +7 -0
- digsim/circuit/_circuit.py +329 -0
- digsim/circuit/_waves_writer.py +61 -0
- digsim/circuit/components/__init__.py +26 -0
- digsim/circuit/components/_bus_bits.py +68 -0
- digsim/circuit/components/_button.py +44 -0
- digsim/circuit/components/_buzzer.py +45 -0
- digsim/circuit/components/_clock.py +54 -0
- digsim/circuit/components/_dip_switch.py +73 -0
- digsim/circuit/components/_flip_flops.py +99 -0
- digsim/circuit/components/_gates.py +246 -0
- digsim/circuit/components/_hexdigit.py +82 -0
- digsim/circuit/components/_ic.py +36 -0
- digsim/circuit/components/_label_wire.py +167 -0
- digsim/circuit/components/_led.py +18 -0
- digsim/circuit/components/_logic_analyzer.py +60 -0
- digsim/circuit/components/_mem64kbyte.py +42 -0
- digsim/circuit/components/_memstdout.py +37 -0
- digsim/circuit/components/_note.py +25 -0
- digsim/circuit/components/_on_off_switch.py +54 -0
- digsim/circuit/components/_seven_segment.py +28 -0
- digsim/circuit/components/_static_level.py +28 -0
- digsim/circuit/components/_static_value.py +44 -0
- digsim/circuit/components/_yosys_atoms.py +1353 -0
- digsim/circuit/components/_yosys_component.py +232 -0
- digsim/circuit/components/atoms/__init__.py +23 -0
- digsim/circuit/components/atoms/_component.py +280 -0
- digsim/circuit/components/atoms/_digsim_exception.py +8 -0
- digsim/circuit/components/atoms/_port.py +398 -0
- digsim/circuit/components/ic/74162.json +1331 -0
- digsim/circuit/components/ic/7448.json +834 -0
- digsim/storage_model/__init__.py +7 -0
- digsim/storage_model/_app.py +58 -0
- digsim/storage_model/_circuit.py +126 -0
- digsim/synth/__init__.py +6 -0
- digsim/synth/__main__.py +67 -0
- digsim/synth/_synthesis.py +156 -0
- digsim/utils/__init__.py +6 -0
- digsim/utils/_yosys_netlist.py +134 -0
- digsim_logic_simulator-0.22.0.dist-info/METADATA +140 -0
- digsim_logic_simulator-0.22.0.dist-info/RECORD +107 -0
- digsim_logic_simulator-0.22.0.dist-info/WHEEL +5 -0
- digsim_logic_simulator-0.22.0.dist-info/entry_points.txt +2 -0
- digsim_logic_simulator-0.22.0.dist-info/licenses/LICENSE.md +32 -0
- digsim_logic_simulator-0.22.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""This module contains the classes for all component ports"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import abc
|
|
9
|
+
from typing import Literal, Optional, Union
|
|
10
|
+
|
|
11
|
+
from ._digsim_exception import DigsimException
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
VALUE_TYPE = Union[int, Literal["X"]]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PortConnectionError(DigsimException):
|
|
18
|
+
"""Exception for illegal connections"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Port(abc.ABC):
|
|
22
|
+
"""The abstract base class for all ports"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, parent, name: str, width: int = 1, output: bool = False):
|
|
25
|
+
self._parent = parent # The parent component
|
|
26
|
+
self._name: str = name # The name of this port
|
|
27
|
+
self._width: int = width # The bit-width of this port
|
|
28
|
+
self._output: bool = output # Is this port an output port
|
|
29
|
+
self._wired_ports: list[Port] = [] # The ports that this port drives
|
|
30
|
+
self._value: VALUE_TYPE = "X" # The value of this port
|
|
31
|
+
self._edge_detect_value: VALUE_TYPE = "X" # Last edge detect value
|
|
32
|
+
self.init() # Initialize the port
|
|
33
|
+
|
|
34
|
+
def init(self):
|
|
35
|
+
"""Initialize port, will be called when compponent/circuit is initialized"""
|
|
36
|
+
self._value = "X"
|
|
37
|
+
self._edge_detect_value = "X"
|
|
38
|
+
self.update_wires("X")
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def wired_ports(self) -> list[Port]:
|
|
42
|
+
"""Get wires from thisport"""
|
|
43
|
+
return self._wired_ports
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def value(self) -> VALUE_TYPE:
|
|
47
|
+
"""Get the value of the port, can be "X" """
|
|
48
|
+
return self._value
|
|
49
|
+
|
|
50
|
+
@value.setter
|
|
51
|
+
def value(self, value: VALUE_TYPE):
|
|
52
|
+
"""Set the value of the port"""
|
|
53
|
+
self.set_value(value)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def width(self) -> int:
|
|
57
|
+
"""Get the bit-width of the port"""
|
|
58
|
+
return self._width
|
|
59
|
+
|
|
60
|
+
@width.setter
|
|
61
|
+
def width(self, width: int):
|
|
62
|
+
"""Set the bit-width of the port, will force a disconnect if it is connected"""
|
|
63
|
+
if width != self._width:
|
|
64
|
+
driver = self.get_driver()
|
|
65
|
+
if driver is not None:
|
|
66
|
+
driver.disconnect(self)
|
|
67
|
+
for port in self._wired_ports[:]:
|
|
68
|
+
self.disconnect(port)
|
|
69
|
+
self._width = width
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def wire(self):
|
|
73
|
+
"""Need a property if to be able to have a setter..."""
|
|
74
|
+
raise PortConnectionError("Cannot get a wire")
|
|
75
|
+
|
|
76
|
+
@wire.setter
|
|
77
|
+
def wire(self, port: PortWire):
|
|
78
|
+
"""Wire setter, connect this port to an input port (of same width)"""
|
|
79
|
+
if port.has_driver():
|
|
80
|
+
raise PortConnectionError(f"The port {port.path()}.{port.name()} already has a driver")
|
|
81
|
+
if self.width != port.width:
|
|
82
|
+
raise PortConnectionError("Cannot connect ports with different widths")
|
|
83
|
+
port.set_driver(self)
|
|
84
|
+
self._wired_ports.append(port)
|
|
85
|
+
port.value = self._value # Update wires when port is connected
|
|
86
|
+
|
|
87
|
+
def remove_wires(self):
|
|
88
|
+
"""Remove wires port"""
|
|
89
|
+
for port in self._wired_ports:
|
|
90
|
+
port.set_driver(None)
|
|
91
|
+
self._wired_ports = []
|
|
92
|
+
|
|
93
|
+
def name(self) -> str:
|
|
94
|
+
"""Get port name"""
|
|
95
|
+
return self._name
|
|
96
|
+
|
|
97
|
+
def path(self) -> str:
|
|
98
|
+
"""Get port path, <component_name>...<component_name>"""
|
|
99
|
+
return self._parent.path()
|
|
100
|
+
|
|
101
|
+
def parent(self):
|
|
102
|
+
"""Get parent component"""
|
|
103
|
+
return self._parent
|
|
104
|
+
|
|
105
|
+
def update_wires(self, value: VALUE_TYPE):
|
|
106
|
+
"""Update connected wires (and self._value) with value"""
|
|
107
|
+
if self._value == value:
|
|
108
|
+
return
|
|
109
|
+
self._value = value
|
|
110
|
+
for port in self._wired_ports:
|
|
111
|
+
port.value = self._value
|
|
112
|
+
|
|
113
|
+
def get_wired_ports_recursive(self, processed_ports: Optional[set] = None) -> list[Port]:
|
|
114
|
+
"""Get all connected ports (iterative), avoiding duplicates."""
|
|
115
|
+
if processed_ports is None:
|
|
116
|
+
processed_ports = set()
|
|
117
|
+
|
|
118
|
+
all_wired_ports = []
|
|
119
|
+
ports_to_process = [self]
|
|
120
|
+
|
|
121
|
+
while ports_to_process:
|
|
122
|
+
port = ports_to_process.pop()
|
|
123
|
+
if port in processed_ports:
|
|
124
|
+
continue
|
|
125
|
+
processed_ports.add(port)
|
|
126
|
+
all_wired_ports.append(port)
|
|
127
|
+
ports_to_process.extend(port.wired_ports)
|
|
128
|
+
return all_wired_ports
|
|
129
|
+
|
|
130
|
+
def is_output(self) -> bool:
|
|
131
|
+
"""Return True if this port is an output port"""
|
|
132
|
+
return self._output
|
|
133
|
+
|
|
134
|
+
def is_input(self) -> bool:
|
|
135
|
+
"""Return True if this port is an input port"""
|
|
136
|
+
return not self._output
|
|
137
|
+
|
|
138
|
+
def is_rising_edge(self) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Return True if a rising edge has occured
|
|
141
|
+
Note: This function can only be called once per 'update'
|
|
142
|
+
"""
|
|
143
|
+
rising_edge = False
|
|
144
|
+
if self.value == 1 and self._edge_detect_value == 0:
|
|
145
|
+
rising_edge = True
|
|
146
|
+
self._edge_detect_value = self.value
|
|
147
|
+
return rising_edge
|
|
148
|
+
|
|
149
|
+
def is_falling_edge(self) -> bool:
|
|
150
|
+
"""
|
|
151
|
+
Return True if a falling edge has occured
|
|
152
|
+
Note: This function can only be called once per 'update'
|
|
153
|
+
"""
|
|
154
|
+
falling_edge = False
|
|
155
|
+
if self.value == 0 and self._edge_detect_value == 1:
|
|
156
|
+
falling_edge = True
|
|
157
|
+
self._edge_detect_value = self.value
|
|
158
|
+
return falling_edge
|
|
159
|
+
|
|
160
|
+
@abc.abstractmethod
|
|
161
|
+
def set_value(self, value: VALUE_TYPE):
|
|
162
|
+
"""Set value on port"""
|
|
163
|
+
|
|
164
|
+
@abc.abstractmethod
|
|
165
|
+
def set_driver(self, port: Port | None):
|
|
166
|
+
"""Set port driver"""
|
|
167
|
+
|
|
168
|
+
@abc.abstractmethod
|
|
169
|
+
def has_driver(self) -> bool:
|
|
170
|
+
"""Return True if port has driver"""
|
|
171
|
+
|
|
172
|
+
def get_driver(self):
|
|
173
|
+
"""Get port driver"""
|
|
174
|
+
|
|
175
|
+
def can_add_wire(self) -> bool:
|
|
176
|
+
"""Return True if it is possible to add a wire to this port"""
|
|
177
|
+
if self.is_output():
|
|
178
|
+
return True
|
|
179
|
+
if not self.has_driver():
|
|
180
|
+
return True
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
def disconnect(self, port: Port):
|
|
184
|
+
"""Disconnect port if it is wired"""
|
|
185
|
+
if port in self._wired_ports:
|
|
186
|
+
index = self._wired_ports.index(port)
|
|
187
|
+
del self._wired_ports[index]
|
|
188
|
+
port.set_driver(None)
|
|
189
|
+
|
|
190
|
+
def strval(self) -> str:
|
|
191
|
+
"""Return value as string"""
|
|
192
|
+
if self.value == "X":
|
|
193
|
+
return "X"
|
|
194
|
+
if self.width > 1:
|
|
195
|
+
return f"0x{self.value:x}"
|
|
196
|
+
return f"{self.value}"
|
|
197
|
+
|
|
198
|
+
def __str__(self) -> str:
|
|
199
|
+
return f"{self._parent.name()}:{self._name}={self.value}"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class PortWire(Port):
|
|
203
|
+
"""
|
|
204
|
+
The PortWire class:
|
|
205
|
+
* The port wire will instantaneously update the driven wires upon change.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
def __init__(self, parent, name: str, width: int = 1, output: bool = False):
|
|
209
|
+
super().__init__(parent, name, width, output)
|
|
210
|
+
self._port_driver: Port | None = None # The port that drives this port
|
|
211
|
+
|
|
212
|
+
def set_value(self, value: VALUE_TYPE):
|
|
213
|
+
if value != self.value:
|
|
214
|
+
self.update_wires(value)
|
|
215
|
+
|
|
216
|
+
def set_driver(self, port: Port | None):
|
|
217
|
+
self._port_driver = port
|
|
218
|
+
|
|
219
|
+
def get_driver(self) -> Port | None:
|
|
220
|
+
"""Get driver for port"""
|
|
221
|
+
return self._port_driver
|
|
222
|
+
|
|
223
|
+
def has_driver(self) -> bool:
|
|
224
|
+
"""Return True if port has driver"""
|
|
225
|
+
return self._port_driver is not None
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class PortIn(PortWire):
|
|
229
|
+
"""
|
|
230
|
+
The PortIn class:
|
|
231
|
+
* The port wire will instantaneously update the driven wires upon change.
|
|
232
|
+
* The port will update the parent component upon change.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
def __init__(self, parent, name: str, width: int = 1):
|
|
236
|
+
super().__init__(parent, name, width, output=False)
|
|
237
|
+
|
|
238
|
+
def set_value(self, value: VALUE_TYPE):
|
|
239
|
+
super().set_value(value)
|
|
240
|
+
self.parent().update()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class PortOutDelta(Port):
|
|
244
|
+
"""
|
|
245
|
+
The PortOutDelta class:
|
|
246
|
+
* The port wire will update the driven wires after a delta cycle.
|
|
247
|
+
* The port will update the parent component if the _update_parent variable is set to true.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
def __init__(self, parent, name: str, width: int = 1, delay_ns: int = 1):
|
|
251
|
+
super().__init__(parent, name, width, output=True)
|
|
252
|
+
self._delay_ns = delay_ns # Propagation delay for this port
|
|
253
|
+
self._update_parent = False # Should this port update parent on change
|
|
254
|
+
|
|
255
|
+
def update_parent(self, update_parent: bool):
|
|
256
|
+
"""Set update parent valiable (True/False)"""
|
|
257
|
+
self._update_parent = update_parent
|
|
258
|
+
|
|
259
|
+
def set_delay_ns(self, delay_ns: int):
|
|
260
|
+
"""Set port propagation delay"""
|
|
261
|
+
self._delay_ns = delay_ns
|
|
262
|
+
|
|
263
|
+
def set_value(self, value: VALUE_TYPE):
|
|
264
|
+
self.parent().add_event(self, value, self._delay_ns)
|
|
265
|
+
|
|
266
|
+
def update_port(self, value: VALUE_TYPE):
|
|
267
|
+
"""Update the port output and the connected wires"""
|
|
268
|
+
self.update_wires(value)
|
|
269
|
+
if self._update_parent:
|
|
270
|
+
self.parent().update()
|
|
271
|
+
|
|
272
|
+
def delta_cycle(self, value: VALUE_TYPE):
|
|
273
|
+
"""Handle the delta cycle event from the circuit"""
|
|
274
|
+
self.update_port(value)
|
|
275
|
+
|
|
276
|
+
def set_driver(self, port: Port | None):
|
|
277
|
+
raise PortConnectionError(f"The port {self.path()}.{self.name()} cannot be driven")
|
|
278
|
+
|
|
279
|
+
def get_driver(self) -> Port | None:
|
|
280
|
+
"""Get driver for port, the output port has no driver"""
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
def has_driver(self) -> bool:
|
|
284
|
+
"""Return False since the port does not have a driver"""
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class PortOutImmediate(PortOutDelta):
|
|
289
|
+
"""
|
|
290
|
+
The PortOutImmediate class:
|
|
291
|
+
* A special version of the PortOutDelta used for direct components (button/switch/value)
|
|
292
|
+
* The port driver will update the driven wires immediately
|
|
293
|
+
* The port will update the parent component if the _update_parent variable is set to true.
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
def __init__(self, parent, name: str, width: int = 1):
|
|
297
|
+
super().__init__(parent, name, width)
|
|
298
|
+
|
|
299
|
+
def set_value(self, value: VALUE_TYPE):
|
|
300
|
+
self.parent().add_event(self, value, 0)
|
|
301
|
+
super().update_port(value)
|
|
302
|
+
|
|
303
|
+
def delta_cycle(self, value: VALUE_TYPE):
|
|
304
|
+
"""
|
|
305
|
+
Do nothing here, the event is just used to updates waves in Circuit class
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class PortWireBit(PortWire):
|
|
310
|
+
"""
|
|
311
|
+
The PortWireBit class is used when several bits should be collected into
|
|
312
|
+
a multi bit bus port.
|
|
313
|
+
The PortWireBit will update its parent (a PortMultiBitWire) upon change.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
def __init__(self, parent, name: str, parent_port: PortMultiBitWire, output: bool):
|
|
317
|
+
super().__init__(parent, name, 1, output)
|
|
318
|
+
self._parent_port = parent_port
|
|
319
|
+
|
|
320
|
+
def set_value(self, value: VALUE_TYPE):
|
|
321
|
+
super().set_value(value)
|
|
322
|
+
self._parent_port.update_value_from_bits()
|
|
323
|
+
|
|
324
|
+
def get_parent_port(self) -> PortMultiBitWire:
|
|
325
|
+
"""Get the parent PortMultiBitWire for this port"""
|
|
326
|
+
return self._parent_port
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class PortMultiBitWire(Port):
|
|
330
|
+
"""
|
|
331
|
+
The PortMultiWireBit class is used when several bits should be collected into
|
|
332
|
+
a multi bit bus port.
|
|
333
|
+
The PortWireMultiBit will add events to the circuit upon change to update vcd output.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
def __init__(self, parent, name: str, width: int, output: bool = False):
|
|
337
|
+
self._port_driver: Port | None = None # The port that drives this port
|
|
338
|
+
self._bits = []
|
|
339
|
+
super().__init__(parent, name, width, output)
|
|
340
|
+
for bit_id in range(self.width):
|
|
341
|
+
self._bits.append(
|
|
342
|
+
PortWireBit(parent, f"{self.name()}_{bit_id}", self, output=not output)
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
def init(self):
|
|
346
|
+
super().init()
|
|
347
|
+
for bit in self._bits:
|
|
348
|
+
bit.init()
|
|
349
|
+
|
|
350
|
+
def set_value(self, value: VALUE_TYPE):
|
|
351
|
+
if isinstance(value, str):
|
|
352
|
+
return
|
|
353
|
+
for bit_id, bit in enumerate(self._bits):
|
|
354
|
+
bit_val = (value >> bit_id) & 1
|
|
355
|
+
bit.value = bit_val
|
|
356
|
+
|
|
357
|
+
def get_wired_ports_recursive(self, processed_ports: Optional[set] = None) -> list[Port]:
|
|
358
|
+
if processed_ports is None:
|
|
359
|
+
processed_ports = set()
|
|
360
|
+
|
|
361
|
+
all_wired_ports = super().get_wired_ports_recursive(processed_ports)
|
|
362
|
+
for bit in self._bits:
|
|
363
|
+
all_wired_ports.extend(bit.get_wired_ports_recursive(processed_ports))
|
|
364
|
+
return all_wired_ports
|
|
365
|
+
|
|
366
|
+
def set_driver(self, port: Port | None):
|
|
367
|
+
"""Set port driver"""
|
|
368
|
+
self._port_driver = port
|
|
369
|
+
|
|
370
|
+
def get_driver(self) -> Port | None:
|
|
371
|
+
"""Get port driver"""
|
|
372
|
+
return self._port_driver
|
|
373
|
+
|
|
374
|
+
def has_driver(self) -> bool:
|
|
375
|
+
"""Return True if port has driver"""
|
|
376
|
+
return self._port_driver is not None
|
|
377
|
+
|
|
378
|
+
def get_bit(self, bit_id: int) -> Port:
|
|
379
|
+
"""Get bit port"""
|
|
380
|
+
return self._bits[bit_id]
|
|
381
|
+
|
|
382
|
+
def update_value_from_bits(self):
|
|
383
|
+
"""Update the port with the value of the bits"""
|
|
384
|
+
value = 0
|
|
385
|
+
for bit_id, bit in enumerate(self._bits):
|
|
386
|
+
if bit.value == "X":
|
|
387
|
+
self.update_wires("X")
|
|
388
|
+
return
|
|
389
|
+
value = value | (bit.value << bit_id)
|
|
390
|
+
self.update_wires(value)
|
|
391
|
+
# Send event just to update waves
|
|
392
|
+
self.parent().add_event(self, value, 0)
|
|
393
|
+
|
|
394
|
+
def delta_cycle(self, value: VALUE_TYPE):
|
|
395
|
+
"""
|
|
396
|
+
Do nothing here, the event passed in 'update_value_from_bits'
|
|
397
|
+
is just used to updates waves in Circuit class
|
|
398
|
+
"""
|