From 8b1ca6350d17bc654b79bc7b10f1901773d927ea Mon Sep 17 00:00:00 2001 From: Donn Date: Thu, 6 Jul 2023 08:06:07 +0000 Subject: [PATCH 1/6] Update OpenLane + Add More IP to CI --- .github/workflows/main.yml | 1 + .gitignore | 3 ++- platforms/sky130A/process_data.yml | 2 +- platforms/sky130A/sky130_fd_sc_hd/tech.yml | 14 +++++++------- tool_metadata.yml | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cf28c7a..fff341f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -81,6 +81,7 @@ jobs: - { count: "32", width: "32", variant: "DEFAULT" } # - { count: "32", width: "32", variant: "1RW1R" } # Timeout - { count: "32", width: "32", variant: "2R1W" } + - { count: "128", width: "32", variant: "DEFAULT" } - { count: "256", width: "8", variant: "DEFAULT" } # - { count: "256", width: "8", variant: "1RW1R" } - { count: "256", width: "16", variant: "DEFAULT" } diff --git a/.gitignore b/.gitignore index c05a201..366a62e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ support/ *.log *.cvcrc *.xz -venv/ \ No newline at end of file +venv/ +/products_path \ No newline at end of file diff --git a/platforms/sky130A/process_data.yml b/platforms/sky130A/process_data.yml index 696955d..b03d4c3 100644 --- a/platforms/sky130A/process_data.yml +++ b/platforms/sky130A/process_data.yml @@ -1,2 +1,2 @@ volare_pdk_family: sky130 -volare_pdk_version: 327e268bdb7191fe07a28bd40eeac055bba9dffd +volare_pdk_version: 3df14f84ab167baf757134739bb1d2c5c044849c diff --git a/platforms/sky130A/sky130_fd_sc_hd/tech.yml b/platforms/sky130A/sky130_fd_sc_hd/tech.yml index 8147e65..b65aa9a 100644 --- a/platforms/sky130A/sky130_fd_sc_hd/tech.yml +++ b/platforms/sky130A/sky130_fd_sc_hd/tech.yml @@ -22,11 +22,11 @@ sta: fast: sky130_fd_sc_hd__ff_n40C_1v95.lib clock_periods: ram: - "8x*": 5.21 - "32x*": 8 - "128x*": 10.1 - "256x*": 10.49 - "512x*": 11.21 - "1024x*": 12.83 - "2048x*": 14.1 + "8x*": 16.5 + "32x*": 16.5 + "128x*": 16.5 + "256x*": 16.5 + "512x*": 16.5 + "1024x*": 16.5 + "2048x*": 16.5 rf: {} diff --git a/tool_metadata.yml b/tool_metadata.yml index 5653766..b8dee16 100644 --- a/tool_metadata.yml +++ b/tool_metadata.yml @@ -1,3 +1,3 @@ - name: openlane repo: https://github.com/The-OpenROAD-Project/OpenLane - commit: 2023.01.20 + commit: 2023.06.26 From d7aed0435d8cc37fe712638456251910b15add66 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Wed, 26 Jul 2023 11:27:15 +0300 Subject: [PATCH 2/6] Rewrite `dffram.py` to use OpenLane 2 + Nix (#187) --- .github/workflows/main.yml | 23 +- .gitignore | 3 +- Makefile | 17 +- Readme.md | 17 +- dffram.ipynb | 12 +- dffram.py | 1017 ++++++++---------------------- docs/Usage.md | 83 ++- placeram/cli.py | 88 ++- requirements.txt | 4 +- scripts/openlane/interactive.tcl | 54 -- scripts/perl/verilog_to_lib.pl | 210 ------ shell.nix | 23 + 12 files changed, 433 insertions(+), 1118 deletions(-) delete mode 100644 scripts/openlane/interactive.tcl delete mode 100755 scripts/perl/verilog_to_lib.pl create mode 100644 shell.nix diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fff341f..4b0ce9f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -91,21 +91,30 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 + - name: Setup Nix + uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-22.11 + - name: Setup Cachix + uses: cachix/cachix-action@v12 + with: + name: openlane - name: Run run: | - pip3 install --upgrade --no-cache-dir -r ./requirements.txt - building_blocks=ram if [ "${{ matrix.variant }}" == "2R1W" ]; then building_blocks=rf fi - ./dffram.py\ - -s ${{ matrix.count }}x${{ matrix.width }}\ - -b sky130A:sky130_fd_sc_hd:$building_blocks\ - -v ${{ matrix.variant }} + nix-shell --run "\ + ./dffram.py ${{ matrix.count }}x${{ matrix.width }}\ + -p sky130A\ + -s sky130_fd_sc_hd\ + -v ${{ matrix.variant }}\ + -b $building_blocks\ + " - echo "PRODUCTS_PATH=$(cat ./products_path)" >> $GITHUB_ENV + echo "PRODUCTS_PATH=$(echo products/*)" >> $GITHUB_ENV - name: Upload Final Views uses: actions/upload-artifact@v3 with: diff --git a/.gitignore b/.gitignore index 366a62e..c8560a8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,5 @@ support/ *.cvcrc *.xz venv/ -/products_path \ No newline at end of file +/products_path +/products \ No newline at end of file diff --git a/Makefile b/Makefile index a4afff9..0596a39 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,25 @@ all: dist .PHONY: dist -dist: venv/created +dist: venv/manifest.txt ./venv/bin/python3 setup.py sdist bdist_wheel .PHONY: lint -lint: venv/created +lint: venv/manifest.txt ./venv/bin/black --check . ./venv/bin/flake8 . -venv: venv/created -venv/created: ./requirements_dev.txt ./requirements.txt +venv: venv/manifest.txt +venv/manifest.txt: ./requirements_dev.txt ./requirements.txt rm -rf venv python3 -m venv ./venv - ./venv/bin/python3 -m pip install --upgrade pip - ./venv/bin/python3 -m pip install --upgrade wheel - ./venv/bin/python3 -m pip install --upgrade\ + PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade pip + PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade wheel + PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade\ -r ./requirements_dev.txt\ -r ./requirements.txt - touch venv/created + PYTHONPATH= ./venv/bin/python3 -m pip freeze > $@ + touch venv/manifest.txt .PHONY: veryclean veryclean: clean diff --git a/Readme.md b/Readme.md index ce85288..e1ceb13 100644 --- a/Readme.md +++ b/Readme.md @@ -11,17 +11,11 @@ Standard Cell Library based Memory Compiler using DFF/Latch cells. -# ✨ Quick Usage -You can try the [Google Colaboratory](https://colab.research.google.com/github/Cloud-V/DFFRAM/blob/main/dffram.ipynb), but also... +# ✨ Installation & Usage +See [this document](./docs/Usage.md). -[Get **Docker**](https://docs.docker.com/get-docker/), set it up nicely, then, in your terminal: - -```sh -pip3 install --user --no-cache-dir --upgrade ./requirements.txt -./dffram.py -s 32x32 -``` - -If all goes well, you've placed your first register file! +You can try the [Google Colaboratory](https://colab.research.google.com/github/Cloud-V/DFFRAM/blob/main/dffram.ipynb), +but it is a bit out-of-date at this point. # Overview The objective of this project is to develop a DFF/Latch-based RAM, Register File and Cache custom compilation flow that utilizes standard cell libraries following a standard ASIC (Application Specific Integrated Circuit) implementation approach. Different views (HDL netlist, HDL functional models, LEF, GDS, Timing, …) are all generated for a given size configuration. @@ -142,7 +136,8 @@ The following table compares the areas and bit densities of RAM macros generated # ⚖️ Copyright and Licensing -Copyright ©2020-2022 The American University in Cairo + +Copyright ©2020-2023 The American University in Cairo Licensed under the Apache License, Version 2.0 (the "Open Source License"); you may not use this file except in compliance with the Open Source License. diff --git a/dffram.ipynb b/dffram.ipynb index 8704ac9..2241fdd 100644 --- a/dffram.ipynb +++ b/dffram.ipynb @@ -6,13 +6,15 @@ "id": "eukW5KG9kq7A" }, "source": [ - "# DFFRAM Compiler\n", + "# DFFRAM Compiler\n", "\n", + "Standard Cell Library based Memory Compiler using DFF/Latch cells.\n", "\n", - " Standard Cell Library based Memory Compiler using DFF/Latch cells.\n", "\n", + "## Warning!\n", "\n", - "\n" + "This notebook should still work, but is out of date and does not reflect the\n", + "latest DFFRAM release. Use at your own risk!" ] }, { @@ -325,7 +327,7 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3.10.9 64-bit", + "display_name": "Python 3.10.9 ('venv': venv)", "language": "python", "name": "python3" }, @@ -343,7 +345,7 @@ }, "vscode": { "interpreter": { - "hash": "3fe098a60de41da4d418149dd85333239847aa92272c8f79d3c6f03653219927" + "hash": "192516fa94e535c4f5cf27eeef389e5df5ced4f52a77092f31776bfbcc9cff03" } } }, diff --git a/dffram.py b/dffram.py index 7a45005..e4f0a61 100755 --- a/dffram.py +++ b/dffram.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf8 -*- -# Copyright ©2020-2022 The American University in Cairo +# Copyright ©2020-2023 The American University in Cairo +# Copyright ©2023 Efabless Corporation # # This file is part of the DFFRAM Memory Compiler. # See https://github.com/Cloud-V/DFFRAM for further info. @@ -17,637 +18,277 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -import sys import re -import uuid import math -import time -import shutil -import fnmatch -import pathlib -import traceback -import subprocess - - -def eprint(*args, **kwargs): - print(*args, **kwargs, file=sys.stderr) - - -try: - import click - import yaml - import volare -except ImportError as e: - eprint(e) - eprint("---") - eprint( - "You need to install dependencies: pip3 install --user --upgrade --no-cache-dir -r ./requirements.txt" - ) - exit(os.EX_CONFIG) - - -def rp(path): - return os.path.realpath(path) - - -def ensure_dir(path): - return pathlib.Path(path).mkdir(parents=True, exist_ok=True) - - -# -- -build_folder = "" -pdk_family = "" -pdk = "" -pdk_version = "" -scl = "" -pdk_root = "" -pdk_tech_dir = "" -pdk_ref_dir = "" -pdk_liberty_dir = "" -pdk_lef_dir = "" -pdk_tlef_dir = "" -pdk_klayout_dir = "" -pdk_magic_dir = "" -pdk_openlane_dir = "" - -tool_metadata_file_path = os.path.join(os.path.dirname(__file__), "tool_metadata.yml") -tool_metadata = yaml.safe_load(open(tool_metadata_file_path).read()) -openlane_version = [tool for tool in tool_metadata if tool["name"] == "openlane"][0][ - "commit" -] -openlane_image = os.getenv( - "OPENLANE_IMAGE_NAME", default=f"efabless/openlane:{openlane_version}" -) - -running_docker_ids = set() - - -def run_docker(image, args): - global running_docker_ids - global command_list - container_id = str(uuid.uuid4()) - running_docker_ids.add(container_id) - cmd = ( - [ - "docker", - "run", - "--rm", - "--name", - container_id, - "-v", - f"{pdk_root}:{pdk_root}", - "-v", - f"{rp('.')}:/mnt/dffram", - "-w", - "/mnt/dffram", - "-e", - f"PDK_ROOT={pdk_root}", - "-e", - f"PDKPATH={pdk_root}/{pdk}", - "-e", - f"PDK={pdk}", - "-e", - "PWD=/mnt/dffram", - "-e", - "LC_ALL=en_US.UTF-8", - "-e", - "LANG=en_US.UTF-8", - ] - + [image] - + args - ) - command_list.append(cmd) - subprocess.check_call(cmd, stdout=sys.stderr, stderr=subprocess.STDOUT) - running_docker_ids.remove(container_id) - - -openlane_scripts_path = "/openlane/scripts" -local_openlane_path = None -venv_lib_path = None - - -def openlane(*args_tuple): - global no_docker_option - args = list(args_tuple) - if local_openlane_path is not None: - env = os.environ.copy() - env["PATH"] = f"{local_openlane_path}:{env['PATH']}" - - if venv_lib_path is not None: - env["PYTHONPATH"] = venv_lib_path - - # Disable tools not typically installed in Colaboratories - env["RUN_KLAYOUT"] = "0" - env["RUN_CVC"] = "0" - env["PDK_ROOT"] = pdk_root - - subprocess.check_call( - args, env=env, stdout=sys.stderr, stderr=subprocess.STDOUT - ) - else: - run_docker(openlane_image, args) - - -def prep(local_pdk_root): - global pdk, scl - global pdk_root, pdk_tech_dir, pdk_ref_dir - global pdk_liberty_dir, pdk_lef_dir, pdk_tlef_dir - global pdk_klayout_dir, pdk_magic_dir, pdk_openlane_dir - pdk_root = os.path.abspath(local_pdk_root) - pdk_path = os.path.join(pdk_root, pdk) - volare.enable(pdk_root=local_pdk_root, pdk=pdk_family, version=pdk_version) - - pdk_tech_dir = os.path.join(pdk_path, "libs.tech") - pdk_ref_dir = os.path.join(pdk_path, "libs.ref") - - pdk_liberty_dir = os.path.join(pdk_ref_dir, scl, "lib") - if not os.path.exists(pdk_liberty_dir): - pdk_liberty_dir = os.path.join(pdk_ref_dir, scl, "liberty") - - pdk_lef_dir = os.path.join(pdk_ref_dir, scl, "lef") - pdk_tlef_dir = os.path.join(pdk_ref_dir, scl, "techlef") - pdk_openlane_dir = os.path.join(pdk_tech_dir, "openlane") - pdk_klayout_dir = os.path.join(pdk_tech_dir, "klayout") - pdk_magic_dir = os.path.join(pdk_tech_dir, "magic") - openlane( - "openroad", - "-exit", - "-python", - f"{openlane_scripts_path}/mergeLef.py", - "-i", - f"{pdk_tlef_dir}/{scl}__nom.tlef", - f"{pdk_lef_dir}/{scl}.lef", - "-o", - f"{build_folder}/merged.lef", - ) - - -command_list = [] - - -def cl(): - with open("./command_list.log", "w") as f: - f.write("\n".join([" ".join(cmd) for cmd in command_list])) +from typing import List +from fnmatch import fnmatch +from decimal import Decimal + +import yaml +import cloup +from openlane.common import mkdirp +from openlane.config import Variable +from openlane.logging import warn, err +from openlane.state import DesignFormat +from openlane.flows import SequentialFlow, cloup_flow_opts, Flow +from openlane.steps import Yosys, OpenROAD, Magic, KLayout, Netgen, Odb, Checker, Misc + + +class PlaceRAM(Odb.OdbpyStep): + id = "DFFRAM.PlaceRAM" + + config_vars = [ + Variable( + "RAM_SIZE", + str, + "The size of the RAM macro being hardened the format {words}x{bits}", + ), + Variable( + "BUILDING_BLOCKS", + str, + "The set of building blocks being used.", + default="ram", + ), + ] + def get_script_path(self): + return "placeram" -# Not true synthesis, just elaboration. -def synthesis( - design, - building_blocks, - block_deinitions, - sta_info, - widths_supported, - word_width_bytes, - out_file, - word_width, - blocks, -): - eprint("--- Synthesis ---") - chparam = "" - if len(widths_supported) > 1: - if blocks == "rf": - chparam = "catch { chparam -set WSIZE %i %s }" % (word_width, design) - else: - chparam = "catch { chparam -set WSIZE %i %s }" % (word_width_bytes, design) - with open(f"{build_folder}/synth.tcl", "w") as f: - f.write( - f""" - yosys -import - set SCL {pdk_liberty_dir}/{sta_info["libs"]["typical"]} - read_liberty -lib -ignore_miss_dir -setattr blackbox $SCL - read_verilog {block_deinitions} - read_verilog {building_blocks} - {chparam} - hierarchy -check -top {design} - synth -flatten - yosys rename -top {design} - opt_clean -purge - splitnets - opt_clean -purge - write_verilog -noattr -noexpr -nodec {out_file} - stat -top {design} -liberty $SCL - exit - """ - ) - - openlane("yosys", f"{build_folder}/synth.tcl") + def get_command(self) -> List[str]: + raw = super().get_command() + [ + "--building-blocks", + f"{self.config['PDK']}:{self.config['STD_CELL_LIBRARY']}:{self.config['BUILDING_BLOCKS']}", + "--size", + self.config["RAM_SIZE"], + ] + raw.insert(raw.index("placeram"), "-m") + return raw -full_width = 0 -full_height = 0 +class Floorplan(OpenROAD.Floorplan): + id = "DFFRAM.Floorplan" + outputs = [ + DesignFormat.ODB, + ] -def floorplan( - design, - sta_info, - wmargin, - hmargin, - width, - height, - in_file, - out_file, - min_height, - min_height_flag, - site_height, - site_name, - tie_lo_cell, - tie_lo_port, - tie_hi_cell, - tie_hi_port, -): - global full_width, full_height - eprint("--- Floorplan ---") - full_width = width + (wmargin * 2) - full_height = height + (hmargin * 2) - - wpm = width + wmargin - hpm = height + hmargin - - track_file = f"{build_folder}/tracks.tcl" - - if min_height_flag: - full_width = width + (wmargin * 2) - full_height = min_height + (hmargin * 2) - hmargin = full_height / 2 - height / 2 - hmargin = math.ceil(hmargin / site_height) * site_height - hpm = height + hmargin - - with open(f"{build_folder}/fp_init.tcl", "w") as f: - f.write( - f""" - read_liberty {pdk_liberty_dir}/{sta_info["libs"]["typical"]} - read_lef {build_folder}/merged.lef - read_verilog {in_file} - link_design {design} - initialize_floorplan\\ - -die_area "0 0 {full_width} {full_height}"\\ - -core_area "{wmargin} {hmargin} {wpm} {hpm}"\\ - -site {site_name} - set tielo_cell {tie_lo_cell} - set tielo_port {tie_lo_port} - set tiehi_cell {tie_hi_cell} - set tiehi_port {tie_hi_port} - insert_tiecells "$tielo_cell/$tielo_port" -prefix "TIE_ZERO_" - insert_tiecells "$tiehi_cell/$tiehi_port" -prefix "TIE_ONE_" - source {build_folder}/tracks.tcl - write_db {out_file} - """ - ) + config_vars = [ + var + for var in OpenROAD.Floorplan.config_vars + if var.name not in ["FP_SIZING", "CORE_AREA", "DIE_AREA"] + ] + [ + Variable( + "HORIZONTAL_HALO", + type=Decimal, + description="The space between the horizontal edges of the die area and the core area in microns.", + units="µm", + default=2.5, + ), + Variable( + "VERTICAL_HALO", + type=Decimal, + description="The space between the vertical edges of the die area and the core area in microns.", + units="µm", + default=2.5, + ), + Variable( + "MINIMUM_HEIGHT", + type=Decimal, + description="A minimum height to be applied", + default=0, + units="µm", + ), + ] - with open(f"{build_folder}/fp_init.sh", "w") as f: - f.write( - f""" - set -e + def run(self, state_in, **kwargs): + min_height = self.config["MINIMUM_HEIGHT"] - python3 {openlane_scripts_path}/new_tracks.py -i {pdk_openlane_dir}/{scl}/tracks.info -o {track_file} - openroad -exit {build_folder}/fp_init.tcl - """ + core_width = Decimal( + state_in.metrics.get("dffram__suggested__core_width") or 20000 ) - - openlane("bash", f"{build_folder}/fp_init.sh") - - -def placeram( - in_file, - out_file, - size, - building_blocks, - dimensions=os.devnull, - density=os.devnull, - represent=os.devnull, -): - eprint("--- placeRAM Script ---") - openlane( - "openroad", - "-exit", - "-python", - "-m", - "placeram", - "--output", - out_file, - "--size", - size, - "--write-dimensions", - dimensions, - "--write-density", - density, - "--represent", - represent, - "--building-blocks", - building_blocks, - in_file, - ) - - -def place_pins(design, sta_info, in_file, out_file, pin_order_file, metal_layer): - eprint("--- Pin Placement ---") - openlane( - "openroad", - "-exit", - "-python", - f"{openlane_scripts_path}/odbpy/io_place.py", - "--config", - pin_order_file, - "--input-lef", - f"{build_folder}/merged.lef", - "--hor-layer", - f"{metal_layer['hor-layer']}", - "--ver-layer", - f"{metal_layer['ver-layer']}", - "--ver-width-mult", - "2", - "--hor-width-mult", - "2", - "--hor-extension", - "0", - "--ver-extension", - "0", - "--length", - "2", - "-o", - out_file, - in_file, - ) - - -def verify_placement(design, sta_info, in_file): - eprint("--- Verify ---") - with open(f"{build_folder}/verify.tcl", "w") as f: - f.write( - f""" - read_db {in_file} - read_liberty {pdk_liberty_dir}/{sta_info["libs"]["typical"]} - if {{[catch check_placement -verbose]}} {{ - puts "Placement failed: Check placement returned a nonzero value." - exit 65 - }} - puts "Placement successful." - """ + core_height = Decimal( + state_in.metrics.get("dffram__suggested__core_height") or 20000 ) - openlane("openroad", "-exit", f"{build_folder}/verify.tcl") - - -def openlane_harden( - design, - clock_period, - final_netlist, - final_placement, - products_path, - sta_info, - routing_threads, - metal_layer, -): - eprint("--- Hardening With OpenLane ---") - design_ol_dir = f"{build_folder}/openlane" - ensure_dir(design_ol_dir) - - netlist_basename = os.path.basename(final_netlist) - current_netlist = f"{design_ol_dir}/{netlist_basename}" - shutil.copy(final_netlist, current_netlist) - - placement_basename = os.path.basename(final_placement) - current_odb = f"{design_ol_dir}/{placement_basename}" - shutil.copy(final_placement, current_odb) - - shutil.copy( - "./scripts/openlane/interactive.tcl", f"{design_ol_dir}/interactive.tcl" - ) - - shutil.copy(f"{pdk_lef_dir}/{scl}.lef", f"{design_ol_dir}/cells.lef") - - with open(f"{design_ol_dir}/config.tcl", "w") as f: - f.write( - f""" - set ::env(DESIGN_NAME) "{design}" - - set ::env(CLOCK_PORT) "CLK" - set ::env(CLOCK_PERIOD) "{clock_period}" - - set ::env(LEC_ENABLE) "0" - set ::env(FP_WELLTAP_CELL) "gf180mcu_fd_sc_mcu7t5v0__filltie*" - - set ::env(GPL_CELL_PADDING) "0" - set ::env(DPL_CELL_PADDING) "0" - set ::env(RUN_FILL_INSERTION) "0" - set ::env(PL_RESIZER_DESIGN_OPTIMIZATIONS) "0" - set ::env(PL_RESIZER_TIMING_OPTIMIZATIONS) "0" - set ::env(GLB_RESIZER_DESIGN_OPTIMIZATIONS) "0" - set ::env(GLB_RESIZER_TIMING_OPTIMIZATIONS) "0" - - set ::env(RT_MAX_LAYER) "{metal_layer['rt-max-layer']}" - set ::env(GRT_ALLOW_CONGESTION) "1" - - set ::env(CELLS_LEF) "$::env(DESIGN_DIR)/cells.lef" - - set ::env(DIE_AREA) "0 0 {full_width} {full_height}" - - set ::env(DIODE_INSERTION_STRATEGY) "0" - set ::env(ROUTING_CORES) {routing_threads} + horizontal_halo = self.config["HORIZONTAL_HALO"] + vertical_halo = self.config["VERTICAL_HALO"] - set ::env(DESIGN_IS_CORE) "0" - set ::env(FP_PDN_CORE_RING) "0" + pdk = self.config["PDK"] + scl = self.config["STD_CELL_LIBRARY"] - set ::env(PRODUCTS_PATH) "{products_path}" + tech_info_path = os.path.join(".", "platforms", pdk, scl, "tech.yml") + tech_info = yaml.safe_load(open(tech_info_path)) + site_info = tech_info.get("site") - set ::env(INITIAL_NETLIST) "$::env(DESIGN_DIR)/{netlist_basename}" - set ::env(INITIAL_ODB) "$::env(DESIGN_DIR)/{placement_basename}" - set ::env(INITIAL_SDC) "$::env(BASE_SDC_FILE)" + site_width = Decimal(1) + site_height = Decimal(1) - set ::env(LVS_CONNECT_BY_LABEL) "1" + if site_info is not None: + site_width = Decimal(site_info["width"]) + site_height = Decimal(site_info["height"]) - set ::env(SYNTH_DRIVING_CELL) "{sta_info["driving_cell"]["name"]}" - set ::env(SYNTH_DRIVING_CELL_PIN) "{sta_info["driving_cell"]["pin"]}" - set ::env(IO_PCT) "0.25" - - """ - ) - - openlane( - "flow.tcl", - "-design", - design_ol_dir, - "-it", - "-file", - f"{design_ol_dir}/interactive.tcl", - ) + horizontal_halo = math.ceil(horizontal_halo / site_width) * site_width + vertical_halo = math.ceil(vertical_halo / site_height) * site_height + else: + if horizontal_halo != 0.0 or vertical_halo != 0.0: + warn( + "Note: This platform does not have site information. The halo will not be rounded up to the nearest number of sites. This may cause off-by-one issues with some tools." + ) + + die_width = core_width + horizontal_halo * 2 + die_height = core_height + vertical_halo * 2 + if die_height < min_height: + die_height = min_height + vertical_halo = (die_height - core_height) / 2 + vertical_halo = math.ceil(vertical_halo / site_height) * site_height + + kwargs, env = self.extract_env(kwargs) + + env["DIE_AREA"] = f"0 0 {die_width} {die_height}" + env[ + "CORE_AREA" + ] = f"{horizontal_halo} {vertical_halo} {horizontal_halo + core_width} {vertical_halo + core_height}" + env["FP_SIZING"] = "absolute" + return super().run(state_in, env=env, **kwargs) + + +@Flow.factory.register() +class DFFRAM(SequentialFlow): + Steps = [ + Yosys.Synthesis, + Misc.LoadBaseSDC, + OpenROAD.STAPrePNR, + Floorplan, + PlaceRAM, + Floorplan, + PlaceRAM, + OpenROAD.IOPlacement, + Odb.CustomIOPlacement, + OpenROAD.GeneratePDN, + OpenROAD.STAMidPNR, + OpenROAD.GlobalRouting, + OpenROAD.STAMidPNR, + OpenROAD.DetailedRouting, + Checker.TrDRC, + Odb.ReportDisconnectedPins, + Checker.DisconnectedPins, + Odb.ReportWireLength, + Checker.WireLength, + OpenROAD.RCX, + OpenROAD.STAPostPNR, + OpenROAD.IRDropReport, + Magic.StreamOut, + Magic.WriteLEF, + KLayout.StreamOut, + KLayout.XOR, + Checker.XOR, + Magic.DRC, + Checker.MagicDRC, + Magic.SpiceExtraction, + Checker.IllegalOverlap, + Netgen.LVS, + Checker.LVS, + ] -@click.command() -# Execution Flow -@click.option("-f", "--from", "frm", default="synthesis", help="Start from this step") -@click.option("-t", "--to", default="gds", help="End after this step") -@click.option( - "--only", default=None, help="Only execute these semicolon;delimited;steps" -) -@click.option("--skip", default=None, help="Skip these semicolon;delimited;steps") - -# Configuration -@click.option( - "-p", - "--pdk-root", - required=False, - default="./pdks", - help="Optionally override the used PDK root", -) -@click.option("-O", "--output-dir", default="./build", help="Output directory.") -@click.option( - "-b", - "--building-blocks", - default="sky130A:sky130_fd_sc_hd:ram", - help="Format {pdk}:{scl}:{name} : ID of the building blocks to use.", -) -@click.option( +@cloup.command() +@cloup.option("-b", "--building-blocks", default="ram") +@cloup.option( "-v", "--variant", default=None, help="Use design variants (such as 1RW1R)" ) -@click.option("-s", "--size", required=True, help="Size") -@click.option( +@cloup.option( "-C", "--clock-period", "default_clock_period", default=20, - type=float, - help="Fallback clock period for STA (when unspecified)", + type=Decimal, + help="Fallback clock period for STA (when unspecified by the platform)", ) -@click.option("--halo", default=2.5, type=float, help="Halo in microns") -@click.option( +@cloup.option( "--horizontal-halo", - default=0.0, - type=float, - help="Horizontal halo in microns (overrides generic halo)", + default=2.5, + type=Decimal, + help="Horizontal halo in µm", ) -@click.option( +@cloup.option( "--vertical-halo", - default=0.0, - type=float, - help="Vertical halo in microns (overrides generic halo)", -) -@click.option( - "-H", "--min-height", default=0.0, type=float, help="Die Area Height in microns" -) -@click.option( - "-j", - "--routing-threads", - type=int, - default=int(os.getenv("ROUTING_CORES") or "1"), - help="Number of threads to be used in routing", -) - -# Enable/Disable -@click.option( - "--klayout/--no-klayout", - default=False, - help="Open the last def in Klayout. (Default: False)", + default=2.5, + type=Decimal, + help="Vertical halo in µm", ) -@click.option( - "--using-local-openlane", - default=None, - type=str, - help="Use this local OpenLane installation instead of a Dockerized installation.", +@cloup.option( + "-H", + "--min-height", + default=0.0, + type=Decimal, + help="Minimum height in µm", ) -def flow( +@cloup_flow_opts(accept_config_files=False) +@cloup.argument("size", default="32x32", nargs=1) +def main( + pdk, + scl, frm, to, - only, - pdk_root, skip, + tag, + last_run, + with_initial_state, size, building_blocks, - default_clock_period, - halo, + variant, horizontal_halo, vertical_halo, - variant, - routing_threads, - klayout, - output_dir, + default_clock_period, min_height, - using_local_openlane, + flow_name, + **kwargs, ): - global build_folder - global pdk_family, pdk, pdk_version, scl - global local_openlane_path - global openlane_scripts_path - global venv_lib_path - - if horizontal_halo == 0.0: - horizontal_halo = halo - - if vertical_halo == 0.0: - vertical_halo = halo - if variant == "DEFAULT": variant = None - local_openlane_path = using_local_openlane - if local_openlane_path is not None: - openlane_scripts_path = os.path.join(local_openlane_path, "scripts") - if not os.getenv("NO_CHECK_INSTALL") == "1": - install_path = os.path.join(local_openlane_path, "install") - if not os.path.isdir(install_path): - eprint(f"Error: OpenLane installation not found at {install_path}.") - exit(os.EX_CONFIG) - - venv_lib = f"{local_openlane_path}/install/venv/lib" - venv_lib_vers = os.listdir(venv_lib) - if len(venv_lib_vers) < 1: - eprint("Installation venv contains no packages.") - exit(os.EX_CONFIG) - - venv_lib_path = os.path.join(venv_lib, venv_lib_vers[0], "site-packages") - - pdk, scl, blocks = building_blocks.split(":") - pdk = pdk or "sky130A" scl = scl or "sky130_fd_sc_hd" - blocks = blocks or "ram" platform = f"{pdk}:{scl}" - building_blocks = f"{platform}:{blocks}" - process_data_file = os.path.join(".", "platforms", pdk, "process_data.yml") - process_data = yaml.safe_load(open(process_data_file)) - pdk_family = process_data["volare_pdk_family"] - pdk_version = process_data["volare_pdk_version"] - - bb_dir = os.path.join(".", "models", blocks) + bb_dir = os.path.join(".", "models", building_blocks) if not os.path.isdir(bb_dir): - eprint(f"Generic building blocks {blocks} not found.") + err(f"Generic building blocks {building_blocks} not found.") exit(os.EX_NOINPUT) pdk_dir = os.path.join(".", "platforms", pdk, scl) if not os.path.isdir(pdk_dir): - eprint(f"Definitions for platform {platform} not found.") + err(f"Definitions for platform {platform} not found.") exit(os.EX_NOINPUT) block_definitions_used = os.path.join(pdk_dir, "block_definitions.v") - bb_used = os.path.join(bb_dir, "model.v") - config_file = os.path.join(bb_dir, "config.yml") - config = yaml.safe_load(open(config_file)) + platform_config_file = os.path.join(bb_dir, "config.yml") + platform_config = yaml.safe_load(open(platform_config_file)) pin_order_file = os.path.join(bb_dir, "pin_order.cfg") - m = re.match(r"(\d+)x(\d+)", size) if m is None: - eprint("Invalid RAM size '%s'." % size) + err(f"Invalid RAM size '{size}'.") exit(os.EX_USAGE) words = int(m[1]) word_width = int(m[2]) - word_width_bytes = word_width / 8 - - if os.getenv("FORCE_ACCEPT_SIZE") is None: - if words not in config["counts"] or word_width not in config["widths"]: - eprint("Size %s not supported by %s." % (size, building_blocks)) + word_width_bytes = word_width // 8 + + if os.getenv("FORCE_ACCEPT_SIZE") != 1: + if ( + words not in platform_config["counts"] + or word_width not in platform_config["widths"] + ): + err("Size %s not supported by %s." % (size, building_blocks)) exit(os.EX_USAGE) - if variant not in config["variants"]: - eprint("Variant %s is unsupported by %s." % (variant, building_blocks)) + if variant not in platform_config["variants"]: + err("Variant %s is unsupported by %s." % (variant, building_blocks)) exit(os.EX_USAGE) - wmargin, hmargin = (horizontal_halo, vertical_halo) # Microns - variant_string = ("_%s" % variant) if variant is not None else "" - design_name_template = config["design_name_template"] + design_name_template = platform_config["design_name_template"] design = os.getenv("FORCE_DESIGN_NAME") or design_name_template.format( **{ "count": words, @@ -656,209 +297,81 @@ def flow( "variant": variant_string, } ) - build_folder = f"{output_dir}/{size}_{variant or 'DEFAULT'}" + + build_dir = os.path.join("build", design) + mkdirp(build_dir) tech_info_path = os.path.join(".", "platforms", pdk, scl, "tech.yml") tech_info = yaml.safe_load(open(tech_info_path)) clock_period = default_clock_period - block_clock_periods = tech_info["sta"]["clock_periods"].get(blocks) + block_clock_periods = tech_info["sta"]["clock_periods"].get(building_blocks) if block_clock_periods is not None: for wildcard, period in block_clock_periods.items(): - if fnmatch.fnmatch(size, wildcard): + if fnmatch(size, wildcard): clock_period = period break - site_info = tech_info.get("site") # Normalize margins in terms of minimum units - if site_info is not None: - site_width = site_info["width"] - site_height = site_info["height"] - site_name = site_info["name"] - - wmargin = math.ceil(wmargin / site_width) * site_width - hmargin = math.ceil(hmargin / site_height) * site_height - else: - if horizontal_halo != 0.0 or vertical_halo != 0.0: - eprint( - "Note: This platform does not have site information. The halo will not be rounded up to the nearest number of sites. This may cause off-by-one issues with some tools." - ) - - sta_info = tech_info.get("sta") - - tie_info = tech_info.get("tie") - - if tie_info is not None: - tie_lo_cell = tie_info["tie_lo_cell"] - tie_lo_port = tie_info["tie_lo_port"] - tie_hi_cell = tie_info["tie_hi_cell"] - tie_hi_port = tie_info["tie_hi_port"] - - metal_layer = tech_info.get("metal_layers") - - ensure_dir(build_folder) - - def i(ext=""): - return f"{build_folder}/{design}{ext}" - - prep(pdk_root) - - start = time.time() - - netlist = i(".nl.v") - dimensions_file = i(".dimensions.txt") - density_file = i(".density.txt") - initial_floorplan = i(".initfp.odb") - initial_placement = i(".initp.odb") - final_floorplan = i(".fp.odb") - no_pins_placement = i(".npp.odb") - final_placement = i(".placed.odb") - - products = f"{build_folder}/products" - - ensure_dir(products) - - width, height = 20000, 20000 + logical_width = word_width_bytes + if building_blocks == "rf": + logical_width = word_width + + TargetFlow = Flow.factory.get(flow_name) or DFFRAM + dffram_flow = TargetFlow( + { + "DESIGN_NAME": design, + "CLOCK_PORT": "CLK", + "CLOCK_PERIOD": clock_period, + "GPL_CELL_PADDING": 0, + "DPL_CELL_PADDING": 0, + "RT_MAX_LAYER": "met4", + "GRT_ALLOW_CONGESTION": True, + "PDK": pdk, + "STD_CELL_LIBRARY": scl, + "RAM_SIZE": size, + "BUILDING_BLOCKS": building_blocks, + "VERILOG_FILES": [ + block_definitions_used, + bb_used, + ], + "SYNTH_ELABORATE_ONLY": True, + "SYNTH_ELABORATE_FLATTEN": True, + "SYNTH_READ_BLACKBOX_LIB": True, + "SYNTH_EXCLUSION_CELL_LIST": "/dev/null", + "SYNTH_PARAMETERS": f"WSIZE={logical_width}", + "GRT_REPAIR_ANTENNAS": False, + "MINIMUM_HEIGHT": min_height, + "VERTICAL_HALO": vertical_halo, + "HORIZONTAL_HALO": horizontal_halo, + "CLOCK_PERIOD": clock_period, + # IO Placement + "FP_PIN_ORDER_CFG": pin_order_file, + "FP_IO_VTHICKNESS_MULT": Decimal(2), + "FP_IO_HTHICKNESS_MULT": Decimal(2), + "FP_IO_HEXTEND": Decimal(0), + "FP_IO_VEXTEND": Decimal(0), + "FP_IO_VLENGTH": 2, + "FP_IO_HLENGTH": 2, + }, + design_dir=build_dir, + ) - def placement(in_width, in_height): - nonlocal width, height, hmargin, wmargin - min_height_flag = False - floorplan( - design, - sta_info, - wmargin, - hmargin, - in_width, - in_height, - netlist, - initial_floorplan, - min_height, - min_height_flag, - site_height, - site_name, - tie_lo_cell, - tie_lo_port, - tie_hi_cell, - tie_hi_port, - ) - placeram( - initial_floorplan, - initial_placement, - size, - building_blocks, - dimensions=dimensions_file, - ) - width, height = map(lambda x: float(x), open(dimensions_file).read().split("x")) - if height < min_height: - min_height_flag = True + final_state = dffram_flow.start( + frm=frm, + to=to, + skip=skip, + tag=tag, + last_run=last_run, + with_initial_state=with_initial_state, + ) - floorplan( + mkdirp("products") + final_state.save_snapshot( + os.path.join( + "products", design, - sta_info, - wmargin, - hmargin, - width, - height, - netlist, - final_floorplan, - min_height, - min_height_flag, - site_height, - site_name, - tie_lo_cell, - tie_lo_port, - tie_hi_cell, - tie_hi_port, ) - placeram( - final_floorplan, - no_pins_placement, - size, - building_blocks, - density=density_file, - ) - place_pins( - design, - sta_info, - no_pins_placement, - final_placement, - pin_order_file, - metal_layer, - ) - verify_placement(design, sta_info, final_placement) - - steps = [ - ( - "synthesis", - lambda: synthesis( - design, - bb_used, - block_definitions_used, - sta_info, - config["widths"], - word_width_bytes, - netlist, - word_width, - blocks, - ), - ), - ("placement", lambda: placement(width, height)), - ( - "openlane_harden", - lambda: openlane_harden( - design, - clock_period, - netlist, - final_placement, - products, - sta_info, - routing_threads, - metal_layer, - ), - ), - ] - - only = only.split(";") if only is not None else None - skip = skip.split(";") if skip is not None else [] - - execute_steps = False - for step in steps: - name, action = step - if frm == name: - execute_steps = True - if execute_steps: - if (only is None or name in only) and (name not in skip): - try: - action() - except KeyboardInterrupt as e: - eprint("\n\nStopping on keyboard interrupt...") - eprint("Killing docker containers...") - for id in running_docker_ids: - subprocess.call(["docker", "kill", id], stdout=os.devnull) - raise e - if to == name: - execute_steps = False - - elapsed = time.time() - start - - eprint("Done in %.2fs." % elapsed) - cl() - - with open("./products_path", "w") as f: - f.write(products) - - -def main(): - try: - flow() - except subprocess.CalledProcessError as e: - eprint("A step has failed:", e) - eprint(f"Quick invoke: {' '.join(e.cmd)}") - cl() - exit(os.EX_UNAVAILABLE) - except Exception: - eprint("An unhandled exception has occurred.", traceback.format_exc()) - cl() - exit(os.EX_UNAVAILABLE) + ) if __name__ == "__main__": diff --git a/docs/Usage.md b/docs/Usage.md index 6dcf1c0..854a9e3 100644 --- a/docs/Usage.md +++ b/docs/Usage.md @@ -1,40 +1,84 @@ # Using DFFRAM -DFFRAM is based around two Python modules: `dffram` and `placeram`. +DFFRAM is based around `placeram`, a Python module, and `dffram.py`, a Python +application. -`dffram.py` is a relatively self-contained flow that uses Openlane and other technologies to place, route and harden RAM. `dffram.py` runs on the host machine. +`placeram` is a custom placer using OpenROAD's Python interface. +It places DFFRAM RAM/RF designs in a predetermined structure to avoid a lengthy +and inefficient manual placement process for RAM. -`placeram` is a custom placer using OpenROAD's Python interface. It places DFFRAM RAM/RF designs in a predetermined structure to avoid a lengthy and inefficient manual placement process for RAM. Unlike `dffram.py`, `placeram` runs in a Docker container to avoid mandating an OpenROAD dependency (which is huge.) +`dffram.py` is an OpenLane-based flow that performs every step of hardening +the RAM modules from elaboration to GDS-II stream out. It incorporates `placeram`. # Dependencies -* A Unix-like Operating System - * Use the Windows Subsystem for Linux on Windows (which I use) -* The Skywater 130nm PDK - * See [Getting Sky130](./md/Getting%20Sky130.md) -* Docker Container -* Python 3.6+ with PIP - * PIP packages `click` and `pyyaml`: `python3 -m pip install click pyyaml` +* macOS or Linux +* The Nix Package Manager -## Recommended -* Klayout (to view the final result) +## Installing Nix +You can install Nix by following the instructions at https://nixos.org/download.html. + +Or more simply, on Ubuntu, run the following in your Terminal: + +```sh +sudo apt-get install -y curl +sh <(curl -L https://nixos.org/nix/install) --daemon --yes +``` +> On not systemd-based Linux systems, replace `--daemon` with `--no-daemon`. + +Or on macOS: + +```sh +sh <(curl -L https://nixos.org/nix/install) --yes +``` + +Enter your password if prompted. This hsould take around 5 minutes. + +Make sure to close all terminals after you're done with this step. + +### Setting up the binary cache +Cachix allows the reproducible Nix builds to be stored on a cloud server so you +do not have to build OpenLane's dependencies from scratch on every computer, +which will take a long time. + +First, you want to install Cachix by running the following in your terminal: + +```sh +nix-env -f "" -iA cachix +``` + +Then set up the OpenLane binary cache as follows: + +```sh +cachix use openlane +``` +If `cachix use openlane` fails, re-run it as follows: + +```sh +sudo env PATH="$PATH" cachix use openlane +``` # Basic ```sh -export PDK_ROOT=/usr/local/pdk git clone https://github.com/Cloud-V/DFFRAM cd DFFRAM -python3 -m pip install -r requirements.txt -./dffram.py -s 8x32 # <8-2048>x<8-64> +nix-shell +./dffram.py 8x32 # <8-2048>x<8-64> ``` # Advanced -The compilation flow at a minimum needs two options: the building blocks and the size. +The compilation flow has four main arguments: + * `--pdk`: The PDK + * `--scl`: The Standard Cell Library + * `--building-blocks`: The building blocks. + * The Size (passed without a flag) + +The building block full set `pdk:scl:blocks` corresponds to `./platforms///_building_blocks//model.v`. Building block sets are fundamentally similar with a number of exceptions, most importantly, the SCL used and supported sizes. + +For example: ```sh -export PDK_ROOT=/usr/local/pdk -./dffram.py -b sky130A:sky130_fd_sc_hd:ram -s 8x32 +./dffram.py -p sky130A -s sky130_fd_sc_hd -b ram 8x32 ``` -The building block set `pdk:scl:name` corresponds to `./platforms///_building_blocks//model.v`. Building block sets are fundamentally similar with a number of exceptions, most importantly, the SCL used and supported sizes. ## Options For a full list of options, please invoke: @@ -47,7 +91,6 @@ DFFRAM supports a number of secret options you can use to further customize your Variable Name|Effect -|- -PRFLOW_CREATE_IMAGE|If set to any value, a step after placement and routing that creates an image with Klayout is added. It's good for sanity checks. FORCE_ACCEPT_SIZE|DFFRAM checks that you are not using a size not officially marked supported as available by a certain building block set. If this environment variable is set to any value, the check is bypassed. FORCE_DESIGN_NAME|Design names are found based on the size. If you'd like to force dffram to use a specific design name instead, set this environment variable to that name. diff --git a/placeram/cli.py b/placeram/cli.py index 97e9267..7793af1 100644 --- a/placeram/cli.py +++ b/placeram/cli.py @@ -17,9 +17,12 @@ # limitations under the License. import os +import re +import traceback try: import odb + import utl except ImportError: print( """ @@ -44,10 +47,9 @@ from . import data from .row import Row + from .util import eprint from .reg_data import DFFRF -import re -import traceback class Placer: @@ -66,9 +68,12 @@ def __init__( odb.read_db(self.db, odb_in) # Technology Setup - self.lib = self.db.getLibs()[0] - self.sites = self.lib.getSites() - self.cells = self.lib.getMasters() + self.libs = self.db.getLibs() + self.sites = [] + self.cells = [] + for lib in self.libs: + self.sites += lib.getSites() + self.cells += lib.getMasters() ## Extract the fill cells for later use ### We use decap cells to substitute fills wherever possible. @@ -82,6 +87,7 @@ def __init__( filter(lambda x: re.match(fill_cell_data["tap"], x.getName()), self.cells) ) self.fill_cells_by_sites = {} + tap_width = None for cell in raw_fill_cells: match_info = re.match(fill_cell_data["fill"], cell.getName()) site_count = int(match_info[1]) @@ -98,6 +104,11 @@ def __init__( fill_cell_sizes = list(self.fill_cells_by_sites.keys()) + if tap_width is None: + eprint("No tap cells found!") + print(fill_cell_sizes) + exit(-1) + # Layout Setup self.block = self.db.getChip().getBlock() self.instances = self.block.getInsts() @@ -169,32 +180,23 @@ def place(self): % (self.core_width, self.core_height) ) + utl.metric_float("dffram__suggested__core_width", self.core_width) + utl.metric_float("dffram__suggested__core_height", self.core_height) + die_area = self.block.getDieArea().area() / ( self.micron_in_units * self.micron_in_units ) self.density = logical_area / die_area + utl.metric_float("dffram__logic__density", self.density) eprint("Density: %.2f%%" % (self.density * 100)) eprint("Done.") def write_db(self, output): return odb.write_db(self.db, output) == 1 - def write_width_height(self, dimensions_file): - try: - with open(dimensions_file, "w") as f: - f.write(str(self.core_width) + "x" + str(self.core_height)) - return True - except Exception: - return False - - def write_density(self, density_file): - try: - with open(density_file, "w") as f: - f.write(str(self.density)) - return True - except Exception: - return False + def write_def(self, output): + return odb.write_def(self.block, output) == 1 def check_readable(file): @@ -203,7 +205,8 @@ def check_readable(file): @click.command() -@click.option("-o", "--output", required=True) +@click.option("-o", "--output-odb", required=True) +@click.option("--output-def", type=str, required=False, default=None) @click.option("-s", "--size", required=True, help="RAM Size (ex. 8x32, 16x32…)") @click.option( "-r", @@ -211,31 +214,27 @@ def check_readable(file): required=False, help="File to print out text representation of hierarchy to. (Pass /dev/stderr or /dev/stdout for stderr or stdout.)", ) -@click.option( - "-d", - "--write-dimensions", - required=False, - help="File to print final width and height to (in the format '{width}x{height}')", -) -@click.option( - "-n", - "--write-density", - required=False, - help="File to print density to (in the format '{density}'- 0<=density<1)", -) @click.option( "-b", "--building-blocks", default="sky130A:sky130_fd_sc_hd:ram", help="Format :: : Name of the building blocks to use.", ) +@click.option( + "-l", + "--input-lef", + default=[], + help="Input LEF files (ignored)", + multiple=True, + type=str, +) @click.argument("odb_in", required=True, nargs=1) def cli( - output, + output_odb, + output_def, + input_lef, size, represent, - write_dimensions, - write_density, building_blocks, odb_in, ): @@ -290,23 +289,16 @@ def cli( placer.place() - if not placer.write_db(output): + if not placer.write_db(output_odb): eprint("Failed to write output ODB file.") exit(os.EX_IOERR) - eprint("Wrote to %s." % output) - - if write_dimensions is not None: - if not placer.write_width_height(write_dimensions): - eprint("Failed to write dimensions file.") - exit(os.EX_IOERR) - eprint("Wrote width and height to %s." % write_dimensions) + eprint("Wrote to %s." % output_odb) - if write_density is not None: - if not placer.write_density(write_density): - eprint("Failed to write density file.") + if output_def is not None: + if not placer.write_def(output_def): + eprint("Failed to write output DEF file.") exit(os.EX_IOERR) - eprint("Wrote density to %s." % write_dimensions) eprint("Done.") diff --git a/requirements.txt b/requirements.txt index 9358ae6..bae0622 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -click +openlane>=2.0.0a49 pyyaml -volare>=0.7.3 \ No newline at end of file +cloup \ No newline at end of file diff --git a/scripts/openlane/interactive.tcl b/scripts/openlane/interactive.tcl deleted file mode 100644 index 931a906..0000000 --- a/scripts/openlane/interactive.tcl +++ /dev/null @@ -1,54 +0,0 @@ - - -package require openlane -set script_dir [file dirname [file normalize [info script]]] -prep -design $script_dir -ignore_mismatches - -set ::env(LIB_SYNTH_COMPLETE_NO_PG) [list] -foreach lib $::env(LIB_SYNTH_COMPLETE) { - set fbasename [file rootname [file tail $lib]] - set lib_path [index_file $::env(synthesis_tmpfiles)/$fbasename.no_pg.lib] - convert_pg_pins $lib $lib_path - lappend ::env(LIB_SYNTH_COMPLETE_NO_PG) $lib_path -} - -set_odb $::env(INITIAL_ODB) -set_netlist $::env(INITIAL_NETLIST) - -set ::env(CURRENT_SDC) $::env(INITIAL_SDC) - -run_power_grid_generation - -global_routing -detailed_routing -run_antenna_check - -run_parasitics_sta -run_magic -if { [info exists ::env(ENABLE_KLAYOUT) ] } { - if { ($::env(ENABLE_KLAYOUT) == 1) } { - run_klayout - run_klayout_gds_xor - } -} - -run_magic_spice_export -run_lvs -run_magic_drc -if { [info exists ::env(ENABLE_KLAYOUT) ] } { - if { ($::env(ENABLE_KLAYOUT) == 1) } { - run_klayout_drc - } -} -if { [info exists ::env(ENABLE_CVC) ] } { - if { ($::env(ENABLE_CVC) == 1) } { - run_lef_cvc - } -} - -save_final_views -save_path $::env(PRODUCTS_PATH) - -calc_total_runtime -save_state -generate_final_summary_report -check_timing_violations diff --git a/scripts/perl/verilog_to_lib.pl b/scripts/perl/verilog_to_lib.pl deleted file mode 100755 index e020494..0000000 --- a/scripts/perl/verilog_to_lib.pl +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env perl -# Author: Sini Mukundan -# https://vlsi.pro/creating-lib-file-from-verilog/ - -use strict; - -if ($#ARGV < 1 ) { - print "usage: perl verilog_to_lib.pl [tran[cap[signal_level]]] \n"; - print " are all required"; - exit; -} - -my $module = $ARGV[0] ; -my $tran = 2.5 ; -my $cap = 0.001; -my $signal_level = "VDD" ; - -if(defined $ARGV[3]) {$tran = $ARGV[3];} -if(defined $ARGV[4]) {$cap = $ARGV[4];} -if(defined $ARGV[5]) {$signal_level = $ARGV[5];} - -my $FF; -my $FO; -open $FF, "< $ARGV[1]" or die "Can't open $ARGV[0] : $!"; -open $FO, ">$ARGV[2]" or die "Can't open $module.lib for write : $!"; - -my $db = createTopLevelDB(); -createDotLib($db,$FO); - -sub createDotLib -{ - my $topLevelDBRef = shift; - my $FO = shift ; - ### Header - print $FO "library\($topLevelDBRef->{'design'}->{'cell'}\) {\n"; - print $FO "\n /* unit attributes */\n"; - print $FO " time_unit : \"1ns\"\;\n"; - print $FO " voltage_unit : \"1V\"\;\n"; - print $FO " current_unit : \"1uA\"\;\n"; - print $FO " pulling_resistance_unit : \"1kohm\"\;\n"; - print $FO " leakage_power_unit : \"1nW\"\;\n"; - print $FO " capacitive_load_unit\(1,pf\)\;\n\n"; - foreach my $direction (keys(%{$topLevelDBRef->{'bus'}})) { - foreach my $bus_type (keys %{$topLevelDBRef->{'bus'}->{$direction}}) { - my @bus_width = split(/_/, $bus_type); - my $bus_hi = $bus_width[1] ; - my $bus_lo = $bus_width[2] ; - my $bus_width = $bus_hi+1-$bus_lo; - print $FO " type \($bus_type\) { \n"; - print $FO " base_type : array ; \n" ; - print $FO " data_type : bit \n" ;; - print $FO " bit_width : $bus_width \n" ;; - print $FO " bit_from : $bus_hi \n" ;; - print $FO " bit_to : $bus_lo ; \n" ; - print $FO " downto : true ; \n" ; - print $FO " } \n" ; - } - } - print $FO "\n cell\($topLevelDBRef->{'design'}->{'cell'}\) {\n"; - foreach my $direction (keys(%{$topLevelDBRef->{'pins'}})) { - foreach my $pin_name (@{$topLevelDBRef->{'pins'}->{$direction}}) { - print $FO (" pin\($pin_name\) { \n"); - print $FO ("\tdirection : $direction ;\n"); - if($direction eq "input") { - print $FO ("\tmax_transition : $tran;\n"); - } - print $FO ("\tcapacitance : $cap; \n"); - print $FO (" } \n") ; - } - } - foreach my $direction (keys(%{$topLevelDBRef->{'bus'}})) { - foreach my $bus_type (keys %{$topLevelDBRef->{'bus'}->{$direction}}) { - my @bus_width = split(/_/, $bus_type); - my $bus_hi = $bus_width[1] ; - my $bus_lo = $bus_width[2] ; - foreach my $bus_name (@{$topLevelDBRef->{'bus'}->{$direction}{$bus_type}}) { - chomp($bus_name); - print "BUS $bus_name : $bus_type : $direction \n" ; - print $FO (" bus\($bus_name\) { \n"); - print $FO ("\tbus_type : $bus_type ;\n"); - print $FO ("\tdirection : $direction ;\n"); - if($direction eq "input") { - print $FO ("\tmax_transition : $tran;\n"); - } - for(my $i=$bus_lo; $i<=$bus_hi; $i++) { - print $FO ("\tpin\($bus_name\[$i\]\) { \n"); - print $FO ("\t\tcapacitance : $cap; \n"); - print $FO ("\t} \n") ; - } - print $FO (" } \n") ; - } - } - } - print $FO (" } \n") ; - print $FO ("} \n") ; -} - -sub createTopLevelDB -{ - my $find_top_module = 0; - my %topLevelDB = () ; - my %pins = () ; - my %bus = () ; - my @input_pins ; - my @output_pins ; - my @inout_pins ; - my @bus_types ; - my %input_bus = () ; - my %output_bus = () ; - my %inout_bus = () ; - my %design = (); - $design{'cell'} = $module; - $design{'tran'} = $tran; - $design{'cap'} = $cap; - $design{'signal_level'} = $signal_level; - while(my $line = <$FF>) { - last if($find_top_module == 1); - if($line=~/module\s+$module/) { - $find_top_module = 1 ; - while(my $line = <$FF>) { - next if($line =~ "\s*//" ); - chomp($line); - if ($line =~/input\s+/ ) { - $line=~s/\s*input\s+//; - $line=~s/;//; - if($line =~/\[(\d+):(\d+)\]/) { - my $bus_type = "bus_$1_$2"; - $line=~s/\[(\d+):(\d+)\]//; - my @line = split(/,/, $line); - unless(grep {$_ eq $bus_type} @bus_types) { - push(@bus_types,$bus_type); - } - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@{$input_bus{$bus_type}}, $pin ); - } - } - else { - my @line = split(/,/, $line); - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@input_pins, $pin); - } - } - } - if ($line =~/output\s+/ ) { - $line=~s/\s*output\s+//; - $line=~s/;//; - if($line =~/\[(\d+):(\d+)\]/) { - my $bus_type = "bus_$1_$2"; - $line=~s/\[(\d+):(\d+)\]//; - my @line = split(/,/, $line); - unless(grep {$_ eq $bus_type} @bus_types) { - push(@bus_types,$bus_type); - } - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@{$output_bus{$bus_type}}, $pin ); - } - } - else { - my @line = split(/,/, $line); - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@output_pins, $pin); - } - } - - } - if ($line =~/inout\s+/ ) { - $line=~s/\s*inout\s+//; - $line=~s/;//; - if($line =~/\[(\d+):(\d+)\]/) { - my $bus_type = "bus_$1_$2"; - $line=~s/\[(\d+):(\d+)\]//; - my @line = split(/,/, $line); - unless(grep {$_ eq $bus_type} @bus_types) { - push(@bus_types,$bus_type); - } - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@{$inout_bus{$bus_type}}, $pin ); - } - } - else { - my @line = split(/,/, $line); - foreach my $pin (@line) { - $pin=~s/\s+//; - push(@inout_pins, $pin); - } - } - - } - - last if($line=~/endmodule/); - } - - } - } - $pins{'input'} = \@input_pins; - $pins{'output'} = \@output_pins; - $pins{'inout'} = \@inout_pins; - $bus{'input'} = \%input_bus; - $bus{'output'} = \%output_bus; - $bus{'inout'} = \%inout_bus; - $topLevelDB{'pins'} = \%pins; - $topLevelDB{'bus'} = \%bus; - $topLevelDB{'design'} = \%design; - return \%topLevelDB; -} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..ed8a01b --- /dev/null +++ b/shell.nix @@ -0,0 +1,23 @@ +# Copyright 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. +{ + pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/0218941ea68b4c625533bead7bbb94ccce52dceb.tar.gz") {} +}: + +let openlane-src = pkgs.fetchFromGitHub { + owner = "efabless"; + repo = "openlane2"; + rev = "95100bca1216ee83ff83ff5e425ad7c2fbece9f4"; + sha256 = "sha256-z9AZTjs2QwjL83+N4KztrjBNEE3zTxagPPtXPLZhRC4="; +}; in import "${openlane-src}/shell.nix" {} \ No newline at end of file From 8f08a1c5e92b74b91bd3a1b7c2d436758ae2a56e Mon Sep 17 00:00:00 2001 From: Donn Date: Thu, 10 Aug 2023 10:56:08 +0300 Subject: [PATCH 3/6] Update to OpenLane 2.0.0b3 --- dffram.py | 3 ++- requirements.txt | 2 +- shell.nix | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dffram.py b/dffram.py index e4f0a61..5acd22f 100755 --- a/dffram.py +++ b/dffram.py @@ -338,7 +338,7 @@ def main( "SYNTH_ELABORATE_FLATTEN": True, "SYNTH_READ_BLACKBOX_LIB": True, "SYNTH_EXCLUSION_CELL_LIST": "/dev/null", - "SYNTH_PARAMETERS": f"WSIZE={logical_width}", + "SYNTH_PARAMETERS": [f"WSIZE={logical_width}"], "GRT_REPAIR_ANTENNAS": False, "MINIMUM_HEIGHT": min_height, "VERTICAL_HALO": vertical_halo, @@ -352,6 +352,7 @@ def main( "FP_IO_VEXTEND": Decimal(0), "FP_IO_VLENGTH": 2, "FP_IO_HLENGTH": 2, + "STA_CORNERS": ["nom_ss_100C_1v60"], }, design_dir=build_dir, ) diff --git a/requirements.txt b/requirements.txt index bae0622..cf76c00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -openlane>=2.0.0a49 +openlane==2.0.0b3 pyyaml cloup \ No newline at end of file diff --git a/shell.nix b/shell.nix index ed8a01b..63519f1 100644 --- a/shell.nix +++ b/shell.nix @@ -18,6 +18,6 @@ let openlane-src = pkgs.fetchFromGitHub { owner = "efabless"; repo = "openlane2"; - rev = "95100bca1216ee83ff83ff5e425ad7c2fbece9f4"; - sha256 = "sha256-z9AZTjs2QwjL83+N4KztrjBNEE3zTxagPPtXPLZhRC4="; + rev = "0b2cb7eb6681b644e65082701175044e6e97cc0f"; + sha256 = "sha256-qYMZJGRfqNhf0FFf2VdF2AOsAqVTlWLrYlc3krvKp0I="; }; in import "${openlane-src}/shell.nix" {} \ No newline at end of file From a0a2b2ce1fd739256191ea8d2900c2adbec013ae Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 21 Sep 2023 16:01:58 +0300 Subject: [PATCH 4/6] Fix PDN Config (#188) ~ Configure PDN as a macro ~ Update OpenLane 2 to Beta 14 ~ Fix bug where `odbpy` returns a goddamn pointer to an int64 --- .github/workflows/main.yml | 10 ++++++++-- Makefile | 5 ++--- dffram.py | 5 +++-- placeram/cli.py | 13 ++++++------- platforms/sky130A/fill_cells.yml | 5 ----- platforms/sky130A/sky130_fd_sc_hd/tech.yml | 11 ++++++++--- requirements.txt | 2 +- shell.nix | 8 +++++--- 8 files changed, 33 insertions(+), 26 deletions(-) delete mode 100644 platforms/sky130A/fill_cells.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4b0ce9f..53e70c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,7 +94,7 @@ jobs: - name: Setup Nix uses: cachix/install-nix-action@v22 with: - nix_path: nixpkgs=channel:nixos-22.11 + nix_path: nixpkgs=channel:nixos-23.05 - name: Setup Cachix uses: cachix/cachix-action@v12 with: @@ -106,7 +106,7 @@ jobs: building_blocks=rf fi - nix-shell --run "\ + nix-shell --pure --run "\ ./dffram.py ${{ matrix.count }}x${{ matrix.width }}\ -p sky130A\ -s sky130_fd_sc_hd\ @@ -115,6 +115,12 @@ jobs: " echo "PRODUCTS_PATH=$(echo products/*)" >> $GITHUB_ENV + # - name: Upload Build Folder [TEMP] + # uses: actions/upload-artifact@v3 + # if: always() + # with: + # name: BUILD_${{ matrix.count }}x${{ matrix.width }}_${{ matrix.variant }} + # path: ./build - name: Upload Final Views uses: actions/upload-artifact@v3 with: diff --git a/Makefile b/Makefile index 0596a39..1ec8f7a 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,13 @@ lint: venv/manifest.txt ./venv/bin/flake8 . venv: venv/manifest.txt -venv/manifest.txt: ./requirements_dev.txt ./requirements.txt +venv/manifest.txt: ./requirements_dev.txt rm -rf venv python3 -m venv ./venv PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade pip PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade wheel PYTHONPATH= ./venv/bin/python3 -m pip install --upgrade\ - -r ./requirements_dev.txt\ - -r ./requirements.txt + -r ./requirements_dev.txt PYTHONPATH= ./venv/bin/python3 -m pip freeze > $@ touch venv/manifest.txt diff --git a/dffram.py b/dffram.py index 5acd22f..a52a4f9 100755 --- a/dffram.py +++ b/dffram.py @@ -352,9 +352,10 @@ def main( "FP_IO_VEXTEND": Decimal(0), "FP_IO_VLENGTH": 2, "FP_IO_HLENGTH": 2, - "STA_CORNERS": ["nom_ss_100C_1v60"], + # PDN + "DESIGN_IS_CORE": False, }, - design_dir=build_dir, + design_dir=os.path.abspath(build_dir), ) final_state = dffram_flow.start( diff --git a/placeram/cli.py b/placeram/cli.py index 7793af1..b5cbd95 100644 --- a/placeram/cli.py +++ b/placeram/cli.py @@ -183,9 +183,9 @@ def place(self): utl.metric_float("dffram__suggested__core_width", self.core_width) utl.metric_float("dffram__suggested__core_height", self.core_height) - die_area = self.block.getDieArea().area() / ( - self.micron_in_units * self.micron_in_units - ) + die_width = self.block.getDieArea().dx() / self.micron_in_units + die_height = self.block.getDieArea().dy() / self.micron_in_units + die_area = die_width * die_height self.density = logical_area / die_area utl.metric_float("dffram__logic__density", self.density) @@ -239,8 +239,8 @@ def cli( odb_in, ): pdk, scl, blocks = building_blocks.split(":") - fill_cells_file = os.path.join(".", "platforms", pdk, "fill_cells.yml") - if not os.path.isfile(fill_cells_file): + platform_tech_file = os.path.join(".", "platforms", pdk, scl, "tech.yml") + if not os.path.isfile(platform_tech_file): eprint("Platform %s not found." % pdk) exit(os.EX_NOINPUT) @@ -264,7 +264,6 @@ def cli( "WARNING: Word length must be a non-zero multiple of 8. Results may be unexpected." ) - platform_tech_file = os.path.join(".", "platforms", pdk, scl, "tech.yml") platform_tech_config = yaml.safe_load(open(platform_tech_file)) tap_distance = platform_tech_config["tap_distance"] @@ -272,7 +271,7 @@ def cli( for input in [odb_in]: check_readable(input) - fill_cell_data = yaml.load(open(fill_cells_file).read(), Loader=yaml.SafeLoader) + fill_cell_data = platform_tech_config["fills"] placer = Placer( odb_in, diff --git a/platforms/sky130A/fill_cells.yml b/platforms/sky130A/fill_cells.yml deleted file mode 100644 index 0655728..0000000 --- a/platforms/sky130A/fill_cells.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Non-logical Cells -decap: sky130_\w+__decap_(\d+) -fill: sky130_\w+__fill_(\d+) -tap: sky130_\w+__tapvpwrvgnd_(\d+) -diode: sky130_\w+__diode_(\d+) diff --git a/platforms/sky130A/sky130_fd_sc_hd/tech.yml b/platforms/sky130A/sky130_fd_sc_hd/tech.yml index b65aa9a..7b0edf5 100644 --- a/platforms/sky130A/sky130_fd_sc_hd/tech.yml +++ b/platforms/sky130A/sky130_fd_sc_hd/tech.yml @@ -1,5 +1,5 @@ tap_distance: 15 -metal_layers: +metal_layers: hor-layer: met3 ver-layer: met2 rt-max-layer: met4 @@ -7,11 +7,11 @@ site: width: 0.46 height: 2.72 name: unithd -tie: +tie: tie_lo_cell: sky130_fd_sc_hd__conb_1 tie_lo_port: LO tie_hi_cell: sky130_fd_sc_hd__conb_1 - tie_hi_port: HI + tie_hi_port: HI sta: driving_cell: name: sky130_fd_sc_hd__inv_8 @@ -30,3 +30,8 @@ sta: "1024x*": 16.5 "2048x*": 16.5 rf: {} +fills: + decap: sky130_fd_sc_hd__decap_(\d+) + fill: sky130_fd_sc_hd__fill_(\d+) + tap: sky130_fd_sc_hd__tapvpwrvgnd_(\d+) + diode: sky130_fd_sc_hd__diode_(\d+) diff --git a/requirements.txt b/requirements.txt index cf76c00..6722f71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -openlane==2.0.0b3 +openlane==2.0.0b14 pyyaml cloup \ No newline at end of file diff --git a/shell.nix b/shell.nix index 63519f1..9cbe8fe 100644 --- a/shell.nix +++ b/shell.nix @@ -18,6 +18,8 @@ let openlane-src = pkgs.fetchFromGitHub { owner = "efabless"; repo = "openlane2"; - rev = "0b2cb7eb6681b644e65082701175044e6e97cc0f"; - sha256 = "sha256-qYMZJGRfqNhf0FFf2VdF2AOsAqVTlWLrYlc3krvKp0I="; -}; in import "${openlane-src}/shell.nix" {} \ No newline at end of file + rev = "0c05b7ea04eac74203a9a33f8a8b519f33220f2f"; + sha256 = "sha256-fjYz6HuIYrGbkb1oA6wPIlEjieYU5hu0aygYQFj2d2I="; +}; in import "${openlane-src}/shell.nix" { + inherit pkgs; +} \ No newline at end of file From f9a18188def2301ac0b3311d748732a47fb6108f Mon Sep 17 00:00:00 2001 From: Leo Moser Date: Sun, 7 Jan 2024 16:58:18 +0100 Subject: [PATCH 5/6] Support for gf180mcuD (#190) It seems like support for gf180mcu is currently broken. I fixed up gf180mcuC and renamed it to gf180mcuD since this is the PDK which is currently used for the MPW shuttles. The 8x32 RAM successfully builds: `./dffram.py -p gf180mcuD -s gf180mcu_fd_sc_mcu7t5v0 -b ram 8x32` But I seem to get a lot of hold violations. Well, better than nothing for now :) --- dffram.py | 4 +++- platforms/gf180mcuC/process_data.yml | 2 -- platforms/{gf180mcuC => gf180mcuD}/fill_cells.yml | 0 .../gf180mcu_fd_sc_mcu7t5v0/block_definitions.v | 0 .../gf180mcu_fd_sc_mcu7t5v0/tech.yml | 7 ++++++- platforms/gf180mcuD/process_data.yml | 2 ++ 6 files changed, 11 insertions(+), 4 deletions(-) delete mode 100644 platforms/gf180mcuC/process_data.yml rename platforms/{gf180mcuC => gf180mcuD}/fill_cells.yml (100%) rename platforms/{gf180mcuC => gf180mcuD}/gf180mcu_fd_sc_mcu7t5v0/block_definitions.v (100%) rename platforms/{gf180mcuC => gf180mcuD}/gf180mcu_fd_sc_mcu7t5v0/tech.yml (78%) create mode 100644 platforms/gf180mcuD/process_data.yml diff --git a/dffram.py b/dffram.py index a52a4f9..21fa5ed 100755 --- a/dffram.py +++ b/dffram.py @@ -316,6 +316,8 @@ def main( if building_blocks == "rf": logical_width = word_width + rt_max_layer = tech_info["metal_layers"]["rt-max-layer"] + TargetFlow = Flow.factory.get(flow_name) or DFFRAM dffram_flow = TargetFlow( { @@ -324,7 +326,7 @@ def main( "CLOCK_PERIOD": clock_period, "GPL_CELL_PADDING": 0, "DPL_CELL_PADDING": 0, - "RT_MAX_LAYER": "met4", + "RT_MAX_LAYER": rt_max_layer, "GRT_ALLOW_CONGESTION": True, "PDK": pdk, "STD_CELL_LIBRARY": scl, diff --git a/platforms/gf180mcuC/process_data.yml b/platforms/gf180mcuC/process_data.yml deleted file mode 100644 index cefc2e4..0000000 --- a/platforms/gf180mcuC/process_data.yml +++ /dev/null @@ -1,2 +0,0 @@ -volare_pdk_family: gf180mcu -volare_pdk_version: 9f1c2b06d2b5a6708cfe0b55679c7e84d37220cc \ No newline at end of file diff --git a/platforms/gf180mcuC/fill_cells.yml b/platforms/gf180mcuD/fill_cells.yml similarity index 100% rename from platforms/gf180mcuC/fill_cells.yml rename to platforms/gf180mcuD/fill_cells.yml diff --git a/platforms/gf180mcuC/gf180mcu_fd_sc_mcu7t5v0/block_definitions.v b/platforms/gf180mcuD/gf180mcu_fd_sc_mcu7t5v0/block_definitions.v similarity index 100% rename from platforms/gf180mcuC/gf180mcu_fd_sc_mcu7t5v0/block_definitions.v rename to platforms/gf180mcuD/gf180mcu_fd_sc_mcu7t5v0/block_definitions.v diff --git a/platforms/gf180mcuC/gf180mcu_fd_sc_mcu7t5v0/tech.yml b/platforms/gf180mcuD/gf180mcu_fd_sc_mcu7t5v0/tech.yml similarity index 78% rename from platforms/gf180mcuC/gf180mcu_fd_sc_mcu7t5v0/tech.yml rename to platforms/gf180mcuD/gf180mcu_fd_sc_mcu7t5v0/tech.yml index fcd9d99..2c3ba5d 100644 --- a/platforms/gf180mcuC/gf180mcu_fd_sc_mcu7t5v0/tech.yml +++ b/platforms/gf180mcuD/gf180mcu_fd_sc_mcu7t5v0/tech.yml @@ -29,4 +29,9 @@ sta: "512x*": 11.21 "1024x*": 12.83 "2048x*": 14.1 - rf: {} \ No newline at end of file + rf: {} +fills: + decap: gf180mcu_fd_sc_mcu7t5v0__fillcap_(\d+) + fill: gf180mcu_fd_sc_mcu7t5v0__fill_(\d+) + tap: gf180mcu_fd_sc_mcu7t5v0__filltie + diode: gf180mcu_fd_sc_mcu7t5v0__antenna diff --git a/platforms/gf180mcuD/process_data.yml b/platforms/gf180mcuD/process_data.yml new file mode 100644 index 0000000..2344047 --- /dev/null +++ b/platforms/gf180mcuD/process_data.yml @@ -0,0 +1,2 @@ +volare_pdk_family: gf180mcu +volare_pdk_version: e0f692f46654d6c7c99fc70a0c94a080dab53571 From 1a772a52dbe772a984a82b76e195d9c7341dc530 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Sun, 7 Jan 2024 18:00:00 +0200 Subject: [PATCH 6/6] Add PDK support status to Readme + update to OpenLane 2.0.0b16 Also fix `unplace.py`, remove `fill_cells.yml` which is now redundant --- Readme.md | 130 ++++++++++++++++++----------- dffram.py | 2 + platforms/gf180mcuD/fill_cells.yml | 6 -- requirements.txt | 4 +- scripts/python/unplace.py | 30 +++++-- shell.nix | 10 +-- 6 files changed, 113 insertions(+), 69 deletions(-) delete mode 100644 platforms/gf180mcuD/fill_cells.yml diff --git a/Readme.md b/Readme.md index e1ceb13..355700d 100644 --- a/Readme.md +++ b/Readme.md @@ -12,70 +12,104 @@ Standard Cell Library based Memory Compiler using DFF/Latch cells. # ✨ Installation & Usage -See [this document](./docs/Usage.md). -You can try the [Google Colaboratory](https://colab.research.google.com/github/Cloud-V/DFFRAM/blob/main/dffram.ipynb), +See [this document](./docs/Usage.md). + +You can try the +[Google Colaboratory](https://colab.research.google.com/github/Cloud-V/DFFRAM/blob/main/dffram.ipynb), but it is a bit out-of-date at this point. +## Platform Support Status + +| Configured Platform | Working | Silicon-proven\* | +| - | - | - | +| `sky130A` | Yes | Yes | +| `sky130B` | Yes | No | +| `gf180mcuD` | No\* (Hold violations in the Netlist) | No | + +> \* Silicon proven does not imply that you should use it without whole-system, +> timing-annotated simulation to make sure that it works for your circuit. +> +> There may be design-specific complications that may render DFFRAM (and indeed +> the entire chip) unusable. Proceed with caution. + # Overview -The objective of this project is to develop a DFF/Latch-based RAM, Register File and Cache custom compilation flow that utilizes standard cell libraries following a standard ASIC (Application Specific Integrated Circuit) implementation approach. Different views (HDL netlist, HDL functional models, LEF, GDS, Timing, …) are all generated for a given size configuration. -The layout targets highly compact designs (85%+) as the cells are placed on the floor plan using a custom placer. Moreover, the custom placer ensures that the routing will be relatively simple. Currently, the compiler uses OpenROAD routers to route the macros with great success. +The objective of this project is to develop a DFF/Latch-based RAM, Register File +and Cache custom compilation flow that utilizes standard cell libraries +following a standard ASIC (Application Specific Integrated Circuit) +implementation approach. Different views (HDL netlist, HDL functional models, +LEF, GDS, Timing, …) are all generated for a given size configuration. -The Compiler relies on basic building blocks to construct the layout of different RAM/RF/Cache configurations. Check [the compiler documentation](./docs/) for more info. The following shows how a 32x32 memory (DFF based) is constructed. +The layout targets highly compact designs (85%+) as the cells are placed on the +floor plan using a custom placer. Moreover, the custom placer ensures that the +routing will be relatively simple. Currently, the compiler uses OpenROAD routers +to route the macros with great success. + +The Compiler relies on basic building blocks to construct the layout of +different RAM/RF/Cache configurations. Check +[the compiler documentation](./docs/) for more info. The following shows how a +32x32 memory (DFF based) is constructed. ![](./docs/img/ram_ex.png) -The generated layouts by the DFFRAM compilers for RAM32 as well as its building blocks are as follows: -- First, a byte, which is just 8 bits placed together... -![GDS layout of a byte](./docs/img/1x8.png) +The generated layouts by the DFFRAM compilers for RAM32 as well as its building +blocks are as follows: -- Put four of those side by side, and you get a 32-bit word... -![GDS layout of a word](./docs/img/1x32.png) +* First, a byte, which is just 8 bits placed together... + ![GDS layout of a byte](./docs/img/1x8.png) -- Stack eight of those for an 8 word bank of RAM... -![GDS layout of 8 words stacked vertically](./docs/img/8x32.png) +* Put four of those side by side, and you get a 32-bit word... + ![GDS layout of a word](./docs/img/1x32.png) -- And stack 4 of these 8 words for a kilobit of RAM! -![GDS layout of 4x8 words stacked vertically](./docs/img/32x32.png) +* Stack eight of those for an 8 word bank of RAM... + ![GDS layout of 8 words stacked vertically](./docs/img/8x32.png) -- We can keep going, but these images aren't getting any smaller. As a bonus though, here is 64 kilobits: -![8kbytes](./docs/img/8kb_layout.png) +* And stack 4 of these 8 words for a kilobit of RAM! + ![GDS layout of 4x8 words stacked vertically](./docs/img/32x32.png) -> That stuff you see on the right of each image? It's clock gates, decoders and the like. Don't worry about it. +* We can keep going, but these images aren't getting any smaller. As a bonus + though, here is 64 kilobits: ![8kbytes](./docs/img/8kb_layout.png) +> That stuff you see on the right of each image? It's clock gates, decoders and +> the like. Don't worry about it. Currently, the can compiler generate the layout of the following configurations: > 1RW1R variants are temporarily disabled due to a bug. -- RAM - - 32 words with byte write enable (1RW and 1RW1R). - - 128 words with byte write enable (1RW and 1RW1R). - - 256 words with byte write enable (1RW and 1RW1R). - - 512 words with byte write enable (1RW and 1RW1R). - - 1024 words with byte write enable (1RW and 1RW1R). - - 2048 words with byte write enable (1RW and 1RW1R). -- Register File - - 32 x 32-bit words (2R1W) - -The [`OpenLane/`](./OpenLane) folder will contain good known OpenLane configurations to build DFFRAM different macros. - +* RAM + * 32 words with byte write enable (1RW and 1RW1R). + * 128 words with byte write enable (1RW and 1RW1R). + * 256 words with byte write enable (1RW and 1RW1R). + * 512 words with byte write enable (1RW and 1RW1R). + * 1024 words with byte write enable (1RW and 1RW1R). + * 2048 words with byte write enable (1RW and 1RW1R). +* Register File + * 32 x 32-bit words (2R1W) + +The [`OpenLane/`](./OpenLane) folder will contain good known OpenLane +configurations to build DFFRAM different macros. + ## File Structure + * `.github` contains files for GitHub actions. * `docs/` contains documentation (😮) -* `rtl/` contains RTL RAM file generators for benchmarking and comparison purposes. +* `rtl/` contains RTL RAM file generators for benchmarking and comparison + purposes. * `platforms/` contains PDK-specific files: * `/` * `/` - * `_building_blocks/` contains a hierarchy of building blocks supported by the compiler. + * `_building_blocks/` contains a hierarchy of building blocks supported by + the compiler. * `placeram/` is the custom placer Python module. * `scripts/` has assisting scripts used by the flow. * `dffram.py` is the compilation flow going from building blocks to LVS. # Comparisons -The following table compares the areas and bit densities of RAM macros generated using different means. +The following table compares the areas and bit densities of RAM macros generated +using different means. @@ -118,8 +152,8 @@ The following table compares the areas and bit densities of RAM macros generated - - + + @@ -129,25 +163,27 @@ The following table compares the areas and bit densities of RAM macros generated
1,584.24 x 788.8 26,196 1940.45 x 1951.17 8,654 2,074 x 2,085 7,578
8 kbytes N/A N/A
- - -1 All support 32-bit word reads and 1, 2, and 4 bytes writes. -2 Values are based on the original layout produced by the compiler. OpenRAM macros are typically wrapped to be useful w/ automated PnR ASIC flows. +1 All support 32-bit word reads and 1, 2, and 4 bytes +writes.\ +2 Values are based on the original layout produced by the +compiler. OpenRAM macros are typically wrapped to be useful w/ automated PnR +ASIC flows. # ⚖️ Copyright and Licensing + Copyright ©2020-2023 The American University in Cairo -Licensed under the Apache License, Version 2.0 (the "Open Source License"); -you may not use this file except in compliance with the Open Source License. -You may obtain a copy of the Open Source License at the root of this repository -(see the file 'License') or at +Licensed under the Apache License, Version 2.0 (the "Open Source License"); you +may not use this file except in compliance with the Open Source License. You may +obtain a copy of the Open Source License at the root of this repository (see the +file 'License') or at > http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the Open Source License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the Open Source License for the specific language governing permissions and -limitations under the Open Source License. +Unless required by applicable law or agreed to in writing, software distributed +under the Open Source License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Open +Source License for the specific language governing permissions and limitations +under the Open Source License. diff --git a/dffram.py b/dffram.py index 21fa5ed..907351d 100755 --- a/dffram.py +++ b/dffram.py @@ -242,6 +242,7 @@ def main( default_clock_period, min_height, flow_name, + pdk_root, **kwargs, ): if variant == "DEFAULT": @@ -358,6 +359,7 @@ def main( "DESIGN_IS_CORE": False, }, design_dir=os.path.abspath(build_dir), + pdk_root=pdk_root, ) final_state = dffram_flow.start( diff --git a/platforms/gf180mcuD/fill_cells.yml b/platforms/gf180mcuD/fill_cells.yml deleted file mode 100644 index 1a96d50..0000000 --- a/platforms/gf180mcuD/fill_cells.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Non-logical Cells -decap: gf180mcu_\w+__fillcap_(\d+) -fill: gf180mcu_\w+__fill_(\d+) -tap: gf180mcu_\w+__filltie -antenna: gf180mcu_\w+__antenna - diff --git a/requirements.txt b/requirements.txt index 6722f71..640b328 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -openlane==2.0.0b14 +openlane==2.0.0b16 pyyaml -cloup \ No newline at end of file +cloup diff --git a/scripts/python/unplace.py b/scripts/python/unplace.py index 46549c6..ddbafb8 100644 --- a/scripts/python/unplace.py +++ b/scripts/python/unplace.py @@ -32,29 +32,43 @@ @click.command() @click.option( - "-p", "--platform", required=True, help="PDK/Platform to use, e.g. sky130A" + "-p", + "--platform", + required=True, + help="Platform (PDK) to use", +) +@click.option( + "-s", + "--scl", + required=True, + help="SCL to use in the format", ) @click.option( "-o", "--output", "output_file", + type=click.Path(file_okay=True, dir_okay=False), default="/dev/stdout", help="Output file", show_default=True, ) -@click.argument("input_file", required=True) -def unplace(platform, output_file, input_file): +@click.argument( + "input_file", + type=click.Path(file_okay=True, dir_okay=False, exists=True), + required=True, +) +def unplace(platform: str, scl: str, output_file: str, input_file: str): dn = os.path.dirname dffram_path = dn(dn(dn(os.path.abspath(__file__)))) - fill_yml_path = os.path.join(dffram_path, "platforms", platform, "fill_cells.yml") + tech_path = os.path.join(dffram_path, "platforms", platform, scl, "tech.yml") try: - fill_yml_str = open(fill_yml_path).read() + tech_str = open(tech_path).read() except FileNotFoundError: - print(f"{fill_yml_path} not found.", file=sys.stderr) + print(f"{tech_path} not found.", file=sys.stderr) exit(os.EX_NOINPUT) - data: dict = yaml.load(fill_yml_str, Loader=yaml.SafeLoader) - rx_list = list(data.values()) + data: dict = yaml.load(tech_str, Loader=yaml.SafeLoader) + rx_list = list(data["fills"].values()) try: input_str = open(input_file).read() diff --git a/shell.nix b/shell.nix index 9cbe8fe..142f0f7 100644 --- a/shell.nix +++ b/shell.nix @@ -12,14 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. { - pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/0218941ea68b4c625533bead7bbb94ccce52dceb.tar.gz") {} + pkgs ? import {} }: let openlane-src = pkgs.fetchFromGitHub { owner = "efabless"; repo = "openlane2"; - rev = "0c05b7ea04eac74203a9a33f8a8b519f33220f2f"; - sha256 = "sha256-fjYz6HuIYrGbkb1oA6wPIlEjieYU5hu0aygYQFj2d2I="; -}; in import "${openlane-src}/shell.nix" { - inherit pkgs; -} \ No newline at end of file + rev = "83ec6c32add40006cc70d951745667d30193f51d"; + sha256 = "sha256-9Xms6eRf3yyaFJVVjk+uPYYM+EDRl4GYAYYUK5QhiLc="; +}; in import "${openlane-src}/shell.nix" {}