sirc 0.1.0__tar.gz

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.
sirc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: sirc
3
+ Version: 0.1.0
4
+ Summary: SIRC - Digital Logic and Circuit Simulation Engine
5
+ Author-email: CRISvsGAME <cris@crisvsgame.com>
6
+ Maintainer-email: CRISvsGAME <cris@crisvsgame.com>
7
+ License-Expression: MIT
8
+ Project-URL: Repository, https://github.com/CRISvsGAME/sirc.git
9
+ Keywords: digital logic,circuit simulation,simulation engine
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ Provides-Extra: dev
18
+ Requires-Dist: build; extra == "dev"
19
+ Requires-Dist: mypy; extra == "dev"
20
+ Requires-Dist: pylint; extra == "dev"
21
+ Requires-Dist: pytest; extra == "dev"
22
+ Requires-Dist: twine; extra == "dev"
23
+
24
+ # SIRC
sirc-0.1.0/README.md ADDED
@@ -0,0 +1 @@
1
+ # SIRC
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sirc"
7
+ version = "0.1.0"
8
+ dependencies = []
9
+ requires-python = ">=3.12"
10
+ authors = [{name = "CRISvsGAME", email = "cris@crisvsgame.com"}]
11
+ maintainers = [{name = "CRISvsGAME", email = "cris@crisvsgame.com"}]
12
+ description = "SIRC - Digital Logic and Circuit Simulation Engine"
13
+ readme = "README.md"
14
+ license = "MIT"
15
+ keywords = ["digital logic", "circuit simulation", "simulation engine"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python",
21
+ "Topic :: Scientific/Engineering",
22
+ ]
23
+
24
+ [project.optional-dependencies]
25
+ dev = ["build", "mypy", "pylint", "pytest", "twine"]
26
+
27
+ [project.urls]
28
+ Repository = "https://github.com/CRISvsGAME/sirc.git"
29
+
30
+ [tool.pylint]
31
+ ignore-patterns = [".*\\.pyi"]
sirc-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
File without changes
@@ -0,0 +1,134 @@
1
+ """
2
+ SIRC Core Device Module.
3
+
4
+ Defines the LogicDevice base class and several common logic devices: VDD, GND,
5
+ Input, Probe, and Port. A LogicDevice owns exactly one terminal Node and may
6
+ drive a single LogicValue onto that Node. LogicDevices do not perform any logic
7
+ resolution. The Simulator handles all evaluation and propagation.
8
+ """
9
+
10
+ from __future__ import annotations
11
+ from abc import ABC
12
+ from sirc.core.logic import LogicValue
13
+ from sirc.core.node import Node
14
+
15
+
16
+ class LogicDevice(ABC):
17
+ """
18
+ Abstract class for single-terminal logic devices.
19
+
20
+ Each LogicDevice owns one Node and may drive one LogicValue onto it. This
21
+ class defines only structural information. The Simulator is responsible for
22
+ injecting driver values and resolving all electrical behaviour.
23
+ """
24
+
25
+ __slots__ = ("_node", "_value")
26
+
27
+ def __init__(self) -> None:
28
+ """Create a new LogicDevice with a terminal Node and default Z value."""
29
+ self._node = Node()
30
+ self._value = LogicValue.Z
31
+
32
+ # --------------------------------------------------------------------------
33
+ # Properties
34
+ # --------------------------------------------------------------------------
35
+
36
+ @property
37
+ def terminal(self) -> Node:
38
+ """Return the terminal Node of this LogicDevice."""
39
+ return self._node
40
+
41
+ @property
42
+ def value(self) -> LogicValue:
43
+ """Return the LogicValue driven by this LogicDevice."""
44
+ return self._value
45
+
46
+ # --------------------------------------------------------------------------
47
+ # Debug Representation
48
+ # --------------------------------------------------------------------------
49
+
50
+ def __repr__(self) -> str:
51
+ """Return a debug representation of this LogicDevice."""
52
+ cls = self.__class__.__name__
53
+ return f"<{cls} value={self.value!r} terminal={self.terminal!r}>"
54
+
55
+
56
+ # ------------------------------------------------------------------------------
57
+ # Power Rail
58
+ # ------------------------------------------------------------------------------
59
+
60
+
61
+ class VDD(LogicDevice):
62
+ """
63
+ Logic "1" power rail device.
64
+
65
+ This device permanently drives its terminal Node with LogicValue.ONE.
66
+ """
67
+
68
+ def __init__(self) -> None:
69
+ super().__init__()
70
+ self._value = LogicValue.ONE
71
+
72
+
73
+ # ------------------------------------------------------------------------------
74
+ # Ground Rail
75
+ # ------------------------------------------------------------------------------
76
+
77
+
78
+ class GND(LogicDevice):
79
+ """
80
+ Logic "0" ground rail device.
81
+
82
+ This device permanently drives its terminal Node with LogicValue.ZERO.
83
+ """
84
+
85
+ def __init__(self) -> None:
86
+ super().__init__()
87
+ self._value = LogicValue.ZERO
88
+
89
+
90
+ # ------------------------------------------------------------------------------
91
+ # Input Device
92
+ # ------------------------------------------------------------------------------
93
+
94
+
95
+ class Input(LogicDevice):
96
+ """
97
+ Logic signal input device.
98
+
99
+ This device allows external setting of its driven LogicValue.
100
+ """
101
+
102
+ def set_value(self, value: LogicValue) -> None:
103
+ """Set the LogicValue driven by this Input device."""
104
+ self._value = value
105
+
106
+
107
+ # ------------------------------------------------------------------------------
108
+ # Probe Device
109
+ # ------------------------------------------------------------------------------
110
+
111
+
112
+ class Probe(LogicDevice):
113
+ """
114
+ Logic signal probe device.
115
+
116
+ This device allows sampling of the LogicValue present on its terminal Node.
117
+ """
118
+
119
+ def sample(self) -> LogicValue:
120
+ """Return the current resolved LogicValue of the terminal Node."""
121
+ return self._node.value
122
+
123
+
124
+ # ------------------------------------------------------------------------------
125
+ # Port Device
126
+ # ------------------------------------------------------------------------------
127
+
128
+
129
+ class Port(LogicDevice):
130
+ """
131
+ Logic signal port device.
132
+
133
+ This device is a passive connection point for linking circuit Nodes.
134
+ """
@@ -0,0 +1,24 @@
1
+ from abc import ABC
2
+ from sirc.core.logic import LogicValue as LogicValue
3
+ from sirc.core.node import Node as Node
4
+
5
+ class LogicDevice(ABC):
6
+ def __init__(self) -> None: ...
7
+ @property
8
+ def terminal(self) -> Node: ...
9
+ @property
10
+ def value(self) -> LogicValue: ...
11
+
12
+ class VDD(LogicDevice):
13
+ def __init__(self) -> None: ...
14
+
15
+ class GND(LogicDevice):
16
+ def __init__(self) -> None: ...
17
+
18
+ class Input(LogicDevice):
19
+ def set_value(self, value: LogicValue) -> None: ...
20
+
21
+ class Probe(LogicDevice):
22
+ def sample(self) -> LogicValue: ...
23
+
24
+ class Port(LogicDevice): ...
@@ -0,0 +1,143 @@
1
+ """
2
+ SIRC Core Logic Module.
3
+
4
+ Defines the four-state digital logic values used throughout the SIRC simulation
5
+ engine. The logic rules follow the four-state resolution semantics defined by
6
+ IEEE 1800-2023, but the terminology used here follows SIRC conventions.
7
+ """
8
+
9
+ from __future__ import annotations
10
+ from enum import Enum, unique
11
+ from typing import Iterable
12
+
13
+
14
+ @unique
15
+ class LogicValue(Enum):
16
+ """
17
+ Four-state digital logic value used by Nodes and drivers in SIRC.
18
+
19
+ Values:
20
+ ZERO (0) -> logical low
21
+ ONE (1) -> logical high
22
+ X -> unknown or conflicting value
23
+ Z -> undriven or high-impedance value
24
+ """
25
+
26
+ ZERO = "0"
27
+ ONE = "1"
28
+ X = "X"
29
+ Z = "Z"
30
+
31
+ # --------------------------------------------------------------------------
32
+ # Helper Properties
33
+ # --------------------------------------------------------------------------
34
+
35
+ @property
36
+ def is_zero(self) -> bool:
37
+ """Return True if this value is ZERO."""
38
+ return self is LogicValue.ZERO
39
+
40
+ @property
41
+ def is_one(self) -> bool:
42
+ """Return True if this value is ONE."""
43
+ return self is LogicValue.ONE
44
+
45
+ @property
46
+ def is_x(self) -> bool:
47
+ """Return True if this value is unknown (X)."""
48
+ return self is LogicValue.X
49
+
50
+ @property
51
+ def is_z(self) -> bool:
52
+ """Return True if this value is high-impedance (Z)."""
53
+ return self is LogicValue.Z
54
+
55
+ # --------------------------------------------------------------------------
56
+ # Two-Driver Resolution
57
+ # --------------------------------------------------------------------------
58
+
59
+ def resolve(self, other: LogicValue) -> LogicValue:
60
+ """
61
+ Resolve two driver values into a single LogicValue.
62
+
63
+ Args:
64
+ other: The second LogicValue driving the same Node.
65
+
66
+ Returns:
67
+ LogicValue: The resolved value.
68
+
69
+ Resolution Table (N = Node):
70
+ N | 0 | 1 | X | Z
71
+ ---+---+---+---+---
72
+ 0 | 0 | X | X | 0
73
+ ---+---+---+---+---
74
+ 1 | X | 1 | X | 1
75
+ ---+---+---+---+---
76
+ X | X | X | X | X
77
+ ---+---+---+---+---
78
+ Z | 0 | 1 | X | Z
79
+ """
80
+ result = self
81
+
82
+ if self is not other:
83
+
84
+ if self.is_x or other.is_x:
85
+ result = LogicValue.X
86
+
87
+ elif self.is_z and other.is_z:
88
+ result = LogicValue.Z
89
+
90
+ elif (self.is_zero and other.is_one) or (self.is_one and other.is_zero):
91
+ result = LogicValue.X
92
+
93
+ elif self.is_z:
94
+ result = other
95
+
96
+ elif other.is_z:
97
+ result = self
98
+
99
+ return result
100
+
101
+ # --------------------------------------------------------------------------
102
+ # Multi-Driver Resolution
103
+ # --------------------------------------------------------------------------
104
+
105
+ @staticmethod
106
+ def resolve_all(values: Iterable[LogicValue]) -> LogicValue:
107
+ """
108
+ Resolve multiple driver values into a single LogicValue.
109
+
110
+ Args:
111
+ values: Iterable of LogicValue instances.
112
+
113
+ Raises:
114
+ ValueError: If no values are provided.
115
+
116
+ Returns:
117
+ LogicValue: The resolved value.
118
+ """
119
+ iterator = iter(values)
120
+
121
+ try:
122
+ result = next(iterator)
123
+ except StopIteration as e:
124
+ raise ValueError("resolve_all() requires at least one LogicValue.") from e
125
+
126
+ for value in iterator:
127
+ result = result.resolve(value)
128
+ if result.is_x:
129
+ return LogicValue.X
130
+
131
+ return result
132
+
133
+ # --------------------------------------------------------------------------
134
+ # Display Helpers
135
+ # --------------------------------------------------------------------------
136
+
137
+ def __str__(self) -> str:
138
+ """Return compact string form ('0', '1', 'X', 'Z')."""
139
+ return self.value
140
+
141
+ def __repr__(self) -> str:
142
+ """Return readable debug representation."""
143
+ return f"LogicValue.{self.name}"
@@ -0,0 +1,19 @@
1
+ from enum import Enum
2
+ from typing import Iterable
3
+
4
+ class LogicValue(Enum):
5
+ ZERO = '0'
6
+ ONE = '1'
7
+ X = 'X'
8
+ Z = 'Z'
9
+ @property
10
+ def is_zero(self) -> bool: ...
11
+ @property
12
+ def is_one(self) -> bool: ...
13
+ @property
14
+ def is_x(self) -> bool: ...
15
+ @property
16
+ def is_z(self) -> bool: ...
17
+ def resolve(self, other: LogicValue) -> LogicValue: ...
18
+ @staticmethod
19
+ def resolve_all(values: Iterable[LogicValue]) -> LogicValue: ...
@@ -0,0 +1,117 @@
1
+ """
2
+ SIRC Core Node Module.
3
+
4
+ Defines the Node class used by the SIRC simulation engine. Nodes represent
5
+ logical connection points in the circuit. Multiple Nodes may be connected,
6
+ forming an electrical group that collectively resolves a single LogicValue.
7
+ """
8
+
9
+ from __future__ import annotations
10
+ from sirc.core.logic import LogicValue
11
+
12
+
13
+ class Node:
14
+ """
15
+ A Node is a passive logical connection point in the SIRC circuit model.
16
+
17
+ It may hold zero or more driver LogicValues and may be directly connected
18
+ to other Nodes. A Node performs no resolution or computation by itself;
19
+ all evaluation and propagation are handled entirely by the Simulator.
20
+ """
21
+
22
+ __slots__ = ("_drivers", "_connections", "_value")
23
+
24
+ def __init__(self) -> None:
25
+ """Create an isolated Node with no drivers and a default Z value."""
26
+ self._drivers: list[LogicValue] = []
27
+ self._connections: set[Node] = set()
28
+ self._value: LogicValue = LogicValue.Z
29
+
30
+ # --------------------------------------------------------------------------
31
+ # Value Handling (Simulator-Controlled)
32
+ # --------------------------------------------------------------------------
33
+
34
+ def set_resolved_value(self, value: LogicValue) -> None:
35
+ """
36
+ Set the resolved LogicValue of this Node.
37
+
38
+ Args:
39
+ value: The resolved LogicValue to set.
40
+ """
41
+ self._value = value
42
+
43
+ @property
44
+ def value(self) -> LogicValue:
45
+ """Return the current resolved LogicValue of this Node."""
46
+ return self._value
47
+
48
+ # --------------------------------------------------------------------------
49
+ # Driver Management
50
+ # --------------------------------------------------------------------------
51
+
52
+ def add_driver(self, value: LogicValue) -> None:
53
+ """
54
+ Add a driver LogicValue to this Node.
55
+
56
+ Args:
57
+ value: The LogicValue driving this Node.
58
+ """
59
+ self._drivers.append(value)
60
+
61
+ def clear_drivers(self) -> None:
62
+ """Remove all driver LogicValues from this Node."""
63
+ self._drivers.clear()
64
+
65
+ def get_drivers(self) -> tuple[LogicValue, ...]:
66
+ """Return all driver LogicValues as an immutable tuple."""
67
+ return tuple(self._drivers)
68
+
69
+ # --------------------------------------------------------------------------
70
+ # Connectivity
71
+ # --------------------------------------------------------------------------
72
+
73
+ def add_connection(self, other: Node) -> None:
74
+ """INTERNAL USE ONLY: Add a direct connection to another Node."""
75
+ self._connections.add(other)
76
+
77
+ def remove_connection(self, other: Node) -> None:
78
+ """INTERNAL USE ONLY: Remove a direct connection to another Node."""
79
+ self._connections.discard(other)
80
+
81
+ def connect(self, other: Node) -> None:
82
+ """
83
+ Create a bidirectional connection between this Node and another.
84
+
85
+ Args:
86
+ other: The Node to connect to.
87
+ """
88
+ if self is other:
89
+ return
90
+
91
+ self.add_connection(other)
92
+ other.add_connection(self)
93
+
94
+ def disconnect(self, other: Node) -> None:
95
+ """
96
+ Remove the bidirectional connection between this Node and another.
97
+
98
+ Args:
99
+ other: The Node to disconnect from.
100
+ """
101
+ if self is other:
102
+ return
103
+
104
+ self.remove_connection(other)
105
+ other.remove_connection(self)
106
+
107
+ def get_connections(self) -> tuple[Node, ...]:
108
+ """Return all directly connected Nodes as an immutable tuple."""
109
+ return tuple(self._connections)
110
+
111
+ # --------------------------------------------------------------------------
112
+ # Debug Representation
113
+ # --------------------------------------------------------------------------
114
+
115
+ def __repr__(self) -> str:
116
+ """Return a debug representation of this Node."""
117
+ return f"<Node value={self._value!r} drivers={tuple(self._drivers)!r}>"
@@ -0,0 +1,15 @@
1
+ from sirc.core.logic import LogicValue as LogicValue
2
+
3
+ class Node:
4
+ def __init__(self) -> None: ...
5
+ def set_resolved_value(self, value: LogicValue) -> None: ...
6
+ @property
7
+ def value(self) -> LogicValue: ...
8
+ def add_driver(self, value: LogicValue) -> None: ...
9
+ def clear_drivers(self) -> None: ...
10
+ def get_drivers(self) -> tuple[LogicValue, ...]: ...
11
+ def add_connection(self, other: Node) -> None: ...
12
+ def remove_connection(self, other: Node) -> None: ...
13
+ def connect(self, other: Node) -> None: ...
14
+ def disconnect(self, other: Node) -> None: ...
15
+ def get_connections(self) -> tuple[Node, ...]: ...
@@ -0,0 +1,122 @@
1
+ """
2
+ SIRC Core Transistor Module.
3
+
4
+ Defines the abstract Transistor class and its NMOS and PMOS implementations. A
5
+ Transistor is a three-terminal digital switch with gate, source, and drain
6
+ Nodes. Transistors do not resolve logic or perform any electrical computation;
7
+ the Simulator evaluates each device's conduction state based on its gate value.
8
+ """
9
+
10
+ from __future__ import annotations
11
+ from abc import ABC, abstractmethod
12
+ from sirc.core.logic import LogicValue
13
+ from sirc.core.node import Node
14
+
15
+
16
+ class Transistor(ABC):
17
+ """
18
+ Abstract class for three-terminal transistor devices.
19
+
20
+ Each Transistor contains:
21
+ - gate : Node controlling conduction
22
+ - source: One side of the controlled channel
23
+ - drain : The other side of the controlled channel
24
+
25
+ This class defines only structural information and simple access helpers.
26
+ Device-specific conduction rules are implemented by subclasses. All logic
27
+ evaluation and node-group management is performed entirely by the Simulator.
28
+ """
29
+
30
+ __slots__ = ("gate", "source", "drain")
31
+
32
+ def __init__(self) -> None:
33
+ """
34
+ Create a new transistor with dedicated gate, source, and drain Nodes.
35
+ All Nodes begin in high-impedance (Z) state with no drivers. These Nodes
36
+ belong exclusively to this device and are never shared.
37
+ """
38
+ self.gate: Node = Node()
39
+ self.source: Node = Node()
40
+ self.drain: Node = Node()
41
+
42
+ # --------------------------------------------------------------------------
43
+ # Abstract Methods
44
+ # --------------------------------------------------------------------------
45
+
46
+ @abstractmethod
47
+ def is_conducting(self) -> bool:
48
+ """
49
+ Return True if this transistor is currently conducting.
50
+
51
+ A conducting transistor forms an electrical path between its source and
52
+ drain. The Simulator uses this result to determine whether the two Nodes
53
+ should be treated as members of the same node-group.
54
+ """
55
+ raise NotImplementedError("Must be implemented by subclasses.")
56
+
57
+ # --------------------------------------------------------------------------
58
+ # Public Methods
59
+ # --------------------------------------------------------------------------
60
+
61
+ def terminals(self) -> tuple[Node, Node, Node]:
62
+ """
63
+ Return a tuple of (gate, source, drain) Nodes.
64
+
65
+ Used by the Simulator for registration and structural traversal.
66
+ """
67
+ return (self.gate, self.source, self.drain)
68
+
69
+ def conduction_nodes(self) -> tuple[Node, Node]:
70
+ """
71
+ Return the (source, drain) Nodes involved in conduction.
72
+
73
+ Used by the Simulator when establishing or removing connectivity.
74
+ """
75
+ return (self.source, self.drain)
76
+
77
+ # --------------------------------------------------------------------------
78
+ # Debug Representation
79
+ # --------------------------------------------------------------------------
80
+
81
+ def __repr__(self) -> str:
82
+ """Return a debug representation of this Transistor."""
83
+ name = self.__class__.__name__
84
+ return f"<{name} gate={self.gate} source={self.source} drain={self.drain}>"
85
+
86
+
87
+ # ------------------------------------------------------------------------------
88
+ # NMOS Transistor Implementation
89
+ # ------------------------------------------------------------------------------
90
+
91
+
92
+ class NMOS(Transistor):
93
+ """
94
+ NMOS transistor device.
95
+
96
+ Conduction Rule:
97
+ - Conducts when the gate value is LogicValue.ONE.
98
+ - Non-conducting for ZERO, X, or Z.
99
+ """
100
+
101
+ def is_conducting(self) -> bool:
102
+ g = self.gate.value
103
+ return g is LogicValue.ONE
104
+
105
+
106
+ # ------------------------------------------------------------------------------
107
+ # PMOS Transistor Implementation
108
+ # ------------------------------------------------------------------------------
109
+
110
+
111
+ class PMOS(Transistor):
112
+ """
113
+ PMOS transistor device.
114
+
115
+ Conduction Rule:
116
+ - Conducts when the gate value is LogicValue.ZERO.
117
+ - Non-conducting for ONE, X, or Z.
118
+ """
119
+
120
+ def is_conducting(self) -> bool:
121
+ g = self.gate.value
122
+ return g is LogicValue.ZERO
@@ -0,0 +1,20 @@
1
+ import abc
2
+ from abc import ABC, abstractmethod
3
+ from sirc.core.logic import LogicValue as LogicValue
4
+ from sirc.core.node import Node as Node
5
+
6
+ class Transistor(ABC, metaclass=abc.ABCMeta):
7
+ gate: Node
8
+ source: Node
9
+ drain: Node
10
+ def __init__(self) -> None: ...
11
+ @abstractmethod
12
+ def is_conducting(self) -> bool: ...
13
+ def terminals(self) -> tuple[Node, Node, Node]: ...
14
+ def conduction_nodes(self) -> tuple[Node, Node]: ...
15
+
16
+ class NMOS(Transistor):
17
+ def is_conducting(self) -> bool: ...
18
+
19
+ class PMOS(Transistor):
20
+ def is_conducting(self) -> bool: ...
File without changes
@@ -0,0 +1,238 @@
1
+ """
2
+ SIRC Device Simulator Module.
3
+
4
+ Provides the DeviceSimulator class, responsible for evaluating a circuit
5
+ composed of Nodes, LogicDevices, and Transistors. The simulator performs
6
+ fixed-point iteration to resolve driver values, establish dynamic conduction
7
+ paths, and propagate LogicValues across connected node-groups.
8
+ """
9
+
10
+ from __future__ import annotations
11
+ from typing import Iterable
12
+ from sirc.core.logic import LogicValue
13
+ from sirc.core.node import Node
14
+ from sirc.core.device import LogicDevice
15
+ from sirc.core.transistor import Transistor
16
+
17
+
18
+ class DeviceSimulator:
19
+ """
20
+ Simulator for evaluating SIRC logic devices and transistors.
21
+
22
+ The DeviceSimulator maintains registered devices, transistors, and nodes.
23
+ Each tick clears drivers, applies device outputs, resolves node-groups via
24
+ DFS, and updates dynamic connectivity created by transistor conduction.
25
+ Fixed-point iteration continues until the circuit reaches a stable state.
26
+ """
27
+
28
+ def __init__(self) -> None:
29
+ """SIRC"""
30
+ self.devices: set[LogicDevice] = set()
31
+ self.transistors: set[Transistor] = set()
32
+ self.nodes: set[Node] = set()
33
+
34
+ # --------------------------------------------------------------------------
35
+ # Device Registration
36
+ # --------------------------------------------------------------------------
37
+
38
+ def register_device(self, device: LogicDevice) -> None:
39
+ """
40
+ Register a LogicDevice and its terminal Node with the simulator.
41
+
42
+ Args:
43
+ device: The LogicDevice to register.
44
+ """
45
+ self.devices.add(device)
46
+ self.nodes.add(device.terminal)
47
+
48
+ def register_devices(self, devices: Iterable[LogicDevice]) -> None:
49
+ """
50
+ Register multiple LogicDevices and terminal Nodes with the simulator.
51
+
52
+ Args:
53
+ devices: An iterable of LogicDevices to register.
54
+ """
55
+ for device in devices:
56
+ self.register_device(device)
57
+
58
+ def register_transistor(self, transistor: Transistor) -> None:
59
+ """
60
+ Register a Transistor and its terminal Nodes with the simulator.
61
+
62
+ Args:
63
+ transistor: The Transistor to register.
64
+ """
65
+ self.transistors.add(transistor)
66
+ self.nodes.update(transistor.terminals())
67
+
68
+ def register_transistors(self, transistors: Iterable[Transistor]) -> None:
69
+ """
70
+ Register multiple Transistors and terminal Nodes with the simulator.
71
+
72
+ Args:
73
+ transistors: An iterable of Transistors to register.
74
+ """
75
+ for transistor in transistors:
76
+ self.register_transistor(transistor)
77
+
78
+ def unregister_device(self, device: LogicDevice) -> None:
79
+ """
80
+ Unregister a LogicDevice and its terminal Node from the simulator.
81
+
82
+ Args:
83
+ device: The LogicDevice to unregister.
84
+ """
85
+ self.devices.discard(device)
86
+ self.nodes.discard(device.terminal)
87
+
88
+ def unregister_devices(self, devices: Iterable[LogicDevice]) -> None:
89
+ """
90
+ Unregister multiple LogicDevices and terminal Nodes from the simulator.
91
+
92
+ Args:
93
+ devices: An iterable of LogicDevices to unregister.
94
+ """
95
+ for device in devices:
96
+ self.unregister_device(device)
97
+
98
+ def unregister_transistor(self, transistor: Transistor) -> None:
99
+ """
100
+ Unregister a Transistor and its terminal Nodes from the simulator.
101
+
102
+ Args:
103
+ transistor: The Transistor to unregister.
104
+ """
105
+ self.transistors.discard(transistor)
106
+ self.nodes.difference_update(transistor.terminals())
107
+
108
+ def unregister_transistors(self, transistors: Iterable[Transistor]) -> None:
109
+ """
110
+ Unregister multiple Transistors and terminal Nodes from the simulator.
111
+
112
+ Args:
113
+ transistors: An iterable of Transistors to unregister.
114
+ """
115
+ for transistor in transistors:
116
+ self.unregister_transistor(transistor)
117
+
118
+ # --------------------------------------------------------------------------
119
+ # Logical Connection
120
+ # --------------------------------------------------------------------------
121
+
122
+ def connect(self, a: Node, b: Node) -> None:
123
+ """
124
+ Create a bidirectional connection between two Nodes.
125
+
126
+ Args:
127
+ a: The first Node.
128
+ b: The second Node.
129
+ """
130
+ a.connect(b)
131
+
132
+ def disconnect(self, a: Node, b: Node) -> None:
133
+ """
134
+ Remove the bidirectional connection between two Nodes.
135
+
136
+ Args:
137
+ a: The first Node.
138
+ b: The second Node.
139
+ """
140
+ a.disconnect(b)
141
+
142
+ # --------------------------------------------------------------------------
143
+ # Simulation Execution
144
+ # --------------------------------------------------------------------------
145
+
146
+ def _dfs(self, collection: Iterable[Node]) -> list[set[Node]]:
147
+ """
148
+ Perform depth-first search to identify connected node-groups.
149
+
150
+ Args:
151
+ collection: Iterable of Nodes to explore.
152
+
153
+ Returns:
154
+ List of sets, each containing Nodes in a connected group.
155
+ """
156
+ visited: set[Node] = set()
157
+ stack: list[Node] = []
158
+ groups: list[set[Node]] = []
159
+
160
+ for start in collection:
161
+ if start in visited:
162
+ continue
163
+
164
+ stack.append(start)
165
+ group: set[Node] = set()
166
+
167
+ while stack:
168
+ node = stack.pop()
169
+
170
+ if node in visited:
171
+ continue
172
+
173
+ visited.add(node)
174
+ group.add(node)
175
+
176
+ for neighbor in node.get_connections():
177
+ if neighbor not in visited:
178
+ stack.append(neighbor)
179
+
180
+ groups.append(group)
181
+
182
+ return groups
183
+
184
+ def _resolve_groups(self, groups: list[set[Node]]) -> None:
185
+ """
186
+ Resolve LogicValues for each connected node-group.
187
+
188
+ Args:
189
+ groups: List of node-groups to resolve.
190
+ """
191
+ for group in groups:
192
+ drivers: list[LogicValue] = [LogicValue.Z]
193
+
194
+ for node in group:
195
+ drivers.extend(node.get_drivers())
196
+
197
+ resolved_value: LogicValue = LogicValue.resolve_all(drivers)
198
+
199
+ for node in group:
200
+ node.set_resolved_value(resolved_value)
201
+
202
+ def tick(self) -> None:
203
+ """
204
+ Execute a simulation tick to update device states and connectivity.
205
+
206
+ This method performs the following steps:
207
+ 1. Clears all Node drivers.
208
+ 2. Applies LogicDevice outputs to their terminal Nodes.
209
+ 3. Repeatedly:
210
+ a. Identifies connected node-groups via DFS.
211
+ b. Resolves LogicValues for each group.
212
+ c. Updates transistor conduction paths.
213
+ 4. Continues until a fixed-point state is reached.
214
+ """
215
+ for node in self.nodes:
216
+ node.clear_drivers()
217
+
218
+ for device in self.devices:
219
+ device.terminal.add_driver(device.value)
220
+
221
+ while True:
222
+ groups = self._dfs(self.nodes)
223
+ self._resolve_groups(groups)
224
+ fixed_point = True
225
+
226
+ for transistor in self.transistors:
227
+ a, b = transistor.conduction_nodes()
228
+ if transistor.is_conducting():
229
+ if a not in b.get_connections():
230
+ transistor.source.connect(transistor.drain)
231
+ fixed_point = False
232
+ else:
233
+ if a in b.get_connections():
234
+ transistor.source.disconnect(transistor.drain)
235
+ fixed_point = False
236
+
237
+ if fixed_point:
238
+ break
@@ -0,0 +1,22 @@
1
+ from sirc.core.device import LogicDevice as LogicDevice
2
+ from sirc.core.logic import LogicValue as LogicValue
3
+ from sirc.core.node import Node as Node
4
+ from sirc.core.transistor import Transistor as Transistor
5
+ from typing import Iterable
6
+
7
+ class DeviceSimulator:
8
+ devices: set[LogicDevice]
9
+ transistors: set[Transistor]
10
+ nodes: set[Node]
11
+ def __init__(self) -> None: ...
12
+ def register_device(self, device: LogicDevice) -> None: ...
13
+ def register_devices(self, devices: Iterable[LogicDevice]) -> None: ...
14
+ def register_transistor(self, transistor: Transistor) -> None: ...
15
+ def register_transistors(self, transistors: Iterable[Transistor]) -> None: ...
16
+ def unregister_device(self, device: LogicDevice) -> None: ...
17
+ def unregister_devices(self, devices: Iterable[LogicDevice]) -> None: ...
18
+ def unregister_transistor(self, transistor: Transistor) -> None: ...
19
+ def unregister_transistors(self, transistors: Iterable[Transistor]) -> None: ...
20
+ def connect(self, a: Node, b: Node) -> None: ...
21
+ def disconnect(self, a: Node, b: Node) -> None: ...
22
+ def tick(self) -> None: ...
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: sirc
3
+ Version: 0.1.0
4
+ Summary: SIRC - Digital Logic and Circuit Simulation Engine
5
+ Author-email: CRISvsGAME <cris@crisvsgame.com>
6
+ Maintainer-email: CRISvsGAME <cris@crisvsgame.com>
7
+ License-Expression: MIT
8
+ Project-URL: Repository, https://github.com/CRISvsGAME/sirc.git
9
+ Keywords: digital logic,circuit simulation,simulation engine
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ Provides-Extra: dev
18
+ Requires-Dist: build; extra == "dev"
19
+ Requires-Dist: mypy; extra == "dev"
20
+ Requires-Dist: pylint; extra == "dev"
21
+ Requires-Dist: pytest; extra == "dev"
22
+ Requires-Dist: twine; extra == "dev"
23
+
24
+ # SIRC
@@ -0,0 +1,20 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/sirc/__init__.py
4
+ src/sirc.egg-info/PKG-INFO
5
+ src/sirc.egg-info/SOURCES.txt
6
+ src/sirc.egg-info/dependency_links.txt
7
+ src/sirc.egg-info/requires.txt
8
+ src/sirc.egg-info/top_level.txt
9
+ src/sirc/core/__init__.py
10
+ src/sirc/core/device.py
11
+ src/sirc/core/device.pyi
12
+ src/sirc/core/logic.py
13
+ src/sirc/core/logic.pyi
14
+ src/sirc/core/node.py
15
+ src/sirc/core/node.pyi
16
+ src/sirc/core/transistor.py
17
+ src/sirc/core/transistor.pyi
18
+ src/sirc/simulator/__init__.py
19
+ src/sirc/simulator/device.py
20
+ src/sirc/simulator/device.pyi
@@ -0,0 +1,7 @@
1
+
2
+ [dev]
3
+ build
4
+ mypy
5
+ pylint
6
+ pytest
7
+ twine
@@ -0,0 +1 @@
1
+ sirc