PySHDL 0.1.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.
PySHDL/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """shdl library."""
2
+
3
+ from importlib import import_module
4
+ from importlib.metadata import PackageNotFoundError, version
5
+
6
+ from .circuit import Circuit
7
+ from .shdlc import Component, Instance, Port, SHDLParser, generate_c_code
8
+
9
+ __all__ = [
10
+ "Circuit",
11
+ "Component",
12
+ "Instance",
13
+ "Port",
14
+ "SHDLParser",
15
+ "generate_c_code",
16
+ "__version__",
17
+ ]
18
+ try:
19
+ __version__ = version("shdl")
20
+ except PackageNotFoundError:
21
+ __version__ = "0.0.0"
PySHDL/circuit.py ADDED
@@ -0,0 +1,180 @@
1
+ """
2
+ SHDL Driver - Simple Python interface for SHDL circuits
3
+ ========================================================
4
+
5
+ A minimal module for compiling and driving SHDL circuits from Python.
6
+ Provides a clean, Pythonic API for circuit simulation.
7
+
8
+ Usage:
9
+ from shdl_driver import SHDLCircuit
10
+
11
+ # Load and compile a circuit
12
+ circuit = SHDLCircuit("adder16.shdl")
13
+
14
+ # Set inputs
15
+ circuit.poke("A", 42)
16
+ circuit.poke("B", 17)
17
+ circuit.poke("Cin", 1)
18
+
19
+ # Read outputs
20
+ result = circuit.peek("Sum")
21
+ print(f"42 + 17 + 1 = {result}")
22
+
23
+ # Advance simulation time
24
+ circuit.step(1)
25
+ """
26
+
27
+ import ctypes
28
+ import os
29
+ import subprocess
30
+ import tempfile
31
+ from pathlib import Path
32
+ from typing import Dict, Optional, Union
33
+
34
+
35
+ class Circuit:
36
+ """
37
+ Interface for driving SHDL circuits from Python.
38
+
39
+ Compiles SHDL to C, builds a shared library, and provides
40
+ Python wrappers for the circuit API.
41
+ """
42
+
43
+ def __init__(self, shdl_file: Union[str, Path], search_paths: Optional[list] = None):
44
+ """
45
+ Load and compile a SHDL circuit.
46
+
47
+ Args:
48
+ shdl_file: Path to the .shdl file
49
+ search_paths: Optional list of directories to search for imports
50
+ """
51
+ self.shdl_file = Path(shdl_file)
52
+ if not self.shdl_file.exists():
53
+ raise FileNotFoundError(f"SHDL file not found: {shdl_file}")
54
+
55
+ # Default search path includes SHDL_components if it exists
56
+ if search_paths is None:
57
+ search_paths = []
58
+ component_dir = self.shdl_file.parent / "SHDL_components"
59
+ if component_dir.exists():
60
+ search_paths.append(str(component_dir))
61
+
62
+ self.search_paths = search_paths
63
+ self._lib = None
64
+ self._compile_and_load()
65
+
66
+ def _compile_and_load(self):
67
+ """Compile SHDL to C and load the shared library."""
68
+ # Generate C code
69
+ c_file = self.shdl_file.with_suffix('.c')
70
+ self._compile_shdl_to_c(c_file)
71
+
72
+ # Build shared library
73
+ so_file = self._build_shared_library(c_file)
74
+
75
+ # Load library
76
+ self._lib = ctypes.CDLL(str(so_file))
77
+
78
+ # Set up function signatures
79
+ self._lib.reset.argtypes = []
80
+ self._lib.reset.restype = None
81
+
82
+ self._lib.poke.argtypes = [ctypes.c_char_p, ctypes.c_uint64]
83
+ self._lib.poke.restype = None
84
+
85
+ self._lib.peek.argtypes = [ctypes.c_char_p]
86
+ self._lib.peek.restype = ctypes.c_uint64
87
+
88
+ self._lib.step.argtypes = [ctypes.c_int]
89
+ self._lib.step.restype = None
90
+
91
+ # Initialize circuit
92
+ self.reset()
93
+
94
+ def _compile_shdl_to_c(self, output_file: Path):
95
+ """Compile SHDL file to C using shdlc compiler."""
96
+ # Look for shdlc in the same directory as the SHDL file
97
+ from .shdlc import generate_c_code, SHDLParser
98
+ shdl_parser = SHDLParser(self.search_paths)
99
+ component = shdl_parser.parse_file(self.shdl_file)
100
+ component = shdl_parser.flatten_all_levels(component)
101
+ c_code = generate_c_code(component)
102
+ with open(output_file, 'w') as f:
103
+ f.write(c_code)
104
+
105
+ def _build_shared_library(self, c_file: Path) -> Path:
106
+ """Build a shared library from C code."""
107
+ so_file = c_file.with_suffix('.so')
108
+
109
+ cmd = [
110
+ "gcc",
111
+ "-shared",
112
+ "-fPIC",
113
+ "-O2",
114
+ "-o", str(so_file),
115
+ "-xc", str(c_file)
116
+ ]
117
+
118
+ try:
119
+ subprocess.run(cmd, check=True, capture_output=True, text=True)
120
+ except subprocess.CalledProcessError as e:
121
+ raise RuntimeError(
122
+ f"Failed to build shared library:\n{e.stderr}"
123
+ )
124
+
125
+ return so_file
126
+
127
+ def reset(self):
128
+ """Reset all circuit state to zero."""
129
+ self._lib.reset()
130
+
131
+ def poke(self, signal: str, value: int):
132
+ """
133
+ Set an input signal to a specific value.
134
+
135
+ Args:
136
+ signal: Name of the input signal
137
+ value: Integer value to set
138
+ """
139
+ self._lib.poke(signal.encode('utf-8'), value)
140
+
141
+ def peek(self, signal: str) -> int:
142
+ """
143
+ Read the current value of a signal.
144
+
145
+ Args:
146
+ signal: Name of the signal (input or output)
147
+
148
+ Returns:
149
+ Current value as an integer
150
+ """
151
+ return self._lib.peek(signal.encode('utf-8'))
152
+
153
+ def step(self, cycles: int = 1):
154
+ """
155
+ Advance simulation by the specified number of cycles.
156
+
157
+ Args:
158
+ cycles: Number of clock cycles to advance (default: 1)
159
+ """
160
+ self._lib.step(cycles)
161
+
162
+
163
+ def test(self, inputs: Dict[str, int], steps: int) -> Dict[str, int]:
164
+ """
165
+ Convenience method: set inputs and return all outputs.
166
+
167
+ Args:
168
+ inputs: Dictionary mapping signal names to values
169
+
170
+ Returns:
171
+ Dictionary with the same keys containing output values
172
+ """
173
+ # Set all inputs
174
+ for signal, value in inputs.items():
175
+ self.poke(signal, value)
176
+
177
+ self.step(steps)
178
+
179
+ # Read back the same signals
180
+ return {signal: self.peek(signal) for signal in inputs.keys()}
PySHDL/cli.py ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SHDL Compiler - Command-line interface
4
+ Usage: shdlc [options] <input.shdl>
5
+ """
6
+
7
+ import argparse
8
+ import subprocess
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ from .shdlc import SHDLParser, generate_c_code
13
+
14
+
15
+ def main() -> int:
16
+ parser = argparse.ArgumentParser(
17
+ description='SHDL Compiler - Compile SHDL hardware description files to C',
18
+ usage='%(prog)s [options] <input.shdl>'
19
+ )
20
+
21
+ parser.add_argument(
22
+ 'input',
23
+ help='Input SHDL file'
24
+ )
25
+
26
+ parser.add_argument(
27
+ '-o', '--output',
28
+ help='Output C file (default: <input>.c)',
29
+ default=None
30
+ )
31
+
32
+ parser.add_argument(
33
+ '-I', '--include',
34
+ action='append',
35
+ dest='include_paths',
36
+ help='Add directory to component search path',
37
+ default=[]
38
+ )
39
+
40
+ parser.add_argument(
41
+ '-c', '--compile-only',
42
+ action='store_true',
43
+ help='Generate C code only, do not compile to binary'
44
+ )
45
+
46
+ parser.add_argument(
47
+ '-O', '--optimize',
48
+ choices=['0', '1', '2', '3'],
49
+ default='3',
50
+ help='GCC optimization level (default: 3)'
51
+ )
52
+
53
+ parser.add_argument(
54
+ '--no-flatten',
55
+ action='store_true',
56
+ help='Do not flatten component hierarchy'
57
+ )
58
+
59
+ parser.add_argument(
60
+ '-v', '--verbose',
61
+ action='store_true',
62
+ help='Verbose output'
63
+ )
64
+
65
+ args = parser.parse_args()
66
+
67
+ # Validate input file
68
+ input_path = Path(args.input)
69
+ if not input_path.exists():
70
+ print(f"shdlc: error: {args.input}: No such file", file=sys.stderr)
71
+ return 1
72
+
73
+ if not input_path.suffix == '.shdl':
74
+ print(f"shdlc: warning: {args.input}: File does not have .shdl extension", file=sys.stderr)
75
+
76
+ # Determine output file
77
+ if args.output:
78
+ output_c = Path(args.output)
79
+ if not output_c.suffix == '.c' and not args.compile_only:
80
+ # Binary output
81
+ output_bin = output_c
82
+ output_c = input_path.with_suffix('.c')
83
+ else:
84
+ output_bin = output_c.with_suffix('')
85
+ else:
86
+ output_c = input_path.with_suffix('.c')
87
+ output_bin = input_path.with_suffix('')
88
+
89
+ # Setup search paths
90
+ search_paths = [input_path.parent]
91
+
92
+ # Add SHDL_components if it exists
93
+ default_lib = input_path.parent / "SHDL_components"
94
+ if default_lib.exists():
95
+ search_paths.append(default_lib)
96
+
97
+ # Add user-specified include paths
98
+ for inc_path in args.include_paths:
99
+ search_paths.append(Path(inc_path))
100
+
101
+ if args.verbose:
102
+ print(f"Input: {input_path}")
103
+ print(f"Output C: {output_c}")
104
+ if not args.compile_only:
105
+ print(f"Output binary: {output_bin}")
106
+ print(f"Search paths: {[str(p) for p in search_paths]}")
107
+
108
+ try:
109
+ # Parse SHDL file
110
+ if args.verbose:
111
+ print("Parsing SHDL file...")
112
+
113
+ shdl_parser = SHDLParser(search_paths)
114
+ component = shdl_parser.parse_file(input_path)
115
+
116
+ if args.verbose:
117
+ print(f" Component: {component.name}")
118
+ print(f" Instances: {len(component.instances)}")
119
+ print(f" Connections: {len(component.connections)}")
120
+
121
+ # Flatten component hierarchy
122
+ if not args.no_flatten:
123
+ if args.verbose:
124
+ print("Flattening component hierarchy...")
125
+
126
+ component = shdl_parser.flatten_all_levels(component)
127
+
128
+ if args.verbose:
129
+ print(f" Instances after flattening: {len(component.instances)}")
130
+ print(f" Connections after flattening: {len(component.connections)}")
131
+ print(f" Gate types: {set(i.component_type for i in component.instances)}")
132
+
133
+ # Generate C code
134
+ if args.verbose:
135
+ print("Generating C code...")
136
+
137
+ c_code = generate_c_code(component)
138
+
139
+ with open(output_c, 'w', encoding='utf-8') as file_handle:
140
+ file_handle.write(c_code)
141
+
142
+ if args.verbose:
143
+ print(f" Generated {output_c} ({len(c_code)} bytes)")
144
+
145
+ # Compile to binary
146
+ if not args.compile_only:
147
+ if args.verbose:
148
+ print("Compiling C code to binary...")
149
+
150
+ # gcc -shared -fPIC -O3 your_design.c -o your_design.so
151
+ gcc_cmd = [
152
+ 'gcc',
153
+ '-shared',
154
+ '-fPIC',
155
+ f'-O{args.optimize}',
156
+ str(output_c),
157
+ '-o', str(output_bin)+".so"
158
+ ]
159
+
160
+ if args.verbose:
161
+ print(f" Command: {' '.join(gcc_cmd)}")
162
+
163
+ result = subprocess.run(gcc_cmd, capture_output=True, text=True)
164
+
165
+ if result.returncode != 0:
166
+ print(f"shdlc: error: gcc compilation failed", file=sys.stderr)
167
+ if result.stderr:
168
+ print(result.stderr, file=sys.stderr)
169
+ return 1
170
+
171
+ if args.verbose:
172
+ print(f" Generated binary: {output_bin}")
173
+
174
+ if not args.verbose:
175
+ # Mimic gcc's quiet success
176
+ pass
177
+ else:
178
+ print("Done!")
179
+
180
+ return 0
181
+
182
+ except Exception as e:
183
+ print(f"shdlc: error: {e}", file=sys.stderr)
184
+ if args.verbose:
185
+ import traceback
186
+ traceback.print_exc()
187
+ return 1
188
+
189
+
190
+ if __name__ == '__main__':
191
+ sys.exit(main())
PySHDL/py.typed ADDED
File without changes