digsim-logic-simulator 0.6.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/_top_bar.py +1 -1
- digsim/app/gui_objects/_component_object.py +1 -1
- digsim/app/gui_objects/_hexdigit_object.py +1 -1
- 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 +3 -1
- {digsim_logic_simulator-0.6.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/METADATA +10 -41
- {digsim_logic_simulator-0.6.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/RECORD +19 -19
- {digsim_logic_simulator-0.6.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/WHEEL +1 -1
- {digsim_logic_simulator-0.6.0.dist-info → digsim_logic_simulator-0.8.0.dist-info/licenses}/LICENSE.md +0 -0
- {digsim_logic_simulator-0.6.0.dist-info → digsim_logic_simulator-0.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
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 classes for all component ports"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import abc
|
|
7
9
|
|
|
8
10
|
from ._digsim_exception import DigsimException
|
|
@@ -15,14 +17,14 @@ class PortConnectionError(DigsimException):
|
|
|
15
17
|
class Port(abc.ABC):
|
|
16
18
|
"""The abstract base class for all ports"""
|
|
17
19
|
|
|
18
|
-
def __init__(self, parent, name, width=1, output=False):
|
|
20
|
+
def __init__(self, parent, name: str, width: int = 1, output: bool = False):
|
|
19
21
|
self._parent = parent # The parent component
|
|
20
|
-
self._name = name # The name of this port
|
|
21
|
-
self._width = width # The bit-width of this port
|
|
22
|
-
self._output = output # Is this port an output port
|
|
23
|
-
self._wired_ports = [] # The ports that this port drives
|
|
24
|
-
self._value = None # The value of this port
|
|
25
|
-
self._edge_detect_value = "X" # Last edge detect value
|
|
22
|
+
self._name: str = name # The name of this port
|
|
23
|
+
self._width: int = width # The bit-width of this port
|
|
24
|
+
self._output: bool = output # Is this port an output port
|
|
25
|
+
self._wired_ports: list[Port] = [] # The ports that this port drives
|
|
26
|
+
self._value: int | str | None = None # The value of this port
|
|
27
|
+
self._edge_detect_value: int | str | None = "X" # Last edge detect value
|
|
26
28
|
self.init() # Initialize the port
|
|
27
29
|
|
|
28
30
|
def init(self):
|
|
@@ -32,27 +34,27 @@ class Port(abc.ABC):
|
|
|
32
34
|
self.update_wires("X")
|
|
33
35
|
|
|
34
36
|
@property
|
|
35
|
-
def wired_ports(self):
|
|
37
|
+
def wired_ports(self) -> list[Port]:
|
|
36
38
|
"""Get wires from thisport"""
|
|
37
39
|
return self._wired_ports
|
|
38
40
|
|
|
39
41
|
@property
|
|
40
|
-
def value(self):
|
|
42
|
+
def value(self) -> int | str | None:
|
|
41
43
|
"""Get the value of the port, can be "X" """
|
|
42
44
|
return self._value
|
|
43
45
|
|
|
44
46
|
@value.setter
|
|
45
|
-
def value(self, value):
|
|
47
|
+
def value(self, value: int | str | None):
|
|
46
48
|
"""Set the value of the port"""
|
|
47
49
|
self.set_value(value)
|
|
48
50
|
|
|
49
51
|
@property
|
|
50
|
-
def width(self):
|
|
52
|
+
def width(self) -> int:
|
|
51
53
|
"""Get the bit-width of the port"""
|
|
52
54
|
return self._width
|
|
53
55
|
|
|
54
56
|
@width.setter
|
|
55
|
-
def width(self, width):
|
|
57
|
+
def width(self, width: int):
|
|
56
58
|
"""Set the bit-width of the port, will force a disconnect if it is connected"""
|
|
57
59
|
if width != self._width:
|
|
58
60
|
driver = self.get_driver()
|
|
@@ -68,7 +70,7 @@ class Port(abc.ABC):
|
|
|
68
70
|
raise PortConnectionError("Cannot get a wire")
|
|
69
71
|
|
|
70
72
|
@wire.setter
|
|
71
|
-
def wire(self, port):
|
|
73
|
+
def wire(self, port: PortWire):
|
|
72
74
|
"""Wire setter, connect this port to an input port (of same width)"""
|
|
73
75
|
if port.has_driver():
|
|
74
76
|
raise PortConnectionError(f"The port {port.path()}.{port.name()} already has a driver")
|
|
@@ -84,11 +86,11 @@ class Port(abc.ABC):
|
|
|
84
86
|
port.set_driver(None)
|
|
85
87
|
self._wired_ports = []
|
|
86
88
|
|
|
87
|
-
def name(self):
|
|
89
|
+
def name(self) -> str:
|
|
88
90
|
"""Get port name"""
|
|
89
91
|
return self._name
|
|
90
92
|
|
|
91
|
-
def path(self):
|
|
93
|
+
def path(self) -> str:
|
|
92
94
|
"""Get port path, <component_name>...<component_name>"""
|
|
93
95
|
return self._parent.path()
|
|
94
96
|
|
|
@@ -96,7 +98,7 @@ class Port(abc.ABC):
|
|
|
96
98
|
"""Get parent component"""
|
|
97
99
|
return self._parent
|
|
98
100
|
|
|
99
|
-
def update_wires(self, value):
|
|
101
|
+
def update_wires(self, value: int | str | None):
|
|
100
102
|
"""Update connected wires (and self._value) with value"""
|
|
101
103
|
if self._value == value:
|
|
102
104
|
return
|
|
@@ -104,26 +106,22 @@ class Port(abc.ABC):
|
|
|
104
106
|
for port in self._wired_ports:
|
|
105
107
|
port.value = self._value
|
|
106
108
|
|
|
107
|
-
def
|
|
108
|
-
"""Get connected ports"""
|
|
109
|
-
return self._wired_ports
|
|
110
|
-
|
|
111
|
-
def get_wired_ports_recursive(self):
|
|
109
|
+
def get_wired_ports_recursive(self) -> list[Port]:
|
|
112
110
|
"""Get all connected ports (recursive)"""
|
|
113
111
|
all_wired_ports = [self]
|
|
114
112
|
for port in self._wired_ports:
|
|
115
113
|
all_wired_ports.extend(port.get_wired_ports_recursive())
|
|
116
114
|
return all_wired_ports
|
|
117
115
|
|
|
118
|
-
def is_output(self):
|
|
116
|
+
def is_output(self) -> bool:
|
|
119
117
|
"""Return True if this port is an output port"""
|
|
120
118
|
return self._output
|
|
121
119
|
|
|
122
|
-
def is_input(self):
|
|
120
|
+
def is_input(self) -> bool:
|
|
123
121
|
"""Return True if this port is an input port"""
|
|
124
122
|
return not self._output
|
|
125
123
|
|
|
126
|
-
def is_rising_edge(self):
|
|
124
|
+
def is_rising_edge(self) -> bool:
|
|
127
125
|
"""
|
|
128
126
|
Return True if a rising edge has occured
|
|
129
127
|
Note: This function can only be called once per 'update'
|
|
@@ -134,7 +132,7 @@ class Port(abc.ABC):
|
|
|
134
132
|
self._edge_detect_value = self.value
|
|
135
133
|
return rising_edge
|
|
136
134
|
|
|
137
|
-
def is_falling_edge(self):
|
|
135
|
+
def is_falling_edge(self) -> bool:
|
|
138
136
|
"""
|
|
139
137
|
Return True if a falling edge has occured
|
|
140
138
|
Note: This function can only be called once per 'update'
|
|
@@ -146,14 +144,21 @@ class Port(abc.ABC):
|
|
|
146
144
|
return falling_edge
|
|
147
145
|
|
|
148
146
|
@abc.abstractmethod
|
|
149
|
-
def set_value(self, value):
|
|
147
|
+
def set_value(self, value: int | str | None):
|
|
150
148
|
"""Set value on port"""
|
|
151
149
|
|
|
152
150
|
@abc.abstractmethod
|
|
153
|
-
def set_driver(self, port):
|
|
151
|
+
def set_driver(self, port: Port | None):
|
|
154
152
|
"""Set port driver"""
|
|
155
153
|
|
|
156
|
-
|
|
154
|
+
@abc.abstractmethod
|
|
155
|
+
def has_driver(self) -> bool:
|
|
156
|
+
"""Return True if port has driver"""
|
|
157
|
+
|
|
158
|
+
def get_driver(self):
|
|
159
|
+
"""Get port driver"""
|
|
160
|
+
|
|
161
|
+
def can_add_wire(self) -> bool:
|
|
157
162
|
"""Return True if it is possible to add a wire to this port"""
|
|
158
163
|
if self.is_output():
|
|
159
164
|
return True
|
|
@@ -161,14 +166,14 @@ class Port(abc.ABC):
|
|
|
161
166
|
return True
|
|
162
167
|
return False
|
|
163
168
|
|
|
164
|
-
def disconnect(self, port):
|
|
169
|
+
def disconnect(self, port: Port):
|
|
165
170
|
"""Disconnect port if it is wired"""
|
|
166
171
|
if port in self._wired_ports:
|
|
167
172
|
index = self._wired_ports.index(port)
|
|
168
173
|
del self._wired_ports[index]
|
|
169
174
|
port.set_driver(None)
|
|
170
175
|
|
|
171
|
-
def strval(self):
|
|
176
|
+
def strval(self) -> str:
|
|
172
177
|
"""Return value as string"""
|
|
173
178
|
if self.value == "X":
|
|
174
179
|
return "X"
|
|
@@ -176,7 +181,7 @@ class Port(abc.ABC):
|
|
|
176
181
|
return f"0x{self.value:x}"
|
|
177
182
|
return f"{self.value}"
|
|
178
183
|
|
|
179
|
-
def __str__(self):
|
|
184
|
+
def __str__(self) -> str:
|
|
180
185
|
return f"{self._parent.name()}:{self._name}={self.value}"
|
|
181
186
|
|
|
182
187
|
|
|
@@ -186,22 +191,22 @@ class PortWire(Port):
|
|
|
186
191
|
* The port wire will instantaneously update the driven wires upon change.
|
|
187
192
|
"""
|
|
188
193
|
|
|
189
|
-
def __init__(self, parent, name, width=1, output=False):
|
|
194
|
+
def __init__(self, parent, name: str, width: int = 1, output: bool = False):
|
|
190
195
|
super().__init__(parent, name, width, output)
|
|
191
|
-
self._port_driver = None # The port that drives this port
|
|
196
|
+
self._port_driver: Port | None = None # The port that drives this port
|
|
192
197
|
|
|
193
|
-
def set_value(self, value):
|
|
198
|
+
def set_value(self, value: int | str | None):
|
|
194
199
|
if value != self.value:
|
|
195
200
|
self.update_wires(value)
|
|
196
201
|
|
|
197
|
-
def set_driver(self, port):
|
|
202
|
+
def set_driver(self, port: Port | None):
|
|
198
203
|
self._port_driver = port
|
|
199
204
|
|
|
200
|
-
def get_driver(self):
|
|
205
|
+
def get_driver(self) -> Port | None:
|
|
201
206
|
"""Get driver for port"""
|
|
202
207
|
return self._port_driver
|
|
203
208
|
|
|
204
|
-
def has_driver(self):
|
|
209
|
+
def has_driver(self) -> bool:
|
|
205
210
|
"""Return True if port has driver"""
|
|
206
211
|
return self._port_driver is not None
|
|
207
212
|
|
|
@@ -213,10 +218,10 @@ class PortIn(PortWire):
|
|
|
213
218
|
* The port will update the parent component upon change.
|
|
214
219
|
"""
|
|
215
220
|
|
|
216
|
-
def __init__(self, parent, name, width=1):
|
|
221
|
+
def __init__(self, parent, name: str, width: int = 1):
|
|
217
222
|
super().__init__(parent, name, width, output=False)
|
|
218
223
|
|
|
219
|
-
def set_value(self, value):
|
|
224
|
+
def set_value(self, value: int | str | None):
|
|
220
225
|
super().set_value(value)
|
|
221
226
|
self.parent().update()
|
|
222
227
|
|
|
@@ -228,40 +233,40 @@ class PortOutDelta(Port):
|
|
|
228
233
|
* The port will update the parent component if the _update_parent variable is set to true.
|
|
229
234
|
"""
|
|
230
235
|
|
|
231
|
-
def __init__(self, parent, name, width=1, delay_ns=1):
|
|
236
|
+
def __init__(self, parent, name: str, width: int = 1, delay_ns: int = 1):
|
|
232
237
|
super().__init__(parent, name, width, output=True)
|
|
233
238
|
self._delay_ns = delay_ns # Propagation delay for this port
|
|
234
239
|
self._update_parent = False # Should this port update parent on change
|
|
235
240
|
|
|
236
|
-
def update_parent(self, update_parent):
|
|
241
|
+
def update_parent(self, update_parent: bool):
|
|
237
242
|
"""Set update parent valiable (True/False)"""
|
|
238
243
|
self._update_parent = update_parent
|
|
239
244
|
|
|
240
|
-
def set_delay_ns(self, delay_ns):
|
|
245
|
+
def set_delay_ns(self, delay_ns: int):
|
|
241
246
|
"""Set port propagation delay"""
|
|
242
247
|
self._delay_ns = delay_ns
|
|
243
248
|
|
|
244
|
-
def set_value(self, value):
|
|
249
|
+
def set_value(self, value: int | str | None):
|
|
245
250
|
self.parent().add_event(self, value, self._delay_ns)
|
|
246
251
|
|
|
247
|
-
def update_port(self, value):
|
|
252
|
+
def update_port(self, value: int | str | None):
|
|
248
253
|
"""Update the port output and the connected wires"""
|
|
249
254
|
self.update_wires(value)
|
|
250
255
|
if self._update_parent:
|
|
251
256
|
self.parent().update()
|
|
252
257
|
|
|
253
|
-
def delta_cycle(self, value):
|
|
258
|
+
def delta_cycle(self, value: int | str | None):
|
|
254
259
|
"""Handle the delta cycle event from the circuit"""
|
|
255
260
|
self.update_port(value)
|
|
256
261
|
|
|
257
|
-
def set_driver(self, port):
|
|
262
|
+
def set_driver(self, port: Port | None):
|
|
258
263
|
raise PortConnectionError(f"The port {self.path()}.{self.name()} cannot be driven")
|
|
259
264
|
|
|
260
|
-
def get_driver(self):
|
|
265
|
+
def get_driver(self) -> Port | None:
|
|
261
266
|
"""Get driver for port, the output port has no driver"""
|
|
262
267
|
return None
|
|
263
268
|
|
|
264
|
-
def has_driver(self):
|
|
269
|
+
def has_driver(self) -> bool:
|
|
265
270
|
"""Return False since the port does not have a driver"""
|
|
266
271
|
return False
|
|
267
272
|
|
|
@@ -274,14 +279,14 @@ class PortOutImmediate(PortOutDelta):
|
|
|
274
279
|
* The port will update the parent component if the _update_parent variable is set to true.
|
|
275
280
|
"""
|
|
276
281
|
|
|
277
|
-
def __init__(self, parent, name, width=1):
|
|
282
|
+
def __init__(self, parent, name: str, width: int = 1):
|
|
278
283
|
super().__init__(parent, name, width)
|
|
279
284
|
|
|
280
|
-
def set_value(self, value):
|
|
285
|
+
def set_value(self, value: int | str | None):
|
|
281
286
|
self.parent().add_event(self, value, 0)
|
|
282
287
|
super().update_port(value)
|
|
283
288
|
|
|
284
|
-
def delta_cycle(self, value):
|
|
289
|
+
def delta_cycle(self, value: int | str | None):
|
|
285
290
|
"""
|
|
286
291
|
Do nothing here, the event is just used to updates waves in Circuit class
|
|
287
292
|
"""
|
|
@@ -294,15 +299,15 @@ class PortWireBit(PortWire):
|
|
|
294
299
|
The PortWireBit will update its parent (a PortMultiBitWire) upon change.
|
|
295
300
|
"""
|
|
296
301
|
|
|
297
|
-
def __init__(self, parent, name, parent_port, output):
|
|
302
|
+
def __init__(self, parent, name: str, parent_port: PortMultiBitWire, output: bool):
|
|
298
303
|
super().__init__(parent, name, 1, output)
|
|
299
304
|
self._parent_port = parent_port
|
|
300
305
|
|
|
301
|
-
def set_value(self, value):
|
|
306
|
+
def set_value(self, value: int | str | None):
|
|
302
307
|
super().set_value(value)
|
|
303
308
|
self._parent_port.update_value_from_bits()
|
|
304
309
|
|
|
305
|
-
def get_parent_port(self):
|
|
310
|
+
def get_parent_port(self) -> PortMultiBitWire:
|
|
306
311
|
"""Get the parent PortMultiBitWire for this port"""
|
|
307
312
|
return self._parent_port
|
|
308
313
|
|
|
@@ -314,8 +319,8 @@ class PortMultiBitWire(Port):
|
|
|
314
319
|
The PortWireMultiBit will add events to the circuit upon change to update vcd output.
|
|
315
320
|
"""
|
|
316
321
|
|
|
317
|
-
def __init__(self, parent, name, width, output=False):
|
|
318
|
-
self._port_driver = None # The port that drives this port
|
|
322
|
+
def __init__(self, parent, name: str, width: int, output: bool = False):
|
|
323
|
+
self._port_driver: Port | None = None # The port that drives this port
|
|
319
324
|
self._bits = []
|
|
320
325
|
super().__init__(parent, name, width, output)
|
|
321
326
|
for bit_id in range(self.width):
|
|
@@ -328,32 +333,32 @@ class PortMultiBitWire(Port):
|
|
|
328
333
|
for bit in self._bits:
|
|
329
334
|
bit.init()
|
|
330
335
|
|
|
331
|
-
def set_value(self, value):
|
|
332
|
-
if value
|
|
336
|
+
def set_value(self, value: int | str | None):
|
|
337
|
+
if value is None or isinstance(value, str):
|
|
333
338
|
return
|
|
334
339
|
for bit_id, bit in enumerate(self._bits):
|
|
335
340
|
bit_val = (value >> bit_id) & 1
|
|
336
341
|
bit.value = bit_val
|
|
337
342
|
|
|
338
|
-
def get_wired_ports_recursive(self):
|
|
343
|
+
def get_wired_ports_recursive(self) -> list[Port]:
|
|
339
344
|
all_wired_ports = super().get_wired_ports_recursive()
|
|
340
345
|
for bit in self._bits:
|
|
341
346
|
all_wired_ports.extend(bit.get_wired_ports_recursive())
|
|
342
347
|
return all_wired_ports
|
|
343
348
|
|
|
344
|
-
def set_driver(self, port):
|
|
349
|
+
def set_driver(self, port: Port | None):
|
|
345
350
|
"""Set port driver"""
|
|
346
351
|
self._port_driver = port
|
|
347
352
|
|
|
348
|
-
def get_driver(self):
|
|
353
|
+
def get_driver(self) -> Port | None:
|
|
349
354
|
"""Get port driver"""
|
|
350
355
|
return self._port_driver
|
|
351
356
|
|
|
352
|
-
def has_driver(self):
|
|
357
|
+
def has_driver(self) -> bool:
|
|
353
358
|
"""Return True if port has driver"""
|
|
354
359
|
return self._port_driver is not None
|
|
355
360
|
|
|
356
|
-
def get_bit(self, bit_id):
|
|
361
|
+
def get_bit(self, bit_id: int) -> Port:
|
|
357
362
|
"""Get bit port"""
|
|
358
363
|
return self._bits[bit_id]
|
|
359
364
|
|
|
@@ -370,7 +375,7 @@ class PortMultiBitWire(Port):
|
|
|
370
375
|
# Send event just to update waves
|
|
371
376
|
self.parent().add_event(self, value, 0)
|
|
372
377
|
|
|
373
|
-
def delta_cycle(self, value):
|
|
378
|
+
def delta_cycle(self, value: int | str | None):
|
|
374
379
|
"""
|
|
375
380
|
Do nothing here, the event passed in 'update_value_from_bits'
|
|
376
381
|
is just used to updates waves in Circuit class
|