Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions backend/src/openroad_mcp/server/orfs/orfs_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def _get_platforms_impl(self) -> str:
ORFS.server.platform = "sky130hd"
return ORFS.server.platform

def _make(self, cmd) -> None:
assert ORFS.server is not None
ORFS.server._check_configuration()
ORFS.server._command(cmd)

def _get_designs_impl(self) -> str:
"""Internal implementation of get_designs"""
# TODO: scrape designs instead of default riscv
Expand All @@ -40,7 +45,7 @@ def _command(self, cmd: str) -> None:
working = os.getcwd()
os.chdir(ORFS.server.flow_dir)

make = f"make DESIGN_CONFIG={ORFS.server.flow_dir}/designs/{ORFS.server.platform}/{ORFS.server.design}/config.mk"
make = f"make DESIGN_CONFIG={ORFS.server.makefile_pointer}"
logging.info(cmd)
build_command = f"{make} {cmd}"
ORFS.server._run_command(build_command)
Expand Down Expand Up @@ -99,9 +104,7 @@ def make(cmd: str) -> str:

Use this for any makefile target not covered by step/jump commands.
"""
assert ORFS.server is not None
ORFS.server._check_configuration()
ORFS.server._command(cmd)
ORFS.server._make(cmd)

return f"finished {cmd}"

Expand Down
50 changes: 28 additions & 22 deletions backend/src/openroad_mcp/server/orfs/orfs_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,31 @@ def _get_default_makefile(self) -> None:
assert ORFS.server is not None
ORFS.server.makefile_pointer = f"{ORFS.server.flow_dir}/designs/{ORFS.server.platform}/{ORFS.server.design}/config.mk"

def _create_dynamic_makefile(self) -> None:
"""Create dynamic makefile."""
assert ORFS.server is not None
if not (ORFS.server.design and ORFS.server.platform):
logging.warning("no custom design/platform selected!")
ORFS.server._get_designs_impl()
ORFS.server._get_platforms_impl()
ORFS.server._get_default_env()
else:
pass

ORFS.server.dynamic_makefile = True
ORFS.server.makefile_pointer = f"{ORFS.server.flow_dir}/designs/{ORFS.server.platform}/{ORFS.server.design}/dynamic_config.mk"
with open(f"{ORFS.server.makefile_pointer}", "w") as f:
for key in ORFS.server.orfs_env.keys():
f.write(f"export {key} = {ORFS.server.orfs_env[key]}\n")
result = ""
for key in ORFS.server.orfs_env.keys():
result += f"{key}: {ORFS.server.orfs_env[key]}\n"
if result:
return result
else:
return "no env vars"


def _get_makefile(self) -> None:
"""Retrieve the current makefile pointer path."""
assert ORFS.server is not None
Expand All @@ -32,7 +57,8 @@ def _get_default_env(self) -> None:
ORFS.server.orfs_env.update(
{
"PLATFORM": f"{ORFS.server.platform}",
"DESIGN_NAME": f"{ORFS.server.design}",
#"DESIGN_NAME": f"{ORFS.server.design}",
"DESIGN_NAME": "riscv", # TODO: temporary fix for default design
"DESIGN_NICKNAME": f"{ORFS.server.design}",
"VERILOG_FILES": "$(sort $(wildcard ./designs/src/$(DESIGN_NICKNAME)/*.v))",
"SDC_FILE": "./designs/$(PLATFORM)/$(DESIGN_NICKNAME)/constraint.sdc",
Expand Down Expand Up @@ -77,27 +103,7 @@ def create_dynamic_makefile(cmd: str) -> str:
Note:
File is written to: {flow_dir}/designs/{platform}/{design}/dynamic_config.mk
"""
assert ORFS.server is not None
if not (ORFS.server.design and ORFS.server.platform):
logging.warning("no custom design/platform selected!")
ORFS.server._get_designs_impl()
ORFS.server._get_platforms_impl()
ORFS.server._get_default_env()
else:
pass

ORFS.server.dynamic_makefile = True
ORFS.server.makefile_pointer = f"{ORFS.server.flow_dir}/designs/{ORFS.server.platform}/{ORFS.server.design}/dynamic_config.mk"
with open(f"{ORFS.server.makefile_pointer}", "w") as f:
for key in ORFS.server.orfs_env.keys():
f.write(f"export {key} = {ORFS.server.orfs_env[key]}\n")
result = ""
for key in ORFS.server.orfs_env.keys():
result += f"{key}: {ORFS.server.orfs_env[key]}\n"
if result:
return result
else:
return "no env vars"
ORFS.server._create_dynamic_makefile()

@staticmethod
@ORFS.mcp.tool
Expand Down
64 changes: 64 additions & 0 deletions backend/src/openroad_mcp/server/orfs/orfs_optimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import json
import scipy

from src.openroad_mcp.server.orfs.orfs_tools import ORFS

class ORFSOptimizer(ORFS):
@staticmethod
@ORFS.mcp.tool
def calc_cost() -> str:
"""
Calculate cost function
"""
result_list = []

param_keys = ["CORE_UTILIZATION"]
BOUNDS = [(0.01, 0.99)]
PARAMS = [0.05]

def evaluate(target):
# Force step increments of at least 0.01
x = [max(BOUNDS[i][0], min(BOUNDS[i][1], round(target[i]/0.01)*0.01)) for i in range(len(target))]

modify = {param_keys[0]: x[0]*100}
ORFS.server.orfs_env.update(modify)

# Run flow
ORFS.server._create_dynamic_makefile()
ORFS.server._make("clean_floorplan")
ORFS.server._make("floorplan")
ORFS.server._make("update_metadata")

# Read metric
with open(f"{ORFS.server.flow_dir}/designs/{ORFS.server.platform}/{ORFS.server.design}/metadata-base-ok.json", 'r') as f:
metrics = json.load(f)

# Floorplan die area as cost
cost = metrics["floorplan__design__die__area"]
result_list.append(x[0]*100)
return cost

def report(xk):
print("Current params:", xk)

ORFS.server.opt_method = "Powell"
ORFS.server.logging(f"Running {ORFS.server.opt_method} optimizer...")

result = scipy.optimize.minimize(
evaluate,
PARAMS,
bounds=BOUNDS,
method=ORFS.server.opt_method,
options={
"maxiter": 1000,
"xtol": 0.01, # minimum change in x before stopping
"ftol": 0.0, # don't stop due to small cost change
"disp": True,
},
callback=report
)

ORFS.server.logging(result)
ORFS.server.logging(result_list)

return ""
6 changes: 5 additions & 1 deletion backend/src/openroad_mcp/server/orfs/orfs_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from src.openroad_mcp.server.orfs.orfs_make import ORFSMake
from src.openroad_mcp.server.orfs.orfs_base import ORFSBase
from src.openroad_mcp.server.orfs.orfs_rag import ORFSRag
from src.openroad_mcp.server.orfs.orfs_optimizer import ORFSOptimizer

logging.basicConfig(
level=os.environ.get("LOGLEVEL", "INFO").upper(),
Expand All @@ -25,7 +26,7 @@ class ORFSEnv(TypedDict):
routing: list[str | None]


class ORFSServer(ORFSBase, ORFSMake, ORFSRag):
class ORFSServer(ORFSBase, ORFSMake, ORFSRag, ORFSOptimizer):
def __init__(self) -> None:
ORFS.server = self

Expand Down Expand Up @@ -60,6 +61,9 @@ def _setup_env(self) -> None:
raise ValueError("ORFS_DIR environment variable is not set")
self.flow_dir = os.path.join(self.orfs_dir, "flow")

def logging(self, msg):
logging.info(msg)


if __name__ == "__main__":
server = ORFSServer()
Expand Down
10 changes: 10 additions & 0 deletions backend/src/prompts/tool_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@
"content": """{{
"name": "step",
"args": {{}}
}}""",
},
###
### optimize
{"role": "user", "content": "Calculate cost function"},
{
"role": "assistant",
"content": """{{
"name": "calc_cost",
"args": {{}}
}}""",
},
###
Expand Down