88
99# Create an MCP server
1010mcp = FastMCP (
11- name = "OpenROAD" ,
12- instructions = """
11+ name = "OpenROAD" ,
12+ instructions = """
1313 This server provides an instance of OpenROAD.
1414 OpenROAD's unified application implementing an RTL-to-GDS Flow. Documentation at https://openroad.readthedocs.io/en/latest/
1515 This MCP server expose several tools that you can use to interact with the current instance.
1616 You don't need to write tcl files, you can direclty use run_tcl to send commands to openroad.
1717 At the begining, check what is the current path OpenROAD is running in with "pwd" and change it if needed with "cd".
18- """
18+ """ ,
1919)
2020
2121tech = Tech ()
2222design = Design (tech )
2323
24+
2425def eval_tcl (command ):
25- # Create a temporary file to capture stdout
26- with tempfile .TemporaryFile (mode = 'w+' ) as tmp :
27- # Flush stdout to ensure order
28- sys .stdout .flush ()
29-
30- # Save original stdout
31- original_stdout_fd = os .dup (1 )
32-
33- try :
34- # Redirect stdout to the temporary file
35- os .dup2 (tmp .fileno (), 1 )
36-
37- # Run the command
38- tcl_result = design .evalTclString (command )
39- finally :
40- # Restore stdout
41- os .dup2 (original_stdout_fd , 1 )
42- os .close (original_stdout_fd )
43-
44- # Read the captured output
45- tmp .seek (0 )
46- stdout_output = tmp .read ()
47-
48- if stdout_output :
49- return stdout_output
50- return tcl_result
26+ # Create a temporary file to capture stdout
27+ with tempfile .TemporaryFile (mode = "w+" ) as tmp :
28+ # Flush stdout to ensure order
29+ sys .stdout .flush ()
30+
31+ # Save original stdout
32+ original_stdout_fd = os .dup (1 )
33+
34+ try :
35+ # Redirect stdout to the temporary file
36+ os .dup2 (tmp .fileno (), 1 )
37+
38+ # Run the command
39+ tcl_result = design .evalTclString (command )
40+ finally :
41+ # Restore stdout
42+ os .dup2 (original_stdout_fd , 1 )
43+ os .close (original_stdout_fd )
44+
45+ # Read the captured output
46+ tmp .seek (0 )
47+ stdout_output = tmp .read ()
48+
49+ if stdout_output :
50+ return stdout_output
51+ return tcl_result
52+
5153
5254@mcp .tool
5355def source (filename : str ) -> str :
54- """Sources a tcl file and runs it into OpenROAD
56+ """Sources a tcl file and runs it into OpenROAD
5557
56- Args:
57- filename: The absolute path to the tcl file to source and run
5858
59- Returns
60- The status of the command.
61- """
59+ Args:
60+ filename: The absolute path to the tcl file to source and run
61+
62+
63+ Returns
64+ The status of the command.
65+ """
66+
67+ return eval_tcl ("source %s" % filename )
6268
63- return eval_tcl ("source %s" % filename )
6469
6570@mcp .tool
6671def cd (path : str ) -> str :
67- """Changes the current folder where OpenROAD is running
72+ """Changes the current folder where OpenROAD is running
6873
69- Args:
70- path: The new path were OpenROAD will run from
7174
72- Returns
73- The status of the command.
74- """
75+ Args:
76+ path: The new path were OpenROAD will run from
77+
78+
79+ Returns
80+ The status of the command.
81+ """
82+
83+ return eval_tcl ("cd %s" % path )
7584
76- return eval_tcl ("cd %s" % path )
7785
7886@mcp .tool
7987def pwd () -> str :
80- """Returns the current path where OpenROAD is running
88+ """Returns the current path where OpenROAD is running
8189
82- Returns
83- The path where OpenROAD is running
84- """
8590
86- return eval_tcl ("pwd" )
91+ Returns
92+ The path where OpenROAD is running
93+ """
94+
95+ return eval_tcl ("pwd" )
8796
8897
8998@mcp .tool
9099def run_tcl (command : str ) -> str :
91- """Runs any tcl command available on OpenROAD
100+ """Runs any tcl command available on OpenROAD
92101
93- Args:
94- command: A tcl command to run on this OpenROAD instance
95102
96- Returns:
97- The text response of the command.
98- """
99- return eval_tcl (command )
103+ Args:
104+ command: A tcl command to run on this OpenROAD instance
105+
106+
107+ Returns:
108+ The text response of the command.
109+ """
110+ return eval_tcl (command )
111+
100112
101113@mcp .tool
102114def read_lef (filename : str , tech : bool , library : bool ) -> str :
103- """Reads Library Exchange Format (.lef) files or tech files or library (.lib) files
115+ """Reads Library Exchange Format (.lef) files or tech files or library (.lib) files
104116
105- Args:
106- filename: The absolute path to the file to read.
107- tech: boolean if we are reading a tech file
108- library: boolean if we are reading a library file
109117
110- Returns
111- The status of the command.
118+ Args:
119+ filename: The absolute path to the file to read.
120+ tech: boolean if we are reading a tech file
121+ library: boolean if we are reading a library file
122+
123+
124+ Returns
125+ The status of the command.
112126
113- """
114127
115- if tech :
116- return eval_tcl ("read_lef -tech %s" % filename )
117- if library :
118- return eval_tcl ("read_lef -library %s" % filename )
119- return eval_tcl ("read_lef %s" % filename )
128+ """
129+
130+ if tech :
131+ return eval_tcl ("read_lef -tech %s" % filename )
132+ if library :
133+ return eval_tcl ("read_lef -library %s" % filename )
134+ return eval_tcl ("read_lef %s" % filename )
120135
121136
122137@mcp .tool
123138def read_def (filename : str ) -> str :
124- """Read Design Exchange Format (.def) files.
139+ """Read Design Exchange Format (.def) files.
125140
126- Args:
127- filename: The absolute path to the file to read.
128141
129- Returns
130- The status of the command.
131- """
142+ Args:
143+ filename: The absolute path to the file to read.
132144
133- return eval_tcl ("read_def %s" % filename )
145+
146+ Returns
147+ The status of the command.
148+ """
149+
150+ return eval_tcl ("read_def %s" % filename )
134151
135152
136153@mcp .tool
137154def read_verilog (filename : str ) -> str :
138- """Read Verilog (.v) input file.
155+ """Read Verilog (.v) input file.
139156
140- Args:
141- filename: The absolute path to the verilog file to read.
142157
143- Returns
144- The status of the command.
145- """
158+ Args:
159+ filename: The absolute path to the verilog file to read.
160+
161+
162+ Returns
163+ The status of the command.
164+ """
165+
166+ return eval_tcl ("read_verilog %s" % filename )
146167
147- return eval_tcl ("read_verilog %s" % filename )
148168
149169@mcp .tool ()
150170def get_property (
151171 object_name : str ,
152172 object_type : Literal ["cell" , "net" , "pin" , "port" , "lib_cell" ],
153- property_name : str
173+ property_name : str ,
154174) -> str :
155175 """
156176 Queries a specific property of a design object in the OpenROAD database.
157-
177+
158178 Use this to inspect specific details of the design, such as the area of a cell,
159179 the capacitance of a net, or the reference name of an instance.
160180
@@ -171,27 +191,23 @@ def get_property(
171191 - For nets: "capacitance", "weight"
172192 - For pins: "direction", "arrival_window"
173193 """
174-
194+
175195 selector_map = {
176196 "cell" : "get_cells" ,
177197 "net" : "get_nets" ,
178198 "pin" : "get_pins" ,
179199 "port" : "get_ports" ,
180- "lib_cell" : "get_lib_cells"
200+ "lib_cell" : "get_lib_cells" ,
181201 }
182-
202+
183203 selector_cmd = selector_map .get (object_type )
184-
204+
185205 tcl_command = f"get_property [{ selector_cmd } {{{ object_name } }}] { property_name } "
186206 return eval_tcl (tcl_command )
187207
188208
189209@mcp .tool ()
190- def get_cells (
191- pattern : str = "*" ,
192- hierarchical : bool = False ,
193- limit : int = 20
194- ) -> str :
210+ def get_cells (pattern : str = "*" , hierarchical : bool = False , limit : int = 20 ) -> str :
195211 """
196212 Searches for cell instances in the design matching a specific name pattern.
197213 Returns a list of instance names (e.g., "u_core/u_alu/u_adder").
@@ -238,53 +254,56 @@ def get_cells(
238254
239255 return eval_tcl (tcl_command )
240256
257+
241258@mcp .tool
242259def reset () -> str :
243- """
244- Resets OpenROAD dropping the database.
245- WARNING: You will need to start from the begining loading all the libraries and designs again.
246- Use this command if you need to change something and OpenROAD does not allow you to do it after loading a design like re-defining corners.
247- """
248- tcl_command = """
260+ """
261+ Resets OpenROAD dropping the database.
262+ WARNING: You will need to start from the begining loading all the libraries and designs again.
263+ Use this command if you need to change something and OpenROAD does not allow you to do it after loading a design like re-defining corners.
264+ """
265+ tcl_command = """
249266 set db [::ord::get_db]
250267 $db clear
251268 dbDatabase_destroy $db
252269 set db [dbDatabase_create]
253270 """
254- return eval_tcl (tcl_command )
271+ return eval_tcl (tcl_command )
255272
256273
257274# Timing tools
258275@mcp .tool
259276def report_wns () -> str :
260- """
261- Calculates and reports the Worst Negative Slack (WNS) for the currently
262- loaded Verilog design in OpenROAD.
277+ """
278+ Calculates and reports the Worst Negative Slack (WNS) for the currently
279+ loaded Verilog design in OpenROAD.
280+
281+ WNS is the critical metric for timing closure; a negative value indicates
282+ that the design does not meet frequency constraints.
283+ """
284+ return eval_tcl ("report_wns" )
263285
264- WNS is the critical metric for timing closure; a negative value indicates
265- that the design does not meet frequency constraints.
266- """
267- return eval_tcl ("report_wns" )
268286
269287@mcp .tool ()
270288def report_tns (digits : int = 2 ) -> str :
271- """
272- Calculates and reports the Total Negative Slack (TNS) for the currently
273- loaded Verilog design in OpenROAD.
274-
275- TNS is the sum of all negative slack across all failing endpoints in the
276- design. While WNS (Worst Negative Slack) indicates the speed limit, TNS
277- indicates the *magnitude* of the failing paths. A large TNS value suggests
278- many paths are failing and the design may need significant architectural
279- changes or different optimization strategies.
280- """
281- return eval_tcl ("report_tns" )
289+ """
290+ Calculates and reports the Total Negative Slack (TNS) for the currently
291+ loaded Verilog design in OpenROAD.
292+
293+ TNS is the sum of all negative slack across all failing endpoints in the
294+ design. While WNS (Worst Negative Slack) indicates the speed limit, TNS
295+ indicates the *magnitude* of the failing paths. A large TNS value suggests
296+ many paths are failing and the design may need significant architectural
297+ changes or different optimization strategies.
298+ """
299+ return eval_tcl ("report_tns" )
300+
282301
283302@mcp .tool ()
284303def report_checks (
285304 path_delay : Literal ["min" , "max" , "min_max" ] = "max" ,
286305 group_count : int = 1 ,
287- detailed : bool = False
306+ detailed : bool = False ,
288307) -> str :
289308 """
290309 Performs detailed timing path analysis (report_checks) on the current design.
@@ -320,4 +339,4 @@ def report_checks(
320339if __name__ == "__main__" :
321340 print (os .getcwd ())
322341 # Run the server
323- mcp .run (transport = "http" , host = "127.0.0.1" , port = 8000 )
342+ mcp .run (transport = "http" , host = "127.0.0.1" , port = 8000 )
0 commit comments