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,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
|
+
}
|