digsim-logic-simulator 0.12.0__py3-none-any.whl → 0.14.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.

Files changed (74) hide show
  1. digsim/__init__.py +1 -1
  2. digsim/app/__main__.py +17 -13
  3. digsim/app/gui/__init__.py +1 -1
  4. digsim/app/gui/_circuit_area.py +32 -21
  5. digsim/app/gui/_component_selection.py +3 -1
  6. digsim/app/gui/_main_window.py +1 -1
  7. digsim/app/gui/_top_bar.py +1 -1
  8. digsim/app/gui/_utils.py +1 -1
  9. digsim/app/gui_objects/__init__.py +2 -2
  10. digsim/app/gui_objects/_bus_bit_object.py +1 -1
  11. digsim/app/gui_objects/_buzzer_object.py +7 -7
  12. digsim/app/gui_objects/_component_context_menu.py +2 -2
  13. digsim/app/gui_objects/_component_object.py +11 -10
  14. digsim/app/gui_objects/_component_port_item.py +1 -3
  15. digsim/app/gui_objects/_dip_switch_object.py +6 -5
  16. digsim/app/gui_objects/_gui_note_object.py +11 -12
  17. digsim/app/gui_objects/_gui_object_factory.py +2 -2
  18. digsim/app/gui_objects/_hexdigit_object.py +1 -1
  19. digsim/app/gui_objects/_image_objects.py +8 -6
  20. digsim/app/gui_objects/_label_object.py +4 -3
  21. digsim/app/gui_objects/_logic_analyzer_object.py +19 -10
  22. digsim/app/gui_objects/_seven_segment_object.py +11 -4
  23. digsim/app/gui_objects/_shortcut_objects.py +4 -3
  24. digsim/app/gui_objects/_yosys_object.py +1 -1
  25. digsim/app/model/__init__.py +1 -1
  26. digsim/app/model/_model.py +8 -4
  27. digsim/app/model/_model_components.py +2 -5
  28. digsim/app/model/_model_new_wire.py +3 -2
  29. digsim/app/model/_model_objects.py +2 -6
  30. digsim/app/model/_model_settings.py +2 -2
  31. digsim/app/model/_model_shortcuts.py +1 -15
  32. digsim/app/settings/__init__.py +1 -1
  33. digsim/app/settings/_component_settings.py +2 -2
  34. digsim/app/settings/_gui_settings.py +1 -1
  35. digsim/app/settings/_shortcut_dialog.py +4 -5
  36. digsim/circuit/__init__.py +1 -1
  37. digsim/circuit/_circuit.py +52 -38
  38. digsim/circuit/components/__init__.py +1 -1
  39. digsim/circuit/components/_bus_bits.py +1 -1
  40. digsim/circuit/components/_button.py +2 -6
  41. digsim/circuit/components/_buzzer.py +1 -1
  42. digsim/circuit/components/_clock.py +1 -1
  43. digsim/circuit/components/_dip_switch.py +1 -1
  44. digsim/circuit/components/_flip_flops.py +1 -1
  45. digsim/circuit/components/_hexdigit.py +1 -1
  46. digsim/circuit/components/_ic.py +1 -1
  47. digsim/circuit/components/_label_wire.py +1 -1
  48. digsim/circuit/components/_led.py +1 -1
  49. digsim/circuit/components/_logic_analyzer.py +1 -1
  50. digsim/circuit/components/_mem64kbyte.py +1 -1
  51. digsim/circuit/components/_memstdout.py +1 -1
  52. digsim/circuit/components/_note.py +1 -1
  53. digsim/circuit/components/_on_off_switch.py +1 -1
  54. digsim/circuit/components/_seven_segment.py +1 -1
  55. digsim/circuit/components/_static_level.py +1 -1
  56. digsim/circuit/components/_static_value.py +1 -1
  57. digsim/circuit/components/_yosys_component.py +2 -2
  58. digsim/circuit/components/atoms/__init__.py +1 -0
  59. digsim/circuit/components/atoms/_component.py +14 -4
  60. digsim/circuit/components/atoms/_digsim_exception.py +1 -1
  61. digsim/circuit/components/atoms/_port.py +45 -29
  62. digsim/storage_model/_app.py +8 -3
  63. digsim/storage_model/_circuit.py +16 -8
  64. digsim/synth/__init__.py +1 -1
  65. digsim/synth/__main__.py +3 -3
  66. digsim/synth/_synthesis.py +6 -3
  67. digsim/utils/__init__.py +1 -1
  68. digsim/utils/_yosys_netlist.py +10 -4
  69. {digsim_logic_simulator-0.12.0.dist-info → digsim_logic_simulator-0.14.0.dist-info}/METADATA +4 -5
  70. digsim_logic_simulator-0.14.0.dist-info/RECORD +105 -0
  71. digsim_logic_simulator-0.12.0.dist-info/RECORD +0 -105
  72. {digsim_logic_simulator-0.12.0.dist-info → digsim_logic_simulator-0.14.0.dist-info}/WHEEL +0 -0
  73. {digsim_logic_simulator-0.12.0.dist-info → digsim_logic_simulator-0.14.0.dist-info}/licenses/LICENSE.md +0 -0
  74. {digsim_logic_simulator-0.12.0.dist-info → digsim_logic_simulator-0.14.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """A button component with an image as symbol the GUI"""
@@ -37,6 +37,8 @@ class ButtonObject(ImageObject):
37
37
  IMAGE_FILENAME = "images/PB.png"
38
38
  BUTTON_RADIUS = 35
39
39
 
40
+ _BUTTON_ACTIVE_PEN = QPen(Qt.green)
41
+
40
42
  def __init__(self, app_model, component, xpos, ypos):
41
43
  super().__init__(app_model, component, xpos, ypos)
42
44
  self.show_name(False)
@@ -45,9 +47,8 @@ class ButtonObject(ImageObject):
45
47
  def paint_component(self, painter):
46
48
  super().paint_component(painter)
47
49
  if self.component.active:
48
- pen = QPen()
50
+ pen = self._BUTTON_ACTIVE_PEN
49
51
  pen.setWidth(4)
50
- pen.setColor(Qt.green)
51
52
  painter.setPen(pen)
52
53
  painter.setBrush(Qt.NoBrush)
53
54
  painter.drawEllipse(
@@ -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
  """A yosys component with an image as symbol the GUI"""
@@ -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.app.model namespace"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """An application model for a GUI simulated circuit"""
@@ -37,15 +37,18 @@ class AppModel(QThread):
37
37
 
38
38
  def __init__(self):
39
39
  super().__init__()
40
- self._model_objects = ModelObjects(self)
41
- self._model_shortcuts = ModelShortcuts(self)
42
- self._model_settings = ModelSettings(self)
40
+ self._setup_model_components()
43
41
  self._started = False
44
42
  self._single_step = False
45
43
  self._changed = False
46
44
  self._gui_event_queue = queue.Queue()
47
45
  self._multi_select = False
48
46
 
47
+ def _setup_model_components(self):
48
+ self._model_objects = ModelObjects(self)
49
+ self._model_shortcuts = ModelShortcuts(self)
50
+ self._model_settings = ModelSettings(self)
51
+
49
52
  @property
50
53
  def objects(self):
51
54
  """return the model objects"""
@@ -100,6 +103,7 @@ class AppModel(QThread):
100
103
  def model_stop(self):
101
104
  """Stop model simulation thread"""
102
105
  self._started = False
106
+ self.wait()
103
107
 
104
108
  def model_reset(self):
105
109
  """Reset model simulation"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """Handle component objects in the model"""
@@ -125,10 +125,7 @@ class ModelComponents:
125
125
 
126
126
  def get_object_list(self):
127
127
  """Get list of component objects"""
128
- component_objects = []
129
- for _, comp in self._component_objects.items():
130
- component_objects.append(comp)
131
- return component_objects
128
+ return list(self._component_objects.values())
132
129
 
133
130
  def update_settings(self, component_object, settings):
134
131
  """Update settings for a component"""
@@ -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
  """Handle new wire object in the model"""
@@ -29,11 +29,12 @@ class NewWire:
29
29
 
30
30
  def end(self, component, portname):
31
31
  """End new wire object"""
32
- self._app_model.objects.push_undo_state()
33
32
  end_port = component.port(portname)
34
33
  if self._start_port.is_output() and end_port.is_input():
34
+ self._app_model.objects.push_undo_state()
35
35
  self._start_port.wire = end_port
36
36
  elif self._start_port.is_input() and end_port.is_output():
37
+ self._app_model.objects.push_undo_state()
37
38
  end_port.wire = self._start_port
38
39
  else:
39
40
  raise PortConnectionError("Cannot connect to port of same type")
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """Handle objects in the model"""
@@ -53,11 +53,7 @@ class ModelObjects:
53
53
 
54
54
  def get_selected(self):
55
55
  """Get selected objects"""
56
- objects = []
57
- for obj in self.get_list():
58
- if obj.selected:
59
- objects.append(obj)
60
- return objects
56
+ return [obj for obj in self.get_list() if obj.selected]
61
57
 
62
58
  def _delete(self, selected_objects):
63
59
  for obj in selected_objects:
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """Handle settings in the model"""
@@ -23,7 +23,7 @@ class ModelSettings:
23
23
 
24
24
  def get_all(self):
25
25
  """Return settings dict"""
26
- return self._settings
26
+ return self._settings.copy()
27
27
 
28
28
  def from_dict(self, circuit_dict):
29
29
  """Get settings from circuit dict"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """Handle shortcuts in the model"""
@@ -38,20 +38,6 @@ class ModelShortcuts:
38
38
  """Get shortcut"""
39
39
  return self._shortcut_component.get(key)
40
40
 
41
- def _qtkey_to_key(self, qt_key):
42
- return {
43
- Qt.Key_0: "0",
44
- Qt.Key_1: "1",
45
- Qt.Key_2: "2",
46
- Qt.Key_3: "3",
47
- Qt.Key_4: "4",
48
- Qt.Key_5: "5",
49
- Qt.Key_6: "6",
50
- Qt.Key_7: "7",
51
- Qt.Key_8: "8",
52
- Qt.Key_9: "9",
53
- }.get(qt_key)
54
-
55
41
  def press(self, qtkey):
56
42
  """Handle shortcut keypress"""
57
43
  key = self.QT_KEY_TO_KEY.get(qtkey)
@@ -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.app.settings namespace"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """The component settings dialog(s)"""
@@ -271,7 +271,7 @@ class ComponentSettingsCheckBoxWidthBool(ComponentSettingsBase):
271
271
  checkbox = QCheckBox(f"bit{checkbox_id}")
272
272
  checkbox.setChecked(True)
273
273
  checkbox.stateChanged.connect(self._update)
274
- self.layout().addWidget(checkbox, checkbox_id % 8, checkbox_id / 8, 1, 1)
274
+ self.layout().addWidget(checkbox, checkbox_id % 8, checkbox_id // 8, 1, 1)
275
275
  self._bit_checkboxes.append(checkbox)
276
276
  disable_all = QPushButton("Disable All")
277
277
  disable_all.clicked.connect(self._disable_all)
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """The GUI settings dialog"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """The shurtcut dialog"""
@@ -20,12 +20,11 @@ class ShortcutDialog(QDialog):
20
20
  self.layout().addWidget(QLabel("Select shortcut key for component"))
21
21
  self._key_selector = QComboBox(parent)
22
22
  for key in "1234567890":
23
- shortcut_key = str(key)
24
- shortcut_string = shortcut_key
25
- shortcut_component = app_model.shortcuts.get_component(shortcut_key)
23
+ shortcut_string = key
24
+ shortcut_component = app_model.shortcuts.get_component(key)
26
25
  if shortcut_component is not None:
27
26
  shortcut_string += f" - {shortcut_component.name()}"
28
- self._key_selector.addItem(shortcut_string, userData=shortcut_key)
27
+ self._key_selector.addItem(shortcut_string, userData=key)
29
28
  self.layout().addWidget(self._key_selector)
30
29
  self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
31
30
  self.buttonBox.accepted.connect(self.accept)
@@ -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 namespace"""
@@ -7,13 +7,14 @@ Module that handles the circuit simulation of components
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- import os
10
+ import heapq
11
+ import pathlib
11
12
  from typing import Tuple
12
13
 
13
14
  from digsim.storage_model import CircuitDataClass, CircuitFileDataClass
14
15
 
15
16
  from ._waves_writer import WavesWriter
16
- from .components.atoms import Component, DigsimException, PortOutDelta
17
+ from .components.atoms import VALUE_TYPE, Component, DigsimException, PortOutDelta
17
18
 
18
19
 
19
20
  class CircuitError(DigsimException):
@@ -26,10 +27,10 @@ class CircuitEvent:
26
27
  delta events in the simulation.
27
28
  """
28
29
 
29
- def __init__(self, time_ns: int, port: PortOutDelta, value: int | str | None):
30
+ def __init__(self, time_ns: int, port: PortOutDelta, value: VALUE_TYPE):
30
31
  self._time_ns: int = time_ns
31
32
  self._port: PortOutDelta = port
32
- self._value: int | str | None = value
33
+ self._value: VALUE_TYPE = value
33
34
 
34
35
  @property
35
36
  def time_ns(self) -> int:
@@ -42,7 +43,7 @@ class CircuitEvent:
42
43
  return self._port
43
44
 
44
45
  @property
45
- def value(self) -> int | str | None:
46
+ def value(self) -> VALUE_TYPE:
46
47
  """Get the delta cycle value of this event"""
47
48
  return self._value
48
49
 
@@ -50,7 +51,7 @@ class CircuitEvent:
50
51
  """Return True if the in the event is the same as"""
51
52
  return port == self._port
52
53
 
53
- def update(self, time_ns: int, value: int | str | None):
54
+ def update(self, time_ns: int, value: VALUE_TYPE):
54
55
  """Update the event with a new time (ns) and a new value"""
55
56
  self._time_ns = time_ns
56
57
  self._value = value
@@ -58,6 +59,14 @@ class CircuitEvent:
58
59
  def __lt__(self, other) -> bool:
59
60
  return other.time_ns > self.time_ns
60
61
 
62
+ def __eq__(self, other) -> bool:
63
+ return (
64
+ isinstance(other, CircuitEvent)
65
+ and self._port == other._port
66
+ and self._time_ns == other._time_ns
67
+ and self._value == other._value
68
+ )
69
+
61
70
 
62
71
  class Circuit:
63
72
  """Class thay handles the circuit simulation"""
@@ -65,6 +74,7 @@ class Circuit:
65
74
  def __init__(self, name: str | None = None, vcd: str | None = None):
66
75
  self._components: dict[str, Component] = {}
67
76
  self._circuit_events: list[CircuitEvent] = []
77
+ self._events_by_port: dict[PortOutDelta, CircuitEvent] = {}
68
78
  self._name: str | None = name
69
79
  self._time_ns: int = 0
70
80
  self._folder: str | None = None
@@ -86,10 +96,7 @@ class Circuit:
86
96
  @property
87
97
  def components(self) -> list[Component]:
88
98
  """Get the components in this circuit"""
89
- comp_array = []
90
- for _, comp in self._components.items():
91
- comp_array.append(comp)
92
- return comp_array
99
+ return list(self._components.values())
93
100
 
94
101
  def load_path(self, path) -> str:
95
102
  """Get the load path relative to the circuit path"""
@@ -100,7 +107,9 @@ class Circuit:
100
107
  def store_path(self, path) -> str:
101
108
  """Get the store path relative to the circuit path"""
102
109
  if self._folder is not None:
103
- return os.path.relpath(path, self._folder)
110
+ return str(
111
+ pathlib.Path(path).resolve().absolute().relative_to(pathlib.Path(self._folder))
112
+ )
104
113
  return path
105
114
 
106
115
  def delete_component(self, component: Component):
@@ -110,16 +119,13 @@ class Circuit:
110
119
 
111
120
  def get_toplevel_components(self) -> list[Component]:
112
121
  """Get toplevel components in the circuit"""
113
- toplevel_components = []
114
- for _, comp in self._components.items():
115
- if comp.is_toplevel():
116
- toplevel_components.append(comp)
117
- return toplevel_components
122
+ return [comp for comp in self._components.values() if comp.is_toplevel()]
118
123
 
119
124
  def init(self):
120
125
  """Initialize circuit and components (and ports)"""
121
126
  self._time_ns = 0
122
127
  self._circuit_events = []
128
+ self._events_by_port = {}
123
129
  if self._vcd is not None:
124
130
  self._vcd_init()
125
131
  for _, comp in self._components.items():
@@ -175,22 +181,32 @@ class Circuit:
175
181
  Process one simulation event
176
182
  Return False if ther are now events of if the stop_time has passed
177
183
  """
178
- if len(self._circuit_events) == 0:
179
- return False, False
180
- self._circuit_events.sort()
181
- if stop_time_ns is None or self._circuit_events[0].time_ns > stop_time_ns:
182
- return False, False
183
- event = self._circuit_events.pop(0)
184
- # print(
185
- # f"Execute event {event.port.path()}.{event.port.name()}"
186
- # " {event.time_ns} {event.value}"
187
- # )
188
- self._time_ns = event.time_ns
189
- event.port.delta_cycle(event.value)
190
- toplevel = event.port.parent().is_toplevel()
191
- if self._vcd is not None:
192
- self._vcd.write(event.port, self._time_ns)
193
- return True, toplevel
184
+ while self._circuit_events:
185
+ event = heapq.heappop(self._circuit_events)
186
+
187
+ # Check if this is the latest event for this port
188
+ if event != self._events_by_port.get(event.port):
189
+ # This is a stale event, ignore it
190
+ continue
191
+
192
+ if stop_time_ns is not None and event.time_ns > stop_time_ns:
193
+ # Put the event back if it's after the stop time
194
+ heapq.heappush(self._circuit_events, event)
195
+ return False, False
196
+
197
+ del self._events_by_port[event.port]
198
+ # print(
199
+ # f"Execute event {event.port.path()}.{event.port.name()}"
200
+ # f" {event.time_ns} {event.value}"
201
+ # )
202
+ self._time_ns = event.time_ns
203
+ event.port.delta_cycle(event.value)
204
+ toplevel = event.port.parent().is_toplevel()
205
+ if self._vcd is not None:
206
+ self._vcd.write(event.port, self._time_ns)
207
+ return True, toplevel
208
+
209
+ return False, False
194
210
 
195
211
  def _is_toplevel_event(self) -> bool:
196
212
  if len(self._circuit_events) == 0:
@@ -231,15 +247,13 @@ class Circuit:
231
247
  if stop_time_ns >= self._time_ns:
232
248
  self.run(ns=stop_time_ns - self._time_ns)
233
249
 
234
- def add_event(self, port: PortOutDelta, value: int | str | None, propagation_delay_ns: int):
250
+ def add_event(self, port: PortOutDelta, value: VALUE_TYPE, propagation_delay_ns: int):
235
251
  """Add delta cycle event, this will also write values to .vcd file"""
236
252
  event_time_ns = self._time_ns + propagation_delay_ns
237
253
  # print(f"Add event {port.parent().name()}:{port.name()} => {value}")
238
- for event in self._circuit_events:
239
- if event.is_same_event(port):
240
- event.update(event_time_ns, value)
241
- return
242
- self._circuit_events.append(CircuitEvent(event_time_ns, port, value))
254
+ event = CircuitEvent(event_time_ns, port, value)
255
+ self._events_by_port[port] = event
256
+ heapq.heappush(self._circuit_events, event)
243
257
 
244
258
  def add_component(self, component: Component):
245
259
  """Add component to circuit"""
@@ -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 namespace"""
@@ -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
  """Module with the bus/bit <=> bus/bit components"""
@@ -1,23 +1,19 @@
1
- # Copyright (c) Fredrik Andersson, 2023
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """A PushButton component"""
5
5
 
6
- import logging
7
-
8
6
  from .atoms import CallbackComponent, PortOutImmediate
9
7
 
10
8
 
11
9
  class PushButton(CallbackComponent):
12
10
  """PushButton component class"""
13
11
 
14
- def __init__(self, circuit, name=None, inverted=False):
12
+ def __init__(self, circuit, name=None):
15
13
  super().__init__(circuit, name)
16
14
  portout = PortOutImmediate(self, "O")
17
15
  self.add_port(portout)
18
16
  portout.update_parent(True)
19
- if inverted:
20
- logging.warning("Setting 'inverted' has been removed")
21
17
 
22
18
  def default_state(self):
23
19
  self.release()
@@ -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
  """Module with the Buzzer component"""
@@ -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
  """Clock component module"""
@@ -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
  """A Dip-switch component"""
@@ -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
  """Module with flip flops and latches"""
@@ -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
  """HexDigit component module"""
@@ -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
  """
@@ -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
  """Label Wire components module"""
@@ -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
  """Module with the LED component"""
@@ -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
  """Module with Logic Analyzer component"""
@@ -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
  """
@@ -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
  """
@@ -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
  """Components that only exist in the model"""
@@ -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
  """An On/Off Switch component"""
@@ -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
  """Module with 7-segment LED component"""
@@ -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
  """Module with the Static Logic components"""
@@ -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
  """Module with the Static Leve component"""
@@ -1,4 +1,4 @@
1
- # Copyright (c) Fredrik Andersson, 2023-2024
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
2
  # All rights reserved
3
3
 
4
4
  """
@@ -26,7 +26,7 @@ class YosysComponent(MultiComponent):
26
26
  def __init__(self, circuit, path=None, name=None, nets=True):
27
27
  super().__init__(circuit, name)
28
28
  self._circuit = circuit
29
- self._path = path
29
+ self._path = str(path)
30
30
  self._gates_comp = None
31
31
  self._net_comp = None
32
32
  self._netlist_module = None
@@ -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
+ VALUE_TYPE,
14
15
  Port,
15
16
  PortConnectionError,
16
17
  PortIn,
@@ -63,10 +63,20 @@ class Component(abc.ABC):
63
63
  self._ports = []
64
64
 
65
65
  def path(self) -> str:
66
- """Get component path"""
67
- if self._parent is not None:
68
- return f"{self._parent.path()}.{self.name()}"
69
- return f"{self.name()}"
66
+ """Get component path (iterative)"""
67
+ path_parts = []
68
+ current_component = self
69
+ while current_component is not None:
70
+ name = current_component.name()
71
+ if not isinstance(name, str):
72
+ raise TypeError(
73
+ f"Component name is not a string: "
74
+ f"{name} ({type(name)}), "
75
+ f"component: {current_component.__class__.__name__}"
76
+ )
77
+ path_parts.append(name)
78
+ current_component = current_component._parent
79
+ return ".".join(reversed(path_parts))
70
80
 
71
81
  @property
72
82
  def ports(self):
@@ -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 digsim exception base"""