diff --git a/Readme.md b/Readme.md index 85a57f0..0b69545 100644 --- a/Readme.md +++ b/Readme.md @@ -46,21 +46,22 @@ ciel --version # About the builds In its current inception, ciel supports builds of **sky130** and **gf180mcu** PDKs using [Open-PDKs](https://github.com/rtimothyedwards/open_pdks), including the following libraries: -|sky130|gf180mcu|ihp-sg13g2| -|-|-|-| -|sky130_fd_io|gf180mcu_fd_io|sg13g2_io| -|sky130_fd_pr|gf180mcu_fd_pr|sg13g2_pr| -|sky130_fd_pr_reram|gf180mcu_fd_pr|sg13g2_pr| -|sky130_fd_sc_hd|gf180mcu_fd_sc_mcu7t5v0|sg13g2_stdcell| -|sky130_ml_xx_hd|gf180mcu_fd_sc_mcu9t5v0|-| -|sky130_fd_sc_hvl|gf180mcu_osu_sc_gp9t3v3|-| -|sky130_fd_sc_lp|gf180mcu_osu_sc_gp12t3v3|-| -|sky130_fd_sc_ls|-|-| -|sky130_fd_sc_ms|-|-| -|sky130_fd_sc_hs|-|-| -|sky130_sram_macros|gf180mcu_fd_ip_sram|sg13g2_sram| +|sky130|gf180mcu|ihp-sg13g2|ics55| +|-|-|-|-| +|sky130_fd_io|gf180mcu_fd_io|sg13g2_io|ICSIOA_N55_3P3_1P6M1TM| +|sky130_fd_pr|gf180mcu_fd_pr|sg13g2_pr|-| +|sky130_fd_pr_reram|gf180mcu_fd_pr|sg13g2_pr|-| +|sky130_fd_sc_hd|gf180mcu_fd_sc_mcu7t5v0|sg13g2_stdcell|ics55_LLSC_H7CR| +|sky130_ml_xx_hd|gf180mcu_fd_sc_mcu9t5v0|-|-| +|sky130_fd_sc_hvl|gf180mcu_osu_sc_gp9t3v3|-|ics55_LLSC_H7CH| +|sky130_fd_sc_lp|gf180mcu_osu_sc_gp12t3v3|-|ics55_LLSC_H7CL| +|sky130_fd_sc_ls|-|-|-| +|sky130_fd_sc_ms|-|-|-| +|sky130_fd_sc_hs|-|-|-| +|sky130_sram_macros|gf180mcu_fd_ip_sram|sg13g2_sram|-| Builds for sky130 and gf180mcu are identified by their [**open_pdks**](https://github.com/rtimothyedwards/open_pdks) commit hashes. Builds for ihp-sg13g2 are identified by their [**IHP-Open-PDK**](https://github.com/ihp-gmbh/ihp-open-pdk) commit hashes. +Builds for ics55 are identified by their [**icesprout55-openpdk**](https://github.com/ckdur/icesprout55-openpdk) commit hashes. # Usage Ciel requires a so-called **PDK Root**. This PDK root can be anywhere on your computer, but by default it's the folder `~/.ciel` in your home directory. If you have the variable `PDK_ROOT` set, ciel will use that instead. You can also manually override both values by supplying the `--pdk-root` commandline argument. diff --git a/ciel/build/ics55.py b/ciel/build/ics55.py new file mode 100644 index 0000000..86c4f10 --- /dev/null +++ b/ciel/build/ics55.py @@ -0,0 +1,172 @@ +# Copyright 2026 Ckristian Duran +# Copyright 2022-2023 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import shutil +import subprocess +from datetime import datetime +from typing import Optional, List, Tuple, Dict +from concurrent.futures import ThreadPoolExecutor + +from rich.console import Console +from rich.progress import Progress + +from .git_multi_clone import GitMultiClone +from ..families import Family +from ..github import ics55_open_repo +from ..common import ( + Version, + get_ciel_dir, + mkdirp, +) + + +def get_ics55( + version, build_directory, jobs=1, repo_path=None +) -> Tuple[str, Optional[str], Optional[str]]: + try: + console = Console() + + if repo_path is None: + with Progress() as progress: + with ThreadPoolExecutor(max_workers=jobs) as executor: + gmc = GitMultiClone(build_directory, progress) + ics_future = executor.submit( + GitMultiClone.clone, + gmc, + ics55_open_repo.link, + version, + ) + repo = ics_future.result() + current_task = progress.add_task("Updating submodules…", total=100) + repo.init_submodule( + callback=lambda x: progress.update(current_task, completed=x) + ) + repo_path = repo.path + console.log(f"Done fetching {ics55_open_repo.name}.") + else: + console.log(f"Using ICS55 at {repo_path} unaltered.") + + return repo_path + + except subprocess.CalledProcessError as e: + print(e) + print(e.stderr) + exit(-1) + + +def build_ics(build_directory, ics55_path, log_dir): + # """Build""" + try: + shutil.rmtree(os.path.join(build_directory, "ics55")) + except FileNotFoundError: + pass + + # Execute the install + console = Console() + def run_sh(script, log_to): + output_file = open(log_to, "w") + try: + subprocess.check_call( + ["bash", "-c", script], + cwd=ics55_path, + stdout=output_file, + stderr=output_file, + stdin=open(os.devnull), + ) + except subprocess.CalledProcessError as e: + console.log( + f"An error occurred while building the PDK. Check {log_to} for more information." + ) + raise e + + config_log = os.path.join(log_dir, "config.log") + console.log(f"Downloading and installing PDK. Logging into {config_log}") + run_sh("make openpdk", log_to=config_log) + + shutil.copytree( + os.path.join(ics55_path, "ics55"), + os.path.join(build_directory, "ics55"), + ignore=lambda dir, files: ( + files if ".git" in os.path.split(dir) else [".git", ".DS_Store"] + ), + ) + + +def install_ics(build_directory, pdk_root, version): + console = Console() + with console.status("Adding build to list of installed versions…"): + ics55_family = Family.by_name["ics55"] + + version_directory = Version(version, "ics55").get_dir(pdk_root) + if ( + os.path.exists(version_directory) + and len(os.listdir(version_directory)) != 0 + ): + backup_path = version_directory + it = 0 + while os.path.exists(backup_path) and len(os.listdir(backup_path)) != 0: + it += 1 + backup_path = Version(f"{version}.bk{it}", "ics55").get_dir( + pdk_root + ) + console.log( + f"Build already found at {version_directory}, moving to {backup_path}…" + ) + shutil.move(version_directory, backup_path) + + console.log("Copying…") + mkdirp(version_directory) + + for variant in ics55_family.variants: + variant_build_path = os.path.join(build_directory, variant) + variant_install_path = os.path.join(version_directory, variant) + if os.path.isdir(variant_build_path): + shutil.copytree(variant_build_path, variant_install_path) + + console.log("Done.") + + +def build( + pdk_root: str, + version: str, + jobs: int = 1, + clear_build_artifacts: bool = True, + include_libraries: Optional[List[str]] = None, + using_repos: Optional[Dict[str, str]] = None, +): + console = Console() + if include_libraries is not None: + console.log( + "Note: all libraries will be acquired as part of the trivial PDK build." + ) + + if using_repos is None: + using_repos = {} + + build_directory = os.path.join( + get_ciel_dir(pdk_root, "ics55"), "build", version + ) + timestamp = datetime.now().strftime("build_ics55-%Y-%m-%d-%H-%M-%S") + log_dir = os.path.join(build_directory, "logs", timestamp) + mkdirp(log_dir) + + console.log(f"Logging to '{log_dir}'…") + + ics55_path = get_ics55(version, build_directory, jobs, using_repos.get("ics")) + build_ics(build_directory, ics55_path, log_dir) + install_ics(build_directory, pdk_root, version) + + if clear_build_artifacts: + shutil.rmtree(build_directory) diff --git a/ciel/families.py b/ciel/families.py index e9b825e..b5b4953 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -14,7 +14,7 @@ from dataclasses import dataclass from typing import Iterable, List, Dict, Optional, Set, ClassVar -from .github import RepoInfo, opdks_repo, ihp_repo +from .github import RepoInfo, opdks_repo, ihp_repo, ics55_open_repo @dataclass @@ -124,3 +124,14 @@ def resolve_libraries( ], repo=ihp_repo, ) +Family.by_name["ics55"] = Family( + name="ics55", + variants=["ics55"], + all_libraries=[ + "ics55_LLSC_H7CH", + "ics55_LLSC_H7CL", + "ics55_LLSC_H7CR", + "ICsprout_55LLULP1233_IO_251013", + ], + repo=ics55_open_repo, +) diff --git a/ciel/github.py b/ciel/github.py index 0ce8ada..b368a3a 100644 --- a/ciel/github.py +++ b/ciel/github.py @@ -60,6 +60,16 @@ def api(self): os.getenv("IHP_REPO_NAME", "IHP-Open-PDK"), ) +ics55_repo = RepoInfo( + os.getenv("ICS_REPO_OWNER", "openecos-projects"), + os.getenv("ICS_REPO_NAME", "icsprout55-pdk"), +) + +ics55_open_repo = RepoInfo( + os.getenv("ICS_REPO_OWNER", "ckdur"), # TODO: Once the PR passes, we should change this + os.getenv("ICS_REPO_NAME", "icsprout55-pdk"), +) + class GitHubSession(httpx.Client): class Token(object):