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,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)
|