CIRCT 22.0.0git
Loading...
Searching...
No Matches
synth.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5from . import synth, hw
6from ._synth_ops_gen import *
7from .._mlir_libs._circt._synth import _LongestPathAnalysis, _LongestPathCollection, _LongestPathDataflowPath, _LongestPathHistory, _LongestPathObject
8
9from dataclasses import dataclass
10from typing import Any, Dict, List, Union
11
12# ============================================================================
13# Core Data Structures for Synth Longest Path Analysis
14# ============================================================================
15
16
17@dataclass
19 """
20 Represents a single element in a hierarchical instance path.
21 In hardware design, modules are instantiated hierarchically. This class
22 represents one level in that hierarchy, containing both the module type
23 and the specific instance name.
24 Attributes:
25 instance_name: The name of this specific instance
26 module_name: The type/name of the module being instantiated
27 """
28 _instance: hw.InstanceOp
29
30 def __init__(self, instance: hw.InstanceOp):
31 self._instance_instance = instance
32
33 @property
34 def instance_name(self) -> str:
35 return self._instance_instance.attributes["instanceName"].value
36
37 @property
38 def module_name(self) -> str:
39 return self._instance_instance.attributes["moduleName"].value
40
41
42@dataclass
43class Object:
44 """
45 Represents a signal or port object in the dataflow graph.
46 This class encapsulates a specific signal within the hardware hierarchy,
47 including its location in the instance hierarchy, signal name, and bit position
48 for multi-bit signals.
49 Attributes:
50 instance_path: Hierarchical path to the module containing this object
51 name: The signal/port name within the module
52 bit_pos: Bit position for multi-bit signals (0 for single-bit)
53 """
54
55 _object: _LongestPathObject
56
57 # TODO: Associate with an MLIR value/op
58
59 def __str__(self) -> str:
60 """
61 Generate a human-readable string representation of this object.
62 Format: "module1:instance1/module2:instance2 signal_name[bit_pos]"
63 """
64 path = "/".join(f"{elem.module_name}:{elem.instance_name}"
66 return f"{path} {self.name}[{self.bit_pos}]"
67
68 def __repr__(self) -> str:
69 return f"Object({self.instance_path}, {self.name}, {self.bit_pos})"
70
71 @property
72 def instance_path(self) -> List[Instance]:
73 """Get the hierarchical instance path to this object."""
74 operations = self._object.instance_path
75
76 return [Instance(op) for op in operations]
77
78 @property
79 def name(self) -> str:
80 """Get the name of this signal/port."""
81 return self._object.name
82
83 @property
84 def bit_pos(self) -> int:
85 """Get the bit position for multi-bit signals."""
86 return self._object.bit_pos
87
88
89@dataclass
91 """
92 Represents a debug point in the timing path history.
93 Debug points are intermediate points along a timing path that provide
94 insight into the delay accumulation and signal propagation through
95 the circuit. Each point captures the state at a specific location.
96 Attributes:
97 object: The signal/object at this debug point
98 delay: Accumulated delay up to this point (in timing units)
99 comment: Optional descriptive comment about this point
100 """
101
102 object: Object
103 delay: int
104 comment: str
105
106 @classmethod
107 def from_dict(cls, data: Dict[str, Any]) -> "DebugPoint":
108 """Create a DebugPoint from a dictionary representation."""
109 return cls(
110 object=Object.from_dict(data["object"]),
111 delay=data["delay"],
112 comment=data["comment"],
113 )
114
115
116@dataclass
118 """
119 Represents a complete dataflow path from end point to start point.
120 A dataflow path captures the complete timing path through a circuit,
121 from an output point (end-point) back to an input point (start-point), including
122 all intermediate debug points and the total delay.
123 Attributes:
124 end_point: The output signal/object where this path ends
125 path: The OpenPath containing the detailed path information
126 root: The root module name for this analysis
127 """
128
129 _path: _LongestPathDataflowPath
130
131 @property
132 def delay(self) -> int:
133 """Get the total delay of this path in timing units."""
134 return self._path.delay
135
136 @property
137 def start_point(self) -> Object:
138 """Get the input signal/object where this path begins."""
139 return Object(self._path.start_point)
140
141 @property
142 def end_point(self) -> Object:
143 """Get the output signal/object where this path ends."""
144 return Object(self._path.end_point)
145
146 @property
147 def history(self) -> List[DebugPoint]:
148 """Get the history of debug points along this path."""
149 return [i for i in LongestPathHistory(self._path.history)]
150
151 @property
152 def root(self) -> str:
153 """Get the root module name for this analysis."""
154 return self._path.root.attributes["sym_name"].value
155
156 # ========================================================================
157 # Visualization and Analysis Methods
158 # ========================================================================
159
160 def to_flamegraph(self) -> str:
161 """
162 Convert this path to FlameGraph format for visualization.
163 FlameGraphs are a visualization technique that shows call stacks or
164 in this case, timing paths through the circuit hierarchy. Each line
165 represents a segment of the path with its associated delay.
166 The format is: "hierarchy_path delay_increment"
167 where hierarchy_path uses semicolons to separate hierarchy levels.
168 Returns:
169 String in FlameGraph format showing the timing path progression
170 """
171 trace = []
172 prefix = f"top:{self.root}"
173
174 # Build hierarchy strings for start and end points
175 start_point_hierarchy = self._build_hierarchy_string(
177 end_point_hierarchy = self._build_hierarchy_string(self.end_point, prefix)
178
179 # Track current position and delay for incremental output
180 current_hierarchy = start_point_hierarchy
181 current_delay = 0
182
183 # Process debug history points in reverse order (from input to output)
184 for debug_point in self.history[::-1]:
185 history_hierarchy = self._build_hierarchy_string(debug_point.object,
186 prefix)
187 if history_hierarchy:
188 # Add segment from current position to this debug point
189 delay_increment = debug_point.delay - current_delay
190 trace.append(f"{current_hierarchy} {delay_increment}")
191
192 # Update current position
193 current_hierarchy = history_hierarchy
194 current_delay = debug_point.delay
195
196 # Add final segment to end-point if there's remaining delay
197 if current_delay != self.delay:
198 final_delay = self.delay - current_delay
199 trace.append(f"{end_point_hierarchy} {final_delay}")
200
201 return "\n".join(trace)
202
203 def _build_hierarchy_string(self, obj: Object, prefix: str = "") -> str:
204 """
205 Build a hierarchical string representation of an Object for FlameGraph format.
206 This method constructs a semicolon-separated hierarchy string that represents
207 the full path from the top-level module down to the specific signal. This
208 format is compatible with FlameGraph visualization tools.
209 Args:
210 obj: Object to represent in the hierarchy
211 prefix: Top-level prefix (typically "top:module_name")
212 Returns:
213 Hierarchical string in format: "top:root;module1:inst1;module2:inst2;signal[bit]"
214 """
215 parts = [prefix]
216
217 # Add each level of the instance hierarchy
218 for elem in obj.instance_path:
219 parts.append(f"{elem.instance_name}:{elem.module_name}")
220
221 # Add the signal name with bit position if applicable
222 signal_part = obj.name
223 signal_part += f"[{obj.bit_pos}]"
224 parts.append(signal_part)
225
226 return ";".join(parts)
227
228
229# ============================================================================
230# Analysis Collection Classes
231# ============================================================================
232
233
235 """
236 A collection of timing paths sorted by delay (longest first).
237 This class provides a Python wrapper around the C++ LongestPathCollection,
238 offering convenient access to timing paths with caching for performance.
239 The paths are pre-sorted by delay in descending order.
240 Attributes:
241 collection: The underlying C++ collection object
242 length: Number of paths in the collection
243 """
244
245 def __init__(self, collection):
246 """
247 Initialize the collection wrapper.
248 Args:
249 collection: The underlying C++ LongestPathCollection object
250 """
251 self.collection = collection
252 self.length = self.collection.get_size()
253
254 # ========================================================================
255 # Collection Interface Methods
256 # ========================================================================
257
258 def __len__(self) -> int:
259 """Get the number of paths in the collection."""
260 return self.length
261
263 self, index: Union[slice,
264 int]) -> Union[DataflowPath, List[DataflowPath]]:
265 """
266 Get a specific path from the collection by index.
267 Supports both integer and slice indexing. Integer indices can be negative.
268
269 Args:
270 index: Integer index or slice object to access paths
271
272 Returns:
273 DataflowPath or list of DataflowPaths for slice access
274
275 Raises:
276 IndexError: If index is out of range
277 """
278 if isinstance(index, slice):
279 return [self[i] for i in range(*index.indices(len(self)))]
280
281 # Handle negative indexing
282 if index < 0:
283 index += self.length
284 if index < 0 or index >= self.length:
285 raise IndexError("Index out of range")
286
287 return DataflowPath(self.collection.get_path(index))
288
289 # ========================================================================
290 # Analysis and Query Methods
291 # ========================================================================
292
293 @property
294 def longest_path(self) -> DataflowPath:
295 """Get the path with the maximum delay (first element since sorted)."""
296 return self[0]
297
298 def get_by_delay_ratio(self, ratio: float) -> DataflowPath:
299 """
300 Get the path at the specified position in the delay-sorted collection.
301 Since paths are sorted by delay in descending order, higher ratios
302 correspond to paths with higher delays (closer to the critical path).
303 Args:
304 ratio: Position ratio between 0.0 and 1.0
305 (e.g., 1.0 = longest delay path, 0.0 = shortest delay path,
306 0.95 = path among the top 5% slowest paths)
307 Returns:
308 DataflowPath at the specified position ratio
309 """
310 assert ratio >= 0.0 and ratio <= 1.0, "Ratio must be between 0.0 and 1.0"
311 index = int(len(self) * (1 - ratio))
312 return self[index]
313
314 def print_summary(self) -> None:
315 """Print a statistical summary of path delays in the collection."""
316 print(f"Total paths: {len(self)}")
317 print(f"Max delay: {self.longest_path.delay}")
318 print(f"Min delay: {self[-1].delay}")
319 print(f"50th percentile delay: {self.get_by_delay_ratio(0.5).delay}")
320 print(f"90th percentile delay: {self.get_by_delay_ratio(0.9).delay}")
321 print(f"95th percentile delay: {self.get_by_delay_ratio(0.95).delay}")
322 print(f"99th percentile delay: {self.get_by_delay_ratio(0.99).delay}")
323 print(f"99.9th percentile delay: {self.get_by_delay_ratio(0.999).delay}")
324
325 def merge(self, src: "LongestPathCollection"):
326 """
327 Merge another collection into this one.
328 Args:
329 src: The collection to merge into this one
330 """
331 self.collection.merge(src.collection)
332 # Re-initialize to reflect the merged collection
333 self.__init__(self.collection)
334
335
336# ============================================================================
337# Main Analysis Interface
338# ============================================================================
339
340
342 """
343 Main interface for performing longest path analysis on Synth dialect.
344 This class provides a Python wrapper around the C++ LongestPathAnalysis,
345 enabling timing analysis of Synth dialect. It can identify critical timing
346 paths and provide detailed path information.
347 Attributes:
348 analysis: The underlying C++ analysis object
349 """
350
351 def __init__(self,
352 module,
353 collect_debug_info: bool = True,
354 keep_only_max_delay_paths: bool = False,
355 lazy_computation: bool = False,
356 top_module_name: str = ""):
357 """
358 Initialize the longest path analysis for a given module.
359 Args:
360 module: The MLIR module to analyze
361 collect_debug_info: Whether to include debug points in the analysis.
362 Debug points provide additional information about the path,
363 but increase analysis time and memory usage.
364 keep_only_max_delay_paths: Keep only maximum-delay paths in collections.
365 lazy_computation: Enable lazy (on-demand) computation.
366 """
367 self.analysis = synth._LongestPathAnalysis(module, collect_debug_info,
368 keep_only_max_delay_paths,
369 lazy_computation,
370 top_module_name)
371
372 def get_paths(self,
373 value,
374 bit_pos: int,
375 elaborate_paths: bool = True) -> LongestPathCollection:
376 """
377 Perform longest path analysis and return all timing paths to the
378 specified value and bit position.
379 Args:
380 value: The value to analyze
381 bit_pos: The bit position to analyze
382 elaborate_paths: Whether to elaborate the paths with detailed information
383 Returns:
384 LongestPathCollection containing all paths sorted by delay
385 """
387 self.analysis.get_paths(value, bit_pos, elaborate_paths))
388
390 module_name: str,
391 elaborate_paths: bool = True) -> LongestPathCollection:
392 """
393 Perform longest path analysis and return all timing paths inside
394 the module hierarchy.
395 This method analyzes the specified module and returns a collection
396 of all timing paths, sorted by delay in descending order.
397 Args:
398 module_name: Name of the module to analyze
399 Returns:
400 LongestPathCollection containing all paths sorted by delay
401 """
403 self.analysis.get_all_paths(module_name, elaborate_paths))
404
405
406@dataclass
408 """
409 Represents the history of a timing path, including intermediate debug points.
410 This class provides a Python wrapper around the C++ LongestPathHistory,
411 enabling iteration over the path's history and access to debug points.
412 Attributes:
413 history: The underlying C++ history object
414 """
415 history: _LongestPathHistory
416
417 def __iter__(self):
418 """Iterate over the debug points in the history."""
419 while not self.historyhistory.empty:
420 object, delay, comment = self.historyhistory.head
421 yield DebugPoint(Object(object), delay, comment)
static void print(TypedAttr val, llvm::raw_ostream &os)
List[DebugPoint] history(self)
Definition synth.py:147
Object start_point(self)
Definition synth.py:137
Object end_point(self)
Definition synth.py:142
_LongestPathDataflowPath _path
Definition synth.py:129
str root(self)
Definition synth.py:152
str _build_hierarchy_string(self, Object obj, str prefix="")
Definition synth.py:203
int delay(self)
Definition synth.py:132
str to_flamegraph(self)
Definition synth.py:160
"DebugPoint" from_dict(cls, Dict[str, Any] data)
Definition synth.py:107
str instance_name(self)
Definition synth.py:34
str module_name(self)
Definition synth.py:38
__init__(self, hw.InstanceOp instance)
Definition synth.py:30
LongestPathCollection get_paths(self, value, int bit_pos, bool elaborate_paths=True)
Definition synth.py:375
LongestPathCollection get_all_paths(self, str module_name, bool elaborate_paths=True)
Definition synth.py:391
__init__(self, module, bool collect_debug_info=True, bool keep_only_max_delay_paths=False, bool lazy_computation=False, str top_module_name="")
Definition synth.py:356
merge(self, "LongestPathCollection" src)
Definition synth.py:325
Union[DataflowPath, List[DataflowPath]] __getitem__(self, Union[slice, int] index)
Definition synth.py:264
DataflowPath longest_path(self)
Definition synth.py:294
DataflowPath get_by_delay_ratio(self, float ratio)
Definition synth.py:298
__init__(self, collection)
Definition synth.py:245
_LongestPathHistory history
Definition synth.py:415
str name(self)
Definition synth.py:79
str __str__(self)
Definition synth.py:59
List[Instance] instance_path(self)
Definition synth.py:72
int bit_pos(self)
Definition synth.py:84
_LongestPathObject _object
Definition synth.py:55
str __repr__(self)
Definition synth.py:68