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,73 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """A Dip-switch component"""
5
+
6
+ from .atoms import CallbackComponent, PortOutImmediate
7
+
8
+
9
+ class DipSwitch(CallbackComponent):
10
+ """On/Off Switch component class"""
11
+
12
+ def __init__(self, circuit, name=None, bits=8):
13
+ super().__init__(circuit, name)
14
+ self._bits = bits
15
+ self.parameter_set("bits", bits)
16
+ self._select = None
17
+ for index in range(0, self._bits):
18
+ portout = PortOutImmediate(self, f"{index}")
19
+ self.add_port(portout)
20
+
21
+ def bits(self):
22
+ """Get number of bits in dipswitch"""
23
+ return self._bits
24
+
25
+ def is_set(self, bit):
26
+ """Test if dip switch bit is set"""
27
+ return self.port(f"{bit}").value == 1
28
+
29
+ def default_state(self):
30
+ for port in self.outports():
31
+ port.value = 0
32
+
33
+ def select(self, idx):
34
+ """Select bit in dipswitch (for toggle)"""
35
+ self._select = idx
36
+
37
+ def selected(self):
38
+ """Get selected bit in dipswitch (for gui)"""
39
+ return self._select
40
+
41
+ def _toggle_bit(self, bit):
42
+ if bit >= self._bits:
43
+ return
44
+ port = self.port(f"{bit}")
45
+ if port.value == 1:
46
+ port.value = 0
47
+ else:
48
+ port.value = 1
49
+
50
+ def toggle(self):
51
+ """Toggle the switch"""
52
+ if self._select is None:
53
+ return
54
+ self._toggle_bit(self._select)
55
+
56
+ def onpress(self):
57
+ self.toggle()
58
+
59
+ @property
60
+ def has_action(self):
61
+ return True
62
+
63
+ @classmethod
64
+ def get_parameters(cls):
65
+ return {
66
+ "bits": {
67
+ "type": "int",
68
+ "min": 2,
69
+ "max": 8,
70
+ "default": 8,
71
+ "description": "Number of bits",
72
+ },
73
+ }
@@ -0,0 +1,99 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """Module with flip flops and latches"""
5
+
6
+ from .atoms import Component, PortIn, PortOutDelta, PortWire
7
+
8
+
9
+ class FlipFlop(Component):
10
+ """FlipFlip factory component"""
11
+
12
+ @classmethod
13
+ def get_parameters(cls):
14
+ return {
15
+ "name": {
16
+ "type": "component_name",
17
+ "component_list": {
18
+ "SRFF": "SR FlipFlop",
19
+ "ClockedSRFF": "SR FlipFlop (Edge triggered)",
20
+ "ClockedJKFF": "JK FlipFlop (Edge triggered)",
21
+ "ClockedTFF": "T FlipFlop (Edge triggered)",
22
+ },
23
+ "description": "Select flip-flop",
24
+ }
25
+ }
26
+
27
+
28
+ class SRFF(Component):
29
+ """SR-FlipFlop"""
30
+
31
+ def __init__(self, circuit, name=None):
32
+ super().__init__(circuit, name, display_name="SR")
33
+ self.add_port(PortIn(self, "S"))
34
+ self.add_port(PortIn(self, "R"))
35
+ self.add_port(PortOutDelta(self, "Q"))
36
+
37
+ def update(self):
38
+ if self.S.value == 1:
39
+ self.Q.value = 1
40
+ if self.R.value == 1:
41
+ self.Q.value = 0
42
+
43
+
44
+ class ClockedJKFF(Component):
45
+ """Clocked JK-FlipFlop"""
46
+
47
+ def __init__(self, circuit, name=None):
48
+ super().__init__(circuit, name, display_name="JK-FlipFlop")
49
+ self.add_port(PortWire(self, "J"))
50
+ self.add_port(PortIn(self, "C"))
51
+ self.add_port(PortWire(self, "K"))
52
+ self.add_port(PortOutDelta(self, "Q"))
53
+
54
+ def update(self):
55
+ rising_edge = self.C.is_rising_edge()
56
+ if rising_edge:
57
+ if self.J.value == 1 and self.K.value:
58
+ self.Q.value = 1 if self.Q.value == 0 else 0
59
+ elif self.J.value == 1:
60
+ self.Q.value = 1
61
+ elif self.K.value == 1:
62
+ self.Q.value = 0
63
+
64
+
65
+ class ClockedSRFF(Component):
66
+ """Clocked SR-FlipFlop"""
67
+
68
+ def __init__(self, circuit, name=None):
69
+ super().__init__(circuit, name, display_name="SR-FlipFlop")
70
+ self.add_port(PortWire(self, "S"))
71
+ self.add_port(PortIn(self, "C"))
72
+ self.add_port(PortWire(self, "R"))
73
+ self.add_port(PortOutDelta(self, "Q"))
74
+
75
+ def update(self):
76
+ rising_edge = self.C.is_rising_edge()
77
+ if rising_edge:
78
+ if self.S.value == 1:
79
+ self.Q.value = 1
80
+ if self.R.value == 1:
81
+ self.Q.value = 0
82
+
83
+
84
+ class ClockedTFF(Component):
85
+ """Clocked T-FlipFlop"""
86
+
87
+ def __init__(self, circuit, name=None):
88
+ super().__init__(circuit, name, display_name="T-FlipFlop")
89
+ self.add_port(PortWire(self, "T"))
90
+ self.add_port(PortIn(self, "C"))
91
+ self.add_port(PortOutDelta(self, "Q"))
92
+
93
+ def init(self):
94
+ self.T.value = 0
95
+
96
+ def update(self):
97
+ rising_edge = self.C.is_rising_edge()
98
+ if rising_edge and self.T.value == 1:
99
+ self.Q.value = 1 if self.Q.value == 0 else 0
@@ -0,0 +1,246 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2024
2
+ # All rights reserved
3
+
4
+ """Module with the basic logic gates"""
5
+
6
+ import math
7
+
8
+ from .atoms import Component, ComponentException, MultiComponent, PortIn, PortOutDelta, PortWire
9
+
10
+
11
+ class NOT(Component):
12
+ """NOT logic gate"""
13
+
14
+ def __init__(self, circuit, name=None):
15
+ super().__init__(circuit, name)
16
+ self.add_port(PortIn(self, "A"))
17
+ self.add_port(PortOutDelta(self, "Y"))
18
+
19
+ def update(self):
20
+ if self.A.value == 0:
21
+ self.Y.value = 1
22
+ elif self.A.value == 1:
23
+ self.Y.value = 0
24
+ else:
25
+ self.Y.value = "X"
26
+
27
+
28
+ class ConfigPortsComponent(Component):
29
+ """Base class for components with configurable input ports"""
30
+
31
+ def __init__(self, circuit, name, ports):
32
+ super().__init__(circuit, name)
33
+ self._inports = []
34
+ portname = "A"
35
+ for _ in range(ports):
36
+ port = PortIn(self, portname)
37
+ self._inports.append(port)
38
+ self.add_port(port)
39
+ portname = chr(ord(portname) + 1)
40
+ self.add_port(PortOutDelta(self, "Y"))
41
+ self.parameter_set("ports", ports)
42
+
43
+ @classmethod
44
+ def get_parameters(cls):
45
+ return {
46
+ "ports": {
47
+ "type": "int",
48
+ "min": 2,
49
+ "max": 8,
50
+ "default": 2,
51
+ "description": "Number of input ports",
52
+ },
53
+ }
54
+
55
+
56
+ class OR(ConfigPortsComponent):
57
+ """OR logic gate"""
58
+
59
+ def __init__(self, circuit, name=None, ports=2):
60
+ super().__init__(circuit, name, ports)
61
+
62
+ def update(self):
63
+ if any(port.value == 1 for port in self._inports):
64
+ self.Y.value = 1
65
+ elif all(port.value == 0 for port in self._inports):
66
+ self.Y.value = 0
67
+ else:
68
+ self.Y.value = "X"
69
+
70
+
71
+ class AND(ConfigPortsComponent):
72
+ """AND logic gate"""
73
+
74
+ def __init__(self, circuit, name=None, ports=2):
75
+ super().__init__(circuit, name, ports)
76
+
77
+ def update(self):
78
+ if all(port.value == 1 for port in self._inports):
79
+ self.Y.value = 1
80
+ elif any(port.value == 0 for port in self._inports):
81
+ self.Y.value = 0
82
+ else:
83
+ self.Y.value = "X"
84
+
85
+
86
+ class XOR(ConfigPortsComponent):
87
+ """XOR logic gate"""
88
+
89
+ def __init__(self, circuit, name=None, ports=2):
90
+ super().__init__(circuit, name, ports)
91
+
92
+ def update(self):
93
+ if any(port.value == "X" for port in self._inports):
94
+ self.Y.value = "X"
95
+ else:
96
+ count = 0
97
+ for port in self._inports:
98
+ count += port.value
99
+ self.Y.value = count % 2
100
+
101
+
102
+ class NAND(ConfigPortsComponent):
103
+ """NAND logic gate"""
104
+
105
+ def __init__(self, circuit, name=None, ports=2):
106
+ super().__init__(circuit, name, ports)
107
+
108
+ def update(self):
109
+ if all(port.value == 1 for port in self._inports):
110
+ self.Y.value = 0
111
+ elif any(port.value == 0 for port in self._inports):
112
+ self.Y.value = 1
113
+ else:
114
+ self.Y.value = "X"
115
+
116
+
117
+ class NOR(ConfigPortsComponent):
118
+ """NOR logic gate"""
119
+
120
+ def __init__(self, circuit, name=None, ports=2):
121
+ super().__init__(circuit, name, ports)
122
+
123
+ def update(self):
124
+ if any(port.value == 1 for port in self._inports):
125
+ self.Y.value = 0
126
+ elif all(port.value == 0 for port in self._inports):
127
+ self.Y.value = 1
128
+ else:
129
+ self.Y.value = "X"
130
+
131
+
132
+ class DFF(Component):
133
+ """D-FlipFlop"""
134
+
135
+ def __init__(self, circuit, name=None, async_reset=False, clock_enable=False, width=1):
136
+ super().__init__(circuit, name)
137
+ self._async_reset = async_reset
138
+ self._clock_enable = clock_enable
139
+ self.add_port(PortWire(self, "D", width=width))
140
+ if self._async_reset:
141
+ self.add_port(PortIn(self, "R"))
142
+ if self._clock_enable:
143
+ self.add_port(PortWire(self, "E"))
144
+ self.add_port(PortIn(self, "C"))
145
+ self.add_port(PortOutDelta(self, "Q", width=width))
146
+ self.parameter_set("width", width)
147
+ self.parameter_set("async_reset", async_reset)
148
+ self.parameter_set("clock_enable", clock_enable)
149
+
150
+ def update(self):
151
+ rising_edge = self.C.is_rising_edge()
152
+ if self._async_reset and self.R.value == 1:
153
+ self.Q.value = 0
154
+ elif rising_edge:
155
+ if self._clock_enable and self.E.value == 0:
156
+ # No clock enable
157
+ return
158
+ self.Q.value = self.D.value
159
+
160
+ @classmethod
161
+ def get_parameters(cls):
162
+ return {
163
+ "async_reset": {
164
+ "type": "bool",
165
+ "default": False,
166
+ "description": "Use asynchronous reset",
167
+ },
168
+ "clock_enable": {
169
+ "type": "bool",
170
+ "default": False,
171
+ "description": "Use clock enable",
172
+ },
173
+ "width": {
174
+ "type": "int",
175
+ "min": 1,
176
+ "max": 32,
177
+ "default": 1,
178
+ "description": "Bitwidth of D and Q ports",
179
+ },
180
+ }
181
+
182
+
183
+ class MUX(Component):
184
+ """MUX"""
185
+
186
+ def __init__(self, circuit, name=None, ports=2, width=1):
187
+ super().__init__(circuit, name)
188
+ if ports not in [2, 4, 8]:
189
+ raise ComponentException(f"Mux cannot have {ports} number of ports")
190
+ self._inports = []
191
+ portname = "A"
192
+ for _ in range(ports):
193
+ port = PortIn(self, portname, width=width)
194
+ self._inports.append(port)
195
+ self.add_port(port)
196
+ portname = chr(ord(portname) + 1)
197
+ self.add_port(PortIn(self, "S", width=int(math.log2(ports))))
198
+ self.add_port(PortOutDelta(self, "Y", width=width))
199
+ self.parameter_set("ports", ports)
200
+ self.parameter_set("width", width)
201
+
202
+ def update(self):
203
+ if self.S.value == "X":
204
+ self.Y.value = "X"
205
+ return
206
+ self.Y.value = self._inports[self.S.value].value
207
+
208
+ @classmethod
209
+ def get_parameters(cls):
210
+ return {
211
+ "ports": {
212
+ "type": "intrange",
213
+ "range": [2, 4, 8],
214
+ "default_index": 0,
215
+ "description": "Number of input ports",
216
+ },
217
+ "width": {
218
+ "type": "int",
219
+ "min": 1,
220
+ "max": 32,
221
+ "default": 1,
222
+ "description": "Bitwidth of mux ports",
223
+ },
224
+ }
225
+
226
+
227
+ class SR(MultiComponent):
228
+ """SR logic gate composed of two NAND gates"""
229
+
230
+ def __init__(self, circuit, name=None):
231
+ super().__init__(circuit, name)
232
+ _nands = NOR(circuit)
233
+ _nandr = NOR(circuit)
234
+ self.add(_nands)
235
+ self.add(_nandr)
236
+ _nands.Y.wire = _nandr.A
237
+ _nandr.Y.wire = _nands.B
238
+ self.add_port(PortWire(self, "S"))
239
+ self.add_port(PortWire(self, "R"))
240
+ self.add_port(PortWire(self, "Q"))
241
+ self.add_port(PortWire(self, "nQ"))
242
+
243
+ self.S.wire = _nands.A
244
+ self.R.wire = _nandr.B
245
+ _nands.Y.wire = self.nQ
246
+ _nandr.Y.wire = self.Q
@@ -0,0 +1,82 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """HexDigit component module"""
5
+
6
+ from .atoms import CallbackComponent, PortIn
7
+
8
+
9
+ class HexDigit(CallbackComponent):
10
+ """HexDigit component class"""
11
+
12
+ VAL_TO_SEGMENTS = {
13
+ 0: "ABCDEF",
14
+ 1: "BC",
15
+ 2: "ABDEG",
16
+ 3: "ABCDG",
17
+ 4: "BCFG",
18
+ 5: "ACDFG",
19
+ 6: "ACDEFG",
20
+ 7: "ABC",
21
+ 8: "ABCDEFG",
22
+ 9: "ABCDFG",
23
+ 10: "ABCEFG",
24
+ 11: "CDEFG",
25
+ 12: "ADEF",
26
+ 13: "BCDEG",
27
+ 14: "ADEFG",
28
+ 15: "AEFG",
29
+ }
30
+
31
+ def __init__(self, circuit, name=None, digits=1, dot=True):
32
+ super().__init__(circuit, name)
33
+ self.add_port(PortIn(self, "val", width=4 * digits))
34
+ self.parameter_set("digits", digits)
35
+ self.parameter_set("dot", dot)
36
+ if dot:
37
+ self.add_port(PortIn(self, "dot", width=digits))
38
+
39
+ def get_digits(self):
40
+ """Get the number of digits"""
41
+ return self.parameter_get("digits")
42
+
43
+ def value(self):
44
+ """Get value"""
45
+ return self.val.value
46
+
47
+ def dot_active(self, digit_id=1):
48
+ """Is the dot active for the selected digit_id?"""
49
+ if not self.parameter_get("dot"):
50
+ return False
51
+ if self.dot.value == "X":
52
+ return False
53
+ dot_value = self.dot.value >> (self.dot.width - (digit_id + 1)) & 1
54
+ return dot_value == 1
55
+
56
+ def segments(self, digit_id=0):
57
+ """Which segments are active for the selected digit_id?"""
58
+ if self.value() == "X":
59
+ segments = ""
60
+ else:
61
+ digit_value = (self.value() >> (self.val.width - (digit_id + 1) * 4)) & 0xF
62
+ segments = self.VAL_TO_SEGMENTS.get(digit_value, "")
63
+ if self.dot_active(digit_id):
64
+ segments += "."
65
+ return segments
66
+
67
+ @classmethod
68
+ def get_parameters(cls):
69
+ return {
70
+ "digits": {
71
+ "type": "int",
72
+ "min": 1,
73
+ "max": 8,
74
+ "default": 1,
75
+ "description": "Number of digits",
76
+ },
77
+ "dot": {
78
+ "type": "bool",
79
+ "default": False,
80
+ "description": "Use decimal dot",
81
+ },
82
+ }
@@ -0,0 +1,36 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """
5
+ Module for creating a yosys component in the ic folder
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ from ._yosys_component import YosysComponent
11
+
12
+
13
+ class IntegratedCircuit(YosysComponent):
14
+ """IC component class (predefined yosys module)"""
15
+
16
+ def __init__(self, circuit, name="IC", ic_name=None):
17
+ path = f"{self.folder()}/{ic_name}.json"
18
+ super().__init__(circuit, name=name, path=path)
19
+ self.parameter_set("ic_name", ic_name)
20
+
21
+ @classmethod
22
+ def folder(cls):
23
+ """Get predefined IC folder"""
24
+ return str(Path(__file__).parent / "ic")
25
+
26
+ def settings_to_dict(self):
27
+ return {"ic_name": self.parameter_get("ic_name")}
28
+
29
+ @classmethod
30
+ def get_parameters(cls):
31
+ return {
32
+ "ic_name": {
33
+ "type": "ic_name",
34
+ "description": "Select IC",
35
+ }
36
+ }
@@ -0,0 +1,167 @@
1
+ # Copyright (c) Fredrik Andersson, 2023-2025
2
+ # All rights reserved
3
+
4
+ """Label Wire components module"""
5
+
6
+ from .atoms import Component, DigsimException, PortIn, PortWire
7
+
8
+
9
+ class _LabelWireStorage:
10
+ """Singleton class with label wires"""
11
+
12
+ _instance = None
13
+ _wire_drivers: dict[str, PortWire] = {}
14
+ _wire_sinks: dict[str, PortIn] = {}
15
+
16
+ def __new__(cls):
17
+ if cls._instance is None:
18
+ if not cls._instance:
19
+ cls._instance = super(_LabelWireStorage, cls).__new__(cls)
20
+ return cls._instance
21
+
22
+ def clear(self):
23
+ """Remove all wires"""
24
+ self._wire_drivers = {}
25
+ self._wire_sinks = {}
26
+
27
+ def get_labels(self):
28
+ """Get label list"""
29
+ return list(self._wire_drivers.keys())
30
+
31
+ def get_wire_width(self, label):
32
+ """Get width of wire"""
33
+ driver_port = self._wire_drivers.get(label)
34
+ if driver_port is None:
35
+ return None
36
+ return driver_port.width
37
+
38
+ def add_driver(self, label, driver_port):
39
+ """Add driver port with label"""
40
+ if label in self._wire_drivers:
41
+ raise DigsimException(f"Wire Driver '{label}' already added")
42
+ self._wire_drivers[label] = driver_port
43
+
44
+ def _get_driver(self, label):
45
+ driver_port = self._wire_drivers.get(label)
46
+ if driver_port is None:
47
+ raise DigsimException(f"Wire Driver '{label}' not found")
48
+ return driver_port
49
+
50
+ def remove_driver(self, label):
51
+ """Remove driver"""
52
+ driver_port = self._get_driver(label)
53
+ for sink_port, sink_label in self._wire_sinks.items():
54
+ if label == sink_label:
55
+ driver_port.disconnect(sink_port)
56
+ del self._wire_drivers[label]
57
+
58
+ def add_sink(self, label, sink_port):
59
+ """Add driver port with label"""
60
+ if sink_port in self._wire_sinks:
61
+ raise DigsimException(f"Wire Sink '{label}' already added")
62
+ self._wire_sinks[sink_port] = label
63
+
64
+ def remove_sink(self, sink_port):
65
+ """Remove sink"""
66
+ del self._wire_sinks[sink_port]
67
+
68
+ def connect(self, sink_port):
69
+ """Connect sink port if needed"""
70
+ if not sink_port.has_driver():
71
+ label = self._wire_sinks.get(sink_port)
72
+ if label is None:
73
+ raise DigsimException("Wire Sink not found")
74
+ driver_port = self._get_driver(label)
75
+ driver_port.wire = sink_port
76
+
77
+
78
+ class LabelWireIn(Component):
79
+ """LabelWireIn component class"""
80
+
81
+ def __init__(self, circuit, name=None, label=None, width=1):
82
+ super().__init__(circuit, name)
83
+ self._label = label
84
+ self._port_in = PortWire(self, self._label, width=width)
85
+ self.add_port(self._port_in)
86
+ self.parameter_set("label", self._label)
87
+ self.parameter_set("width", width)
88
+ self._label_wires = _LabelWireStorage()
89
+ self._label_wires.add_driver(self._label, self._port_in)
90
+
91
+ def clear(self):
92
+ self._label_wires.clear()
93
+
94
+ def label(self):
95
+ """Return Label"""
96
+ return self._label
97
+
98
+ def remove_connections(self):
99
+ self._label_wires.remove_driver(self._label)
100
+ super().remove_connections()
101
+
102
+ @classmethod
103
+ def get_parameters(cls):
104
+ label_wires = _LabelWireStorage()
105
+ label_list = label_wires.get_labels()
106
+
107
+ return {
108
+ "label": {
109
+ "type": "str",
110
+ "single_line": True,
111
+ "default": "WireLabel",
112
+ "description": "Label name",
113
+ "invalid_list": label_list,
114
+ },
115
+ "width": {
116
+ "type": "int",
117
+ "min": 1,
118
+ "max": 32,
119
+ "default": 1,
120
+ "description": "Bitwidth of wire",
121
+ },
122
+ }
123
+
124
+
125
+ class LabelWireOut(Component):
126
+ """LabelWireOut component class"""
127
+
128
+ def __init__(self, circuit, name=None, label=None, width=1):
129
+ super().__init__(circuit, name)
130
+ self._label_wires = _LabelWireStorage()
131
+ label_width = self._label_wires.get_wire_width(label)
132
+ width = label_width if label_width is not None else width
133
+ self._label = label
134
+ self._port_out = PortWire(self, self._label, width=width, output=True)
135
+ self.add_port(self._port_out)
136
+ self.parameter_set("label", self._label)
137
+ self.parameter_set("width", width)
138
+ self._label_wires = _LabelWireStorage()
139
+ self._label_wires.add_sink(self._label, self._port_out)
140
+
141
+ def clear(self):
142
+ self._label_wires.clear()
143
+
144
+ def label(self):
145
+ """Return Label"""
146
+ return self._label
147
+
148
+ def init(self):
149
+ self._label_wires.connect(self._port_out)
150
+ super().init()
151
+
152
+ def remove_connections(self):
153
+ self._label_wires.remove_sink(self._port_out)
154
+ super().remove_connections()
155
+
156
+ @classmethod
157
+ def get_parameters(cls):
158
+ label_wires = _LabelWireStorage()
159
+ label_list = label_wires.get_labels()
160
+
161
+ return {
162
+ "label": {
163
+ "type": "list",
164
+ "items": label_list,
165
+ "description": "Select label",
166
+ }
167
+ }