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.
Files changed (107) hide show
  1. digsim/__init__.py +6 -0
  2. digsim/app/__main__.py +12 -0
  3. digsim/app/cli.py +68 -0
  4. digsim/app/gui/__init__.py +6 -0
  5. digsim/app/gui/_circuit_area.py +468 -0
  6. digsim/app/gui/_component_selection.py +154 -0
  7. digsim/app/gui/_main_window.py +163 -0
  8. digsim/app/gui/_top_bar.py +339 -0
  9. digsim/app/gui/_utils.py +26 -0
  10. digsim/app/gui/_warning_dialog.py +46 -0
  11. digsim/app/gui_objects/__init__.py +7 -0
  12. digsim/app/gui_objects/_bus_bit_object.py +94 -0
  13. digsim/app/gui_objects/_buzzer_object.py +97 -0
  14. digsim/app/gui_objects/_component_context_menu.py +79 -0
  15. digsim/app/gui_objects/_component_object.py +374 -0
  16. digsim/app/gui_objects/_component_port_item.py +63 -0
  17. digsim/app/gui_objects/_dip_switch_object.py +104 -0
  18. digsim/app/gui_objects/_gui_note_object.py +80 -0
  19. digsim/app/gui_objects/_gui_object_factory.py +80 -0
  20. digsim/app/gui_objects/_hexdigit_object.py +53 -0
  21. digsim/app/gui_objects/_image_objects.py +239 -0
  22. digsim/app/gui_objects/_label_object.py +97 -0
  23. digsim/app/gui_objects/_logic_analyzer_object.py +86 -0
  24. digsim/app/gui_objects/_seven_segment_object.py +131 -0
  25. digsim/app/gui_objects/_shortcut_objects.py +82 -0
  26. digsim/app/gui_objects/_yosys_object.py +32 -0
  27. digsim/app/gui_objects/images/AND.png +0 -0
  28. digsim/app/gui_objects/images/Analyzer.png +0 -0
  29. digsim/app/gui_objects/images/BUF.png +0 -0
  30. digsim/app/gui_objects/images/Buzzer.png +0 -0
  31. digsim/app/gui_objects/images/Clock.png +0 -0
  32. digsim/app/gui_objects/images/DFF.png +0 -0
  33. digsim/app/gui_objects/images/DIP_SWITCH.png +0 -0
  34. digsim/app/gui_objects/images/FlipFlop.png +0 -0
  35. digsim/app/gui_objects/images/IC.png +0 -0
  36. digsim/app/gui_objects/images/LED_OFF.png +0 -0
  37. digsim/app/gui_objects/images/LED_ON.png +0 -0
  38. digsim/app/gui_objects/images/MUX.png +0 -0
  39. digsim/app/gui_objects/images/NAND.png +0 -0
  40. digsim/app/gui_objects/images/NOR.png +0 -0
  41. digsim/app/gui_objects/images/NOT.png +0 -0
  42. digsim/app/gui_objects/images/ONE.png +0 -0
  43. digsim/app/gui_objects/images/OR.png +0 -0
  44. digsim/app/gui_objects/images/PB.png +0 -0
  45. digsim/app/gui_objects/images/Switch_OFF.png +0 -0
  46. digsim/app/gui_objects/images/Switch_ON.png +0 -0
  47. digsim/app/gui_objects/images/XNOR.png +0 -0
  48. digsim/app/gui_objects/images/XOR.png +0 -0
  49. digsim/app/gui_objects/images/YOSYS.png +0 -0
  50. digsim/app/gui_objects/images/ZERO.png +0 -0
  51. digsim/app/images/app_icon.png +0 -0
  52. digsim/app/model/__init__.py +6 -0
  53. digsim/app/model/_model.py +210 -0
  54. digsim/app/model/_model_components.py +162 -0
  55. digsim/app/model/_model_new_wire.py +57 -0
  56. digsim/app/model/_model_objects.py +155 -0
  57. digsim/app/model/_model_settings.py +35 -0
  58. digsim/app/model/_model_shortcuts.py +72 -0
  59. digsim/app/settings/__init__.py +8 -0
  60. digsim/app/settings/_component_settings.py +415 -0
  61. digsim/app/settings/_gui_settings.py +71 -0
  62. digsim/app/settings/_shortcut_dialog.py +39 -0
  63. digsim/circuit/__init__.py +7 -0
  64. digsim/circuit/_circuit.py +329 -0
  65. digsim/circuit/_waves_writer.py +61 -0
  66. digsim/circuit/components/__init__.py +26 -0
  67. digsim/circuit/components/_bus_bits.py +68 -0
  68. digsim/circuit/components/_button.py +44 -0
  69. digsim/circuit/components/_buzzer.py +45 -0
  70. digsim/circuit/components/_clock.py +54 -0
  71. digsim/circuit/components/_dip_switch.py +73 -0
  72. digsim/circuit/components/_flip_flops.py +99 -0
  73. digsim/circuit/components/_gates.py +246 -0
  74. digsim/circuit/components/_hexdigit.py +82 -0
  75. digsim/circuit/components/_ic.py +36 -0
  76. digsim/circuit/components/_label_wire.py +167 -0
  77. digsim/circuit/components/_led.py +18 -0
  78. digsim/circuit/components/_logic_analyzer.py +60 -0
  79. digsim/circuit/components/_mem64kbyte.py +42 -0
  80. digsim/circuit/components/_memstdout.py +37 -0
  81. digsim/circuit/components/_note.py +25 -0
  82. digsim/circuit/components/_on_off_switch.py +54 -0
  83. digsim/circuit/components/_seven_segment.py +28 -0
  84. digsim/circuit/components/_static_level.py +28 -0
  85. digsim/circuit/components/_static_value.py +44 -0
  86. digsim/circuit/components/_yosys_atoms.py +1353 -0
  87. digsim/circuit/components/_yosys_component.py +232 -0
  88. digsim/circuit/components/atoms/__init__.py +23 -0
  89. digsim/circuit/components/atoms/_component.py +280 -0
  90. digsim/circuit/components/atoms/_digsim_exception.py +8 -0
  91. digsim/circuit/components/atoms/_port.py +398 -0
  92. digsim/circuit/components/ic/74162.json +1331 -0
  93. digsim/circuit/components/ic/7448.json +834 -0
  94. digsim/storage_model/__init__.py +7 -0
  95. digsim/storage_model/_app.py +58 -0
  96. digsim/storage_model/_circuit.py +126 -0
  97. digsim/synth/__init__.py +6 -0
  98. digsim/synth/__main__.py +67 -0
  99. digsim/synth/_synthesis.py +156 -0
  100. digsim/utils/__init__.py +6 -0
  101. digsim/utils/_yosys_netlist.py +134 -0
  102. digsim_logic_simulator-0.22.0.dist-info/METADATA +140 -0
  103. digsim_logic_simulator-0.22.0.dist-info/RECORD +107 -0
  104. digsim_logic_simulator-0.22.0.dist-info/WHEEL +5 -0
  105. digsim_logic_simulator-0.22.0.dist-info/entry_points.txt +2 -0
  106. digsim_logic_simulator-0.22.0.dist-info/licenses/LICENSE.md +32 -0
  107. digsim_logic_simulator-0.22.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,232 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """
5
+ Module with classes to create a yosys component
6
+ from a yosys json netlist.
7
+ """
8
+
9
+ import json
10
+
11
+ import digsim.circuit.components._yosys_atoms
12
+ from digsim.synth import Synthesis
13
+ from digsim.utils import YosysCell, YosysModule, YosysNetlist
14
+
15
+ from ._static_level import GND, VDD
16
+ from .atoms import Component, DigsimException, MultiComponent, PortMultiBitWire
17
+
18
+
19
+ class YosysComponentException(DigsimException):
20
+ """Yosys component exception class"""
21
+
22
+
23
+ class YosysComponent(MultiComponent):
24
+ """Class to create a yosys component from a yosys json netlist"""
25
+
26
+ def __init__(self, circuit, path=None, name=None, nets=True):
27
+ super().__init__(circuit, name)
28
+ self._circuit = circuit
29
+ self._path = str(path)
30
+ self._gates_comp = None
31
+ self._net_comp = None
32
+ self._netlist_module = None
33
+ self._netlist_nets = None
34
+ self._setup_base()
35
+
36
+ if nets:
37
+ self._net_comp = Component(self._circuit, "nets")
38
+ self.add(self._net_comp)
39
+
40
+ if path is not None:
41
+ self._load_file()
42
+
43
+ def _setup_base(self):
44
+ self._gates_comp = MultiComponent(self._circuit, "gates")
45
+ self.add(self._gates_comp)
46
+
47
+ def create_from_netlist(self, netlist_object):
48
+ """Create component from netlist object"""
49
+ modules = netlist_object.get_modules()
50
+ module_name = list(modules.keys())[0]
51
+ self._netlist_module = netlist_object.get_modules()[module_name]
52
+ self._netlist_nets = self._netlist_module.get_nets()
53
+
54
+ # Set Name
55
+ self.set_name(module_name)
56
+ self.set_display_name(module_name)
57
+ # Add External Ports
58
+ for portname, port_dict in self._netlist_module.ports.items():
59
+ external_port = PortMultiBitWire(
60
+ self, portname, width=len(port_dict.bits), output=port_dict.is_output
61
+ )
62
+ self.add_port(external_port)
63
+ # Create component
64
+ self._create_component()
65
+
66
+ def reload_from_netlist(self, netlist_object):
67
+ """Reload netlist from netlist object"""
68
+ modules = netlist_object.get_modules()
69
+ module_name = list(modules.keys())[0]
70
+ reload_module = netlist_object.get_modules()[module_name]
71
+ reload_nets = reload_module.get_nets()
72
+
73
+ if not self._netlist_module.is_same_interface(reload_module):
74
+ raise YosysComponentException("Yosys component interface differs")
75
+
76
+ # Disconnect ports
77
+ self._disconnect_external_ports()
78
+ # Remove yosys atoms
79
+ self._gates_comp.remove_all_components()
80
+ # Remove nets
81
+ if self._net_comp is not None:
82
+ self._net_comp.delete_all_ports()
83
+ # Setup netlist
84
+ self._netlist_module = reload_module
85
+ self._netlist_nets = reload_nets
86
+ # Create component
87
+ self._create_component()
88
+
89
+ def _create_cells(self):
90
+ """Create cells in component"""
91
+ components_dict = {}
92
+ for cellname, cell in self._netlist_module.cells.items():
93
+ if cell.type == "$scopeinfo":
94
+ continue
95
+ component_class = getattr(
96
+ digsim.circuit.components._yosys_atoms, cell.component_type()
97
+ )
98
+ component = component_class(self._circuit, name=cell.component_name(cellname))
99
+ self._gates_comp.add(component)
100
+ components_dict[cellname] = component
101
+
102
+ vdd = VDD(self._circuit)
103
+ self._gates_comp.add(vdd)
104
+ components_dict["VDD"] = vdd
105
+ gnd = GND(self._circuit)
106
+ self._gates_comp.add(gnd)
107
+ components_dict["GND"] = gnd
108
+
109
+ return components_dict
110
+
111
+ def _connect_sinks(self, components_dict, src_comp_port, sinks):
112
+ """Connect a source port to multiple sinks"""
113
+ for sink_port in sinks:
114
+ if isinstance(sink_port.parent, YosysModule):
115
+ # Connect cell output to module top
116
+ dst_port = self.port(sink_port.name).get_bit(sink_port.bit_index)
117
+ src_comp_port.wire = dst_port
118
+ else:
119
+ # Connect cell output to cell input
120
+ dst_comp = components_dict[sink_port.parent_name]
121
+ src_comp_port.wire = dst_comp.port(sink_port.name)
122
+
123
+ def _connect_cells(self, components_dict):
124
+ """Connect all cells"""
125
+ for net, source in self._netlist_nets.source.items():
126
+ if isinstance(source.parent, YosysModule):
127
+ # Only connect cells here
128
+ continue
129
+ if isinstance(source.parent, YosysCell):
130
+ src_comp = components_dict[source.parent_name]
131
+ self._connect_sinks(
132
+ components_dict, src_comp.port(source.name), self._netlist_nets.sinks[net]
133
+ )
134
+
135
+ gnd_sinks = self._netlist_nets.sinks.get("0", [])
136
+ self._connect_sinks(components_dict, components_dict["GND"].port("O"), gnd_sinks)
137
+ vdd_sinks = self._netlist_nets.sinks.get("1", [])
138
+ self._connect_sinks(components_dict, components_dict["VDD"].port("O"), vdd_sinks)
139
+
140
+ def _connect_external_input_port(self, components_dict, portname, port_dict):
141
+ """Connect external input port"""
142
+ for bit_idx, net in enumerate(port_dict.bits):
143
+ for sink_port in self._netlist_nets.sinks[net]:
144
+ if isinstance(sink_port.parent, YosysModule):
145
+ # Connect module input to module output
146
+ dst_port = self.port(sink_port.name).get_bit(sink_port.bit_index)
147
+ self.port(portname).get_bit(bit_idx).wire = dst_port
148
+ else:
149
+ # Connect module input to cell input
150
+ self._connect_sinks(
151
+ components_dict, self.port(portname).get_bit(bit_idx), [sink_port]
152
+ )
153
+
154
+ def _connect_external_input(self, components_dict):
155
+ """Connect all external input ports"""
156
+ for portname, port_dict in self._netlist_module.ports.items():
157
+ if port_dict.direction == "output":
158
+ continue
159
+ self._connect_external_input_port(components_dict, portname, port_dict)
160
+
161
+ def _create_component(self):
162
+ """Create yosys component"""
163
+ # Create cells
164
+ components_dict = self._create_cells()
165
+ # Connect cells
166
+ self._connect_cells(components_dict)
167
+ # Connect external input ports
168
+ self._connect_external_input(components_dict)
169
+
170
+ def _synth_verilog(self):
171
+ """Synthesize verilog to netlist"""
172
+ modules = Synthesis.list_modules(self._path)
173
+ if len(modules) == 1:
174
+ toplevel = modules[0]
175
+ else:
176
+ raise YosysComponentException("Current only one module per verilog file is supported")
177
+
178
+ synthesis = Synthesis(self._path, toplevel)
179
+ return synthesis.synth_to_dict(silent=True)
180
+
181
+ def _load_netlist_dict(self):
182
+ """Load yosys netlist dict"""
183
+ if self._path.endswith(".json"):
184
+ with open(self._path, encoding="utf-8") as json_file:
185
+ netlist_dict = json.load(json_file)
186
+ elif self._path.endswith(".v"):
187
+ netlist_dict = self._synth_verilog()
188
+ else:
189
+ raise YosysComponentException(f"Unknown file extension '{self._path}'")
190
+
191
+ yosys_netlist = YosysNetlist(**netlist_dict)
192
+ modules = yosys_netlist.get_modules()
193
+
194
+ if len(modules) > 1:
195
+ raise YosysComponentException("Only one module per file is supported")
196
+
197
+ return yosys_netlist
198
+
199
+ def _load_file(self):
200
+ """Load yosys verilog/json-netlist file"""
201
+ yosys_netlist = self._load_netlist_dict()
202
+ self.create_from_netlist(yosys_netlist)
203
+
204
+ def reload_file(self):
205
+ """Reload yosys verilog/json-netlist file"""
206
+ yosys_netlist = self._load_netlist_dict()
207
+ self.reload_from_netlist(yosys_netlist)
208
+
209
+ def _disconnect_external_ports(self):
210
+ """Disconnect external ports before reload"""
211
+ for port in self.inports():
212
+ for bit_id in range(port.width):
213
+ bit_port = port.get_bit(bit_id)
214
+ bit_port.remove_wires()
215
+ for port in self.outports():
216
+ for bit_id in range(port.width):
217
+ bit_port = port.get_bit(bit_id)
218
+ bit_port.set_driver(None)
219
+
220
+ def settings_to_dict(self):
221
+ path = self.circuit.store_path(self._path)
222
+ return {"path": path}
223
+
224
+ @classmethod
225
+ def get_parameters(cls):
226
+ return {
227
+ "path": {
228
+ "type": "path",
229
+ "fileinfo": "Yosys JSON Netlist (*.json);;Verilog File (*.v)",
230
+ "description": "Select verilog file or Yosys json netlist",
231
+ }
232
+ }
@@ -0,0 +1,23 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """All classes within digsim.circuit.components.atoms namespace"""
5
+
6
+ from ._component import ( # noqa: F401
7
+ CallbackComponent,
8
+ Component,
9
+ ComponentException,
10
+ MultiComponent,
11
+ )
12
+ from ._digsim_exception import DigsimException # noqa: F401
13
+ from ._port import ( # noqa: F401
14
+ VALUE_TYPE,
15
+ Port,
16
+ PortConnectionError,
17
+ PortIn,
18
+ PortMultiBitWire,
19
+ PortOutDelta,
20
+ PortOutImmediate,
21
+ PortWire,
22
+ PortWireBit,
23
+ )
@@ -0,0 +1,280 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """This module contains the base classes for all component types"""
5
+
6
+ from __future__ import annotations
7
+
8
+ import abc
9
+ import copy
10
+ from typing import Callable
11
+
12
+ from ._digsim_exception import DigsimException
13
+ from ._port import Port
14
+
15
+
16
+ class ComponentException(DigsimException):
17
+ """Component error exception class"""
18
+
19
+
20
+ class Component(abc.ABC):
21
+ """The component base class"""
22
+
23
+ def __init__(self, circuit, name: str | None = None, display_name: str | None = None):
24
+ self._circuit = circuit
25
+ self._name: str = name or self.__class__.__name__
26
+ self._parent: Component | None = None
27
+ self._ports: list[Port] = []
28
+ self._circuit.add_component(self)
29
+ self._display_name: str = display_name or self.__class__.__name__
30
+ self._parameters: dict[str, int | str | bool] = {}
31
+
32
+ def init(self):
33
+ """Initialize port, will be called when circuit is initialized"""
34
+ for port in self._ports:
35
+ port.init()
36
+
37
+ def default_state(self):
38
+ """Setup default state, for example port outputs"""
39
+
40
+ def clear(self):
41
+ """Remove static state within the component class"""
42
+
43
+ def parameter_set(self, parameter: str, value: int | str | bool):
44
+ """Set component parameter"""
45
+ self._parameters[parameter] = value
46
+
47
+ def parameter_get(self, parameter: str) -> int | str | bool:
48
+ """Get component parameter"""
49
+ return self._parameters[parameter]
50
+
51
+ def add_port(self, port: Port):
52
+ """
53
+ Add port to component,
54
+ also add a 'portname' variable to the component with help of the 'self.__dict__'
55
+ """
56
+ self.__dict__[port.name()] = port
57
+ self._ports.append(port)
58
+
59
+ def delete_all_ports(self):
60
+ """
61
+ Deleta all component ports
62
+ """
63
+ self._ports = []
64
+
65
+ def path(self) -> str:
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))
80
+
81
+ @property
82
+ def ports(self):
83
+ """Get component ports"""
84
+ return self._ports
85
+
86
+ def _get_ports(self, output) -> list[Port]:
87
+ sel_ports = []
88
+ for port in self._ports:
89
+ if port.is_output() == output:
90
+ sel_ports.append(port)
91
+ return sel_ports
92
+
93
+ def inports(self) -> list[Port]:
94
+ """Get component input ports"""
95
+ return self._get_ports(False)
96
+
97
+ def outports(self) -> list[Port]:
98
+ """Get component output ports"""
99
+ return self._get_ports(True)
100
+
101
+ def port(self, portname: str) -> Port:
102
+ """Get port with name 'portname'"""
103
+ for port in self._ports:
104
+ if port.name() == portname:
105
+ return port
106
+ raise ComponentException(f"Port '{self.name()}:{portname}' not found")
107
+
108
+ @property
109
+ def circuit(self):
110
+ """Get the circuit for the current component"""
111
+ return self._circuit
112
+
113
+ def name(self) -> str:
114
+ """Get the component name"""
115
+ return self._name
116
+
117
+ def set_name(self, name: str, update_circuit: bool = True):
118
+ """Set the component name"""
119
+ if update_circuit:
120
+ self.circuit.change_component_name(self, name)
121
+ else:
122
+ self._name = name
123
+
124
+ def display_name(self) -> str:
125
+ """Get the component display name"""
126
+ return self._display_name
127
+
128
+ def set_display_name(self, display_name: str):
129
+ """Set the component display name"""
130
+ self._display_name = display_name
131
+
132
+ @property
133
+ def parent(self) -> Component | None:
134
+ """Get parent component"""
135
+ return self._parent
136
+
137
+ @parent.setter
138
+ def parent(self, parent: Component):
139
+ """Set component parent"""
140
+ self._parent = parent
141
+
142
+ def is_toplevel(self) -> bool:
143
+ """Return True if this component is a toplevel component"""
144
+ return self._parent is None
145
+
146
+ @property
147
+ def wire(self):
148
+ """Property needed to be able to have a setter"""
149
+ raise ComponentException(f"Cannot get wire for component '{self.display_name}'")
150
+
151
+ @wire.setter
152
+ def wire(self, port: Port):
153
+ """Some components have a single output port, they can wired at component level"""
154
+ self.outports()[0].wire = port
155
+
156
+ def update(self):
157
+ """This function is called if a port change and that port should update its parent"""
158
+
159
+ def remove_connections(self):
160
+ """Remove component connections"""
161
+ for src_port in self.outports():
162
+ for dst_port in src_port.wired_ports:
163
+ dst_port.set_driver(None)
164
+
165
+ for dst_port in self.inports():
166
+ if dst_port.has_driver():
167
+ dst_port.get_driver().disconnect(dst_port)
168
+
169
+ def add_event(self, port: Port, value: int, delay_ns: int):
170
+ """Add delta cycle event"""
171
+ self.circuit.add_event(port, value, delay_ns)
172
+
173
+ def __str__(self):
174
+ comp_str = f"{self.display_name()}"
175
+ for port in self.inports():
176
+ comp_str += f"\n - I:{port.name()}={port.value}"
177
+ for port in self.outports():
178
+ comp_str += f"\n - O:{port.name()}={port.value}"
179
+ return comp_str
180
+
181
+ @property
182
+ def has_action(self) -> bool:
183
+ """Return True if this component is interactive"""
184
+ return False
185
+
186
+ @property
187
+ def active(self) -> bool:
188
+ """Return True if this component is active/activated ('on' for a switch for example)"""
189
+ return False
190
+
191
+ def onpress(self):
192
+ """What to happen for an interactive activation"""
193
+
194
+ def onrelease(self):
195
+ """What to happen for an interactive de-activation"""
196
+
197
+ def settings_from_dict(self, settings):
198
+ """Get component settings from dict"""
199
+ raise ComponentException(f"No setup for component '{self.display_name}'")
200
+
201
+ def settings_to_dict(self) -> dict[str, int | str | bool]:
202
+ """Return component settings as a dict"""
203
+ return copy.deepcopy(self._parameters)
204
+
205
+ @classmethod
206
+ def get_parameters(cls):
207
+ """Return parameters"""
208
+ return {}
209
+
210
+ def update_settings(self, settings: dict[str, int | str | bool]):
211
+ """Update parameters from settings dict"""
212
+ for setting, value in settings.items():
213
+ self.parameter_set(setting, value)
214
+ self.reconfigure()
215
+
216
+ def reconfigure(self):
217
+ """Update the component from the parameters"""
218
+
219
+ def get_reconfigurable_parameters(self):
220
+ """Return reconfigurable parameters"""
221
+ reconfigurable_parameters = {}
222
+ for parameter, parameter_dict in self.get_parameters().items():
223
+ if parameter_dict.get("reconfigurable", False):
224
+ reconfigurable_parameters[parameter] = copy.deepcopy(parameter_dict)
225
+ reconfigurable_parameters[parameter]["default"] = self.parameter_get(parameter)
226
+ if reconfigurable_parameters[parameter]["type"] == "width_pow2":
227
+ reconfigurable_parameters[parameter]["type"] = int
228
+ reconfigurable_parameters[parameter]["min"] = 0
229
+ reconfigurable_parameters[parameter]["max"] = (
230
+ 2 ** self.parameter_get("width") - 1
231
+ )
232
+ return reconfigurable_parameters
233
+
234
+
235
+ class MultiComponent(Component):
236
+ """A component that holds one or several sub components"""
237
+
238
+ def __init__(self, circuit, name: str):
239
+ super().__init__(circuit, name)
240
+ self._components: list[Component] = []
241
+
242
+ def init(self):
243
+ super().init()
244
+ for component in self._components:
245
+ component.init()
246
+
247
+ def remove_all_components(self):
248
+ """Remove all sub components from multicomponent"""
249
+ self._components = []
250
+
251
+ def default_state(self):
252
+ super().default_state()
253
+ for component in self._components:
254
+ component.default_state()
255
+
256
+ def add(self, component):
257
+ """Add sub-component to MultiComponent"""
258
+ self._components.append(component)
259
+ component.parent = self
260
+
261
+
262
+ class CallbackComponent(Component):
263
+ """
264
+ A component that will call a callback function upon change,
265
+ this is used to for example output text in stdout or update GUI
266
+ objects when the component change value.
267
+ """
268
+
269
+ def __init__(self, circuit, name: str, callback: Callable[[Component], None] | None = None):
270
+ super().__init__(circuit, name)
271
+ self._callback = callback
272
+
273
+ def set_callback(self, callback: Callable[[Component], None]):
274
+ """Set CallbackComponent callback function"""
275
+ self._callback = callback
276
+
277
+ def update(self):
278
+ """Call CallbackComponent callback function if available"""
279
+ if self._callback is not None:
280
+ self._callback(self)
@@ -0,0 +1,8 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """The digsim exception base"""
5
+
6
+
7
+ class DigsimException(Exception):
8
+ """The digsim exception base class"""