diff --git a/.github/workflows/github-actions-docs-test.yml b/.github/workflows/github-actions-docs-test.yml new file mode 100644 index 00000000000..693502c8ff6 --- /dev/null +++ b/.github/workflows/github-actions-docs-test.yml @@ -0,0 +1,34 @@ +name: Docs Tester + +on: + push: + +jobs: + docs-test-job: + name: 'Test docs for Tcl syntax and README' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: true + + - name: Install required package + run: | + sudo apt-get update && sudo apt-get install -y pandoc + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Preprocess files + run: | + cd docs && make preprocess -j${nproc} + - name: Run Tcl syntax parser + run: | + python3 docs/src/test/man_tcl_params.py + - name: Run readme parser + run : | + cd docs && make clean + python3 src/test/readme_check.py diff --git a/CMakeLists.txt b/CMakeLists.txt index faddf8797a1..fd0a19a6d48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,3 +153,27 @@ if(ENABLE_TESTS) endif() add_subdirectory(src) + + +#################################################################### + +# Build man pages (Optional) + +# Use the processor_count command to get the number of cores +include(ProcessorCount) +ProcessorCount(PROCESSOR_COUNT) +message("Number of processor cores: ${PROCESSOR_COUNT}") + +option(BUILD_MAN "Enable building man pages" OFF) +if(BUILD_MAN) + add_custom_target( + man_page ALL + COMMAND make clean && make preprocess && make all -j${PROCESSOR_COUNT} + WORKING_DIRECTORY ${OPENROAD_HOME}/docs + ) + + # Based on ${CMAKE_INSTALL_PREFIX}, we want to go to ${CMAKE_INSTALL_PREFIX}/share/man + set(MANPAGE_DIR ${OPENROAD_HOME}/docs/cat) + install(DIRECTORY ${MANPAGE_DIR} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man) + +endif() \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000000..5f4b7bbeb0b --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,10 @@ +# dont save intermediate results +md/man2/*md +md/man3/*md +html +man +cat + + +# for doc tests +src/test/results diff --git a/docs/Makefile b/docs/Makefile index 465256aec3f..3ec67297280 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,29 +1,164 @@ -# Minimal makefile for Sphinx documentation -# +# Makefile is divided into two parts: +# Pandoc compilation - Manpages +# Sphinx compilation - ReadTheDocs + +# _____ _ _ _____ ____ _____ +# | __ \ /\ | \ | | __ \ / __ \ / ____| +# | |__) / \ | \| | | | | | | | | +# | ___/ /\ \ | . ` | | | | | | | | +# | | / ____ \| |\ | |__| | |__| | |____ +# |_| /_/ \_\_| \_|_____/ \____/ \_____| + +# Define variables +PANDOC = pandoc +NROFF = nroff +SRC_DIR = md +MAN_ROOT_DIR = man +HTML_ROOT_DIR = html +CAT_ROOT_DIR = cat +MAN1 = man1 +MAN2 = man2 +MAN3 = man3 + +# Exclude these set of keywords +EXCLUDE_KEYWORDS = ant cts dft dpl drt fin gpl grt gui ifp mpl \ + mpl2 odb pad par pdn ppl psm rcx rmp rsz sta \ + stt tap upf utl +EXCLUDE_FILES := $(addsuffix .md,$(EXCLUDE_KEYWORDS)) + +MAN1_DIR = $(MAN_ROOT_DIR)/$(MAN1) +SRC1_DIR = $(SRC_DIR)/$(MAN1) +MAN1_FILES = $(wildcard $(SRC1_DIR)/*.md) +MAN1_PAGES = $(patsubst $(SRC1_DIR)/%.md,$(MAN1_DIR)/%.1,$(MAN1_FILES)) +HTML1_DIR = $(HTML_ROOT_DIR)/html1 +HTML1_PAGES = $(patsubst $(SRC1_DIR)/%.md,$(HTML1_DIR)/%.html,$(MAN1_FILES)) +CAT1_DIR = $(CAT_ROOT_DIR)/cat1 +CAT1_PAGES = $(patsubst $(SRC1_DIR)/%.md,$(CAT1_DIR)/%.1,$(MAN1_FILES)) + +MAN2_DIR = $(MAN_ROOT_DIR)/$(MAN2) +SRC2_DIR = $(SRC_DIR)/$(MAN2) +MAN2_FILES = $(wildcard $(SRC2_DIR)/*.md) +MAN2_FILES := $(filter-out $(foreach keyword, $(EXCLUDE_FILES), %$(keyword)), $(MAN2_FILES)) +MAN2_PAGES = $(patsubst $(SRC2_DIR)/%.md,$(MAN2_DIR)/%.2,$(MAN2_FILES)) +HTML2_DIR = $(HTML_ROOT_DIR)/html2 +HTML2_PAGES = $(patsubst $(SRC2_DIR)/%.md,$(HTML2_DIR)/%.html,$(MAN2_FILES)) +CAT2_DIR = $(CAT_ROOT_DIR)/cat2 +CAT2_PAGES = $(patsubst $(SRC2_DIR)/%.md,$(CAT2_DIR)/%.2,$(MAN2_FILES)) + +MAN3_DIR = $(MAN_ROOT_DIR)/$(MAN3) +SRC3_DIR = $(SRC_DIR)/$(MAN3) +MAN3_FILES = $(wildcard $(SRC3_DIR)/*.md) +MAN3_PAGES = $(patsubst $(SRC3_DIR)/%.md,$(MAN3_DIR)/%.3,$(MAN3_FILES)) +HTML3_DIR = $(HTML_ROOT_DIR)/html3 +HTML3_PAGES = $(patsubst $(SRC3_DIR)/%.md,$(HTML3_DIR)/%.html,$(MAN3_FILES)) +CAT3_DIR = $(CAT_ROOT_DIR)/cat3 +CAT3_PAGES = $(patsubst $(SRC3_DIR)/%.md,$(CAT3_DIR)/%.3,$(MAN3_FILES)) + +# Default target +all: doc web cat + +# Target to do symlinks and pandoc-compatible conversion +preprocess: + ./src/scripts/link_readmes.sh && python3 src/scripts/md_roff_compat.py + +# Target to generate all man pages +doc: $(MAN1_PAGES) $(MAN2_PAGES) $(MAN3_PAGES) + +# Target to generate all web pages (changed name to disambiguate from sphinx) +web: $(HTML1_PAGES) $(HTML2_PAGES) $(HTML3_PAGES) + +# Target to generate all cat pages +cat: $(CAT1_PAGES) $(CAT2_PAGES) $(CAT3_PAGES) + @echo $(CAT1_PAGES) + +# Rule to create the man directory +$(MAN1_DIR): + mkdir -p $(MAN1_DIR) +$(MAN2_DIR): + mkdir -p $(MAN2_DIR) +$(MAN3_DIR): + mkdir -p $(MAN3_DIR) +$(HTML1_DIR): + mkdir -p $(HTML1_DIR) +$(HTML2_DIR): + mkdir -p $(HTML2_DIR) +$(HTML3_DIR): + mkdir -p $(HTML3_DIR) +$(CAT1_DIR): + mkdir -p $(CAT1_DIR) +$(CAT2_DIR): + mkdir -p $(CAT2_DIR) +$(CAT3_DIR): + mkdir -p $(CAT3_DIR) + +# Rule to generate a roff file from a corresponding Markdown file +$(MAN1_DIR)/%.1: $(SRC1_DIR)/%.md | $(MAN1_DIR) + $(PANDOC) -s -t man $< -o $@ --quiet +$(MAN2_DIR)/%.2: $(SRC2_DIR)/%.md | $(MAN2_DIR) + $(PANDOC) -s -t man $< -o $@ --quiet +$(MAN3_DIR)/%.3: $(SRC3_DIR)/%.md | $(MAN3_DIR) + $(PANDOC) -s -t man $< -o $@ --quiet + +# Rule to generate a html file from a corresponding roff file +$(HTML1_DIR)/%.html: $(MAN1_DIR)/%.1 | $(HTML1_DIR) + $(PANDOC) -s -o html $< -o $@ --quiet +$(HTML2_DIR)/%.html: $(MAN2_DIR)/%.2 | $(HTML2_DIR) + $(PANDOC) -s -o html $< -o $@ --quiet +$(HTML3_DIR)/%.html: $(MAN3_DIR)/%.3 | $(HTML3_DIR) + $(PANDOC) -s -o html $< -o $@ --quiet + +# Rule to generate a cat file from a corresponding roff file +$(CAT1_DIR)/%.md: $(MAN1_DIR)/%.1 | $(CAT1_DIR) + nroff -man $< | col -b > $@ +$(CAT2_DIR)/%.md: $(MAN2_DIR)/%.2 | $(CAT2_DIR) + nroff -man $< | col -b > $@ +$(CAT3_DIR)/%.md: $(MAN3_DIR)/%.3 | $(CAT3_DIR) + nroff -man $< | col -b > $@ +#$(PANDOC) -s -o markdown $< -o $@ +#sed -i 's/\\\[/\[/g; s/\\]/\]/g; s/\\_/_/g' $@ + +$(CAT1_DIR)/%.1: $(CAT1_DIR)/%.md + mv $< $@ +$(CAT2_DIR)/%.2: $(CAT2_DIR)/%.md + mv $< $@ +$(CAT3_DIR)/%.3: $(CAT3_DIR)/%.md + mv $< $@ + +# Phony targets +.PHONY: all + +# _____ _____ _ _ _____ _ ___ __ +# / ____| __ \| | | |_ _| \ | \ \ / / +# | (___ | |__) | |__| | | | | \| |\ V / +# \___ \| ___/| __ | | | | . ` | > < +# ____) | | | | | |_| |_| |\ |/ . \ +# |_____/|_| |_| |_|_____|_| \_/_/ \_\ -# You can set these variables from the command line, and also -# from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = BUILDDIR = build -# Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) +html: Makefile + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) rm -f main ./revert-links.py - + checklinks: $(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) || echo @echo @echo "Check finished. Report is in $(BUILDDIR)." rm -f main ./revert-links.py + +# Clean target to remove all man pages/Sphinx docs +clean: + rm -rf $(MAN1_DIR) $(MAN2_DIR) $(MAN3_DIR) + rm -rf $(HTML1_DIR) $(HTML2_DIR) $(HTML3_DIR) + rm -rf $(CAT1_DIR) $(CAT2_DIR) $(CAT3_DIR) + rm -rf ./md/man2/*md + rm -rf ./md/man3/*md + rm -rf $(BUILDDIR) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 4a4d4ae1bd2..73bba993db3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,52 @@ # OpenROAD Documentation -This documentation is available at [https://openroad.readthedocs.io/en/latest/](https://openroad.readthedocs.io/en/latest/) +This `docs/` hierarchy houses code and raw files to +build the on-line documentation (using Sphinx) and +manual pages using (using Pandoc) -## Build locally +This on-line documentation is available at [https://openroad.readthedocs.io/en/latest/](https://openroad.readthedocs.io/en/latest/). -### Requires: -- Python 3.x -- Pip -- `virtualenv` +## Prerequisites -### Install prerequisites: +- To install pandoc, refer to this [link](https://github.com/jgm/pandoc/blob/main/INSTALL.md). `apt-get` *should* just work for Ubuntu. +- To install sphinx requirements, **create a virtual environment (e.g. conda/virtualenv)** and then run `pip install -r requirements.txt`. -``` shell -virtualenv .venv -source .venv/bin/activate -pip install -r requirements.txt +### Build instructions for Pandoc manpages + +The `-j16` command is optional for speeding up the manpage compilation process by using multiple jobs +based on the number of cores in your system. + +```shell +make clean + +# Note this step is important as it regenerates the documentation using latest sources. +make preprocess && make all -j16 +``` + +#### To view manpages + +- To run `man` commands inside OpenROAD, you can either use the Linux `man` binary: +```tcl +# create a man wrapper +source man/scripts/main.tcl +man openroad ``` -### Build: +- Or just within OpenROAD itself. +```tcl +# you will be prompted to enter the RELATIVE path to cat folders which is optional. +man openroad +``` + +### Build instructions for Sphinx docs + +#### HTML docs ``` shell make html ``` -### Check for broken links: +#### Check for broken links ``` shell make checklinks diff --git a/docs/conf.py b/docs/conf.py index c24e9619cae..9ab51b2ae12 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,6 +80,10 @@ 'main/src/odb/src/def/doc/README.md', 'main/src/odb/src/lef/README.md', 'main/docs', + 'md', # manpage dir + 'man', # manpage dir + 'cat', # manpage dir + 'html' # manpage dir ] # The name of the Pygments (syntax highlighting) style to use. diff --git a/docs/contrib/DeveloperGuide.md b/docs/contrib/DeveloperGuide.md index a182d174cff..44699025d51 100644 --- a/docs/contrib/DeveloperGuide.md +++ b/docs/contrib/DeveloperGuide.md @@ -134,6 +134,13 @@ examples. Use swig to define internal functions to C++ functionality. Tcl files can be included by encoding them in CMake into a string that is evaluated at run time (See [`Resizer::init()`](../main/src/rsz/src/Resizer.cc)). +:::{Note} +Please refer to the top-level Tcl formatting [guide](TclFormat.md). +Our top-level Tcl files, in particular, have to be formatted in this specific +manner because of the automatic parsing used to convert the READMEs into +manpages. +::: + ## Errors Tools should report errors to the user using the `ord::error` function @@ -234,6 +241,13 @@ toolize [-key1 key1] [-flag1] positional_argument1 Tool commands should be documented in the top-level OpenROAD `README.md` file. Detailed documentation should be the `tool/README.md` file. +:::{Note} +Please refer to the README formatting [guide](ReadmeFormat.md). +Our top-level READMEs, in particular, have to be formatted in this specific +manner because of the automatic parsing used to convert the READMEs into +manpages. +::: + ## Tool Flow Namespace Tool namespaces are usually three-lettered lowercase letters. @@ -291,6 +305,7 @@ dependencies make this vastly more complicated. 1. `regression` script should only write files in a directory that is in the tool's `.gitignore` so the hierarchy does not have modified files in it as a result or running the regressions. 1. Regressions report no memory errors with `valgrind` (stretch goal). 1. Regressions report no memory leaks with `valgrind` (difficult). +1. Ensure the top-level README and Tcl format are compliant. ## Code Linting and Formatting OpenROAD uses both `clang-tidy` and `clang-format` to perform automatic linting and formatting whenever a pull request is submitted. To run these locally, please first setup Clang Tooling using this [guide](https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html). Thereafter, you may run these commands: diff --git a/docs/contrib/ReadmeFormat.md b/docs/contrib/ReadmeFormat.md new file mode 100644 index 00000000000..651bf3cb8f2 --- /dev/null +++ b/docs/contrib/ReadmeFormat.md @@ -0,0 +1,94 @@ +# Tool Name + +The top-level READMEs in each tool folder (`~/OpenROAD/src//README.md`) +has to be formatted in this particular manner. For most part, you can copy +the format and replace the contents where necessary. + +## Commands + +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` + +### Command ABC + +The `command_abc` command performs... + +The developer arguments are... + +Note for commands, you must adhere to the top-level Tcl definition +for the same command. +- Arguments: cannot be preceded with dashes +- Flags/Keys: verify if it is optional or required, then insert the +necessary square brackets. Also, keys have to be followed with a specifier +whereas flags only require the `-flag` itself. + +As far as possible, do place the positional arguments last. + +```tcl +command_abc + -key1 key1 + [-flag1] + [-flagDev] + arg1 +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `arg1` | Description for `arg1`. | +| `-key1` | Description for `key1`. | +| `-flag1` | Description for `flag1`. | + +#### Developer Arguments + +If there are some developer arguments you want to highlight to the end user +not to worry about - you can park them in the same level below the main +`Options` category. + +| Switch Name | Description | +| ----- | ----- | +| `-flagDev` | Description for `flagDev`. | + +## Useful Developer Commands + +If you are a developer, you might find these useful. More details can be found in the [source file]() or the [swig file](). + +| Command Name | Description | +| ----- | ----- | +| `command_abc_debug` | Debug something. | + +## Example scripts + +Examples scripts demonstrating ... + +```shell +./test/asdfg.tcl +``` + +## Regression tests + +There are a set of regression tests in `./test`. Refer to this [section](../../README.md#regression-tests) for more information. + +Simply run the following script: + +```shell +./test/regression +``` + +## Limitations + +## FAQs + +Check out [GitHub discussion]() +about this tool. + +## Authors + +## References + +## License + +BSD 3-Clause License. See [LICENSE](../../LICENSE) file. \ No newline at end of file diff --git a/docs/contrib/TclFormat.md b/docs/contrib/TclFormat.md new file mode 100644 index 00000000000..5b47d962d5a --- /dev/null +++ b/docs/contrib/TclFormat.md @@ -0,0 +1,42 @@ +# Tcl Format + +The most important part to take note of are: +`sta::define_cmd_args` - which defines what is printed when +a user types `help command` in the OR shell; as well as +`sta::parse_key_args` - which defines what keys/flags the +command actually parses. + +Let us use `check_antennas` command for an example. + +## Specification + +```tcl +sta::define_cmd_args "check_antennas" { [-verbose]\ + [-net net]} + +proc check_antennas { args } { + sta::parse_key_args "check_antennas" args \ + keys {-report_file -net} \ + flags {-verbose -report_violating_nets} +... +} +``` + +## Do not compile + +If you add `;# checker off` behind the command's +`sta::define_cmd_args {} ` and `sta::parse_key_args {}` +the function will not be compiled in the Manpages and +included in the doctests. + +```tcl +sta::define_cmd_args "check_antennas" { [-verbose]\ + [-net net]} ;# checker off + +proc check_antennas { args } { + sta::parse_key_args "check_antennas" args \ + keys {-report_file -net} \ + flags {-verbose -report_violating_nets};# checker off +... +} +``` \ No newline at end of file diff --git a/docs/getMessages.py b/docs/getMessages.py index 0b37694c4e7..2bb9e921851 100644 --- a/docs/getMessages.py +++ b/docs/getMessages.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import re command = "python ../etc/find_messages.py -d ../src" output = os.popen(command).read() @@ -22,7 +23,17 @@ msgType = columns[-2] tool = columns[0].lower() try: - message = open(f"../src/{tool}/doc/messages/{num}.md").read().strip() + # aim is to match all level1 header and their corresponding text. + message = open(f"../src/{tool}/doc/messages/{num}.md").read() + pattern = re.compile(r'#\s*(?P[^\n]+)\n*(?P.*?)(?=\n#|$)', re.DOTALL) + matches = pattern.finditer(message) + m = [] + for match in matches: + header1 = match.group('header1') + body_text = match.group('body_text').strip() + m.append(f"{header1}-{body_text}") + message = " ".join(x for x in m) + except OSError as e: message = "-" if not message: message = "-" diff --git a/docs/md/man1/openroad.md b/docs/md/man1/openroad.md new file mode 100644 index 00000000000..d2af1365922 --- /dev/null +++ b/docs/md/man1/openroad.md @@ -0,0 +1,118 @@ +--- +title: openroad(0) +date: 2024/02/09 +--- + +# NAME + +openroad - OpenROAD command-line-interface. + +# SYNOPSIS + +openroad [-help] [-version] [-no_init] [-no_splash] + [-exit] [-gui] [-threads count|max] + [-log file_name] [-metrics file_name] [-python] + cmd_file + + +# DESCRIPTION + +The **openroad** command is the command-line interface for OpenROAD, an open-source initiative for chip design and optimization. It provides a range of options to control its behavior and execute commands specified in the cmd_file. + +# OPTIONS + +`-help`: Display help information. + +`-version`: Display version information. + +`-no_init`: Skip initialization steps. + +`-no_splash`: Do not show the license splash at startup. + +`-exit`: Exit after executing commands in cmd_file. + +`-gui`: Launch the graphical user interface (GUI). + +`-threads count|max`: Specify the number of threads to +use (or use max available). + +`-log file_name`: Log output to the specified file. + +`-metrics file_name`: Save performance metrics to the +specified file. + +`-python`: Execute the cmd_file as a Python script. + +# ARGUMENTS + +`cmd_file`: The command file containing OpenROAD commands to be executed. + +# EXAMPLES + +**openroad -help**: Prints the help information + +**openroad -version -exit**: Prints the version information, and exits the program thereafter. + +**openroad -threads max -log test.log -metrics test.metrics**: Prints the log and metric +respectively to `test.log` and `test.metrics`. Also runs the script with maximum + available number of threads. + +**openroad -gui**: Launches OpenROAD GUI, which further interactive Tcl commands can be run. + +# TCL COMMANDS + +These Tcl-based commands are callable from within the +OpenROAD binary. + +- read_lef [-tech] [-library] filename + + - Read Library Exchange Format (.lef) files. + + - tech: Technology-specific information + + - library: .lib files + +- read_def filename + + - Read Design Exchange Format (.def) files. + +- write_def [-version 5.8|5.7|5.6|5.5|5.4|5.3] filename + - Write Design Exchange Format (.def) files. + + - version 5.8|5.7|5.6|5.5|5.4|5.3: Corresponding DEF + version to use. + +- read_verilog filename + + - Read Verilog (.v) input file. + +- write_verilog filename + + - Write Verilog (.v) file based on current database. + +- read_db filename + + - Read OpenDB (.odb) database files. + +- write_db filename + + - Write OpenDB (.odb) database files. + +- write_abstract_lef filename + + - Write abstract Library Exchange Format (.lef) files + based on current database. + +- write_cdl [-include_fillers] -masters out_filename + + - Write Circuit Design Language (.cdl) netlist file. + +# ENVIRONMENT + +# FILES + +# SEE ALSO + +# HISTORY + +# BUGS diff --git a/docs/src/scripts/extract_utils.py b/docs/src/scripts/extract_utils.py new file mode 100644 index 00000000000..ee4643b9a06 --- /dev/null +++ b/docs/src/scripts/extract_utils.py @@ -0,0 +1,181 @@ +############################################################################### +## +## BSD 3-Clause License +## +## Copyright (c) 2024, The Regents of the University of California +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, this +## list of conditions and the following disclaimer. +## +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and#or other materials provided with the distribution. +## +## * Neither the name of the copyright holder nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +# This code contains the necessary regex parsing functions for manpage compilation. + +import re + +def extract_headers(text, level = 1): + assert isinstance(level, int) and level >= 1 + pattern = r'^#{%d}\s+(.*)$' % level + headers = re.findall(pattern, text, flags=re.MULTILINE) + # TODO: Handle developer commands + #if "Useful Developer Commands" in headers: headers.remove("Useful Developer Commands") + return headers + +def extract_tcl_command(text): + # objective is to extract tcl command from the synopsis + pattern = r'```tcl\s*(.*?)\s' + headers = re.findall(pattern, text, flags=re.MULTILINE) + return headers + +def extract_description(text): + # this is so that it always tries to match the longer headers first, to disambiguate + sorted_headers = sorted(extract_headers(text,3), key=len, reverse=True) + headers = "|".join(re.escape(x) for x in sorted_headers) + pattern = rf'### ({headers})(.*?)```tcl' + custom_strings = re.findall(pattern, text, flags=re.DOTALL) + return [custom_string[1].strip() for custom_string in custom_strings] + +def extract_tcl_code(text): + pattern = r'```tcl\s+(.*?)```' + tcl_code_matches = re.findall(pattern, text, flags=re.DOTALL) + # remove the last tcl match + tcl_code_matches = [x for x in tcl_code_matches if "./test/gcd.tcl" not in x] + return tcl_code_matches + +def extract_arguments(text): + # Goal is to extract all the text from the end of tcl code to the next ### header. + # Returns options and arguments. + level2 = extract_headers(text, 2) + level3 = extract_headers(text, 3) + + # form these 2 regex styles. + # ### Header 1 {text} ### Header2; ### Header n-2 {text} ### Header n-1 + # ### Header n {text} ## closest_level2_header + first = [rf'### ({level3[i]})(.*?)### ({level3[i+1]})' for i in range(len(level3) - 1)] + + # find the next closest level2 header to the last level3 header. + closest_level2 = [text.find(f"## {x}") - text.find(f"### {level3[-1]}") for x in level2] + closest_level2_idx = [idx for idx, x in enumerate(closest_level2) if x > 0][0] + + # This will disambiguate cases where different level headers share the same name. + second = [rf"### ({level3[-1]})(.*?)## ({level2[closest_level2_idx]})"] + final_options, final_args = [], [] + for idx, regex in enumerate(first + second): + match = re.findall(regex, text, flags = re.DOTALL) + #print(regex) + # get text until the next header + a = match[0][1] + a = a[a.find("#"):] + options = a.split("####")[1:] + if not options: + final_options.append([]) + final_args.append([]) + continue + options, args = options[0], options[1:] + final_options.append(extract_tables(options)) + tmp_arg = [] + for arg in args: + tmp_arg.extend(extract_tables(arg)) + final_args.append(tmp_arg) + return final_options, final_args + +def extract_tables(text): + # Find all lines that start with "|" + table_pattern = r'^\s*\|.*$' + table_matches = re.findall(table_pattern, text, flags=re.MULTILINE) + + # Exclude matches containing HTML tags + table_matches = [table for table in table_matches if not re.search(r'<.*?>', table)] + + # Remove text containing switch + table_matches = [table for table in table_matches if "Switch Name" not in table] + + # Remove text containing "---" + table_matches = [table for table in table_matches if "---" not in table] + + return table_matches + +def extract_help(text): + # Logic now captures everything between { to earliest "proc" + help_pattern = re.compile(r''' + sta::define_cmd_args\s+ + "(.*?)"\s* + (.*?)proc\s + ''', + re.VERBOSE | re.DOTALL) + + matches = re.findall(help_pattern, text) + + # remove nodocs (usually dev commands) + matches = [tup for tup in matches if ";#checkeroff" not in tup[1].replace(" ","")] + return matches + +def extract_proc(text): + proc_pattern = re.compile(r''' + sta::parse_key_args\s+ + "(.*?)"\s* + args\s* + (.*?keys.*?}) + (.*?flags.*?}) + (\s*;\s*\#\s*checker\s*off)? + ''', + re.VERBOSE | re.DOTALL) + + matches = re.findall(proc_pattern, text) + + # remove nodocs (usually dev commands) + matches = [tup for tup in matches if not tup[3].replace(" ","") == ";#checkeroff"] + return matches + +def parse_switch(text): + # Find the index of the 1nd and last occurrence of "|". Since some content might contain "|" + switch_name = text.split("|")[1] + switch_name = switch_name.replace("`", "").strip() + second_pipe_index = text.find("|", text.find("|") + 1) + last_pipe_index = text.rfind("|") + switch_description = text[second_pipe_index+1: last_pipe_index-1] + return switch_name, switch_description + +def clean_whitespaces(text): + tmp = text.strip().replace("\\", "").replace("\n", "") + return " ".join(tmp.split()) + +def clean_parse_syntax(text): + tmp = text.replace("keys", "").replace("flags", "")\ + .replace("{", "").replace("}", "") + return ' '.join([f'[{option}]' for option in tmp.split()]) + +def check_function_signatures(text1, text2): + set1 = set(re.findall(r'-\w+', text1)) + set2 = set(re.findall(r'-\w+', text2)) + if set1 == set2: return True + print(sorted(list(set1))) + print(sorted(list(set2))) + return False + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/docs/src/scripts/link_readmes.sh b/docs/src/scripts/link_readmes.sh new file mode 100755 index 00000000000..aec0329e70e --- /dev/null +++ b/docs/src/scripts/link_readmes.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +############################################################################### +## +## BSD 3-Clause License +## +## Copyright (c) 2024, The Regents of the University of California +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, this +## list of conditions and the following disclaimer. +## +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and#or other materials provided with the distribution. +## +## * Neither the name of the copyright holder nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +# This code does the symlink for module-level READMEs to the working folder +# for manpage compilations. + +SRC_BASE_PATH="../src" +DEST_BASE_PATH="./md/man2" +mkdir -p $DEST_BASE_PATH + +# Loop through all folders inside "../src" +for MODULE_PATH in "$SRC_BASE_PATH"/*; do + if [ -d "$MODULE_PATH" ]; then + MODULE=$(basename "$MODULE_PATH") + SRC_PATH=$(realpath $SRC_BASE_PATH/$MODULE/README.md) + DEST_PATH="$(realpath $DEST_BASE_PATH/$MODULE).md" + + # Check if README.md exists before copying + if [ -e "$SRC_PATH" ]; then + ln -s -f "$SRC_PATH" "$DEST_PATH" + echo "File linked successfully." + else + echo "ERROR: README.md not found in $MODULE_PATH" + fi + fi +done diff --git a/docs/src/scripts/manpage.py b/docs/src/scripts/manpage.py new file mode 100644 index 00000000000..c993d2969e8 --- /dev/null +++ b/docs/src/scripts/manpage.py @@ -0,0 +1,139 @@ +############################################################################### +## +## BSD 3-Clause License +## +## Copyright (c) 2024, The Regents of the University of California +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, this +## list of conditions and the following disclaimer. +## +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and#or other materials provided with the distribution. +## +## * Neither the name of the copyright holder nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +# This code describes the ManPage class, which consists of the data classes +# as well as the code needed to write the roff-compatible manpage file. + +import io +import datetime + +# identify key section and stored in ManPage class. +class ManPage(): + def __init__(self, man_level = 2): + assert man_level in [2,3], "only writable for man2/man3" + self.name = "" + self.desc = "" + self.synopsis = "" + self.switches = {} + self.args = {} + self.datetime = datetime.datetime.now().strftime("%y/%m/%d") + self.man_level = f"man{man_level}" + + def write_roff_file(self, dst_dir = './md/man2'): + assert self.name, print("func name not set") + assert self.desc, print("func desc not set") + assert self.synopsis, print("func synopsis not set") + # it is okay for a function to have no switches. + #assert self.switches, print("func switches not set") + filepath = f"{dst_dir}/{self.name}.md" + with open(filepath, "w") as f: + self.write_header(f) + self.write_name(f) + self.write_synopsis(f) + self.write_description(f) + self.write_options(f) + self.write_arguments(f) + self.write_placeholder(f) #TODO. + + def write_header(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"---\n") + f.write(f"title: {self.name}({self.man_level[-1]})\n") + f.write(f"date: {self.datetime}\n") + f.write(f"---\n") + + def write_name(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# NAME\n\n") + f.write(f"{self.name} - {' '.join(self.name.split('_'))}\n") + + def write_synopsis(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# SYNOPSIS\n\n") + f.write(f"{self.synopsis}\n") + + def write_description(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# DESCRIPTION\n\n") + f.write(f"{self.desc}\n") + + def write_options(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# OPTIONS\n") + if not self.switches: + f.write(f"\nThis command has no switches.\n") + for key, val in self.switches.items(): + f.write(f"\n`{key}`: {val}\n") + + def write_arguments(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# ARGUMENTS\n") + if not self.args: + f.write(f"\nThis command has no arguments.\n") + for key, val in self.args.items(): + f.write(f"\n`{key}`: {val}\n") + + + def write_placeholder(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + # TODO: these are all not populated currently, not parseable from docs. + # TODO: Arguments can actually be parsed, but you need to preprocess the synopsis further. + sections = ["EXAMPLES", "SEE ALSO"] + for s in sections: + f.write(f"\n# {s}\n") + + def write_copyright(self, f): + assert isinstance(f, io.TextIOBase) and\ + f.writable(), "File pointer is not open for writing." + + f.write(f"\n# COPYRIGHT\n\n") + f.write(f"Copyright (c) 2024, The Regents of the University of California. All rights reserved.\n") + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/docs/src/scripts/md_roff_compat.py b/docs/src/scripts/md_roff_compat.py new file mode 100644 index 00000000000..9d22349a9ab --- /dev/null +++ b/docs/src/scripts/md_roff_compat.py @@ -0,0 +1,153 @@ +############################################################################### +## +## BSD 3-Clause License +## +## Copyright (c) 2024, The Regents of the University of California +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, this +## list of conditions and the following disclaimer. +## +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and#or other materials provided with the distribution. +## +## * Neither the name of the copyright holder nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +# This code contains the scripts to convert the individual module READMEs +# into individual functions for man2 and man3 level. + +import os +from manpage import ManPage +from extract_utils import extract_tcl_command, extract_description +from extract_utils import extract_tcl_code, extract_arguments +from extract_utils import extract_tables, parse_switch + +# Undocumented manpages. +# sta: documentation is hosted elsewhere. (not currently in RTD also.) +# odb: documentation is hosted on doxygen. + +tools = ["ant", "cts", "dbSta", "dft", "dpl", "dpo", "drt",\ + "dst", "fin", "gpl", "grt", "gui", "ifp", "mpl",\ + "mpl2", "odb", "pad", "par", "pdn", "ppl", "psm",\ + "rcx", "rmp", "rsz", "sta", "stt", "tap", "upf", "utl"] + +# Process man2 (except odb and sta) +DEST_DIR2 = SRC_DIR = "./md/man2" +exclude2 = ["odb", "sta"] +docs2 = [f"{SRC_DIR}/{tool}.md" for tool in tools if tool not in exclude2] + +# Process man3 (add extra path for ORD messages) +SRC_DIR = "../src" +DEST_DIR3 = "./md/man3" +exclude = ["sta"] #sta excluded because its format is different, and no severity level. +docs3 = [f"{SRC_DIR}/{tool}/messages.txt" for tool in tools if tool not in exclude] +docs3.append("../messages.txt") + +def man2(path=DEST_DIR2): + for doc in docs2: + if not os.path.exists(doc): + print(f"{doc} doesn't exist. Continuing") + continue + man2_translate(doc, path) + +def man2_translate(doc, path): + with open(doc) as f: + text = f.read() + # new function names (reading tcl synopsis + convert gui:: to gui_) + func_names = extract_tcl_command(text) + func_names = ["_".join(s.lower().split()) for s in func_names] + func_names = [s.replace("::", "_") for s in func_names] + + # function description + func_descs = extract_description(text) + + # synopsis content + func_synopsis = extract_tcl_code(text) + + # arguments + func_options, func_args = extract_arguments(text) + + print(f'{os.path.basename(doc)}') + print(f'''Names: {len(func_names)},\ + Desc: {len(func_descs)},\ + Syn: {len(func_synopsis)},\ + Options: {len(func_options)},\ + Args: {len(func_args)}''') + assert len(func_names) == len(func_descs) == len(func_synopsis) == len(func_options) == len(func_args),\ + "Counts for all 5 categories must match up." + + for func_id in range(len(func_synopsis)): + manpage = ManPage() + manpage.name = func_names[func_id] + manpage.desc = func_descs[func_id] + manpage.synopsis = func_synopsis[func_id] + if func_options[func_id]: + # convert it to dict + # TODO change this into a function. Or subsume under option/args parsing. + switches_dict = {} + for line in func_options[func_id]: + key, val = parse_switch(line) + switches_dict[key] = val + manpage.switches = switches_dict + + if func_args[func_id]: + # convert it to dict + args_dict = {} + for line in func_args[func_id]: + key, val = parse_switch(line) + args_dict[key] = val + manpage.args = args_dict + + manpage.write_roff_file(path) + +def man3(path=DEST_DIR3): + for doc in docs3: + print(f"Processing {doc}") + if not os.path.exists(doc): + print(f"{doc} doesn't exist. Continuing") + continue + man3_translate(doc, path) + +def man3_translate(doc, path): + _info, _warn, _error = 0, 0, 0 + with open(doc) as f: + for line in f: + parts = line.split() + module, num, message, level = parts[0], parts[1],\ + " ".join(parts[3:-2]), parts[-2] + manpage = ManPage() + manpage.name = f"{module}-{num}" + if "with-total" in manpage.name: print(parts); exit() + manpage.synopsis = "N/A." + manpage.desc = f"Type: {level}\n\n{message}" + manpage.write_roff_file(path) + + # tabulate counts + if level == 'INFO': _info += 1 + elif level == 'WARN': _warn += 1 + elif level == 'ERROR': _error += 1 + print(f"Info: {_info}, Warn: {_warn}, Error: {_error}") + +if __name__ == "__main__": + man2() + man3() \ No newline at end of file diff --git a/docs/src/test/README.md b/docs/src/test/README.md new file mode 100644 index 00000000000..04b3c6aa6fa --- /dev/null +++ b/docs/src/test/README.md @@ -0,0 +1,84 @@ +# Manpages Test Framework + +There are 4 regression tests we conduct for the manpages, namely: +- Translator +- Count output files +- Man functionality check +- Man-to-Tcl check + +There are also 2 CI tests as part of the +They can be found in `.github/workflows/github-actions-docs-test.yml` file. + +## Regression tests + +This is divided into static and dynamic regression tests. +Static tests are designed to test the script functionality, whereas +dynamic tests are designed to test the documentation compatibitity with the +script and will be housed in individual module folders. + +### Static Test + +#### Translator + +The code file can be found [here](translator.py). +The objective of this test is to test if the underlying `README.md` +file can be converted to roff format via regex extraction. Namely, +the script checks for equality in the number of function names, +descriptors, synopsis, options and arguments detected per Tcl command. + +#### Man functionality check + +The code file can be found [here](man_func.tcl). +The objective of this test is to check the functionality of the Tcl +`man` command implemented within the OpenROAD binary. +Mode 1 is where we run `man -manpath `, and mode 2 +is where we do not specify the `-manpath` argument and just run +`man `. + +This check makes sure that the files are compiled in the correct location +and viewable by the `man` command. + +### Dynamic Test + +For all the tests below, do make sure to update it locally every +time you make a change to the `README.md`, update messages, or +make a change to the top-level `Tcl` code. + +#### README-messages check + +The name of this test is `{MODULE}_readme_msgs_check.py`. +The objective of this test is to check the number of +parsed module commands and messages. + +### Man-to-Tcl check + +The name of this test is `{MODULE}_man_tcl_check.py` +The objective of this test is to ensure that there are similar counts of +command in the following: `proc`, `help`, `man`. + +`proc` and `help` commands are parsed from the Tcl file, whereas +`man` commands are parsed from the README file. + +## CI tests + +These two tests ensure that the documents and top-level Tcl files +are formatted properly to catch manpage extraction or compilation errors. + +- Tcl Syntax Parser [code](man_tcl_params.py) +- Readme Syntax Parser [code](readme_parser.py) + +## New Test Checklist + +Adding a new test called `func`, you must create/update the following: +- `func.py|tcl`: Test script. +- `func.ok`: Log output of the test. +- `.*ok`: Ideal file output of the test (can be `def`, `lef` etc). +- `regression_tests.tcl`: Update the name of the test. In this case, `func`. + +## Authors + +Jack Luar (Advisor: Cho Moon) + +## License + +BSD 3-Clause License. See [LICENSE](../../../LICENSE) file. \ No newline at end of file diff --git a/docs/src/test/extract_utils.py b/docs/src/test/extract_utils.py new file mode 120000 index 00000000000..6222d07ceea --- /dev/null +++ b/docs/src/test/extract_utils.py @@ -0,0 +1 @@ +../scripts/extract_utils.py \ No newline at end of file diff --git a/docs/src/test/helpers.py b/docs/src/test/helpers.py new file mode 120000 index 00000000000..e10a2da7588 --- /dev/null +++ b/docs/src/test/helpers.py @@ -0,0 +1 @@ +../../../test/helpers.py \ No newline at end of file diff --git a/docs/src/test/helpers.tcl b/docs/src/test/helpers.tcl new file mode 120000 index 00000000000..509ca07b27e --- /dev/null +++ b/docs/src/test/helpers.tcl @@ -0,0 +1 @@ +../../../test/helpers.tcl \ No newline at end of file diff --git a/docs/src/test/man_func.ok b/docs/src/test/man_func.ok new file mode 100644 index 00000000000..f340c5fb75f --- /dev/null +++ b/docs/src/test/man_func.ok @@ -0,0 +1,216 @@ +openroad(0) openroad(0) + + + +NAME + openroad - OpenROAD command-line-interface. + +SYNOPSIS + openroad [-help] [-version] [-no_init] [-no_splash] [-exit] [-gui] + [-threads count|max] [-log file_name] [-metrics file_name] [-python] + cmd_file + +DESCRIPTION + The openroad command is the command-line interface for OpenROAD, an + open-source initiative for chip design and optimization. It provides a + range of options to control its behavior and execute commands specified + in the cmd_file. + +OPTIONS + -help: Display help information. + + -version: Display version information. + + -no_init: Skip initialization steps. + + -no_splash: Do not show the license splash at startup. + + -exit: Exit after executing commands in cmd_file. + + -gui: Launch the graphical user interface (GUI). + + -threads count|max: Specify the number of threads to use (or use max + available). + + -log file_name: Log output to the specified file. + + -metrics file_name: Save performance metrics to the specified file. + + -python: Execute the cmd_file as a Python script. + +ARGUMENTS + cmd_file: The command file containing OpenROAD commands to be executed. + +EXAMPLES + openroad -help: Prints the help information + + openroad -version -exit: Prints the version information, and exits the + program thereafter. + + openroad -threads max -log test.log -metrics test.metrics: Prints the + log and metric respectively to test.log and test.metrics. Also runs + the script with maximum available number of threads. + + openroad -gui: Launches OpenROAD GUI, which further interactive Tcl + commands can be run. + +TCL COMMANDS + These Tcl-based commands are callable from within the OpenROAD binary. + + • read_lef [-tech] [-library] filename + + • Read Library Exchange Format (.lef) files. + + • tech: Technology-specific information + + • library: .lib files + + • read_def filename + + • Read Design Exchange Format (.def) files. + + • write_def [-version 5.8|5.7|5.6|5.5|5.4|5.3] filename + + • Write Design Exchange Format (.def) files. + + • version 5.8|5.7|5.6|5.5|5.4|5.3: Corresponding DEF version to use. + + • read_verilog filename + • Read Verilog (.v) input file. + + • write_verilog filename + + • Write Verilog (.v) file based on current database. + + • read_db filename + + • Read OpenDB (.odb) database files. + + • write_db filename + + • Write OpenDB (.odb) database files. + + • write_abstract_lef filename + + • Write abstract Library Exchange Format (.lef) files based on cur‐ + rent database. + + • write_cdl [-include_fillers] -masters out_filename + + • Write Circuit Design Language (.cdl) netlist file. + +ENVIRONMENT +FILES +SEE ALSO +HISTORY +BUGS + 2024/02/09 openroad(0) + +openroad(0) openroad(0) + + + +NAME + openroad - OpenROAD command-line-interface. + +SYNOPSIS + openroad [-help] [-version] [-no_init] [-no_splash] [-exit] [-gui] + [-threads count|max] [-log file_name] [-metrics file_name] [-python] + cmd_file + +DESCRIPTION + The openroad command is the command-line interface for OpenROAD, an + open-source initiative for chip design and optimization. It provides a + range of options to control its behavior and execute commands specified + in the cmd_file. + +OPTIONS + -help: Display help information. + + -version: Display version information. + + -no_init: Skip initialization steps. + + -no_splash: Do not show the license splash at startup. + + -exit: Exit after executing commands in cmd_file. + + -gui: Launch the graphical user interface (GUI). + + -threads count|max: Specify the number of threads to use (or use max + available). + + -log file_name: Log output to the specified file. + + -metrics file_name: Save performance metrics to the specified file. + + -python: Execute the cmd_file as a Python script. + +ARGUMENTS + cmd_file: The command file containing OpenROAD commands to be executed. + +EXAMPLES + openroad -help: Prints the help information + + openroad -version -exit: Prints the version information, and exits the + program thereafter. + + openroad -threads max -log test.log -metrics test.metrics: Prints the + log and metric respectively to test.log and test.metrics. Also runs + the script with maximum available number of threads. + + openroad -gui: Launches OpenROAD GUI, which further interactive Tcl + commands can be run. + +TCL COMMANDS + These Tcl-based commands are callable from within the OpenROAD binary. + + • read_lef [-tech] [-library] filename + + • Read Library Exchange Format (.lef) files. + + • tech: Technology-specific information + + • library: .lib files + + • read_def filename + + • Read Design Exchange Format (.def) files. + + • write_def [-version 5.8|5.7|5.6|5.5|5.4|5.3] filename + + • Write Design Exchange Format (.def) files. + + • version 5.8|5.7|5.6|5.5|5.4|5.3: Corresponding DEF version to use. + + • read_verilog filename + • Read Verilog (.v) input file. + + • write_verilog filename + + • Write Verilog (.v) file based on current database. + + • read_db filename + + • Read OpenDB (.odb) database files. + + • write_db filename + + • Write OpenDB (.odb) database files. + + • write_abstract_lef filename + + • Write abstract Library Exchange Format (.lef) files based on cur‐ + rent database. + + • write_cdl [-include_fillers] -masters out_filename + + • Write Circuit Design Language (.cdl) netlist file. + +ENVIRONMENT +FILES +SEE ALSO +HISTORY +BUGS + 2024/02/09 openroad(0) + diff --git a/docs/src/test/man_func.tcl b/docs/src/test/man_func.tcl new file mode 100644 index 00000000000..41a368f4a23 --- /dev/null +++ b/docs/src/test/man_func.tcl @@ -0,0 +1,11 @@ +source "helpers.tcl" +# Tcl man functionality test + +# Test 1) Running man path with set manpath variable. +set test_dir [file dirname [file normalize [info script]]] +set manpath_dir [file normalize [file dirname [file dirname [file normalize $test_dir]]]] +set manpath_dir "$manpath_dir/cat" +set output [man openroad -manpath $manpath_dir -no_query] + +# Test 2) Running manpath with no set manpath variable. +set output [man openroad -no_query] diff --git a/docs/src/test/man_tcl_params.py b/docs/src/test/man_tcl_params.py new file mode 100644 index 00000000000..f7dc2cf9137 --- /dev/null +++ b/docs/src/test/man_tcl_params.py @@ -0,0 +1,78 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc +from extract_utils import clean_whitespaces, clean_parse_syntax, check_function_signatures + +# Test objective: Make sure similar output in all three: help, proc, and readme + +# Store results +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + + for match in matches: + cmd, rest = match[0], match[1] + cmd, rest = clean_whitespaces(cmd), clean_whitespaces(rest) + help_dict[cmd] = rest + + # Proc patterns + matches = extract_proc(content) + + for match in matches: + cmd, keys, flags = match[0], match[1], match[2] + cmd, rest = clean_whitespaces(cmd), clean_whitespaces(keys + " " + flags) + rest = clean_parse_syntax(rest) + proc_dict[cmd] = rest + +for path in glob.glob("./src/*/README.md"): + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + matches = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + + for match in matches: + cmd = match.split()[0] + rest = " ".join(match.split()[1:]) + readme_dict[cmd] = rest + +succeeded = 0 +for cmd in help_dict: + print("----------") + print(cmd) + isValid = True + if cmd not in help_dict: print("command not parsed in help_dict"); isValid = False + if cmd not in proc_dict: print("command not parsed in proc_dict"); isValid = False + if cmd not in readme_dict: print("command not parsed in readme_dict"); isValid = False + + # If invalid, don't need to test further. + assert isValid, "Keys/flags are missing in one of Help/Proc/Readme." + + # Test switches here + s1, s2, s3 = help_dict[cmd], proc_dict[cmd], readme_dict[cmd] + res1, res2, res3 = check_function_signatures(s1,s2), \ + check_function_signatures(s1,s3), \ + check_function_signatures(s2,s3) + assert res1 and res2 and res3, print(f"Help/Proc: {res1}\nHelp/Rdme: {res2}\nProc/Rdme: {res3}") + succeeded += 1 + print("Success.") + +print(f"----------\nSucceeded: {succeeded} out of {len(help_dict)} tests.") diff --git a/docs/src/test/manpage.py b/docs/src/test/manpage.py new file mode 120000 index 00000000000..f6ff8d0e836 --- /dev/null +++ b/docs/src/test/manpage.py @@ -0,0 +1 @@ +../scripts/manpage.py \ No newline at end of file diff --git a/docs/src/test/md_roff_compat.py b/docs/src/test/md_roff_compat.py new file mode 120000 index 00000000000..3dab319e35c --- /dev/null +++ b/docs/src/test/md_roff_compat.py @@ -0,0 +1 @@ +../scripts/md_roff_compat.py \ No newline at end of file diff --git a/docs/src/test/readme_check.py b/docs/src/test/readme_check.py new file mode 100644 index 00000000000..9ee38371486 --- /dev/null +++ b/docs/src/test/readme_check.py @@ -0,0 +1,33 @@ +import os +import sys +from md_roff_compat import man2, man3 + +# Test objective: if the translator script can run without errors for all the docs. +# goal is to return an error if the header cannot be extracted. + +# check man2 +SRC_BASE_PATH = "../src" +DEST_BASE_PATH = "./md/man2" + +# Create the destination directory if it doesn't exist +os.makedirs(DEST_BASE_PATH, exist_ok=True) + +# Loop through all folders inside "../src" +for module_path in os.listdir(SRC_BASE_PATH): + full_module_path = os.path.join(SRC_BASE_PATH, module_path) + + if os.path.isdir(full_module_path): + module = os.path.basename(full_module_path) + src_path = os.path.realpath(os.path.join(SRC_BASE_PATH, module, "README.md")) + dest_path = os.path.realpath(os.path.join(DEST_BASE_PATH, module + ".md")) + + # Check if README.md exists before copying + if os.path.exists(src_path): + # Create a symbolic link from src_path to dest_path + os.symlink(src_path, dest_path) + print(f"File linked successfully.") + else: + print(f"ERROR: README.md not found in {full_module_path}") + +# Run man2 command +man2() \ No newline at end of file diff --git a/docs/src/test/regression b/docs/src/test/regression new file mode 120000 index 00000000000..9dd00c591a9 --- /dev/null +++ b/docs/src/test/regression @@ -0,0 +1 @@ +../../../test/shared/regression \ No newline at end of file diff --git a/docs/src/test/regression_tests.tcl b/docs/src/test/regression_tests.tcl new file mode 100644 index 00000000000..b7f7b62f5c4 --- /dev/null +++ b/docs/src/test/regression_tests.tcl @@ -0,0 +1,4 @@ +record_tests { + translator + man_func +} diff --git a/docs/src/test/save_ok b/docs/src/test/save_ok new file mode 120000 index 00000000000..4f5b707628d --- /dev/null +++ b/docs/src/test/save_ok @@ -0,0 +1 @@ +../../../test/save_ok \ No newline at end of file diff --git a/docs/src/test/translator.md b/docs/src/test/translator.md new file mode 100644 index 00000000000..deb5663ae55 --- /dev/null +++ b/docs/src/test/translator.md @@ -0,0 +1,245 @@ +# Global Placement + +The global placement module in OpenROAD (`gpl`) is based on the open-source +RePlAce tool, from the paper "Advancing Solution Quality and Routability Validation +in Global Placement". + +Features: + +- Analytic and nonlinear placement algorithm. Solves + electrostatic force equations using Nesterov's method. + ([link](https://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf)) +- Verified with various commercial technologies and research enablements using OpenDB + (7/14/16/28/45/55/65nm). +- Verified deterministic solution generation with various compilers and OS. +- Supports Mixed-size placement mode. + +## Commands + +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` + +### Global Placement + +When using the `-timing_driven` flag, `gpl` does a virtual `repair_design` +to find slacks and +weight nets with low slack. It adjusts the worst slacks (modified with +`-timing_driven_nets_percentage`) using a multiplier (modified with +`-timing_driven_net_weight_max`). The multiplier +is scaled from the full value for the worst slack, to 1.0 at the +`timing_driven_nets_percentage` point. Use the `set_wire_rc` command to set +resistance and capacitance of estimated wires used for timing. + +Routability-driven arguments +- They begin with `-routability`. +- `-routability_check_overflow`, `-routability_max_density`, `-routability_max_bloat_iter`, `-routability_max_inflation_iter`, `-routability_target_rc_metric`, `-routability_inflation_ratio_coef`, `-routability_max_inflation_ratio`, `-routability_rc_coefficients` + +Timing-driven arguments +- They begin with `-timing_driven`. +- `-timing_driven_net_reweight_overflow`, `-timing_driven_net_weight_max`, `-timing_driven_nets_percentage` + +```tcl +global_placement + [-timing_driven] + [-routability_driven] + [-disable_timing_driven] + [-disable_routability_driven] + [-skip_initial_place] + [-incremental] + [-bin_grid_count grid_count] + [-density target_density] + [-init_density_penalty init_density_penalty] + [-init_wirelength_coef init_wirelength_coef] + [-min_phi_coef min_phi_conef] + [-max_phi_coef max_phi_coef] + [-reference_hpwl reference_hpwl] + [-overflow overflow] + [-initial_place_max_iter initial_place_max_iter] + [-initial_place_max_fanout initial_place_max_fanout] + [-pad_left pad_left] + [-pad_right pad_right] + [-force_cpu] + [-skip_io] + [-skip_nesterov_place] + [-routability_check_overflow routability_check_overflow] + [-routability_max_density routability_max_density] + [-routability_max_bloat_iter routability_max_bloat_iter] + [-routability_max_inflation_iter routability_max_inflation_iter] + [-routability_target_rc_metric routability_target_rc_metric] + [-routability_inflation_ratio_coef routability_inflation_ratio_coef] + [-routability_max_inflation_ratio routability_max_inflation_ratio] + [-routability_rc_coefficients routability_rc_coefficients] + [-timing_driven_net_reweight_overflow] + [-timing_driven_net_weight_max] + [-timing_driven_nets_percentage] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-timing_driven` | Enable timing-driven mode. See [link](#timing-driven-arguments) for timing-specific arguments. | +| `-routability_driven` | Enable routability-driven mode. See [link](#routability-driven-arguments) for routability-specific arguments. | +| `-skip_initial_place` | Skip the initial placement (Biconjugate gradient stabilized, or BiCGSTAB solving) before Nesterov placement. Initial placement improves HPWL by ~5% on large designs. Equivalent to `-initial_place_max_iter 0`. | +| `-incremental` | Enable the incremental global placement. Users would need to tune other parameters (e.g., `init_density_penalty`) with pre-placed solutions. | +| `-bin_grid_count` | Set bin grid's counts. The internal heuristic defines the default value. Allowed values are integers `[64,128,256,512,...]`. | +| `-density` | Set target density. The default value is `0.7` (i.e., 70%). Allowed values are floats `[0, 1]`. | +| `-init_density_penalty` | Set initial density penalty. The default value is `8e-5`. Allowed values are floats `[1e-6, 1e6]`. | +| `-init_wirelength_coef` | Set initial wirelength coefficient. The default value is `0.25`. Allowed values are floats. | +| `-min_phi_coef` | Set `pcof_min` ($\mu_k$ Lower Bound). The default value is `0.95`. Allowed values are floats `[0.95, 1.05]`. | +| `-max_phi_coef` | Set `pcof_max` ($\mu_k$ Upper Bound). Default value is 1.05. Allowed values are `[1.00-1.20, float]`. | +| `-overflow` | Set target overflow for termination condition. The default value is `0.1`. Allowed values are floats `[0, 1]`. | +| `-initial_place_max_iter` | Set maximum iterations in the initial place. The default value is 20. Allowed values are integers `[0, MAX_INT]`. | +| `-initial_place_max_fanout` | Set net escape condition in initial place when $fanout \geq initial\_place\_max\_fanout$. The default value is 200. Allowed values are integers `[1, MAX_INT]`. | +| `-pad_left` | Set left padding in terms of number of sites. The default value is 0, and the allowed values are integers `[1, MAX_INT]` | +| `-pad_right` | Set right padding in terms of number of sites. The default value is 0, and the allowed values are integers `[1, MAX_INT]` | +| `-force_cpu` | Force to use the CPU solver even if the GPU is available. | +| `-skip_io` | Flag to ignore the IO ports when computing wirelength during placement. The default value is False, allowed values are boolean. | + +#### Routability-Driven Arguments + +| Switch Name | Description | +| ----- | ----- | +| `-routability_check_overflow` | Set overflow threshold for routability mode. The default value is `0.2`, and the allowed values are floats `[0, 1]`. | +| `-routability_max_density` | Set density threshold for routability mode. The default value is `0.99`, and the allowed values are floats `[0, 1]`. | +| `-routability_max_bloat_iter` | Set bloat iteration threshold for routability mode. The default value is `1`, and the allowed values are integers `[1, MAX_INT]`.| +| `-routability_max_inflation_iter` | Set inflation iteration threshold for routability mode. The default value is `4`, and the allowed values are integers `[1, MAX_INT]`. | +| `-routability_target_rc_metric` | Set target RC metric for routability mode. The default value is `1.25`, and the allowed values are floats. | +| `-routability_inflation_ratio_coef` | Set inflation ratio coefficient for routability mode. The default value is `2.5`, and the allowed values are floats. | +| `-routability_max_inflation_ratio` | Set inflation ratio threshold for routability mode. The default value is `2.5`, and the allowed values are floats. | +| `-routability_rc_coefficients` | Set routability RC coefficients. It comes in the form of a Tcl List `{k1, k2, k3, k4}`. The default value for each coefficient is `{1.0, 1.0, 0.0, 0.0}` respectively, and the allowed values are floats. | + +#### Timing-Driven Arguments + +| Switch Name | Description | +| ----- | ----- | +| `-timing_driven_net_reweight_overflow` | Set overflow threshold for timing-driven net reweighting. Allowed value is a Tcl list of integers where each number is `[0, 100]`. | +| `-timing_driven_net_weight_max` | Set the multiplier for the most timing-critical nets. The default value is `1.9`, and the allowed values are floats. | +| `-timing_driven_nets_percentage` | Set the reweighted percentage of nets in timing-driven mode. The default value is 10. Allowed values are floats `[0, 100]`. | + +### Cluster Flops + +This command does flop clustering based on parameters. + +```tcl +cluster_flops + [-tray_weight tray_weight]\ + [-timing_weight timing_weight]\ + [-max_split_size max_split_size]\ + [-num_paths num_paths] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-tray_weight` | Tray weight, default value is 20.0, type `float`. | +| `-timing_weight` | Timing weight, default value is 1.0, type `float`. | +| `-max_split_size` | Maximum split size, default value is -1, type `int`.| +| `-num_paths` | KIV, default value is 0, type `int`. | + + +## Useful Developer Commands + +If you are a developer, you might find these useful. More details can be found in the [source file](./src/replace.cpp) or the [swig file](./src/replace.i). + +``` +# debugging global placement +global_placement_debug -pause -update -inst -draw_bins -initial + +# adds padding and gets global placement uniform target density +get_global_placement_uniform_density -pad_left -pad_right +``` + +Example scripts demonstrating how to run `gpl` on a sample design on `core01` as follows: + +```shell +./test/core01.tcl +``` + +## Regression tests + +There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). + +Simply run the following script: + +```shell +./test/regression +``` + +## Limitations + +## Using the Python interface to gpl + +This API tries to stay close to the API defined in `C++` class `Replace` +that is located [here](include/gpl/Replace.h). + +When initializing a design, a sequence of Python commands might look like +the following: + +```python +from openroad import Design, Tech +tech = Tech() +tech.readLef(...) +design = Design(tech) +design.readDef(...) +gpl = design.getReplace() +``` + +Here is an example of some options / configurations to the global placer. +(See [Replace.h](include/gpl/Replace.h) for a complete list) + +```python +gpl.setInitialPlaceMaxIter(iter) +gpl.setSkipIoMode(skip_io) +gpl.setTimingDrivenMode(timing_driven) +gpl.setTimingNetWeightMax(weight) +``` + +There are some useful Python functions located in the file +[grt_aux.py](test/grt_aux.py) but these are not considered a part of the *final* +API and they may change. + +## FAQs + +Check out [GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/discussions/categories/q-a?discussions_q=category%3AQ%26A+replace+in%3Atitle) +about this tool. + +## References + +- C.-K. Cheng, A. B. Kahng, I. Kang and L. Wang, "RePlAce: Advancing + Solution Quality and Routability Validation in Global Placement", IEEE + Transactions on Computer-Aided Design of Integrated Circuits and Systems, + 38(9) (2019), pp. 1717-1730. [(.pdf)](https://vlsicad.ucsd.edu/Publications/Journals/j126.pdf) +- J. Lu, P. Chen, C.-C. Chang, L. Sha, D. J.-H. Huang, C.-C. Teng and + C.-K. Cheng, "ePlace: Electrostatics based Placement using Fast Fourier + Transform and Nesterov's Method", ACM TODAES 20(2) (2015), article 17. [(.pdf)](https://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) +- J. Lu, H. Zhuang, P. Chen, H. Chang, C.-C. Chang, Y.-C. Wong, L. Sha, + D. J.-H. Huang, Y. Luo, C.-C. Teng and C.-K. Cheng, "ePlace-MS: + Electrostatics based Placement for Mixed-Size Circuits", IEEE TCAD 34(5) + (2015), pp. 685-698. [(.pdf)](https://cseweb.ucsd.edu/~jlu/papers/eplace-ms-tcad14/paper.pdf) +- A. B. Kahng, J. Li and L. Wang, + "Improved Flop Tray-Based Design Implementation for Power Reduction", + IEEE/ACM ICCAD, 2016, pp. 20:1-20:8. +- The timing-driven mode has been implemented by + Mingyu Woo (only available in [legacy repo in standalone + branch](https://github.com/The-OpenROAD-Project/RePlAce/tree/standalone).) +- The routability-driven mode has been implemented by Mingyu Woo. +- Timing-driven mode re-implementation is ongoing with the current + clean-code structure. + + ## Authors + +- Authors/maintainer since Jan 2020: Mingyu Woo (Ph.D. Advisor: + Andrew. B. Kahng) +- Original open-sourcing of RePlAce: August 2018, by Ilgweon Kang + (Ph.D. Advisor: Chung-Kuan Cheng), Lutong Wang (Ph.D. Advisor: Andrew + B. Kahng), and Mingyu Woo (Ph.D. Advisor: Andrew B. Kahng). +- Also thanks to Dr. Jingwei Lu for open-sourcing the previous + ePlace-MS/ePlace project code. + +## License + +BSD 3-Clause License. See [LICENSE](LICENSE) file. \ No newline at end of file diff --git a/docs/src/test/translator.ok b/docs/src/test/translator.ok new file mode 100644 index 00000000000..c276fa3f4e3 --- /dev/null +++ b/docs/src/test/translator.ok @@ -0,0 +1,3 @@ +translator.md +Names: 2, Desc: 2, Syn: 2, Options: 2, Args: 2 +Info: 70, Warn: 10, Error: 14 diff --git a/docs/src/test/translator.py b/docs/src/test/translator.py new file mode 100644 index 00000000000..a60b9759c71 --- /dev/null +++ b/docs/src/test/translator.py @@ -0,0 +1,8 @@ +import os +from md_roff_compat import man2_translate, man3_translate + +# Test objective: if the translator functionality works. +res_dir = os.path.join(os.getcwd(), "results/docs") +os.makedirs(res_dir, exist_ok = True) +man2_translate("translator.md", res_dir) +man3_translate("translator.txt", res_dir) \ No newline at end of file diff --git a/docs/src/test/translator.txt b/docs/src/test/translator.txt new file mode 100644 index 00000000000..62d09b68dec --- /dev/null +++ b/docs/src/test/translator.txt @@ -0,0 +1,94 @@ +GPL 0002 placerBase.cpp:778 DBU: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L778 +GPL 0003 placerBase.cpp:806 SiteSize: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L806 +GPL 0004 placerBase.cpp:807 CoreAreaLxLy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L807 +GPL 0005 placerBase.cpp:808 CoreAreaUxUy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L808 +GPL 0006 placerBase.cpp:1263 NumInstances: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1263 +GPL 0007 placerBase.cpp:1267 NumPlaceInstances: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1267 +GPL 0008 placerBase.cpp:1268 NumFixedInstances: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1268 +GPL 0009 placerBase.cpp:1269 NumDummyInstances: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1269 +GPL 0010 placerBase.cpp:1270 NumNets: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1270 +GPL 0011 placerBase.cpp:1271 NumPins: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1271 +GPL 0012 placerBase.cpp:1273 DieAreaLxLy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1273 +GPL 0013 placerBase.cpp:1274 DieAreaUxUy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1274 +GPL 0014 placerBase.cpp:1275 CoreAreaLxLy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1275 +GPL 0015 placerBase.cpp:1276 CoreAreaUxUy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1276 +GPL 0016 placerBase.cpp:1282 CoreArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1282 +GPL 0017 placerBase.cpp:1283 NonPlaceInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1283 +GPL 0018 placerBase.cpp:1285 PlaceInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1285 +GPL 0019 placerBase.cpp:1286 Util(%): {:.2f} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1286 +GPL 0020 placerBase.cpp:1288 StdInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1288 +GPL 0021 placerBase.cpp:1289 MacroInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1289 +GPL 0023 nesterovBase.cpp:614 TargetDensity: {:.2f} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L614 +GPL 0024 nesterovBase.cpp:615 AveragePlaceInstArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L615 +GPL 0025 nesterovBase.cpp:616 IdealBinArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L616 +GPL 0026 nesterovBase.cpp:617 IdealBinCnt: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L617 +GPL 0027 nesterovBase.cpp:618 TotalBinArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L618 +GPL 0028 nesterovBase.cpp:648 BinCnt: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L648 +GPL 0029 nesterovBase.cpp:653 BinSize: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L653 +GPL 0030 nesterovBase.cpp:670 NumBins: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L670 +GPL 0031 nesterovBase.cpp:1292 FillerInit: NumGCells: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1292 +GPL 0032 nesterovBase.cpp:1293 FillerInit: NumGNets: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1293 +GPL 0033 nesterovBase.cpp:1294 FillerInit: NumGPins: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1294 +GPL 0034 nesterovBase.cpp:1651 gCellFiller: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1651 +GPL 0035 nesterovBase.cpp:1669 NewTotalFillerArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1669 +GPL 0036 routeBase.cpp:149 TileLxLy: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L149 +GPL 0037 routeBase.cpp:150 TileSize: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L150 +GPL 0038 routeBase.cpp:151 TileCnt: {} {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L151 +GPL 0039 routeBase.cpp:152 numRoutingLayers: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L152 +GPL 0040 routeBase.cpp:174 NumTiles: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L174 +GPL 0045 routeBase.cpp:605 InflatedAreaDelta: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L605 +GPL 0046 routeBase.cpp:606 TargetDensity: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L606 +GPL 0047 routeBase.cpp:624 SavedMinRC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L624 +GPL 0048 routeBase.cpp:625 SavedTargetDensity: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L625 +GPL 0049 routeBase.cpp:637 WhiteSpaceArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L637 +GPL 0050 routeBase.cpp:638 NesterovInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L638 +GPL 0051 routeBase.cpp:639 TotalFillerArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L639 +GPL 0052 routeBase.cpp:640 TotalGCellsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L640 +GPL 0053 routeBase.cpp:644 ExpectedTotalGCellsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L644 +GPL 0054 routeBase.cpp:663 NewTargetDensity: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L663 +GPL 0055 routeBase.cpp:664 NewWhiteSpaceArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L664 +GPL 0056 routeBase.cpp:665 MovableArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L665 +GPL 0057 routeBase.cpp:666 NewNesterovInstsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L666 +GPL 0058 routeBase.cpp:668 NewTotalFillerArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L668 +GPL 0059 routeBase.cpp:669 NewTotalGCellsArea: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L669 +GPL 0063 routeBase.cpp:735 TotalRouteOverflowH2: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L735 +GPL 0064 routeBase.cpp:736 TotalRouteOverflowV2: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L736 +GPL 0065 routeBase.cpp:737 OverflowTileCnt2: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L737 +GPL 0066 routeBase.cpp:792 0.5%RC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L792 +GPL 0067 routeBase.cpp:793 1.0%RC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L793 +GPL 0068 routeBase.cpp:794 2.0%RC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L794 +GPL 0069 routeBase.cpp:795 5.0%RC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L795 +GPL 0070 routeBase.cpp:797 0.5rcK: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L797 +GPL 0071 routeBase.cpp:798 1.0rcK: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L798 +GPL 0072 routeBase.cpp:799 2.0rcK: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L799 +GPL 0073 routeBase.cpp:800 5.0rcK: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L800 +GPL 0074 routeBase.cpp:808 FinalRC: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L808 +GPL 0075 routeBase.cpp:821 Routability numCall: {} inflationIterCnt: {} bloatIterCnt: {} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/routeBase.cpp#L821 +GPL 0100 timingBase.cpp:163 worst slack {:.3g} INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/timingBase.cpp#L163 +GPL 0102 timingBase.cpp:166 No slacks found. Timing-driven mode disabled. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/timingBase.cpp#L166 +GPL 0103 timingBase.cpp:204 Weighted {} nets. INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/timingBase.cpp#L204 +GPL 0114 timingBase.cpp:153 No net slacks found. Timing-driven mode disabled. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/timingBase.cpp#L153 +GPL 0115 replace.tcl:146 -disable_timing_driven is deprecated. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L146 +GPL 0116 replace.tcl:158 -disable_routability_driven is deprecated. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L158 +GPL 0118 placerBase.cpp:797 core area outside of die. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L797 +GPL 0119 placerBase.cpp:840 instance {} height is larger than core. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L840 +GPL 0120 placerBase.cpp:844 instance {} width is larger than core. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L844 +GPL 0121 replace.tcl:118 No liberty libraries found. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L118 +GPL 0122 mbff.cpp:164 {} is not in 2^[0,{}] ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/mbff.cpp#L164 +GPL 0130 replace.tcl:317 No rows defined in design. Use initialize_floorplan to add rows. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L317 +GPL 0131 replace.tcl:419 No rows defined in design. Use initialize_floorplan to add rows. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L419 +GPL 0132 replace.cpp:186 Locked {} instances INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.cpp#L186 +GPL 0133 replace.cpp:203 Unlocked instances INFO https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.cpp#L203 +GPL 0134 placerBase.cpp:109 Master {} is not marked as a BLOCK in LEF but is more WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L109 +GPL 0135 replace.tcl:180 Target density must be in \[0, 1\]. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L180 +GPL 0136 replace.cpp:290 No placeable instances - skipping placement. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.cpp#L290 +GPL 0150 replace.tcl:122 -skip_io will disable timing driven mode. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L122 +GPL 0151 replace.tcl:153 -skip_io will disable routability driven mode. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/replace.tcl#L153 +GPL 0250 initialPlace.cpp:104 GPU is not available. CPU solve is being used. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/initialPlace.cpp#L104 +GPL 0251 initialPlace.cpp:109 CPU solver is forced to be used. WARN https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/initialPlace.cpp#L109 +GPL 0301 placerBase.cpp:1292 Utilization exceeds 100%. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L1292 +GPL 0302 nesterovBase.cpp:1401 Use a higher -density or ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1401 +GPL 0303 nesterovBase.cpp:1633 Use a higher -density or ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovBase.cpp#L1633 +GPL 0304 nesterovPlace.cpp:267 RePlAce diverged at initial iteration with steplength being {}. ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/nesterovPlace.cpp#L267 +GPL 0305 placerBase.cpp:791 Unable to find a site ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/placerBase.cpp#L791 +GPL 9032 mbff.cpp:546 Could not recognize port {} ERROR https://github.com/The-OpenROAD-Project/OpenROAD/tree/master/workspace/src/gpl/src/mbff.cpp#L546 \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml index 4644fbc976d..eace34d1d5d 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -92,6 +92,10 @@ entries: title: Logger - file: contrib/CI title: CI + - file: contrib/ReadmeFormat + title: README Format + - file: contrib/TclFormat + title: Tcl Format - file: main/CODE_OF_CONDUCT title: Code of Conduct diff --git a/etc/Build.sh b/etc/Build.sh index d4b0aba1aa2..0178baa3223 100755 --- a/etc/Build.sh +++ b/etc/Build.sh @@ -43,6 +43,7 @@ OPTIONS: -coverage Enable cmake coverage options -clean Remove build dir before compile -no-gui Disable GUI support + -build-man Build Man Pages (optional) -threads=NUM_THREADS Number of threads to use during compile. Default: \`nproc\` on linux or \`sysctl -n hw.logicalcpu\` on macOS @@ -72,6 +73,9 @@ while [ "$#" -gt 0 ]; do -no-gui) cmakeOptions+=" -DBUILD_GUI=OFF" ;; + -build-man) + cmakeOptions+=" -DBUILD_MAN=ON" + ;; -compiler=*) compiler="${1#*=}" ;; diff --git a/etc/DependencyInstaller.sh b/etc/DependencyInstaller.sh index 91daea35eac..430de400958 100755 --- a/etc/DependencyInstaller.sh +++ b/etc/DependencyInstaller.sh @@ -246,6 +246,7 @@ _installUbuntuPackages() { g++ \ gcc \ git \ + groff \ lcov \ libffi-dev \ libgomp1 \ @@ -254,6 +255,7 @@ _installUbuntuPackages() { libpcre3-dev \ libreadline-dev \ libtcl \ + pandoc \ python3-dev \ qt5-image-formats-plugins \ tcl \ @@ -296,6 +298,9 @@ _installRHELCleanUp() { } _installRHELPackages() { + arch=amd64 + version=3.1.11.1 + yum -y update if [[ $(yum repolist | egrep -c "rhel-8-for-x86_64-appstream-rpms") -eq 0 ]]; then yum -y install http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os/Packages/centos-gpg-keys-8-6.el8.noarch.rpm @@ -344,6 +349,10 @@ _installRHELPackages() { yum install -y \ http://repo.okay.com.mx/centos/8/x86_64/release/bison-3.0.4-10.el8.x86_64.rpm \ https://forensics.cert.org/centos/cert/7/x86_64/flex-2.6.1-9.el7.x86_64.rpm + + wget https://github.com/jgm/pandoc/releases/download/${version}/pandoc-${version}-linux-${arch}.tar.gz &&\ + tar xvzf pandoc-${version}-linux-${arch}.tar.gz --strip-components 1 -C /usr/local/ &&\ + rm -rf pandoc-${version}-linux-${arch}.tar.gz } _installCentosCleanUp() { @@ -368,11 +377,13 @@ _installCentosPackages() { yum install -y \ devtoolset-8 \ devtoolset-8-libatomic-devel \ + groff \ libffi-devel \ libgomp \ libstdc++ \ llvm-toolset-7.0 \ llvm-toolset-7.0-libomp-devel \ + pandoc \ pcre-devel \ pcre2-devel \ python-devel \ @@ -414,6 +425,7 @@ _installOpenSusePackages() { gcc \ gcc11-c++ \ git \ + groff \ gzip \ lcov \ libffi-devel \ @@ -425,6 +437,7 @@ _installOpenSusePackages() { libqt5-qtstyleplugins \ libstdc++6-devel-gcc8 \ llvm \ + pandoc \ pcre-devel \ pcre2-devel \ python3-devel \ @@ -477,7 +490,7 @@ Then, rerun this script. EOF exit 1 fi - brew install bison boost cmake eigen flex libomp pyqt5 python swig tcl-tk zlib + brew install bison boost cmake eigen flex groff libomp pandoc pyqt5 python swig tcl-tk zlib # Some systems neeed this to correclty find OpenMP package during build brew link --force libomp @@ -513,6 +526,7 @@ _installDebianPackages() { g++ \ gcc \ git \ + groff \ lcov \ libgomp1 \ libomp-dev \ @@ -520,6 +534,7 @@ _installDebianPackages() { libpcre3-dev \ libreadline-dev \ libtcl \ + pandoc \ python3-dev \ qt5-image-formats-plugins \ tcl-dev \ diff --git a/etc/find_messages.py b/etc/find_messages.py index 40a87a3fd48..9416eb65d6a 100755 --- a/etc/find_messages.py +++ b/etc/find_messages.py @@ -83,6 +83,7 @@ def parse_args(): (?P("((?:[^"\\]|\\.)+?\s*)+" )) # message ''', re.VERBOSE | re.MULTILINE) + warn_regexp_tcl = \ re.compile(r''' (?Pinfo|warn|error|critical) # type @@ -109,6 +110,7 @@ def scan_file(path, file_name, msgs): # remove quotes and join strings message = match.group('message') + message = message.replace("\n", "") message = message.rstrip()[1:-1] message = re.sub(r'"\s*"', '', message) message_type = match.group('type').upper() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 842b6bfd178..52324fa3efc 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -468,6 +468,11 @@ endif() # executable install(TARGETS openroad DESTINATION bin) + +if(BUILD_MAN) + install(DIRECTORY ${MANPAGE_DIR} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man) +endif() + ################################################################ add_custom_target(openroad_tags etags -o TAGS diff --git a/src/Main.cc b/src/Main.cc index a2d2e3fca1c..c7edc7979c3 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -468,8 +468,8 @@ int ord::tclAppInit(Tcl_Interp* interp) static void showUsage(const char* prog, const char* init_filename) { - printf("Usage: %s [-help] [-version] [-no_init] [-exit] [-gui] ", prog); - printf("[-threads count|max] [-log file_name] [-metrics file_name] "); + printf("Usage: %s [-help] [-version] [-no_init] [-no_splash] [-exit] ", prog); + printf("[-gui] [-threads count|max] [-log file_name] [-metrics file_name] "); printf("cmd_file\n"); printf(" -help show help and exit\n"); printf(" -version show version and exit\n"); diff --git a/src/ant/README.md b/src/ant/README.md index 5c3afde6583..4077a5081b8 100644 --- a/src/ant/README.md +++ b/src/ant/README.md @@ -29,6 +29,8 @@ Antenna violations can be repaired after global routing with the `repair_design` ### Check Antennas +The `check_antennas` command will check for antenna violations. + ```tcl check_antennas [-net net] @@ -62,16 +64,16 @@ about this tool. ## Algorithm | | | -|:--:|:--:| -| Antenna Checker Algorithm: WireGraph Example | Step 1: (a) Start from the root node (ITerm) using upper Via to find a node for a new wire. (b) Save the ITerm area for cumulative gate/diffusion area. | +| --- | --- | +| **Antenna Checker Algorithm**: WireGraph Example | **Step 1**: (a) Start from the root node (ITerm) using upper Via to find a node for a new wire. (b) Save the ITerm area for cumulative gate/diffusion area. | | | | -| Step 2: From the node of the wire, find all the nodes in the wire through segment wires and find the "root" node of this wire. | Step 3: (a) From the "root" node of the wire, along the outgoing segment edge that goes to other nodes belonging to this wire, calculate the area of this wire. (b) Then, find all the ITerms below these nodes, except for the root node (directly use an ITerm or lower Vias to find ITerms for lower metals). (c) Sum up the areas of all the ITerms found with the cumulative areas and calculate the PAR of this wire. (d) Add the PAR value and the wire info (layer, Index) into the PAR table. Add the new area to the cumulative areas. | +| **Step 2**: From the node of the wire, find all the nodes in the wire through segment wires and find the "root" node of this wire. | **Step 3**: (a) From the "root" node of the wire, along the outgoing segment edge that goes to other nodes belonging to this wire, calculate the area of this wire. (b) Then, find all the ITerms below these nodes, except for the root node (directly use an ITerm or lower Vias to find ITerms for lower metals). (c) Sum up the areas of all the ITerms found with the cumulative areas and calculate the PAR of this wire. (d) Add the PAR value and the wire info (layer, Index) into the PAR table. Add the new area to the cumulative areas. | | | | -| Step 4: Find all the upper Vias on this wire (for all the nodes on this wire), and go to the higher-level metal. | Step 5: Repeat Steps 2 and 3 for new-found upper-level wires. | +| **Step 4**: Find all the upper Vias on this wire (for all the nodes on this wire), and go to the higher-level metal. | **Step 5**: Repeat Steps 2 and 3 for new-found upper-level wires. | | | | -| Step 6: Repeat Steps 4 and 5 until we reach a wire that cannot have upper Vias for its nodes (highest-level metal). | Step 7: Pick up another ITerm as a root node and repeat Steps 1 to 6, skipping the wires already in the PAR table. Repeat this for all the ITerms to get a whole PAR table. | +| **Step 6**: Repeat Steps 4 and 5 until we reach a wire that cannot have upper Vias for its nodes (highest-level metal). | **Step 7**: Pick up another ITerm as a root node and repeat Steps 1 to 6, skipping the wires already in the PAR table. Repeat this for all the ITerms to get a whole PAR table. | | | -| Step 8: (a) Pick up a gate ITerm and a node of a wire (e.g., M4,1). Find possible paths that connect them, look up the PAR value of the wires along these paths, and add them up to get the CAR of the (gate, wire) pair. (b) Compare to the AntennaRule to see if the CAR violates the rules. (c) Check this for all (gate, wire) pairs. | +| **Step 8**: (a) Pick up a gate ITerm and a node of a wire (e.g., M4,1). Find possible paths that connect them, look up the PAR value of the wires along these paths, and add them up to get the CAR of the (gate, wire) pair. (b) Compare to the AntennaRule to see if the CAR violates the rules. (c) Check this for all (gate, wire) pairs. | ## License diff --git a/src/ant/src/AntennaChecker.tcl b/src/ant/src/AntennaChecker.tcl index a7802aca164..d9c60239ce0 100644 --- a/src/ant/src/AntennaChecker.tcl +++ b/src/ant/src/AntennaChecker.tcl @@ -30,12 +30,13 @@ # POSSIBILITY OF SUCH DAMAGE. sta::define_cmd_args "check_antennas" { [-verbose]\ - [-net net]} + [-net net] +};# checker off proc check_antennas { args } { sta::parse_key_args "check_antennas" args \ keys {-report_file -net} \ - flags {-verbose -report_violating_nets} + flags {-verbose -report_violating_nets};# checker off sta::check_argc_eq0 "check_antennas" $args diff --git a/src/ant/test/ant_man_tcl_check.ok b/src/ant/test/ant_man_tcl_check.ok new file mode 100644 index 00000000000..6757efec626 --- /dev/null +++ b/src/ant/test/ant_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/ant 0 0 1 +Command counts do not match. diff --git a/src/ant/test/ant_man_tcl_check.py b/src/ant/test/ant_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/ant/test/ant_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/ant/test/ant_readme_msgs_check.ok b/src/ant/test/ant_readme_msgs_check.ok new file mode 100644 index 00000000000..bb0f872b511 --- /dev/null +++ b/src/ant/test/ant_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 2, Warn: 4, Error: 3 diff --git a/src/ant/test/ant_readme_msgs_check.py b/src/ant/test/ant_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/ant/test/ant_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/ant/test/extract_utils.py b/src/ant/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/ant/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/ant/test/manpage.py b/src/ant/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/ant/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/ant/test/md_roff_compat.py b/src/ant/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/ant/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/ant/test/regression_tests.tcl b/src/ant/test/regression_tests.tcl index 67e264e8a56..5f2a1ae465e 100644 --- a/src/ant/test/regression_tests.tcl +++ b/src/ant/test/regression_tests.tcl @@ -4,4 +4,6 @@ record_tests { check_grt1 ant_check ant_report + ant_readme_msgs_check + ant_man_tcl_check } diff --git a/src/cts/README.md b/src/cts/README.md index e155ef60ad1..4350eaa70d7 100644 --- a/src/cts/README.md +++ b/src/cts/README.md @@ -16,6 +16,9 @@ command. Use `set_wire_rc` command to set the clock routing layer. ### Configure CTS Characterization +Configure key CTS characterization parameters, for example maximum slew and capacitance, +as well as the number of steps they will be divided for characterization. + ```tcl configure_cts_characterization [-max_slew max_slew] @@ -35,22 +38,29 @@ configure_cts_characterization ### Clock Tree Synthesis +Perform clock tree synthesis. + ```tcl clock_tree_synthesis - -buf_list - [-root_buf root_buf] [-wire_unit wire_unit] + [-buf_list ] + [-root_buf root_buf] [-clk_nets ] + [-tree_buf ] [-distance_between_buffers] [-branching_point_buffers_distance] [-clustering_exponent] [-clustering_unbalance_ratio] - [-sink_clustering_enable] [-sink_clustering_size cluster_size] [-sink_clustering_max_diameter max_diameter] + [-sink_clustering_enable] [-balance_levels] + [-sink_clustering_levels levels] [-num_static_layers] [-sink_clustering_buffer] + [-obstruction_aware] + [-apply_ndr] + [-insertion_delay] [-use_dummy_load] [-insertion_delay] [-sink_buffer_max_cap_derate derate_value] @@ -85,9 +95,7 @@ clock_tree_synthesis ### Report CTS -Another command available from `cts` is `report_cts`. It is used to -extract metrics after a successful `clock_tree_synthesis` run. These are: - +This command is used to extract the following metrics after a successful `clock_tree_synthesis` run. - Number of Clock Roots - Number of Buffers Inserted - Number of Clock Subnets @@ -104,7 +112,7 @@ report_cts | ----- | ----- | | `-out_file` | The file to save `cts` reports. If this parameter is omitted, the report is streamed to `stdout` and not saved. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/TritonCTS.cpp) or the [swig file](./src/TritonCTS.i). @@ -114,7 +122,7 @@ If you are a developer, you might find these useful. More details can be found i ## Example scripts -```tcl +``` clock_tree_synthesis -root_buf "BUF_X4" \ -buf_list "BUF_X4" \ -wire_unit 20 diff --git a/src/cts/src/TritonCTS.tcl b/src/cts/src/TritonCTS.tcl index 0a82ad97c79..7709f91bd6d 100644 --- a/src/cts/src/TritonCTS.tcl +++ b/src/cts/src/TritonCTS.tcl @@ -91,7 +91,7 @@ sta::define_cmd_args "clock_tree_synthesis" {[-wire_unit unit] [-sink_buffer_max_cap_derate] \ [-use_dummy_load] \ [-delay_buffer_derate] \ - } +};# checker off proc clock_tree_synthesis { args } { sta::parse_key_args "clock_tree_synthesis" args \ @@ -103,7 +103,8 @@ proc clock_tree_synthesis { args } { -sink_clustering_levels -tree_buf \ -sink_buffer_max_cap_derate -delay_buffer_derate} \ flags {-post_cts_disable -sink_clustering_enable -balance_levels \ - -obstruction_aware -apply_ndr -insertion_delay -no_insertion_delay -use_dummy_load} + -obstruction_aware -apply_ndr -insertion_delay -no_insertion_delay -use_dummy_load + };# checker off sta::check_argc_eq0 "clock_tree_synthesis" $args @@ -247,7 +248,7 @@ proc report_cts { args } { namespace eval cts { proc clock_tree_synthesis_debug { args } { sta::parse_key_args "clock_tree_synthesis_debug" args \ - keys {} flags {-plot} + keys {} flags {-plot}; # checker off sta::check_argc_eq0 "clock_tree_synthesis_debug" $args cts::set_plot_option [info exists flags(-plot)] diff --git a/src/cts/test/cts_man_tcl_check.ok b/src/cts/test/cts_man_tcl_check.ok new file mode 100644 index 00000000000..a5d9fad3280 --- /dev/null +++ b/src/cts/test/cts_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/cts 2 2 3 +Command counts do not match. diff --git a/src/cts/test/cts_man_tcl_check.py b/src/cts/test/cts_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/cts/test/cts_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/cts/test/cts_readme_msgs_check.ok b/src/cts/test/cts_readme_msgs_check.ok new file mode 100644 index 00000000000..6893d6db79b --- /dev/null +++ b/src/cts/test/cts_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 3, Desc: 3, Syn: 3, Options: 3, Args: 3 +Info: 62, Warn: 10, Error: 34 diff --git a/src/cts/test/cts_readme_msgs_check.py b/src/cts/test/cts_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/cts/test/cts_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/cts/test/extract_utils.py b/src/cts/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/cts/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/cts/test/manpage.py b/src/cts/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/cts/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/cts/test/md_roff_compat.py b/src/cts/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/cts/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/cts/test/regression_tests.tcl b/src/cts/test/regression_tests.tcl index 6eaa204871b..89ba8e27a9e 100644 --- a/src/cts/test/regression_tests.tcl +++ b/src/cts/test/regression_tests.tcl @@ -18,4 +18,6 @@ record_tests { array_ins_delay insertion_delay dummy_load + cts_readme_msgs_check + cts_man_tcl_check } diff --git a/src/dft/README.md b/src/dft/README.md index 084c9a9a406..badcf3b811c 100644 --- a/src/dft/README.md +++ b/src/dft/README.md @@ -1,7 +1,8 @@ # DFT: Design for Testing -This tool is an implementation of Design For Testing. New nets and logic are added to allow IC designs to -be tested for errors in manufacturing. Physical imperfections can cause hard failures and variability can cause timing errors. +The Design for Testing module in OpenROAD (`dft`) is an implementation of Design For Testing. +New nets and logic are added to allow IC designs to be tested for errors in manufacturing. +Physical imperfections can cause hard failures and variability can cause timing errors. A simple DFT insertion consist of the following parts: @@ -11,52 +12,57 @@ A simple DFT insertion consist of the following parts: * One or more scan chains (shift registers created from your scan cells). * A scan_enable pin to allow your design to enter and leave the test mode. +## Commands -# TCL Commands +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` -## set_dft_config +### Set DFT Config +The command `set_dft_config` sets the DFT configuration variables. -``` -set_dft_config [-max_length ] - [-clock_mixing ] +```tcl +set_dft_config + [-max_length ] + [-clock_mixing ] ``` -* `-max_length`: The maxinum number of bits that can be in each scan chain. -* `-clock_mixing`: How architect will mix the scan flops based on the clock driver. - * `no_mix`: Creates scan chains with only one type of clock and edge. This - may create unbalanced chains. - * `clock_mix`: Craetes scan chains mixing clocks and edges. Falling edge - flops are going to be stitched before rising edge. +#### Options -## report_dft_config +| Switch Name | Description | +| ---- | ---- | +| `-max_length` | The maxinum number of bits that can be in each scan chain. | +| `-clock_mixing` | How architect will mix the scan flops based on the clock driver. `no_mix`: Creates scan chains with only one type of clock and edge. This may create unbalanced chains. `clock_mix`: Craetes scan chains mixing clocks and edges. Falling edge flops are going to be stitched before rising edge. | -``` -report_dft_config -``` +### Report DFT Config Prints the current DFT configuration to be used by `preview_dft` and `insert_dft`. -## preview_dft - -``` -preview_dft [-verbose] +```tcl +report_dft_config ``` +### Preview DFT + Prints a preview of the scan chains that will be stitched by `insert_dft`. Use this command to iterate and try different DFT configurations. This command do not perform any modification to the design. -* `-verbose`: Shows more information about each one of the scan chains that will - be created. +```tcl +preview_dft + [-verbose] +``` +#### Options -## insert_dft +| Switch Name | Description | +| ---- | ---- | +| `-verbose` | Shows more information about each one of the scan chains that will be created. | -``` -insert_dft -``` +### Insert DFT Implements the scan chains into the design by performing the following actions: @@ -67,7 +73,12 @@ Implements the scan chains into the design by performing the following actions: The end result will be a design with scan flops connected to form the scan chains. -# Example + +```tcl +insert_dft +``` + +## Example scripts This example will create scan chains with a max length of 10 bits mixing all the scan flops in the scan chains. @@ -79,7 +90,17 @@ preview_dft -verbose insert_dft ``` -# Limitations +## Regression tests + +There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). + +Simply run the following script: + +```shell +./test/regression +``` + +## Limitations * There are no optimizations for the scan chains. This is a WIP. * There is no way to specify existing scan ports to be used by scan insertion. diff --git a/src/dft/src/dft.tcl b/src/dft/src/dft.tcl index eb4e9bcf274..6a552894e1e 100644 --- a/src/dft/src/dft.tcl +++ b/src/dft/src/dft.tcl @@ -29,6 +29,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +sta::define_cmd_args "preview_dft" { [-verbose]} + proc preview_dft { args } { sta::parse_key_args "preview_dft" args \ keys {} \ @@ -41,10 +43,16 @@ proc preview_dft { args } { dft::preview_dft $verbose } -proc insert_dft {} { +sta::define_cmd_args "insert_dft" { } +proc insert_dft { args } { + sta::parse_key_args "insert_dft" args \ + keys {} flags {} dft::insert_dft } + +sta::define_cmd_args "set_dft_config" { [-max_length max_length] \ + [-clock_mixing clock_mixing]} proc set_dft_config { args } { sta::parse_key_args "set_dft_config" args \ keys {-max_length -clock_mixing} \ @@ -65,6 +73,8 @@ proc set_dft_config { args } { } } +sta::define_cmd_args "report_dft_config" { } proc report_dft_config {} { + sta::parse_key_args "report_dft_config" args keys {} flags {} dft::report_dft_config } diff --git a/src/dft/test/dft_man_tcl_check.ok b/src/dft/test/dft_man_tcl_check.ok new file mode 100644 index 00000000000..50ac03b689e --- /dev/null +++ b/src/dft/test/dft_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/dft 4 4 4 +Command counts match. diff --git a/src/dft/test/dft_man_tcl_check.py b/src/dft/test/dft_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/dft/test/dft_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/dft/test/dft_readme_msgs_check.ok b/src/dft/test/dft_readme_msgs_check.ok new file mode 100644 index 00000000000..cf0b187e6e1 --- /dev/null +++ b/src/dft/test/dft_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 4, Desc: 4, Syn: 4, Options: 4, Args: 4 +Info: 1, Warn: 3, Error: 2 diff --git a/src/dft/test/dft_readme_msgs_check.py b/src/dft/test/dft_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/dft/test/dft_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/dft/test/extract_utils.py b/src/dft/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/dft/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/dft/test/manpage.py b/src/dft/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/dft/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/dft/test/md_roff_compat.py b/src/dft/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/dft/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/dft/test/regression_tests.tcl b/src/dft/test/regression_tests.tcl index 2682ce1ec37..7f62d985454 100644 --- a/src/dft/test/regression_tests.tcl +++ b/src/dft/test/regression_tests.tcl @@ -4,4 +4,6 @@ record_tests { sub_modules_sky130 scan_architect_no_mix_sky130 scan_architect_clock_mix_sky130 + dft_man_tcl_check + dft_readme_msgs_check } diff --git a/src/dpl/README.md b/src/dpl/README.md index 80f7973c163..74fd1834ef1 100644 --- a/src/dpl/README.md +++ b/src/dpl/README.md @@ -8,6 +8,11 @@ Open-Source Detailed Placement Engine. Its key features are: ## Commands +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` + ### Detailed Placement The `detailed_placement` command performs detailed placement of instances @@ -26,7 +31,7 @@ detailed_placement | ----- | ----- | | `-max_displacement` | Max distance that an instance can be moved (in microns) when finding a site where it can be placed. Either set one value for both directions or set `{disp_x disp_y}` for individual directions. The default values are `{0, 0}`, and the allowed values within are integers `[0, MAX_INT]`. | | `-disallow_one_site_gaps` | Disable one site gap during placement check. | -| `-report_file_name` | File name for saving the report to (e.g. `report.json`. | +| `-report_file_name` | File name for saving the report to (e.g. `report.json`.) | ### Set Placement Padding @@ -92,8 +97,6 @@ This command removes all filler cells. remove_fillers ``` -No arguments are needed for this function. - ### Check Placement The `check_placement` command checks the placement legality. It returns @@ -103,7 +106,7 @@ The `check_placement` command checks the placement legality. It returns check_placement [-verbose] [-disallow_one_site_gaps] - [-report_filename filename] + [-report_file_name filename] ``` #### Options @@ -123,9 +126,7 @@ a weak attempt to reduce the total half-perimeter wirelength (HPWL). optimize_mirroring ``` -No arguments are needed for this function. - -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/Opendp.cpp) or the [swig file](./src/Opendp.i). diff --git a/src/dpl/include/dpl/Opendp.h b/src/dpl/include/dpl/Opendp.h index 56a24a92c3b..21471f67246 100644 --- a/src/dpl/include/dpl/Opendp.h +++ b/src/dpl/include/dpl/Opendp.h @@ -386,22 +386,12 @@ class Opendp int getHybridSiteIndex(dbSite* site); int calculateHybridSitesRowCount(dbSite* parent_hybrid_site) const; - Point legalPt(const Cell* cell, - const Point& pt, - int row_height = -1, - int site_width = -1) const; + Point legalPt(const Cell* cell, const Point& pt, int row_height = -1) const; Point legalGridPt(const Cell* cell, const Point& pt, - int row_height = -1, - int site_width = -1) const; - Point legalPt(const Cell* cell, - bool padded, - int row_height = -1, - int site_width = -1) const; - Point legalGridPt(const Cell* cell, - bool padded, - int row_height = -1, - int site_width = -1) const; + int row_height = -1) const; + Point legalPt(const Cell* cell, bool padded, int row_height = -1) const; + Point legalGridPt(const Cell* cell, bool padded, int row_height = -1) const; Point nearestBlockEdge(const Cell* cell, const Point& legal_pt, const Rect& block_bbox) const; @@ -475,42 +465,34 @@ class Opendp Pixel* gridPixel(int grid_idx, int x, int y) const; // Cell initial location wrt core origin. - int getSiteWidth(const Cell* cell) const; int getRowCount(const Cell* cell) const; int getRowCount(int row_height) const; - int gridPaddedWidth(const Cell* cell, int site_width) const; int gridPaddedWidth(const Cell* cell) const; int64_t paddedArea(const Cell* cell) const; int coordinateToHeight(int y_coordinate, GridMapKey gmk) const; int gridNearestHeight(const Cell* cell) const; int gridNearestHeight(const Cell* cell, int row_height) const; int gridNearestWidth(const Cell* cell) const; - int gridNearestWidth(const Cell* cell, int site_width) const; int gridHeight(const Cell* cell) const; GridInfo getGridInfo(const Cell* cell) const; - int gridX(int x, int site_width) const; + int gridX(int x) const; int gridX(const Cell* cell) const; - int gridX(const Cell* cell, int site_width) const; int gridPaddedX(const Cell* cell) const; - int gridPaddedX(const Cell* cell, int site_width) const; int gridY(int y, const Cell* cell) const; int gridY(const Cell* cell) const; pair gridY(int y, const dbSite::RowPattern& grid_sites) const; pair gridEndY(int y, const dbSite::RowPattern& grid_sites) const; int gridPaddedEndX(const Cell* cell) const; - int gridPaddedEndX(const Cell* cell, int site_width) const; - int gridEndX(int x, int site_width) const; + int gridEndX(int x) const; int gridEndX(const Cell* cell) const; - int gridEndX(const Cell* cell, int site_width) const; int gridEndY(int y, const Cell* cell) const; int gridEndY(const Cell* cell) const; - void setGridPaddedLoc(Cell* cell, int x, int y, int site_width) const; + void setGridPaddedLoc(Cell* cell, int x, int y) const; std::pair getRowInfo(const Cell* cell) const; // Lower left corner in core coordinates. Point initialLocation(const Cell* cell, bool padded) const; bool isStdCell(const Cell* cell) const; static bool isBlock(const Cell* cell); - int paddedWidth(const Cell* cell, int site_width) const; int paddedWidth(const Cell* cell) const; bool isPaddedType(dbInst* inst) const; int padLeft(const Cell* cell) const; diff --git a/src/dpl/src/CheckPlacement.cpp b/src/dpl/src/CheckPlacement.cpp index 0254f70c03e..5f310355bb8 100644 --- a/src/dpl/src/CheckPlacement.cpp +++ b/src/dpl/src/CheckPlacement.cpp @@ -330,9 +330,8 @@ bool Opendp::isPlaced(const Cell* cell) bool Opendp::checkInRows(const Cell& cell) const { auto grid_info = getRowInfo(&cell); - int site_width = getSiteWidth(&cell); - int x_ll = gridX(&cell, site_width); - int x_ur = gridEndX(&cell, site_width); + int x_ll = gridX(&cell); + int x_ur = gridEndX(&cell); int y_ll = gridY(&cell); int y_ur = gridEndY(&cell); debugPrint(logger_, diff --git a/src/dpl/src/Grid.cpp b/src/dpl/src/Grid.cpp index aade9addcd9..2520c9f2de6 100644 --- a/src/dpl/src/Grid.cpp +++ b/src/dpl/src/Grid.cpp @@ -418,8 +418,8 @@ void Opendp::visitCellPixels( Rect rect = obs->getBox(); inst->getTransform().apply(rect); - int x_start = gridX(rect.xMin() - core_.xMin(), site_width_); - int x_end = gridEndX(rect.xMax() - core_.xMin(), site_width_); + int x_start = gridX(rect.xMin() - core_.xMin()); + int x_end = gridEndX(rect.xMax() - core_.xMin()); int y_start = gridY(rect.yMin() - core_.yMin(), &cell); int y_end = gridEndY(rect.yMax() - core_.yMin(), &cell); @@ -447,10 +447,8 @@ void Opendp::visitCellPixels( } } if (!have_obstructions) { - int x_start - = padded ? gridPaddedX(&cell, site_width_) : gridX(&cell, site_width_); - int x_end = padded ? gridPaddedEndX(&cell, site_width_) - : gridEndX(&cell, site_width_); + int x_start = padded ? gridPaddedX(&cell) : gridX(&cell); + int x_end = padded ? gridPaddedEndX(&cell) : gridEndX(&cell); int y_start = gridY(&cell); int y_end = gridEndY(&cell); auto src_gmk = getGridMapKey(&cell); @@ -486,7 +484,6 @@ void Opendp::visitCellBoundaryPixels( void(Pixel* pixel, odb::Direction2D edge, int x, int y)>& visitor) const { dbInst* inst = cell.db_inst_; - int site_width = getSiteWidth(&cell); const GridMapKey& gmk = getGridMapKey(&cell); GridInfo grid_info = grid_info_map_.at(gmk); const int index_in_grid = grid_info.getGridIndex(); @@ -502,8 +499,8 @@ void Opendp::visitCellBoundaryPixels( Rect rect = obs->getBox(); inst->getTransform().apply(rect); - int x_start = gridX(rect.xMin() - core_.xMin(), site_width); - int x_end = gridEndX(rect.xMax() - core_.xMin(), site_width); + int x_start = gridX(rect.xMin() - core_.xMin()); + int x_end = gridEndX(rect.xMax() - core_.xMin()); int y_start = gridY(rect.yMin() - core_.yMin(), grid_sites).first; int y_end = gridEndY(rect.yMax() - core_.yMin(), grid_sites).first; for (int x = x_start; x < x_end; x++) { @@ -596,12 +593,10 @@ void Opendp::groupAssignCellRegions() { for (Group& group : groups_) { int64_t site_count = 0; - int site_width = site_width_; int row_height = row_height_; if (!group.cells_.empty()) { auto group_cell = group.cells_.at(0); - site_width = getSiteWidth(group_cell); - int max_row_site_count = divFloor(core_.dx(), site_width); + int max_row_site_count = divFloor(core_.dx(), site_width_); row_height = getRowHeight(group_cell); int row_count = divFloor(core_.dy(), row_height); auto gmk = getGridMapKey(group_cell); @@ -616,7 +611,7 @@ void Opendp::groupAssignCellRegions() } } } - int64_t site_area = site_count * site_width * row_height; + int64_t site_area = site_count * site_width_ * row_height; int64_t cell_area = 0; for (Cell* cell : group.cells_) { @@ -697,7 +692,6 @@ void Opendp::groupInitPixels() continue; } int row_height = group.cells_[0]->height_; - int site_width = getSiteWidth(group.cells_[0]); GridMapKey gmk = getGridMapKey(group.cells_[0]); GridInfo& grid_info = grid_info_map_.at(gmk); int grid_index = grid_info.getGridIndex(); @@ -716,22 +710,22 @@ void Opendp::groupInitPixels() int row_end = divFloor(rect.yMax(), row_height); for (int k = row_start; k < row_end; k++) { - int col_start = divCeil(rect.xMin(), site_width); - int col_end = divFloor(rect.xMax(), site_width); + int col_start = divCeil(rect.xMin(), site_width_); + int col_end = divFloor(rect.xMax(), site_width_); for (int l = col_start; l < col_end; l++) { Pixel* pixel = gridPixel(grid_index, l, k); pixel->util += 1.0; } - if (rect.xMin() % site_width != 0) { + if (rect.xMin() % site_width_ != 0) { Pixel* pixel = gridPixel(grid_index, col_start, k); pixel->util - -= (rect.xMin() % site_width) / static_cast(site_width); + -= (rect.xMin() % site_width_) / static_cast(site_width_); } - if (rect.xMax() % site_width != 0) { + if (rect.xMax() % site_width_ != 0) { Pixel* pixel = gridPixel(grid_index, col_end - 1, k); - pixel->util -= ((site_width - rect.xMax()) % site_width) - / static_cast(site_width); + pixel->util -= ((site_width_ - rect.xMax()) % site_width_) + / static_cast(site_width_); } } } @@ -740,8 +734,8 @@ void Opendp::groupInitPixels() int row_end = divFloor(rect.yMax(), row_height); for (int k = row_start; k < row_end; k++) { - int col_start = divCeil(rect.xMin(), site_width); - int col_end = divFloor(rect.xMax(), site_width); + int col_start = divCeil(rect.xMin(), site_width_); + int col_end = divFloor(rect.xMax(), site_width_); // Assign group to each pixel. for (int l = col_start; l < col_end; l++) { @@ -765,8 +759,7 @@ void Opendp::erasePixel(Cell* cell) { if (!(isFixed(cell) || !cell->is_placed_)) { auto gmk = getGridMapKey(cell); - int site_width = getSiteWidth(cell); - int x_end = gridPaddedEndX(cell, site_width); + int x_end = gridPaddedEndX(cell); int y_end = gridEndY(cell); debugPrint(logger_, DPL, @@ -794,7 +787,7 @@ void Opendp::erasePixel(Cell* cell) ++layer_y_end; } - for (int x = gridPaddedX(cell, site_width); x < x_end; x++) { + for (int x = gridPaddedX(cell); x < x_end; x++) { for (int y = layer_y_start; y < layer_y_end; y++) { Pixel* pixel = gridPixel(target_grid_info.getGridIndex(), x, y); if (nullptr == pixel) { @@ -860,11 +853,10 @@ void Opendp::paintPixel(Cell* cell, int grid_x, int grid_y) int x_end = grid_x + gridPaddedWidth(cell); int grid_height = gridHeight(cell); int y_end = grid_y + grid_height; - int site_width = getSiteWidth(cell); GridMapKey gmk = getGridMapKey(cell); GridInfo grid_info = grid_info_map_.at(gmk); const int index_in_grid = gmk.grid_index; - setGridPaddedLoc(cell, grid_x, grid_y, site_width); + setGridPaddedLoc(cell, grid_x, grid_y); cell->is_placed_ = true; for (int x = grid_x; x < x_end; x++) { for (int y = grid_y; y < y_end; y++) { diff --git a/src/dpl/src/Opendp.cpp b/src/dpl/src/Opendp.cpp index 501ba0c5d5a..d2fe596eab8 100644 --- a/src/dpl/src/Opendp.cpp +++ b/src/dpl/src/Opendp.cpp @@ -273,10 +273,9 @@ Point Opendp::initialLocation(const Cell* cell, bool padded) const { int loc_x, loc_y; cell->db_inst_->getLocation(loc_x, loc_y); - int site_width = getSiteWidth(cell); loc_x -= core_.xMin(); if (padded) { - loc_x -= padLeft(cell) * site_width; + loc_x -= padLeft(cell) * site_width_; } loc_y -= core_.yMin(); return Point(loc_x, loc_y); @@ -446,26 +445,14 @@ int Opendp::padRight(dbInst* inst) const return 0; } -int Opendp::paddedWidth(const Cell* cell, int site_width) const -{ - return cell->width_ + (padLeft(cell) + padRight(cell)) * site_width; -} - int Opendp::paddedWidth(const Cell* cell) const { - int site_width = getSiteWidth(cell); - return cell->width_ + (padLeft(cell) + padRight(cell)) * site_width; -} - -int Opendp::gridPaddedWidth(const Cell* cell, int site_width) const -{ - return divCeil(paddedWidth(cell), site_width); + return cell->width_ + (padLeft(cell) + padRight(cell)) * site_width_; } int Opendp::gridPaddedWidth(const Cell* cell) const { - int site_width = getSiteWidth(cell); - return divCeil(paddedWidth(cell), site_width); + return divCeil(paddedWidth(cell), site_width_); } int Opendp::coordinateToHeight(int y_coordinate, GridMapKey gmk) const @@ -502,16 +489,9 @@ int64_t Opendp::paddedArea(const Cell* cell) const return int64_t(paddedWidth(cell)) * cell->height_; } -// Callers should probably be using gridPaddedWidth. -int Opendp::gridNearestWidth(const Cell* cell, int site_width) const -{ - return divRound(paddedWidth(cell), site_width); -} - int Opendp::gridNearestWidth(const Cell* cell) const { - int site_width = getSiteWidth(cell); - return divRound(paddedWidth(cell), site_width); + return divRound(paddedWidth(cell), site_width_); } // Callers should probably be using gridHeight. @@ -526,35 +506,24 @@ int Opendp::gridNearestHeight(const Cell* cell) const return divRound(cell->height_, row_height); } -int Opendp::gridEndX(int x, int site_width) const -{ - return divCeil(x, site_width); -} - -int Opendp::gridX(int x, int site_width) const +int Opendp::gridEndX(int x) const { - return x / site_width; + return divCeil(x, site_width_); } -int Opendp::gridX(const Cell* cell, int site_width) const +int Opendp::gridX(int x) const { - return gridX(cell->x_, site_width); + return x / site_width_; } int Opendp::gridX(const Cell* cell) const { - return gridX(cell->x_, getSiteWidth(cell)); + return gridX(cell->x_); } int Opendp::gridPaddedX(const Cell* cell) const { - return gridX(cell->x_ - padLeft(cell) * getSiteWidth(cell), - getSiteWidth(cell)); -} - -int Opendp::gridPaddedX(const Cell* cell, int site_width) const -{ - return gridX(cell->x_ - padLeft(cell) * site_width, site_width); + return gridX(cell->x_ - padLeft(cell) * site_width_); } int Opendp::getRowCount(const Cell* cell) const @@ -625,13 +594,6 @@ GridInfo Opendp::getGridInfo(const Cell* cell) const return grid_info_map_.at(getGridMapKey(cell)); } -int Opendp::getSiteWidth(const Cell* cell) const -{ - // TODO: this is not complete, it is here so that future changes to the code - // are easier - return site_width_; -} - pair Opendp::gridY(int y, const dbSite::RowPattern& grid_sites) const { int sum_heights @@ -695,9 +657,9 @@ int Opendp::gridY(const int y, const Cell* cell) const return y / getRowHeight(cell); } -void Opendp::setGridPaddedLoc(Cell* cell, int x, int y, int site_width) const +void Opendp::setGridPaddedLoc(Cell* cell, int x, int y) const { - cell->x_ = (x + padLeft(cell)) * site_width; + cell->x_ = (x + padLeft(cell)) * site_width_; if (cell->isHybrid()) { auto grid_info = grid_info_map_.at(getGridMapKey(cell)); int total_sites_height = grid_info.getSitesTotalHeight(); @@ -726,28 +688,15 @@ void Opendp::setGridPaddedLoc(Cell* cell, int x, int y, int site_width) const cell->y_ = y * getRowHeight(cell); } -int Opendp::gridPaddedEndX(const Cell* cell, int site_width) const -{ - return divCeil(cell->x_ + cell->width_ + padRight(cell) * site_width, - site_width); -} - int Opendp::gridPaddedEndX(const Cell* cell) const { - int site_width = getSiteWidth(cell); - return divCeil(cell->x_ + cell->width_ + padRight(cell) * site_width, - site_width); -} - -int Opendp::gridEndX(const Cell* cell, int site_width) const -{ - return divCeil(cell->x_ + cell->width_, site_width); + return divCeil(cell->x_ + cell->width_ + padRight(cell) * site_width_, + site_width_); } int Opendp::gridEndX(const Cell* cell) const { - int site_width = getSiteWidth(cell); - return divCeil(cell->x_ + cell->width_, site_width); + return divCeil(cell->x_ + cell->width_, site_width_); } int Opendp::gridEndY(const Cell* cell) const diff --git a/src/dpl/src/Opendp.tcl b/src/dpl/src/Opendp.tcl index 8444168ca1b..c496ffa6795 100644 --- a/src/dpl/src/Opendp.tcl +++ b/src/dpl/src/Opendp.tcl @@ -139,6 +139,7 @@ proc filler_placement { args } { sta::define_cmd_args "remove_fillers" {} proc remove_fillers { args } { + sta::parse_key_args "remove_fillers" args keys {} flags {} sta::check_argc_eq0 "remove_fillers" $args dpl::remove_fillers_cmd } @@ -163,6 +164,7 @@ proc check_placement { args } { sta::define_cmd_args "optimize_mirroring" {} proc optimize_mirroring { args } { + sta::parse_key_args "optimize_mirroring" args keys {} flags {} sta::check_argc_eq0 "optimize_mirroring" $args dpl::optimize_mirroring_cmd } diff --git a/src/dpl/src/Place.cpp b/src/dpl/src/Place.cpp index ca90925b9bd..584b820a2ca 100644 --- a/src/dpl/src/Place.cpp +++ b/src/dpl/src/Place.cpp @@ -619,7 +619,6 @@ void Opendp::shiftMove(Cell* cell) int grid_x = grid_pt.getX(); int grid_y = grid_pt.getY(); int row_height = getRowHeight(cell); - int site_width = getSiteWidth(cell); GridMapKey grid_key = getGridMapKey(cell); auto grid_mapped_entry = grid_info_map_.find(grid_key); if (grid_mapped_entry == grid_info_map_.end()) { @@ -629,7 +628,7 @@ void Opendp::shiftMove(Cell* cell) int grid_index = grid_mapped_entry->second.getGridIndex(); // magic number alert int boundary_margin = 3; - int margin_width = gridPaddedWidth(cell, site_width) * boundary_margin; + int margin_width = gridPaddedWidth(cell) * boundary_margin; std::set region_cells; for (int x = grid_x - margin_width; x < grid_x + margin_width; x++) { for (int y = grid_y - boundary_margin; y < grid_y + boundary_margin; y++) { @@ -690,8 +689,7 @@ bool Opendp::swapCells(Cell* cell1, Cell* cell2) bool Opendp::refineMove(Cell* cell) { int row_height = getRowHeight(cell); - int site_width = getSiteWidth(cell); - Point grid_pt = legalGridPt(cell, true, row_height, site_width); + Point grid_pt = legalGridPt(cell, true, row_height); int grid_x = grid_pt.getX(); int grid_y = grid_pt.getY(); PixelPt pixel_pt = diamondSearch(cell, grid_x, grid_y); @@ -707,8 +705,9 @@ bool Opendp::refineMove(Cell* cell) return false; } - int dist_change = distChange( - cell, pixel_pt.pt.getX() * site_width, pixel_pt.pt.getY() * row_height); + int dist_change = distChange(cell, + pixel_pt.pt.getX() * site_width_, + pixel_pt.pt.getY() * row_height); if (dist_change < 0) { erasePixel(cell); @@ -753,15 +752,14 @@ PixelPt Opendp::diamondSearch(const Cell* cell, int y_max = y + scaled_max_displacement_y_; auto [row_height, grid_info] = getRowInfo(cell); - int site_width = getSiteWidth(cell); // Restrict search to group boundary. Group* group = cell->group_; if (group) { // Map boundary to grid staying inside. - Rect grid_boundary(divCeil(group->boundary.xMin(), site_width), + Rect grid_boundary(divCeil(group->boundary.xMin(), site_width_), divCeil(group->boundary.yMin(), row_height), - group->boundary.xMax() / site_width, + group->boundary.xMax() / site_width_, group->boundary.yMax() / row_height); Point min = grid_boundary.closestPtInside(Point(x_min, y_min)); Point max = grid_boundary.closestPtInside(Point(x_max, y_max)); @@ -877,7 +875,7 @@ void Opendp::diamondSearchSide(const Cell* cell, } else { y_dist = abs(y - avail_pt.pt.getY()) * getRowHeight(cell); } - int avail_dist = abs(x - avail_pt.pt.getX()) * getSiteWidth(cell) + y_dist; + int avail_dist = abs(x - avail_pt.pt.getX()) * getSiteWidth() + y_dist; if (best_pt.pixel == nullptr || avail_dist < best_dist) { best_pt = avail_pt; best_dist = avail_dist; @@ -1107,10 +1105,7 @@ bool Opendp::checkPixels(const Cell* cell, // Legalize cell origin // inside the core // row site -Point Opendp::legalPt(const Cell* cell, - const Point& pt, - int row_height, - int site_width) const +Point Opendp::legalPt(const Cell* cell, const Point& pt, int row_height) const { // Move inside core. if (row_height == -1) { @@ -1122,14 +1117,11 @@ Point Opendp::legalPt(const Cell* cell, DPL, 19, "Cannot find grid info for row height {}.", row_height); } auto grid_info = grid_mapped_entry->second; - if (site_width == -1) { - site_width = getSiteWidth(cell); - } int core_x = min(max(0, pt.getX()), - grid_info.getSiteCount() * site_width - cell->width_); + grid_info.getSiteCount() * site_width_ - cell->width_); // Align with row site. - int grid_x = divRound(core_x, site_width); - int legal_x = grid_x * site_width; + int grid_x = divRound(core_x, site_width_); + int legal_x = grid_x * site_width_; int legal_y = 0; if (cell->isHybrid()) { int index(0), height(0); @@ -1155,17 +1147,13 @@ Point Opendp::legalPt(const Cell* cell, Point Opendp::legalGridPt(const Cell* cell, const Point& pt, - int row_height, - int site_width) const + int row_height) const { - if (site_width == -1) { - site_width = getSiteWidth(cell); - } if (row_height == -1) { row_height = getRowHeight(cell); } - Point legal = legalPt(cell, pt, row_height, site_width); - return Point(gridX(legal.getX(), site_width), gridY(legal.getY(), cell)); + Point legal = legalPt(cell, pt, row_height); + return Point(gridX(legal.getX()), gridY(legal.getY(), cell)); } Point Opendp::nearestBlockEdge(const Cell* cell, @@ -1175,7 +1163,6 @@ Point Opendp::nearestBlockEdge(const Cell* cell, const int legal_x = legal_pt.getX(); const int legal_y = legal_pt.getY(); const int row_height = getRowHeight(cell); - const int site_width = getSiteWidth(cell); const int x_min_dist = abs(legal_x - block_bbox.xMin()); const int x_max_dist = abs(block_bbox.xMax() - (legal_x + cell->width_)); const int y_min_dist = abs(legal_y - block_bbox.yMin()); @@ -1185,16 +1172,12 @@ Point Opendp::nearestBlockEdge(const Cell* cell, // left of block return legalPt(cell, Point(block_bbox.xMin() - cell->width_, legal_pt.getY()), - row_height, - site_width); + row_height); } if (x_max_dist <= x_min_dist && x_max_dist <= y_min_dist && x_max_dist <= y_max_dist) { // right of block - return legalPt(cell, - Point(block_bbox.xMax(), legal_pt.getY()), - row_height, - site_width); + return legalPt(cell, Point(block_bbox.xMax(), legal_pt.getY()), row_height); } if (y_min_dist <= x_min_dist && y_min_dist <= x_max_dist && y_min_dist <= y_max_dist) { @@ -1203,15 +1186,13 @@ Point Opendp::nearestBlockEdge(const Cell* cell, Point(legal_pt.getX(), divFloor(block_bbox.yMin(), row_height) * row_height - cell->height_), - row_height, - site_width); + row_height); } // above block return legalPt(cell, Point(legal_pt.getX(), divCeil(block_bbox.yMax(), row_height) * row_height), - row_height, - site_width); + row_height); } // Find the nearest valid site left/right/above/below, if any. @@ -1223,7 +1204,6 @@ bool Opendp::moveHopeless(const Cell* cell, int& grid_x, int& grid_y) const int best_x = grid_x; int best_y = grid_y; int best_dist = std::numeric_limits::max(); - int site_width = getSiteWidth(cell); auto [row_height, grid_info] = getRowInfo(cell); int grid_index = grid_info.getGridIndex(); int layer_site_count = grid_info.getSiteCount(); @@ -1234,7 +1214,7 @@ bool Opendp::moveHopeless(const Cell* cell, int& grid_x, int& grid_y) const // this initialization for (int x = grid_x - 1; x >= 0; --x) { // left if (grid_[grid_index][grid_y][x].is_valid) { - best_dist = (grid_x - x - 1) * site_width; + best_dist = (grid_x - x - 1) * site_width_; best_x = x; best_y = grid_y; break; @@ -1242,7 +1222,7 @@ bool Opendp::moveHopeless(const Cell* cell, int& grid_x, int& grid_y) const } for (int x = grid_x + 1; x < layer_site_count; ++x) { // right if (grid_[grid_index][grid_y][x].is_valid) { - const int dist = (x - grid_x) * site_width - cell->width_; + const int dist = (x - grid_x) * site_width_ - cell->width_; if (dist < best_dist) { best_dist = dist; best_x = x; @@ -1307,20 +1287,18 @@ Point Opendp::pointOffMacro(const Cell& cell) Point init = initialLocation(&cell, false); int init_x = init.getX(); int init_y = init.getY(); - int site_width = site_width_; auto grid_info = getGridInfo(&cell); - Pixel* pixel1 = gridPixel(grid_info.getGridIndex(), - gridX(init_x, site_width), - gridY(init_y, &cell)); + Pixel* pixel1 = gridPixel( + grid_info.getGridIndex(), gridX(init_x), gridY(init_y, &cell)); Pixel* pixel2 = gridPixel(grid_info.getGridIndex(), - gridX(init_x + cell.width_, site_width), + gridX(init_x + cell.width_), gridY(init_y, &cell)); Pixel* pixel3 = gridPixel(grid_info.getGridIndex(), - gridX(init_x, site_width), + gridX(init_x), gridY(init_y + cell.height_, &cell)); Pixel* pixel4 = gridPixel(grid_info.getGridIndex(), - gridX(init_x + cell.width_, site_width), + gridX(init_x + cell.width_), gridY(init_y + cell.height_, &cell)); Cell* block = nullptr; @@ -1366,10 +1344,9 @@ void Opendp::legalCellPos(dbInst* db_inst) // transform to grid Pos for align const Point legal_grid_pt - = Point(gridX(new_pos.getX(), site_width_), gridY(new_pos.getY(), &cell)); + = Point(gridX(new_pos.getX()), gridY(new_pos.getY(), &cell)); // Transform position on real position - setGridPaddedLoc( - &cell, legal_grid_pt.getX(), legal_grid_pt.getY(), site_width_); + setGridPaddedLoc(&cell, legal_grid_pt.getX(), legal_grid_pt.getY()); // Set position of cell on db db_inst->setLocation(core_.xMin() + cell.x_, core_.yMin() + cell.y_); } @@ -1379,10 +1356,7 @@ void Opendp::legalCellPos(dbInst* db_inst) // row site // not on top of a macro // not in a hopeless site -Point Opendp::legalPt(const Cell* cell, - bool padded, - int row_height, - int site_width) const +Point Opendp::legalPt(const Cell* cell, bool padded, int row_height) const { if (isFixed(cell)) { logger_->critical(DPL, 26, "legalPt called on fixed cell."); @@ -1391,14 +1365,11 @@ Point Opendp::legalPt(const Cell* cell, if (row_height == -1) { row_height = getRowHeight(cell); } - if (site_width == -1) { - site_width = getSiteWidth(cell); - } Point init = initialLocation(cell, padded); - Point legal_pt = legalPt(cell, init, row_height, site_width); + Point legal_pt = legalPt(cell, init, row_height); auto grid_info = getGridInfo(cell); - int grid_x = gridX(legal_pt.getX(), site_width); + int grid_x = gridX(legal_pt.getX()); int grid_y, height; int y = legal_pt.getY() + grid_info.getOffset(); std::tie(grid_y, height) = gridY(y, grid_info.getSites()); @@ -1406,7 +1377,7 @@ Point Opendp::legalPt(const Cell* cell, if (pixel) { // Move std cells off of macros. First try the is_hopeless strategy if (pixel->is_hopeless && moveHopeless(cell, grid_x, grid_y)) { - legal_pt = Point(grid_x * site_width, grid_y * row_height); + legal_pt = Point(grid_x * site_width_, grid_y * row_height); pixel = gridPixel(grid_info.getGridIndex(), grid_x, grid_y); } @@ -1434,19 +1405,13 @@ Point Opendp::legalPt(const Cell* cell, return legal_pt; } -Point Opendp::legalGridPt(const Cell* cell, - bool padded, - int row_height, - int site_width) const +Point Opendp::legalGridPt(const Cell* cell, bool padded, int row_height) const { - if (site_width == -1) { - site_width = getSiteWidth(cell); - } if (row_height == -1) { row_height = getRowHeight(cell); } - Point pt = legalPt(cell, padded, row_height, site_width); - return Point(gridX(pt.getX(), site_width), gridY(pt.getY(), cell)); + Point pt = legalPt(cell, padded, row_height); + return Point(gridX(pt.getX()), gridY(pt.getY(), cell)); } //////////////////////////////////////////////////////////////// diff --git a/src/dpl/test/dpl_man_tcl_check.ok b/src/dpl/test/dpl_man_tcl_check.ok new file mode 100644 index 00000000000..ad740b24be8 --- /dev/null +++ b/src/dpl/test/dpl_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/dpl 6 6 6 +Command counts match. diff --git a/src/dpl/test/dpl_man_tcl_check.py b/src/dpl/test/dpl_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/dpl/test/dpl_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/dpl/test/dpl_readme_msgs_check.ok b/src/dpl/test/dpl_readme_msgs_check.ok new file mode 100644 index 00000000000..b09d5b2abc4 --- /dev/null +++ b/src/dpl/test/dpl_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 6, Desc: 6, Syn: 6, Options: 6, Args: 6 +Info: 7, Warn: 4, Error: 29 diff --git a/src/dpl/test/dpl_readme_msgs_check.py b/src/dpl/test/dpl_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/dpl/test/dpl_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/dpl/test/extract_utils.py b/src/dpl/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/dpl/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/dpl/test/manpage.py b/src/dpl/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/dpl/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/dpl/test/md_roff_compat.py b/src/dpl/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/dpl/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/dpl/test/regression_tests.tcl b/src/dpl/test/regression_tests.tcl index f1afb7a1c63..ff2fd531913 100644 --- a/src/dpl/test/regression_tests.tcl +++ b/src/dpl/test/regression_tests.tcl @@ -60,4 +60,6 @@ record_tests { simple08 simple09 simple10 + dpl_man_tcl_check + dpl_readme_msgs_check } diff --git a/src/drt/README.md b/src/drt/README.md index a64c5d30175..195beae2e6f 100644 --- a/src/drt/README.md +++ b/src/drt/README.md @@ -25,6 +25,14 @@ guide format. ### Detailed Route +This command performs detailed routing. + +Developer arguments +- `-or_seed`, `-or_k` + +Distributed arguments +- `-distributed` , `-remote_host`, `-remote_port`, `-shared_volume`, `-cloud_size` + ```tcl detailed_route [-output_maze filename] @@ -38,7 +46,7 @@ detailed_route [-via_in_pin_bottom_layer layer] [-via_in_pin_top_layer layer] [-or_seed seed] - [-or_k_ k] + [-or_k k] [-bottom_routing_layer layer] [-top_routing_layer layer] [-verbose level] @@ -52,6 +60,7 @@ detailed_route [-min_access_points count] [-save_guide_updates] [-repair_pdn_vias layer] + [-single_step_dr] ``` #### Options @@ -102,11 +111,12 @@ detailed_route_debug [-maze] [-net name] [-pin name] - [-worker x y] + [-box x1 y1 x2 y2] [-iter iter] [-pa_markers] [-dump_dr] [-dump_dir dir] + [-dump_last_worker] [-pa_edge] [-pa_commit] [-write_net_tracks] @@ -122,6 +132,7 @@ detailed_route_debug | `-maze` | Enable debug for maze routing. | | `-net` | Enable debug for net name. | | `-pin` | Enable debug for pin name. | +| `-box` | Set the box for debugging given by lower left/upper right coordinates. | | `-worker` | Debugs routes that pass through the point `{x, y}`. | | `-iter` | Specifies the number of debug iterations. The default value is `0`, and the accepted values are integers `[0, MAX_INT`. | | `-pa_markers` | Enable pin access markers. | @@ -131,7 +142,9 @@ detailed_route_debug | `-pa_commit` | Enable visibility of pin access commits. | | `-write_net_tracks` | Enable writing of net track assigments. | -### Check Pin Access +### Check Pin Access + +This function checks pin access. ```tcl pin_access @@ -158,7 +171,7 @@ pin_access | `-verbose` | Sets verbose mode if the value is greater than 1, else non-verbose mode (must be integer, or error will be triggered.) | | `-distributed` | Refer to distributed arguments [here](#distributed-arguments). | -### Distributed arguments +#### Distributed Arguments We have compiled all distributed arguments in this section. @@ -174,11 +187,11 @@ Additional setup is required. Please refer to this [guide](./doc/Distributed.md) | `-shared_volume` | The mount path of the nfs shared folder. | | `-cloud_size` | The number of workers. | -### Useful developer functions +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/TritonRoute.cpp) or the [swig file](./src/TritonRoute.i). -| Function Name | Description | +| Command Name | Description | | ----- | ----- | | `detailed_route_set_default_via` | Set default via. | | `detailed_route_set_unidirectional_layer` | Set unidirectional layer. | diff --git a/src/drt/src/TritonRoute.tcl b/src/drt/src/TritonRoute.tcl index 43feb581813..7354dc66e90 100644 --- a/src/drt/src/TritonRoute.tcl +++ b/src/drt/src/TritonRoute.tcl @@ -45,7 +45,7 @@ sta::define_cmd_args "detailed_route" { [-via_in_pin_bottom_layer layer] [-via_in_pin_top_layer layer] [-or_seed seed] - [-or_k_ k] + [-or_k k] [-bottom_routing_layer layer] [-top_routing_layer layer] [-verbose level] @@ -59,6 +59,7 @@ sta::define_cmd_args "detailed_route" { [-min_access_points count] [-save_guide_updates] [-repair_pdn_vias layer] + [-single_step_dr] } proc detailed_route { args } { @@ -362,12 +363,12 @@ sta::define_cmd_args "detailed_route_run_worker" { [-dump_dir dir] [-worker_dir dir] [-drc_rpt drc] -} +};# checker off proc detailed_route_run_worker { args } { sta::parse_key_args "detailed_route_run_worker" args \ keys {-dump_dir -worker_dir -drc_rpt} \ - flags {} + flags {};# checker off sta::check_argc_eq0 "detailed_route_run_worker" $args if { [info exists keys(-dump_dir)] } { set dump_dir $keys(-dump_dir) @@ -397,13 +398,13 @@ sta::define_cmd_args "detailed_route_worker_debug" { [-marker_decay m_decay] [-ripup_mode mode] [-follow_guide f_guide] -} +};# checker off proc detailed_route_worker_debug { args } { sta::parse_key_args "detailed_route_worker_debug" args \ keys {-maze_end_iter -drc_cost -marker_cost -fixed_shape_cost \ -marker_decay -ripup_mode -follow_guide} \ - flags {} + flags {};# checker off if {[info exists keys(-maze_end_iter)]} { set maze_end_iter $keys(-maze_end_iter) } else { @@ -473,11 +474,11 @@ proc step_dr { args } { sta::define_cmd_args "check_drc" { [-box box] [-output_file filename] -} +};# checker off proc check_drc { args } { sta::parse_key_args "check_drc" args \ keys { -box -output_file } \ - flags {} + flags {};# checker off sta::check_argc_eq0 "check_drc" $args set box { 0 0 0 0 } if {[info exists keys(-box)]} { diff --git a/src/drt/src/io/io.cpp b/src/drt/src/io/io.cpp index 5bbb865c5e4..0989e98c661 100644 --- a/src/drt/src/io/io.cpp +++ b/src/drt/src/io/io.cpp @@ -2288,6 +2288,16 @@ void io::Parser::setMasters(odb::dbDatabase* db) const frLayerNum numLayers = tech_->getLayers().size(); std::vector> pin_shapes; pin_shapes.resize(numLayers); + auto addPinFig + = [&pin_shapes](const Rect& box, frLayerNum lNum, frMPin* pinIn) { + std::unique_ptr pinFig = std::make_unique(); + pinFig->setBBox(box); + pinFig->addToPin(pinIn); + pinFig->setLayerNum(lNum); + std::unique_ptr uptr(std::move(pinFig)); + pinIn->addPinFig(std::move(uptr)); + pin_shapes[lNum].insert(std::make_pair(box, pinIn)); + }; for (auto lib : db->getLibs()) { for (odb::dbMaster* master : lib->getMasters()) { @@ -2321,54 +2331,61 @@ void io::Parser::setMasters(odb::dbDatabase* db) term->setType(_term->getSigType()); term->setDirection(_term->getIoType()); - bool warned = false; int i = 0; for (auto mpin : _term->getMPins()) { auto pinIn = std::make_unique(); pinIn->setId(i++); for (auto box : mpin->getGeometry()) { - frLayerNum layerNum = -1; auto layer = box->getTechLayer(); if (!layer) { - if (!warned) { - logger_->warn(DRT, - 323, - "Via(s) in pin {} of {} will be ignored", - _term->getName(), - master->getName()); - warned = true; - } - continue; - } - std::string layer_name = layer->getName(); - if (tech_->name2layer_.find(layer_name) - == tech_->name2layer_.end()) { - auto type = box->getTechLayer()->getType(); - if (type == odb::dbTechLayerType::ROUTING - || type == odb::dbTechLayerType::CUT) { + if (tech_->name2via_.find(box->getTechVia()->getName()) + == tech_->name2via_.end()) { logger_->warn(DRT, - 122, - "Layer {} is skipped for {}/{}.", - layer_name, - tmpMaster->getName(), + 193, + "Skipping unsupported via {} in macro pin {}/{}.", + box->getTechVia()->getName(), + master->getName(), _term->getName()); } - continue; + int x, y; + box->getViaXY(x, y); + auto viaDef = tech_->name2via_[box->getTechVia()->getName()]; + auto tmpP = std::make_unique(viaDef); + tmpP->setOrigin({x, y}); + // layer1 rect + addPinFig( + tmpP->getLayer1BBox(), viaDef->getLayer1Num(), pinIn.get()); + // layer2 rect + addPinFig( + tmpP->getLayer2BBox(), viaDef->getLayer2Num(), pinIn.get()); + // cut rect + addPinFig( + tmpP->getCutBBox(), viaDef->getCutLayerNum(), pinIn.get()); + } else { + std::string layer_name = layer->getName(); + if (tech_->name2layer_.find(layer_name) + == tech_->name2layer_.end()) { + auto type = box->getTechLayer()->getType(); + if (type == odb::dbTechLayerType::ROUTING + || type == odb::dbTechLayerType::CUT) { + logger_->warn(DRT, + 122, + "Layer {} is skipped for {}/{}.", + layer_name, + tmpMaster->getName(), + _term->getName()); + } + continue; + } + frLayerNum layerNum + = tech_->name2layer_.at(layer_name)->getLayerNum(); + + frCoord xl = box->xMin(); + frCoord yl = box->yMin(); + frCoord xh = box->xMax(); + frCoord yh = box->yMax(); + addPinFig(Rect(xl, yl, xh, yh), layerNum, pinIn.get()); } - layerNum = tech_->name2layer_.at(layer_name)->getLayerNum(); - - frCoord xl = box->xMin(); - frCoord yl = box->yMin(); - frCoord xh = box->xMax(); - frCoord yh = box->yMax(); - std::unique_ptr pinFig = std::make_unique(); - pinFig->setBBox(Rect(xl, yl, xh, yh)); - pinFig->addToPin(pinIn.get()); - pinFig->setLayerNum(layerNum); - std::unique_ptr uptr(std::move(pinFig)); - pinIn->addPinFig(std::move(uptr)); - pin_shapes[layerNum].insert( - std::make_pair(Rect{xl, yl, xh, yh}, pinIn.get())); } term->addPin(std::move(pinIn)); } diff --git a/src/drt/src/pa/FlexPA_prep.cpp b/src/drt/src/pa/FlexPA_prep.cpp index b16b2feed38..d3e179401f9 100644 --- a/src/drt/src/pa/FlexPA_prep.cpp +++ b/src/drt/src/pa/FlexPA_prep.cpp @@ -157,7 +157,7 @@ void FlexPA::prepPoint_pin_genPoints_rect_genCenter( cnt++; } } - if (cnt >= 2) { + if (cnt >= 3) { return; } diff --git a/src/drt/test/drt_man_tcl_check.ok b/src/drt/test/drt_man_tcl_check.ok new file mode 100644 index 00000000000..f4b07988732 --- /dev/null +++ b/src/drt/test/drt_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/drt 3 3 3 +Command counts match. diff --git a/src/drt/test/drt_man_tcl_check.py b/src/drt/test/drt_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/drt/test/drt_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/drt/test/drt_readme_msgs_check.ok b/src/drt/test/drt_readme_msgs_check.ok new file mode 100644 index 00000000000..9a28d23b531 --- /dev/null +++ b/src/drt/test/drt_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 3, Desc: 3, Syn: 3, Options: 3, Args: 3 +Info: 78, Warn: 111, Error: 172 diff --git a/src/drt/test/drt_readme_msgs_check.py b/src/drt/test/drt_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/drt/test/drt_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/drt/test/extract_utils.py b/src/drt/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/drt/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/drt/test/manpage.py b/src/drt/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/drt/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/drt/test/md_roff_compat.py b/src/drt/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/drt/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/drt/test/regression_tests.tcl b/src/drt/test/regression_tests.tcl index ee76928f682..6f04f5cc1d1 100644 --- a/src/drt/test/regression_tests.tcl +++ b/src/drt/test/regression_tests.tcl @@ -9,6 +9,8 @@ record_tests { top_level_term top_level_term2 drc_test + drt_man_tcl_check + drt_readme_msgs_check } record_pass_fail_tests { gc_test diff --git a/src/fin/README.md b/src/fin/README.md index 4f6455e22cd..5fe2f66ec42 100644 --- a/src/fin/README.md +++ b/src/fin/README.md @@ -13,6 +13,8 @@ configuration file. ### Density Fill +This command performs density fill to meet metal density DRC rules. + ```tcl density_fill [-rules rules_file] diff --git a/src/fin/test/extract_utils.py b/src/fin/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/fin/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/fin/test/fin_man_tcl_check.ok b/src/fin/test/fin_man_tcl_check.ok new file mode 100644 index 00000000000..b04371a2745 --- /dev/null +++ b/src/fin/test/fin_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/fin 1 1 1 +Command counts match. diff --git a/src/fin/test/fin_man_tcl_check.py b/src/fin/test/fin_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/fin/test/fin_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/fin/test/fin_readme_msgs_check.ok b/src/fin/test/fin_readme_msgs_check.ok new file mode 100644 index 00000000000..85ebc6fde59 --- /dev/null +++ b/src/fin/test/fin_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 5, Warn: 1, Error: 4 diff --git a/src/fin/test/fin_readme_msgs_check.py b/src/fin/test/fin_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/fin/test/fin_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/fin/test/manpage.py b/src/fin/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/fin/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/fin/test/md_roff_compat.py b/src/fin/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/fin/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/fin/test/regression_tests.tcl b/src/fin/test/regression_tests.tcl index 5685436788d..e73738e2046 100644 --- a/src/fin/test/regression_tests.tcl +++ b/src/fin/test/regression_tests.tcl @@ -1,3 +1,5 @@ record_tests { gcd_fill + fin_man_tcl_check + fin_readme_msgs_check } diff --git a/src/gpl/README.md b/src/gpl/README.md index 92fd05ffc2d..abeabc3d78b 100644 --- a/src/gpl/README.md +++ b/src/gpl/README.md @@ -36,10 +36,20 @@ is scaled from the full value for the worst slack, to 1.0 at the `timing_driven_nets_percentage` point. Use the `set_wire_rc` command to set resistance and capacitance of estimated wires used for timing. -``` +Routability-driven arguments +- They begin with `-routability`. +- `-routability_check_overflow`, `-routability_max_density`, `-routability_max_bloat_iter`, `-routability_max_inflation_iter`, `-routability_target_rc_metric`, `-routability_inflation_ratio_coef`, `-routability_max_inflation_ratio`, `-routability_rc_coefficients` + +Timing-driven arguments +- They begin with `-timing_driven`. +- `-timing_driven_net_reweight_overflow`, `-timing_driven_net_weight_max`, `-timing_driven_nets_percentage` + +```tcl global_placement [-timing_driven] [-routability_driven] + [-disable_timing_driven] + [-disable_routability_driven] [-skip_initial_place] [-incremental] [-bin_grid_count grid_count] @@ -48,21 +58,21 @@ global_placement [-init_wirelength_coef init_wirelength_coef] [-min_phi_coef min_phi_conef] [-max_phi_coef max_phi_coef] + [-reference_hpwl reference_hpwl] [-overflow overflow] [-initial_place_max_iter initial_place_max_iter] [-initial_place_max_fanout initial_place_max_fanout] [-pad_left pad_left] [-pad_right pad_right] - [-verbose_level level] [-force_cpu] [-skip_io] + [-skip_nesterov_place] [-routability_check_overflow routability_check_overflow] [-routability_max_density routability_max_density] [-routability_max_bloat_iter routability_max_bloat_iter] [-routability_max_inflation_iter routability_max_inflation_iter] [-routability_target_rc_metric routability_target_rc_metric] [-routability_inflation_ratio_coef routability_inflation_ratio_coef] - [-routability_pitch_scale routability_pitch_scale] [-routability_max_inflation_ratio routability_max_inflation_ratio] [-routability_rc_coefficients routability_rc_coefficients] [-timing_driven_net_reweight_overflow] @@ -70,7 +80,7 @@ global_placement [-timing_driven_nets_percentage] ``` -#### General Arguments +#### Options | Switch Name | Description | | ----- | ----- | @@ -89,7 +99,6 @@ global_placement | `-initial_place_max_fanout` | Set net escape condition in initial place when $fanout \geq initial\_place\_max\_fanout$. The default value is 200. Allowed values are integers `[1, MAX_INT]`. | | `-pad_left` | Set left padding in terms of number of sites. The default value is 0, and the allowed values are integers `[1, MAX_INT]` | | `-pad_right` | Set right padding in terms of number of sites. The default value is 0, and the allowed values are integers `[1, MAX_INT]` | -| `-verbose_level` | Set verbose level for `gpl`. The default value is 1. Allowed values are integers `[0, 5]`. | | `-force_cpu` | Force to use the CPU solver even if the GPU is available. | | `-skip_io` | Flag to ignore the IO ports when computing wirelength during placement. The default value is False, allowed values are boolean. | @@ -114,42 +123,39 @@ global_placement | `-timing_driven_net_weight_max` | Set the multiplier for the most timing-critical nets. The default value is `1.9`, and the allowed values are floats. | | `-timing_driven_nets_percentage` | Set the reweighted percentage of nets in timing-driven mode. The default value is 10. Allowed values are floats `[0, 100]`. | - -### Useful developer functions - -If you are a developer, you might find these useful. More details can be found in the [source file](./src/replace.cpp) or the [swig file](./src/replace.i). - -```tcl -# debugging global placement -global_placement_debug -pause -update -inst -draw_bins -initial - -# adds padding and gets global placement uniform target density -get_global_placement_uniform_density -pad_left -pad_right -``` - ### Cluster Flops -Cluster single bit flops into multi-bit flops. +This command does flop clustering based on parameters. -``` +```tcl cluster_flops - [-tray_weight tray_weight] - [-timing_weight timing_weight] - [-max_split_size max_split_size] + [-tray_weight tray_weight]\ + [-timing_weight timing_weight]\ + [-max_split_size max_split_size]\ [-num_paths num_paths] ``` -#### General Arguments +#### Options | Switch Name | Description | | ----- | ----- | -| `-tray_weight` | Set the weighting factor for tray cost (recommended to be `[20.0, float]`). | -| `-timing_weight` | Set the weighting factor for timing-critical paths in (recommended to be `[1.0. float]`). | -| `-max_split_size` | The maximum size of a single pointset after running the pointset decomposition algorithm for runtime improvement (default = 250). | -| `num_paths` | Number of timing-critical paths to consider (default = 0). | +| `-tray_weight` | Tray weight, default value is 20.0, type `float`. | +| `-timing_weight` | Timing weight, default value is 1.0, type `float`. | +| `-max_split_size` | Maximum split size, default value is -1, type `int`.| +| `-num_paths` | KIV, default value is 0, type `int`. | + + +## Useful Developer Commands + +If you are a developer, you might find these useful. More details can be found in the [source file](./src/replace.cpp) or the [swig file](./src/replace.i). +``` +# debugging global placement +global_placement_debug -pause -update -inst -draw_bins -initial -## Example Scripts +# adds padding and gets global placement uniform target density +get_global_placement_uniform_density -pad_left -pad_right +``` Example scripts demonstrating how to run `gpl` on a sample design on `core01` as follows: @@ -240,4 +246,4 @@ about this tool. ## License -BSD 3-Clause License. See [LICENSE](LICENSE) file. +BSD 3-Clause License. See [LICENSE](LICENSE) file. \ No newline at end of file diff --git a/src/gpl/src/replace.tcl b/src/gpl/src/replace.tcl index 8b88ebc3d99..e62d930d35a 100644 --- a/src/gpl/src/replace.tcl +++ b/src/gpl/src/replace.tcl @@ -36,6 +36,8 @@ sta::define_cmd_args "global_placement" {\ [-skip_nesterov_place]\ [-timing_driven]\ [-routability_driven]\ + [-disable_timing_driven]\ + [-disable_routability_driven]\ [-incremental]\ [-force_cpu]\ [-skip_io]\ @@ -326,8 +328,8 @@ sta::define_cmd_args "cluster_flops" {\ proc cluster_flops { args } { sta::parse_key_args "cluster_flops" args \ - keys { -tray_weight -timing_weight -max_split_size -num_paths } - + keys { -tray_weight -timing_weight -max_split_size -num_paths } \ + flags {} set tray_weight 20.0 set timing_weight 1.0 @@ -358,7 +360,7 @@ namespace eval gpl { proc global_placement_debug { args } { sta::parse_key_args "global_placement_debug" args \ keys {-pause -update -inst} \ - flags {-draw_bins -initial} + flags {-draw_bins -initial};# checker off set pause 10 if { [info exists keys(-pause)] } { @@ -385,7 +387,8 @@ proc global_placement_debug { args } { proc get_global_placement_uniform_density { args } { sta::parse_key_args "get_global_placement_uniform_density" args \ - keys { -pad_left -pad_right } + keys { -pad_left -pad_right } \ + flags {};# checker off # no need for init IP, TD and RD gpl::set_initial_place_max_iter_cmd 0 diff --git a/src/gpl/test/extract_utils.py b/src/gpl/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/gpl/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/gpl/test/gpl_man_tcl_check.ok b/src/gpl/test/gpl_man_tcl_check.ok new file mode 100644 index 00000000000..2fc6ec80604 --- /dev/null +++ b/src/gpl/test/gpl_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/gpl 2 2 2 +Command counts match. diff --git a/src/gpl/test/gpl_man_tcl_check.py b/src/gpl/test/gpl_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/gpl/test/gpl_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/gpl/test/gpl_readme_msgs_check.ok b/src/gpl/test/gpl_readme_msgs_check.ok new file mode 100644 index 00000000000..ea7ba6f6d29 --- /dev/null +++ b/src/gpl/test/gpl_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 2, Desc: 2, Syn: 2, Options: 2, Args: 2 +Info: 70, Warn: 10, Error: 14 diff --git a/src/gpl/test/gpl_readme_msgs_check.py b/src/gpl/test/gpl_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/gpl/test/gpl_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/gpl/test/manpage.py b/src/gpl/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/gpl/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/gpl/test/md_roff_compat.py b/src/gpl/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/gpl/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/gpl/test/regression_tests.tcl b/src/gpl/test/regression_tests.tcl index 8b961997ea7..5cd77b5e7d3 100644 --- a/src/gpl/test/regression_tests.tcl +++ b/src/gpl/test/regression_tests.tcl @@ -27,5 +27,7 @@ record_tests { convergence01 nograd01 clust01 + gpl_man_tcl_check + gpl_readme_msgs_check } # clust02 diff --git a/src/grt/README.md b/src/grt/README.md index 2a63829d917..d9edcdb9fa6 100644 --- a/src/grt/README.md +++ b/src/grt/README.md @@ -13,6 +13,9 @@ FastRoute4.1 algorithm. ### Global Route +This command performs global routing with the option to use a `guide_file`. +You may also choose to use incremental global routing using `-start_incremental`. + ```tcl global_route [-guide_file out_file] @@ -22,6 +25,8 @@ global_route [-grid_origin {x y}] [-critical_nets_percentage percent] [-allow_congestion] + [-allow_overflow] + [-overflow_iterations] [-verbose] [-start_incremental] [-end_incremental] @@ -44,6 +49,9 @@ global_route ### Set Routing Layers +This command sets the minimum and maximum routing layers for signal and clock nets. +Example: `set_routing_layers -signal Metal2-Metal10 -clock Metal6-Metal9` + ```tcl set_routing_layers [-signal min-max] @@ -57,10 +65,12 @@ set_routing_layers | `-signal` | Set the min and max routing signal layer (names) in this format "%s-%s". | | `-clock` | Set the min and max routing clock layer (names) in this format "%s-%s". | -Example: `set_routing_layers -signal Metal2-Metal10 -clock Metal6-Metal9` - ### Set Macro Extension +This command sets the halo (in terms of GCells) along the boundaries of macros. +A `GCell` is typically defined in terms of `Mx` routing tracks. +Example: `set_macro_extension 2` + ```tcl set_macro_extension extension ``` @@ -69,12 +79,12 @@ set_macro_extension extension | Argument Name | Description | | ----- | ----- | -| `extension` | Number of `GCells` added to the blockage boundaries from macros. A `GCell` is typically defined in terms of `Mx` routing tracks. The default `GCell` size is 15 `M3` pitches. | - -Example: `set_macro_extension 2` +| `extension` | Number of `GCells` added to the blockage boundaries from macros. The default `GCell` size is 15 `M3` pitches. | ### Set Pin Offset +This command sets the pin offset distance. + ```tcl set_pin_offset offset ``` @@ -92,17 +102,6 @@ adjustments in the routing layers of the design. Such adjustments reduce the nu routing tracks that the global router assumes to exist. This promotes the spreading of routing and reduces peak congestion, to reduce challenges for detailed routing. -```tcl -set_global_routing_layer_adjustment layer adjustment -``` - -#### Options - -| Argument Name | Description | -| ----- | ----- | -| `layer` | Integer for the layer number (e.g. for M1 you would use 1). | -| `adjustment` | Float indicating the percentage reduction of each edge in the specified layer. | - You can set adjustment for a specific layer, e.g., `set_global_routing_layer_adjustment Metal4 0.5` reduces the routing resources of routing layer `Metal4` by 50%. You can also set adjustment @@ -111,44 +110,23 @@ also set resource adjustment for a layer range, e.g.: `set_global_routing_layer_ Metal4-Metal8 0.3` reduces the routing resources of routing layers `Metal4`, `Metal5`, `Metal6`, `Metal7` and `Metal8` by 30%. -### Set Routing Alpha - -By default the global router uses heuristic rectilinear Steiner minimum -trees (RSMTs) as an initial basis to construct route guides. An RSMT -tries to minimize the total wirelength needed to connect a given set -of pins. The Prim-Dijkstra heuristic is an alternative net topology -algorithm that supports a trade-off between total wirelength and maximum -path depth from the net driver to its loads. The `set_routing_alpha` -command enables the Prim/Dijkstra algorithm and sets the alpha parameter -used to trade-off wirelength and path depth. Alpha is between 0.0 -and 1.0. When alpha is 0.0 the net topology minimizes total wirelength -(i.e. capacitance). When alpha is 1.0 it minimizes longest path between -the driver and loads (i.e., maximum resistance). Typical values are -0.4-0.8. You can call it multiple times for different nets. - ```tcl -set_routing_alpha - [-net net_name] - [-min_fanout fanout] - [-min_hpwl hpwl] - [-clock_nets] - alpha +set_global_routing_layer_adjustment layer adjustment ``` #### Options -| Switch Name | Description | +| Argument Name | Description | | ----- | ----- | -| `-net` | Net name. | -| `-min_fanout` | Set the minimum number for fanout. | -| `-min_hpwl` | Set the minimum half-perimetere wirelength (microns). | -| `-clock_nets` | Flag to set routing alpha for clock nets. The default value is `False`, and the allowed values are bools. | -| `alpha` | Set the trade-off value between wirelength and path depth. The allowed values are floats `[0, 1]`. | +| `layer` | Integer for the layer number (e.g. for M1 you would use 1). | +| `adjustment` | Float indicating the percentage reduction of each edge in the specified layer. | -Example: `set_routing_alpha -net clk 0.3` sets the alpha value of 0.3 for net *clk*. ### Set Global Routing Region Adjustment +Set global routing region adjustment. +Example: `set_global_routing_region_adjustment {1.5 2 20 30.5} -layer Metal4 -adjustment 0.7` + ```tcl set_global_routing_region_adjustment {lower_left_x lower_left_y upper_right_x upper_right_y} @@ -164,13 +142,15 @@ set_global_routing_region_adjustment | `-layer` | Integer for the layer number (e.g. for M1 you would use 1). | | `-adjustment` | Float indicating the percentage reduction of each edge in the specified layer. | -Example: `set_global_routing_region_adjustment {1.5 2 20 30.5} -layer Metal4 -adjustment 0.7` - ### Set Global Routing Randomness -The randomized global routing shuffles the -order of the nets and randomly subtracts or adds to the capacities of -a random set of edges. +The command randomizes global routing by shuffling the order of the nets +and randomly subtracts or adds to the capacities of a random set of edges. + +Example: +`set_global_routing_random -seed 42 \ + -capacities_perturbation_percentage 50 \ + -perturbation_amount 2` ```tcl set_global_routing_random @@ -187,11 +167,6 @@ set_global_routing_random | `-capacities_perturbation_percentage` | Sets the percentage of edges whose capacities are perturbed. By default, the edge capacities are perturbed by adding or subtracting 1 (track) from the original capacity. | | `-perturbation_amount` | Sets the perturbation value of the edge capacities. This option is only meaningful when `-capacities_perturbation_percentage` is used. | -Example: -`set_global_routing_random -seed 42 \ - -capacities_perturbation_percentage 50 \ - -perturbation_amount 2` - ### Set Specific Nets to Route The `set_nets_to_route` command defines a list of nets to route. Only the nets @@ -218,21 +193,6 @@ iteration to repair antennas. Filler instances added by the `filler_placement` command should NOT be in the database when `repair_antennas` is called. -```tcl -repair_antennas - [diode_cell] - [-iterations iterations] - [-ratio_margin margin] -``` - -#### Options - -| Switch Name | Description | -| ----- | ----- | -| `diode_cell` | Diode cell to fix antenna violations. | -| `-iterations` | Number of iterations. The default value is `1`, and the allowed values are integers `[0, MAX_INT]`. | -| `-ratio_margin` | Add a margin to the antenna ratios. The default value is `0`, and the allowed values are integers `[0, 100]`. | - See LEF/DEF 5.8 Language Reference, Appendix C, "Calculating and Fixing Process Antenna Violations" for a [description](coriolis.lip6.fr/doc/lefdef/lefdefref/lefdefref.pdf) of antenna violations. @@ -250,31 +210,21 @@ diodes are inserted. The following warning message will be reported: [WARNING GRT-0243] Unable to repair antennas on net with diodes. ``` -### Write Global Routing Guides - ```tcl -write_guides file_name +repair_antennas + [diode_cell] + [-iterations iterations] + [-ratio_margin margin] ``` +#### Options + | Switch Name | Description | | ----- | ----- | -| `file_name` | Guide file name. | - -Example: `write_guides route.guide`. - -### Estimate Global Routing Parasitics - -To estimate RC parasitics based on global route results, use the `-global_routing` -option of the `estimate_parasitics` command. +| `diode_cell` | Diode cell to fix antenna violations. | +| `-iterations` | Number of iterations. The default value is `1`, and the allowed values are integers `[0, MAX_INT]`. | +| `-ratio_margin` | Add a margin to the antenna ratios. The default value is `0`, and the allowed values are integers `[0, 100]`. | -```{note} -To see the function definition for `estimate_parasitics`, refer to -[Resizer docs](../rsz/README.md#estimate-parasitics). -``` - -```tcl -estimate_parasitics -global_routing -``` ### Plot Global Routing Guides @@ -302,6 +252,8 @@ and the `-detailed_route` flags to report the wire length from global and detail respectively. If none of these flags are used, the tool will identify the state of the design and report the wire length accordingly. +Example: `report_wire_length -net {clk net60} -global_route -detailed_route -verbose -file out.csv` + ```tcl report_wire_length [-net net_list] @@ -321,9 +273,7 @@ report_wire_length | `-detailed_route` | Report the wire length of the detailed routing. | | `-verbose` | This flag enables the full reporting of the layer-wise wirelength information. | -Example: `report_wire_length -net {clk net60} -global_route -detailed_route -verbose -file out.csv` - -### Debug Mode +### Global Route Debug Mode The `global_route_debug` command allows you to start a debug mode to view the status of the Steiner Trees. It also allows you to dump the input positions for the Steiner tree creation of a net. @@ -351,16 +301,10 @@ global_route_debug | `-saveSttInput` | File name to save `stt` input of a net. | | `-net` | The name of the net name to be displayed. | -## Example scripts - -Examples scripts demonstrating how to run FastRoute on a sample design of `gcd` as follows: - -```shell -./test/gcd.tcl -``` - ### Read Global Routing Guides +This command reads global routing guides. + ```tcl read_guides file_name ``` @@ -371,11 +315,19 @@ read_guides file_name | ----- | ----- | | `file_name` | Path to global routing guide. | -### Useful developer functions +## Example scripts + +Examples scripts demonstrating how to run FastRoute on a sample design of `gcd` as follows: + +```shell +./test/gcd.tcl +``` + +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/GlobalRouter.cpp) or the [swig file](./src/GlobalRouter.i). -| Function Name | Description | +| Command Name | Description | | ----- | ----- | | `check_routing_layer` | Check if the layer is within the min/max routing layer specified. | | `parse_layer_name` | Get routing layer number from layer name | diff --git a/src/grt/src/GlobalRouter.tcl b/src/grt/src/GlobalRouter.tcl index 5edac7e84f4..6c1a7667abf 100644 --- a/src/grt/src/GlobalRouter.tcl +++ b/src/grt/src/GlobalRouter.tcl @@ -36,6 +36,10 @@ sta::define_cmd_args "set_global_routing_layer_adjustment" { layer adj } proc set_global_routing_layer_adjustment { args } { + sta::parse_key_args "set_global_routing_layer_adjustment" args \ + keys {} \ + flags {} + if {[llength $args] == 2} { lassign $args layer adj @@ -70,7 +74,7 @@ sta::define_cmd_args "set_global_routing_region_adjustment" { region \ proc set_global_routing_region_adjustment { args } { sta::parse_key_args "set_global_routing_region_adjustment" args \ - keys {-layer -adjustment} + keys {-layer -adjustment} flags {} if { ![ord::db_has_tech] } { utl::error GRT 47 "Missing dbTech." @@ -117,13 +121,13 @@ proc set_global_routing_region_adjustment { args } { } } -sta::define_cmd_args "set_routing_layers" { [-signal layers] \ - [-clock layers] \ -} +sta::define_cmd_args "set_routing_layers" { [-signal min-max] \ + [-clock min-max] \ +};# checker off proc set_routing_layers { args } { sta::parse_key_args "set_routing_layers" args \ - keys {-signal -clock} + keys {-signal -clock} flags {};# checker off sta::check_argc_eq0 "set_routing_layers" $args @@ -139,6 +143,9 @@ proc set_routing_layers { args } { sta::define_cmd_args "set_macro_extension" { extension } proc set_macro_extension { args } { + sta::parse_key_args "set_macro_extension" args \ + keys {} \ + flags {} if {[llength $args] == 1} { lassign $args extension sta::check_positive_integer "macro_extension" $extension @@ -151,6 +158,9 @@ proc set_macro_extension { args } { sta::define_cmd_args "set_pin_offset" { offset } proc set_pin_offset { args } { + sta::parse_key_args "set_pin_offset" args \ + keys {} \ + flags {} if {[llength $args] == 1} { lassign $args offset sta::check_positive_integer "pin_offset" $offset @@ -167,7 +177,7 @@ sta::define_cmd_args "set_global_routing_random" { [-seed seed] \ proc set_global_routing_random { args } { sta::parse_key_args "set_global_routing_random" args \ - keys { -seed -capacities_perturbation_percentage -perturbation_amount } + keys { -seed -capacities_perturbation_percentage -perturbation_amount } flags {} sta::check_argc_eq0 "set_global_routing_random" $args @@ -201,6 +211,8 @@ sta::define_cmd_args "global_route" {[-guide_file out_file] \ [-grid_origin origin] \ [-critical_nets_percentage percent] \ [-allow_congestion] \ + [-allow_overflow] \ + [-overflow_iterations iterations] \ [-verbose] \ [-start_incremental] \ [-end_incremental] @@ -289,13 +301,13 @@ proc global_route { args } { } } -sta::define_cmd_args "repair_antennas" { [diode_cell] \ +sta::define_cmd_args "repair_antennas" { diode_cell \ [-iterations iterations] \ [-ratio_margin ratio_margin]} proc repair_antennas { args } { sta::parse_key_args "repair_antennas" args \ - keys {-iterations -ratio_margin} + keys {-iterations -ratio_margin} flags {} if { [grt::have_routes] } { if { [llength $args] == 0 } { # repairAntennas locates diode @@ -368,6 +380,9 @@ proc set_nets_to_route { args } { sta::define_cmd_args "read_guides" { file_name } proc read_guides { args } { + sta::parse_key_args "read_guides" args \ + keys {} \ + flags {} set file_name $args grt::read_guides $file_name } diff --git a/src/grt/test/extract_utils.py b/src/grt/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/grt/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/grt/test/grt_man_tcl_check.ok b/src/grt/test/grt_man_tcl_check.ok new file mode 100644 index 00000000000..85a03d6503a --- /dev/null +++ b/src/grt/test/grt_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/grt 12 12 13 +Command counts do not match. diff --git a/src/grt/test/grt_man_tcl_check.py b/src/grt/test/grt_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/grt/test/grt_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/grt/test/grt_readme_msgs_check.ok b/src/grt/test/grt_readme_msgs_check.ok new file mode 100644 index 00000000000..18475a1e69d --- /dev/null +++ b/src/grt/test/grt_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 13, Desc: 13, Syn: 13, Options: 13, Args: 13 +Info: 29, Warn: 42, Error: 90 diff --git a/src/grt/test/grt_readme_msgs_check.py b/src/grt/test/grt_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/grt/test/grt_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/grt/test/manpage.py b/src/grt/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/grt/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/grt/test/md_roff_compat.py b/src/grt/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/grt/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/grt/test/regression_tests.tcl b/src/grt/test/regression_tests.tcl index 76f6d9e1314..aa04ca25a63 100644 --- a/src/grt/test/regression_tests.tcl +++ b/src/grt/test/regression_tests.tcl @@ -63,4 +63,6 @@ record_tests { tracks3 unplaced_inst upper_layer_net + grt_man_tcl_check + grt_readme_msgs_check } diff --git a/src/gui/README.md b/src/gui/README.md index 28f78a85c8d..0b7e2a698c2 100644 --- a/src/gui/README.md +++ b/src/gui/README.md @@ -1,282 +1,414 @@ # Graphical User Interface -The graphical user interface can be access by launching OpenROAD with ``-gui`` or by opening it from the command-line with ``gui::show``. +The graphical user interface can be access by launching OpenROAD with ``-gui`` or +by opening it from the command-line with ``gui::show``. ## Commands -### Add buttons to the toolbar - -``` -create_toolbar_button [-name name] - -text button_text - -script tcl_script - [-echo] +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. ``` -Returns: name of the new button, either ``name`` or ``buttonX``. +### Add Buttons to the Toolbar -Options description: -- ``button_text``: The text to put on the button. -- ``tcl_script``: The tcl script to evaluate when the button is pressed. -- ``name``: (optional) name of the button, used when deleting the button. -- ``echo``: (optional) indicate that the commands in the ``tcl_script`` should be echoed in the log. +This command creates toolbar button with name set using the +`-text` flag and accompanying logic in the `-script` flag. -To remove the button: +Returns: name of the new button, either ``name`` or ``buttonX``. -``` -gui::remove_toolbar_button name +```tcl +create_toolbar_button + [-name name] + -text button_text + -script tcl_script + [-echo] ``` -### Add items to the menubar +#### Options -``` -create_menu_item [-name name] - [-path menu_path] - -text item_text - -script tcl_script - [-shortcut key_shortcut] - [-echo] +| Switch Name | Description | +| ---- | ---- | +| `-name` | The name of the button, used when deleting the button. | +| `-text` | The text to put on the button. | +| `-script` | The tcl script to evaluate when the button is pressed. | +| `-echo` | This indicate that the commands in the ``tcl_script`` should be echoed in the log. | + +### Remove Toolbar Button + +To remove toolbar button: + +```tcl +gui::remove_toolbar_button + name ``` -Returns: name of the new item, either ``name`` or ``actionX``. +#### Options -Options description: -- ``item_text``: The text to put on the item. -- ``tcl_script``: The tcl script to evaluate when the button is pressed. -- ``name``: (optional) name of the item, used when deleting the item. -- ``menu_path``: (optional) Menu path to place the new item in (hierarchy is separated by /), defaults to "Custom Scripts", but this can also be "Tools" or "New menu/New submenu". -- ``key_shortcut``: (optional) key shortcut to trigger this item. -- ``echo``: (optional) indicate that the commands in the ``tcl_script`` should be echoed in the log. +| Switch Name | Description | +| ---- | ---- | +| `-name` | The name of the button, used when deleting the button. | -To remove the item: +### Add items to the Menubar -``` -gui::remove_menu_item name +This command add items to the menubar. +Returns: name of the new item, either ``name`` or ``actionX``. + + +```tcl +create_menu_item + [-name name] + [-path menu_path] + -text item_text + -script tcl_script + [-shortcut key_shortcut] + [-echo] ``` +#### Options -### Save screenshot of layout +| Switch Name | Description | +| ---- | ---- | +| `-name` | (optional) name of the item, used when deleting the item.| +| `-path`| (optional) Menu path to place the new item in (hierarchy is separated by /), defaults to "Custom Scripts", but this can also be "Tools" or "New menu/New submenu".| +| `-text` | The text to put on the item.| +| `-script` | The tcl script to evaluate when the button is pressed.| +| `-shortcut`| (optional) key shortcut to trigger this item.| +| `-echo` | (optional) indicate that the commands in the ``tcl_script`` should be echoed in the log. | -This command can be both be used when the GUI is active and not active. +### Remove items from the Menubar -``` -save_image [-resolution microns_per_pixel] - [-area {x0 y0 x1 y1}] - [-width width] - [-display_option {option value}] - filename +To remove menu item: + +```tcl +gui::remove_menu_item + name ``` -Options description: -- ``filename`` path to save the image to. -- ``x0, y0`` first corner of the layout area (in microns) to be saved, default is to save what is visible on the screen unless called when gui is not active and then it selected the whole block. -- ``x1, y1`` second corner of the layout area (in microns) to be saved, default is to save what is visible on the screen unless called when gui is not active and then it selected the whole block. -- ``microns_per_pixel`` resolution in microns per pixel to use when saving the image, default will match what the GUI has selected. -- ``width`` width of the output image in pixels, default will be computed from the resolution. Cannot be used with ``-resolution``. -- ``option`` specific setting for a display option to show or hide specific elements. For example, to hide metal1 ``-display_option {Layers/metal1 false}``, to show routing tracks ``-display_option {Tracks/Pref true}``, or to show everthing ``-display_option {* true}``. +#### Options -### Save screenshot of clock trees +| Switch Name | Description | +| ---- | ---- | +| `-name` | name of the item, used when deleting the item.| -``` -save_clocktree_image filename - -clock clock_name - [-width width] - [-height height] - [-corner corner] +### Save Image + +This command can be both be used when the GUI is active and not active +to save a screenshot with various options. + +```tcl +save_image + [-resolution microns_per_pixel] + [-area {x0 y0 x1 y1}] + [-width width] + [-display_option {option value}] + filename ``` -Options description: -- ``filename`` path to save the image to. -- ``-clock`` name of the clock to save the clocktree for. -- ``-corner`` name of the timing corner to save the clocktree for, default to the first corner defined. -- ``-height`` height of the image in pixels, defaults to the height of the GUI widget. -- ``-width`` width of the image in pixels, defaults to the width of the GUI widget. +#### Options -### Selecting objects +| Switch Name | Description | +| ---- | ---- | +| `filename` | path to save the image to. | +| `-area` | x0, y0 - first corner of the layout area (in microns) to be saved, default is to save what is visible on the screen unless called when gui is not active and then it selected the whole block. x1, y1 - second corner of the layout area (in microns) to be saved, default is to save what is visible on the screen unless called when gui is not active and then it selected the whole block.| +| `-resolution`| resolution in microns per pixel to use when saving the image, default will match what the GUI has selected.| +| `-width`| width of the output image in pixels, default will be computed from the resolution. Cannot be used with ``-resolution``.| +| `-display_option`| specific setting for a display option to show or hide specific elements. For example, to hide metal1 ``-display_option {Layers/metal1 false}``, to show routing tracks ``-display_option {Tracks/Pref true}``, or to show everthing ``-display_option {* true}``.| -``` -select -type object_type - [-name glob_pattern] - [-filter attribute=value] - [-case_insensitive] - [-highlight group] +### Save Clocktree Image + +This command saves the screenshot of clocktree given options +to `filename`. + +```tcl +save_clocktree_image + filename + -clock clock_name + [-width width] + [-height height] + [-corner corner] ``` -Returns: number of objects selected. +#### Options -Options description: -- ``object_type``: name of the object type. For example, ``Inst`` for instances, ``Net`` for nets, and ``DRC`` for DRC violations. -- ``glob_pattern``: (optional) filter selection by the specified name. For example, to only select clk nets ``*clk*``. Use ``-case_insensitive`` to filter based on case insensitive instead of case sensitive. -- ``attribute=value``: (optional) filter selection based on the objects' properties. ``attribute`` represents the property's name and ``value`` the property's value. In case the property holds a collection (e. g. BTerms in a Net) or a table (e. g. Layers in a Generate Via Rule) ``value`` can be any element within those. A special case exists for checking whether a collection is empty or not by using the value ``CONNECTED``. This can be useful to select a specific group of elements (e. g. BTerms=CONNECTED will select only Nets connected to Input/Output Pins). -- ``group``: (optional) add the selection to the specific highlighting group. Values can be 0 to 7. +| Switch Name | Description | +| ---- | ---- | +|`filename`| path to save the image to. | +|`-clock`| name of the clock to save the clocktree for. | +|`-corner`| name of the timing corner to save the clocktree for, default to the first corner defined. | +|`-height`| height of the image in pixels, defaults to the height of the GUI widget. | +|`-width`| width of the image in pixels, defaults to the width of the GUI widget. | -### Displaying timing cones +### Select Objects +This command selects object based on options. +Returns: number of objects selected. + +```tcl +select + -type object_type + [-name glob_pattern] + [-filter attribute=value] + [-case_insensitive] + [-highlight group] ``` -display_timing_cone pin - [-fanin] - [-fanout] - [-off] -``` -Options description: -- ``pin``: name of the instance or block pin. -- ``fanin``: (optional) display the fanin timing cone. -- ``fanout``: (optional) display the fanout timing cone. -- ``off``: (optional) remove the timing cone. +#### Options + +| Switch Name | Description | +| ---- | ---- | +|`-type`| name of the object type. For example, ``Inst`` for instances, ``Net`` for nets, and ``DRC`` for DRC violations.| +|`-name`| (optional) filter selection by the specified name. For example, to only select clk nets ``*clk*``. Use ``-case_insensitive`` to filter based on case insensitive instead of case sensitive.| +|`-filter`| (optional) filter selection based on the objects' properties. ``attribute`` represents the property's name and ``value`` the property's value. In case the property holds a collection (e. g. BTerms in a Net) or a table (e. g. Layers in a Generate Via Rule) ``value`` can be any element within those. A special case exists for checking whether a collection is empty or not by using the value ``CONNECTED``. This can be useful to select a specific group of elements (e. g. BTerms=CONNECTED will select only Nets connected to Input/Output Pins).| +|`-highlight`| (optional) add the selection to the specific highlighting group. Values can be 0 to 7. | -### Limit drawing to specific nets +### Display Timing Cones +This command displays timing cones for a pin given options. + +```tcl +display_timing_cone + pin + [-fanin] + [-fanout] + [-off] ``` -focus_net net - [-remove] - [-clear] + +#### Options + +| Switch Name | Description | +| ---- | ---- | +|`pin` | name of the instance or block pin. | +|`-fanin`| (optional) display the fanin timing cone. | +|`-fanout`| (optional) display the fanout timing cone. | +|`-off`| (optional) remove the timing cone. | + +### Focus Net + +This command limits the drawing to specified net. + +```tcl +focus_net + net + [-remove] + [-clear] ``` -Options description: -- ``pin``: name of the net. -- ``remove``: (optional) removes the net from from the focus. -- ``clear``: (optional) clears all nets from focus. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `pin` | name of the net. | +| `-remove` | (optional) removes the net from from the focus. | +| `-clear` | (optional) clears all nets from focus. | ## TCL functions -### Support +### Is GUI Enabled Determine is the GUI is active: -``` +```tcl gui::enabled ``` +### Trigger GUI to Load Design + Announce to the GUI that a design was loaded (note: this is only needed when the design was loaded through the odb API and not via ``read_def`` or ``read_db``): -``` +```tcl gui::design_created ``` +### Load DRC Result + To load the results of a DRC report: +```tcl +gui::load_drc + filename ``` -gui::load_drc filename -``` -### Opening and closing +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `filename` | Filename for DRC report. | + +### Show GUI To open the GUI from the command-line (this command does not return until the GUI is closed): +```tcl +gui::show + script + interactive ``` -gui::show -gui::show script -gui::show script interactive -``` -Options description: -- ``script`` TCL script to evaluate in the GUI. -- ``interactive`` indicates if true the GUI should open in an interactive session (default), or if false that the GUI would execute the script and return to the terminal. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `script` | TCL script to evaluate in the GUI. | +| `interactive` | Boolean if true, the GUI should open in an interactive session (default), or if false that the GUI would execute the script and return to the terminal.| + +### Hide GUI To close the GUI and return to the command-line: -``` +```tcl gui::hide ``` -### Layout navigation +### Layout Fit To fit the whole layout in the window: -``` +```tcl gui::fit ``` +### Zoom to a specific region + To zoom in our out to a specific region: +```tcl +gui::zoom_to + x0 y0 x1 y1 ``` -gui::zoom_to x0 y0 x1 y1 -``` -Options description: -- ``x0, y0`` first corner of the layout area in microns. -- ``x1, y1`` second corner of the layout area in microns. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x0, y0, x1, y1`| first and second corner of the layout area in microns.| + +### Zoom In To zoom in the layout: +```tcl +gui::zoom_in + x y ``` -gui::zoom_in -gui::zoom_in x y -``` -Options description: -- ``x, y`` new center of layout. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x, y` | new center of layout in microns.| + +### Zoom Out To zoom out the layout: -``` -gui::zoom_out -gui::zoom_out x y +```tcl +gui::zoom_out + x y ``` -Options description: -- ``x, y`` new center of layout. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x, y` | new center of layout in microns.| + +### Center At To move the layout to new area: +```tcl +gui::center_at + x y ``` -gui::center_at x y -``` -Options description: -- ``x, y`` new center of layout. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x, y` | new center of layout in microns.| + +### Set Resolution To change the resolution to a specific value: +```tcl +gui::set_resolution + resolution ``` -gui::set_resolution resolution -``` -Options description: -- ``resolution`` database units per pixel. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `resolution` | database units per pixel. | -### Selections +### Add a single net to selection To add a single net to the selected items: -``` -gui::selection_add_net name +```tcl +gui::selection_add_net + name ``` -Options description: -- ``name`` name of the net to add. +#### Options -To add several nets to the selected items: +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the net to add.| +### Add multiple nets to selection + +To add several nets to the selected items using a regex: + +```tcl +gui::selection_add_nets + name_regex ``` -gui::selection_add_nets name_regex -``` -Options description: -- ``name_regex`` regular expression of the net names to add. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name_regex`| regular expression of the net names to add.| + +### Add a single inst to selection To add a single instance to the selected items: -``` -gui::selection_add_inst name +```tcl +gui::selection_add_inst + name ``` -Options description: -- ``name`` name of the instance to add. +#### Options -To add several instances to the selected items: +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the instance to add. | +### Add multiple insts to selection + +To add several instances to the selected items using a regex: + +```tcl +gui::selection_add_insts + name_regex ``` -gui::selection_add_insts name_regex -``` -Options description: -- ``name_regex`` regular expression of the instance names to add. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name_regex` | regular expression of the instance names to add. | + +### Select at point or area To add items at a specific point or in an area: +Example usage: ``` gui::select_at x y gui::select_at x y append @@ -284,81 +416,132 @@ gui::select_at x0 y0 x1 y1 gui::select_at x0 y0 x1 y1 append ``` -Options description: -- ``x, y`` point in the layout area in microns. -- ``x0, y0`` first corner of the layout area in microns. -- ``x1, y1`` second corner of the layout area in microns. -- ``append`` if ``true`` (the default value) append the new selections to the current selection list, else replace the selection list with the new selections. +```tcl +gui::select_at + x0 y0 x1 y1 + [append] -To navigate through multiple selected items: +Or +gui::select_at + x y + [append] ``` + +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x, y` | point in the layout area in microns. | +| `x0, y0, x1, y1`| first and second corner of the layout area in microns. | +| `append`| if ``true`` (the default value) append the new selections to the current selection list, else replace the selection list with the new selections. | + +### Select next item from selection + +To navigate through multiple selected items: +Returns: current index of the selected item. + +```tcl gui::select_next -gui::select_previous ``` +### Select previous item from selection + +To navigate through multiple selected items: Returns: current index of the selected item. +```tcl +gui::select_previous +``` + +### Clear Selection + To clear the current set of selected items: -``` +```tcl gui::clear_selections ``` +### Get Selection Property + To get the properties for the current selection in the Inspector: +```tcl +gui::get_selection_property + name ``` -gui::get_selection_property name -``` -Options description: -- ``name`` name of the property. For example, ``Type`` for object type or ``bbox`` for the bounding box of the object. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the property. For example, ``Type`` for object type or ``bbox`` for the bounding box of the object. | + +### Animate Selection To animate the current selection in the Inspector: +```tcl +gui::selection_animate + [repeat] ``` -gui::selection_animate -gui::selection_animate repeat -``` -Options description: -- ``repeat``: indicate how many times the animation should repeat, default value is 0 repeats. If the value is 0, the animation will repeat indefinitely. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `repeat` | indicate how many times the animation should repeat, default value is 0 repeats. If the value is 0, the animation will repeat indefinitely.| -### Highlighting +### Highlight Net To highlight a net: +```tcl +gui::highlight_net + name + [highlight_group] ``` -gui::highlight_net name -gui::highlight_net name highlight_group -``` -Options description: -- ``name`` name of the net to highlight. -- ``highlight_group`` group to add the highlighted net to, defaults to ``0``, valid groups are ``0 - 7``. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the net to highlight.| +| `highlight_group` | group to add the highlighted net to, defaults to ``0``, valid groups are ``0 - 7``. | + +### Highlight Instance To highlight an instance: +```tcl +gui::highlight_inst + name + [highlight_group] ``` -gui::highlight_inst name -gui::highlight_inst name highlight_group -``` -Options description: -- ``name`` name of the instance to highlight. -- ``highlight_group`` group to add the highlighted instance to, defaults to ``0``, valid groups are ``0 - 7``. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the instance to highlight. | +| `highlight_group` | group to add the highlighted instance to, defaults to ``0``, valid groups are ``0 - 7``. | + +### Clear Highlight Groups To clear the highlight groups: -``` +```tcl gui::clear_highlights -gui::clear_highlights highlight_group + [highlight_group] ``` -Options description: -- ``highlight_group`` group to clear, defaults to ``0``, valid groups are ``-1 - 7``. Use ``-1`` to clear all groups. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `highlight_group` | group to clear, defaults to ``0``, valid groups are ``-1 - 7``. Use ``-1`` to clear all groups. | -### Rulers +### Add Ruler to Layout To add a ruler to the layout: @@ -367,140 +550,212 @@ To disable snapping for the ruler when adding, hold the ``Ctrl`` key, and to all 2. or use the command: -``` -gui::add_ruler x0 y0 x1 y1 -gui::add_ruler x0 y0 x1 y1 label -gui::add_ruler x0 y0 x1 y1 label name -gui::add_ruler x0 y0 x1 y1 label name euclidian +Returns: name of the newly created ruler. + +```tcl +gui::add_ruler + x0 y0 x1 y1 + [label] + [name] + [euclidian] ``` -Returns: name of the newly created ruler. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `x0, y0, x1, y1` | first and second end point of the ruler in microns. | +| `label` | text label for the ruler. | +| `name` | name of the ruler. | +| `euclidian` | ``1`` for euclidian ruler, and ``0`` for regular ruler. | -Options description: -- ``x0, y0`` first end point of the ruler in microns. -- ``x1, y1`` second end point of the ruler in microns. -- ``label`` text label for the ruler. -- ``name`` name of the ruler. -- ``euclidian`` ``1`` for euclidian ruler, and ``0`` for regular ruler. +### Delete a single ruler To remove a single ruler: +```tcl +gui::delete_ruler + name ``` -gui::delete_ruler name -``` -Options description: -- ``name`` name of the ruler. +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | name of the ruler. | + +### Clear All Rulers To remove all the rulers: -``` +```tcl gui::clear_rulers ``` -### Heat Maps +### Set Heatmap -The currently availble heat maps are: +To control the settings in the heat maps: +The currently availble heat maps are: - ``Power`` - ``Routing`` - ``Placement`` - ``IRDrop`` - ``RUDY`` [^RUDY] -To control the settings in the heat maps: +These options can also be modified in the GUI by double-clicking the underlined display control for the heat map. -``` -gui::set_heatmap name option -gui::set_heatmap name option value +```tcl +gui::set_heatmap + name + [option] + [value] ``` -Options description: -- ``name`` is the name of the heatmap. -- ``option`` is the name of the option to modify. If option is ``rebuild`` the map will be destroyed and rebuilt. -- ``value`` is the new value for the specified option. This is not used when rebuilding map. +#### Options -These options can also be modified in the GUI by double-clicking the underlined display control for the heat map. +| Switch Name | Description | +| ---- | ---- | +| `name` | is the name of the heatmap. | +| `option` | is the name of the option to modify. If option is ``rebuild`` the map will be destroyed and rebuilt. | +| `value` | is the new value for the specified option. This is not used when rebuilding map. | +### Dump Heatmap to file To save the raw data from the heat maps ins a comma separated value (CSV) format: -``` -gui::dump_heatmap name filename +```tcl +gui::dump_heatmap + name + filename ``` -Options description: -- ``name`` is the name of the heatmap. -- ``filename`` path to the file to write the data to. +#### Options -[^RUDY]: RUDY means Rectangular Uniform wire DensitY, which can predict the routing density very rough and quickly. You can see this notion in [this paper](https://past.date-conference.com/proceedings-archive/2007/DATE07/PDFFILES/08.7_1.PDF) +| Switch Name | Description | +| ---- | ---- | +|`name` | is the name of the heatmap. | +|`filename` | path to the file to write the data to. | +[^RUDY]: RUDY means Rectangular Uniform wire DensitY, which can predict the routing density very rough and quickly. You can see this notion in [this paper](https://past.date-conference.com/proceedings-archive/2007/DATE07/PDFFILES/08.7_1.PDF) ### GUI Display Controls Control the visible and selected elements in the layout: +```tcl +gui::set_display_controls + name + [display_type] + [value] ``` -gui::set_display_controls name display_type value -``` -Options description: -- ``name`` is the name of the control. For example, for the power nets option this would be ``Signals/Power`` or could be ``Layers/*`` to set the option for all the layers. -- ``display_type`` is either ``visible`` or ``selectable`` -- ``value`` is either ``true`` or ``false`` +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | is the name of the control. For example, for the power nets option this would be ``Signals/Power`` or could be ``Layers/*`` to set the option for all the layers. | +| `display_type` | is either ``visible`` or ``selectable`` | +| `value` |is either ``true`` or ``false`` | + +### Check Display Controls To check the visibility or selectability of elements in the layout: -``` -gui::check_display_controls name display_type +```tcl +gui::check_display_controls + name + display_type ``` -Options description: -- ``name`` is the name of the control. For example, for the power nets option this would be ``Signals/Power`` or could be ``Layers/*`` to set the option for all the layers. -- ``display_type`` is either ``visible`` or ``selectable`` +#### Options +| Switch Name | Description | +| ---- | ---- | +| `name` | is the name of the control. For example, for the power nets option this would be ``Signals/Power`` or could be ``Layers/*`` to set the option for all the layers. | +| `display_type` | is either ``visible`` or ``selectable`` | -When performing a batch operation changing the display controls settings, the following commands can be used to save the current state of the display controls and restore them at the end. +### Save Display Controls -``` +When performing a batch operation changing the display controls settings, +the following command can be used to save the current state of the display controls. + +```tcl gui::save_display_controls +``` + +### Restore Display Controls + +This command restores display controls. + +```tcl gui::restore_display_controls ``` -### GUI Controls +### Input Dialog To request user input via the GUI: +Returns: a string with the input, or empty string if canceled. -``` -gui::input_dialog title question +```tcl +gui::input_dialog + title + question ``` -Returns: a string with the input, or empty string if canceled. +#### Options -Options description: -- ``title`` is the title of the input message box. -- ``question`` is the text for the message box. +| Switch Name | Description | +| ---- | ---- | +| `title` | is the title of the input message box. | +| `question` | is the text for the message box. | + +### Pause script execution Pause the execution of the script: -``` +```tcl gui::pause -gui::pause timeout + [timeout] ``` -Options description: -- ``timeout`` is specified in milliseconds, if it is not provided the pause will last until the user presses the Continue button. +#### Options -To open or close a specific layout widget: +| Switch Name | Description | +| ---- | ---- | +| `timeout` | is specified in milliseconds, if it is not provided the pause will last until the user presses the Continue button.| +### Show widget + +To open a specific layout widget: + +```tcl +gui::show_widget + name ``` -gui::show_widget name -gui::hide_widget name + +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | of the widget. For example, the display controls would be "Display Control". | + +### Hide widget + +To close a specific layout widget: + +```tcl +gui::hide_widget + name ``` -Options description: -- ``name`` of the widget. For example, the display controls would be "Display Control". +#### Options + +| Switch Name | Description | +| ---- | ---- | +| `name` | of the widget. For example, the display controls would be "Display Control". | + ## License diff --git a/src/gui/test/extract_utils.py b/src/gui/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/gui/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/gui/test/gui_man_tcl_check.ok b/src/gui/test/gui_man_tcl_check.ok new file mode 100644 index 00000000000..f8dbc63bfc4 --- /dev/null +++ b/src/gui/test/gui_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/gui 7 7 7 +Command counts match. diff --git a/src/gui/test/gui_man_tcl_check.py b/src/gui/test/gui_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/gui/test/gui_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/gui/test/gui_readme_msgs_check.ok b/src/gui/test/gui_readme_msgs_check.ok new file mode 100644 index 00000000000..df855d11cbe --- /dev/null +++ b/src/gui/test/gui_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 46, Desc: 46, Syn: 46, Options: 46, Args: 46 +Info: 1, Warn: 33, Error: 64 diff --git a/src/gui/test/gui_readme_msgs_check.py b/src/gui/test/gui_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/gui/test/gui_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/gui/test/manpage.py b/src/gui/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/gui/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/gui/test/md_roff_compat.py b/src/gui/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/gui/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/gui/test/regression_tests.tcl b/src/gui/test/regression_tests.tcl index e125917d335..76955f29577 100644 --- a/src/gui/test/regression_tests.tcl +++ b/src/gui/test/regression_tests.tcl @@ -1,3 +1,5 @@ record_tests { supported + gui_man_tcl_check + gui_readme_msgs_check } diff --git a/src/ifp/README.md b/src/ifp/README.md index e58e7ef01e4..ed5403e0a96 100644 --- a/src/ifp/README.md +++ b/src/ifp/README.md @@ -13,45 +13,14 @@ This tool initializes floorplan constraints, die/core area, and makes tracks. Do note that there are two ways of setting the floorplan dimensions. The user can either specify manually die/core area, or -specify the utilization/aspect ratio. +specify the utilization/aspect ratio. If you set both, +unexpected behaviour might occur. -#### Method 1: Automatic die size calculation +- Method 1: Automatic die size calculation +Example: `initialize_floorplan -utilization 70 -aspect_ratio 1.0 -core_space 0.0 -sites FreePDK45_38x28_10R_NP_162NW_34O` -``` -initialize_floorplan - [-utilization util] - [-aspect_ratio ratio] - [-core_space space | {bottom top left right}] - [-sites site_name] -``` -##### Options - -| Switch Name | Description | -| ----- | ----- | -| `-utilization` | Percentage utilization. Allowed values are `double` `>0`, more than 100% utilization can be useful with mock abstracts. | -| `-aspect_ratio` | Ratio $\frac{height}{width}$. The default value is `1.0` and the allowed values are floats `[0, 1.0]`. | -| `-core_space` | Space around the core, default `0.0` microns. Allowed values are either one value for all margins or a set of four values, one for each margin. The order of the four values are: `{bottom top left right}`. | -| `-sites` | Tcl list of sites to make rows for (e.g. `{SITEXX, SITEYY}`) | - - -#### Method 2: Set die/core area - -```tcl -initialize_floorplan - [-die_area {llx lly urx ury}] - [-core_area {llx lly urx ury}] - [-additional_sites site_names] - -site site_name -``` - -##### Options - -| Switch Name | Description | -| ----- | ----- | -| `-die_area` | Die area coordinates in microns (lower left x/y and upper right x/y coordinates). | -| `-core_area` | Core area coordinates in microns (lower left x/y and upper right x/y coordinates). | -| `-site` | The LEF site to make rows for. | -| `-additional_sites` | Any additional LEF site to make rows. | +- Method 2: Set die/core area +Example: `initialize_floorplan -die_area 0 0 2000 2000 -core_area 100 100 1900 1900` -sites FreePDK45_38x28_10R_NP_162NW_34O` The die area and core area used to write ROWs can be specified explicitly with the `-die_area` and `-core_area` arguments. Alternatively, the die and @@ -80,10 +49,33 @@ die = ( 0, 0 ) core_height + core_space_bottom + core_space_top ) ``` + +```tcl +initialize_floorplan + [-utilization util] + [-aspect_ratio ratio] + [-core_space space | {bottom top left right}] + [-die_area {llx lly urx ury}] + [-core_area {llx lly urx ury}] + [-additional_sites site_names] + [-site site_name] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-utilization` | Percentage utilization. Allowed values are `double` in the range `(0-100]`. | +| `-aspect_ratio` | Ratio $\frac{height}{width}$. The default value is `1.0` and the allowed values are floats `[0, 1.0]`. | +| `-core_space` | Space around the core, default `0.0` microns. Allowed values are either one value for all margins or a set of four values, one for each margin. The order of the four values are: `{bottom top left right}`. | +| `-additional_sites` | Tcl list of sites to make rows for (e.g. `{SITEXX, SITEYY}`) | +| `-site` | Site name. | +| `-die_area` | Die area coordinates in microns (lower left x/y and upper right x/y coordinates). | +| `-core_area` | Core area coordinates in microns (lower left x/y and upper right x/y coordinates). | + ### Make Tracks The `initialize_floorplan` command removes existing tracks. - Use the `make_tracks` command to add routing tracks to a floorplan. ```tcl @@ -103,9 +95,9 @@ make_tracks | `-x_pitch`, `-y_pitch` | If set, overrides the LEF technology x-/y- pitch. Use the same unit as in the LEF file. | | `-x_offset`, `-y_offset` | If set, overrides the LEF technology x-/y- offset. Use the same unit as in the LEFT file. | -### Inserting tieoff cells +### Insert tieoff cells -To insert tiecells: +This comamnd inserts tiecells. ```tcl insert_tiecells @@ -120,7 +112,7 @@ insert_tiecells | `tie_pin` | Indicates the master and port to use to tie off nets. For example, `LOGIC0_X1/Z` for the Nangate45 library, where `LOGIC0_X1` is the master and `Z` is the output port on the master. | | `-prefix` | Used to control the prefix of the new tiecell names. This will default to `TIEOFF_`. | -### Useful developer functions +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/InitFloorplan.cc) or the [swig file](./src/InitFloorPlan.i). @@ -132,7 +124,7 @@ If you are a developer, you might find these useful. More details can be found i Example scripts on running `ifp` for a sample design of `mpd_top` are as follows: -```tcl +``` ./test/upf_test.tcl ``` diff --git a/src/ifp/test/extract_utils.py b/src/ifp/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/ifp/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/ifp/test/ifp_man_tcl_check.ok b/src/ifp/test/ifp_man_tcl_check.ok new file mode 100644 index 00000000000..ed32b5cea50 --- /dev/null +++ b/src/ifp/test/ifp_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/ifp 3 3 3 +Command counts match. diff --git a/src/ifp/test/ifp_man_tcl_check.py b/src/ifp/test/ifp_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/ifp/test/ifp_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/ifp/test/ifp_readme_msgs_check.ok b/src/ifp/test/ifp_readme_msgs_check.ok new file mode 100644 index 00000000000..ee4adee6b6b --- /dev/null +++ b/src/ifp/test/ifp_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 3, Desc: 3, Syn: 3, Options: 3, Args: 3 +Info: 4, Warn: 7, Error: 17 diff --git a/src/ifp/test/ifp_readme_msgs_check.py b/src/ifp/test/ifp_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/ifp/test/ifp_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/ifp/test/manpage.py b/src/ifp/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/ifp/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/ifp/test/md_roff_compat.py b/src/ifp/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/ifp/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/ifp/test/regression_tests.tcl b/src/ifp/test/regression_tests.tcl index 4d75425151d..b9fe5033613 100644 --- a/src/ifp/test/regression_tests.tcl +++ b/src/ifp/test/regression_tests.tcl @@ -24,5 +24,7 @@ record_tests { tiecells upf_test upf_shifter_test + ifp_man_tcl_check + ifp_readme_msgs_check } diff --git a/src/mpl/README.md b/src/mpl/README.md index eab4a8f6b2b..d718aa1cd12 100644 --- a/src/mpl/README.md +++ b/src/mpl/README.md @@ -20,6 +20,14 @@ heuristic evaluation function is kept. ### Macro Placement +This command performs macro placement. +For placement style, `corner_max_wl` means that choosing the partitions that maximise the wirelength +of connections between the macros to force them to the corners. Vice versa for `corner_min_wl`. + +Macros will be placed with $max(halo * 2, channel)$ spacing between macros, and between +macros and the fence/die boundary. If no solutions are found, try reducing the +channel/halo. + ```tcl macro_placement [-halo {halo_x halo_y}] @@ -39,15 +47,8 @@ macro_placement | `-snap_layer` | Snap macro origins to this routing layer track. | | `-style` | Placement style, to choose either `corner_max_wl` or `corner_min_wl`. The default value is `corner_max_wl`. | -For placement style, `corner_max_wl` means that choosing the partitions that maximise the wirelength -of connections between the macros to force them to the corners. Vice versa for `corner_min_wl`. - -Macros will be placed with $max(halo * 2, channel)$ spacing between macros, and between -macros and the fence/die boundary. If no solutions are found, try reducing the -channel/halo. - -### Useful developer functions +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/MacroPlacer.cpp) or the [swig file](./src/MacroPlacer.i). @@ -59,7 +60,7 @@ If you are a developer, you might find these useful. More details can be found i Example scripts demonstrating how to run TritonMacroPlace on a sample design of `east_west` as follows: -```tcl +``` ./test/east_west.tcl ./test/east_west1.tcl ./test/east_west2.tcl diff --git a/src/mpl/src/MacroPlacer.tcl b/src/mpl/src/MacroPlacer.tcl index 4e6cea45a08..f01f6feb3d7 100644 --- a/src/mpl/src/MacroPlacer.tcl +++ b/src/mpl/src/MacroPlacer.tcl @@ -119,13 +119,13 @@ proc macro_placement { args } { sta::define_cmd_args "macro_placement_debug" { [-partitions] -} +}; # checker off -# This seg faults if the gui is not present -cherry proc macro_placement_debug { args } { + # This seg faults if the gui is not present -cherry sta::parse_key_args "macro_placement_debug" args \ keys {} \ - flags {-partitions} + flags {-partitions};# checker off set partitions [info exists flags(-partitions)] diff --git a/src/mpl/test/extract_utils.py b/src/mpl/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/mpl/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/mpl/test/manpage.py b/src/mpl/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/mpl/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/mpl/test/md_roff_compat.py b/src/mpl/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/mpl/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/mpl/test/mpl_man_tcl_check.ok b/src/mpl/test/mpl_man_tcl_check.ok new file mode 100644 index 00000000000..3628f2bff80 --- /dev/null +++ b/src/mpl/test/mpl_man_tcl_check.ok @@ -0,0 +1,5 @@ +Tool Dir Help count Proc count Readme count +./src/mpl 1 1 1 +Command counts match. +./src/mpl2 2 2 2 +Command counts match. diff --git a/src/mpl/test/mpl_man_tcl_check.py b/src/mpl/test/mpl_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/mpl/test/mpl_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/mpl/test/mpl_readme_msgs_check.ok b/src/mpl/test/mpl_readme_msgs_check.ok new file mode 100644 index 00000000000..98d6e205c93 --- /dev/null +++ b/src/mpl/test/mpl_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 12, Warn: 7, Error: 8 diff --git a/src/mpl/test/mpl_readme_msgs_check.py b/src/mpl/test/mpl_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/mpl/test/mpl_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/mpl/test/regression_tests.tcl b/src/mpl/test/regression_tests.tcl index 19ea1c77ce3..de7b70eca10 100644 --- a/src/mpl/test/regression_tests.tcl +++ b/src/mpl/test/regression_tests.tcl @@ -4,4 +4,6 @@ record_tests { east_west1 east_west2 snap_layer1 + mpl_man_tcl_check + mpl_readme_msgs_check } diff --git a/src/mpl2/README.md b/src/mpl2/README.md index 7a862442c7a..861e16e30c1 100644 --- a/src/mpl2/README.md +++ b/src/mpl2/README.md @@ -11,12 +11,15 @@ planning approach that exploits the hierarchy and data flow inherent in the desi - Parameters without square brackets `-param2 param2` are required. ``` -### Hier-RTLMP algorithm +### RTL Macro Placer + +This command executes the Hier-RTLMP algorithm for macro placement. ```tcl rtl_macro_placer [-max_num_macro max_num_macro] [-min_num_macro min_num_macro] + [-max_num_macro max_num_macro] [-max_num_inst max_num_inst] [-min_num_inst min_num_inst] [-tolerance tolerance] @@ -26,6 +29,7 @@ rtl_macro_placer [-large_net_threshold large_net_threshold] [-signature_net_threshold signature_net_threshold] [-halo_width halo_width] + [-halo_height halo_height] [-fence_lx fence_lx] [-fence_ly fence_ly] [-fence_ux fence_ux] @@ -48,7 +52,7 @@ rtl_macro_placer [-write_macro_placement file_name] ``` -#### Generic Parameters +#### Options | Switch Name | Description | | ----- | ----- | @@ -71,7 +75,6 @@ rtl_macro_placer | `-report_directory` | Save reports to this directory. | | `-write_macro_placement` | Generates a file with the placement of the macros placed by HierRTLMP flow in the format of multiple calls for the `place_macro` command. | - #### Simulated Annealing Weight parameters Do note that while action probabilities are normalized to 1.0, the weights are not necessarily normalized. @@ -87,14 +90,6 @@ Do note that while action probabilities are normalized to 1.0, the weights are n | `-notch_weight` | Weight for the notch, or the existence of dead space that cannot be used for placement & routing. Note that this cost applies only to hard macro clusters. The allowed values are floats, and the default value is `10.0`. | | `-macro_blockage_weight` | Weight for macro blockage, or the overlapping instances of the macro. The allowed values are floats, and the default value is `10.0`. | -### Write Macro Placement - -Command to generate a file with the placement of the macros in the design using multiple calls for the `place_macro` command: - -```tcl -write_macro_placement file_name -``` - ### Place Macro Command for placement of one specific macro. diff --git a/src/mpl2/src/mpl.tcl b/src/mpl2/src/mpl.tcl index 24b216686e0..f48dee0054d 100644 --- a/src/mpl2/src/mpl.tcl +++ b/src/mpl2/src/mpl.tcl @@ -260,7 +260,7 @@ sta::define_cmd_args "place_macro" {-macro_name macro_name \ proc place_macro { args } { sta::parse_key_args "place_macro" args \ - keys {-macro_name -location -orientation} + keys {-macro_name -location -orientation} flags {} if {[info exists keys(-macro_name)]} { set macro_name $keys(-macro_name) @@ -311,7 +311,7 @@ proc parse_macro_name {cmd macro_name} { proc mpl_debug { args } { sta::parse_key_args "mpl_debug" args \ keys {} \ - flags {-coarse -fine -show_bundled_nets} + flags {-coarse -fine -show_bundled_nets};# checker off set coarse [info exists flags(-coarse)] set fine [info exists flags(-fine)] @@ -323,4 +323,4 @@ proc mpl_debug { args } { mpl2::set_debug_cmd $coarse $fine [info exists flags(-show_bundled_nets)] } -} +} \ No newline at end of file diff --git a/src/mpl2/test/extract_utils.py b/src/mpl2/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/mpl2/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/mpl2/test/manpage.py b/src/mpl2/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/mpl2/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/mpl2/test/md_roff_compat.py b/src/mpl2/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/mpl2/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/mpl2/test/mpl2_man_tcl_check.ok b/src/mpl2/test/mpl2_man_tcl_check.ok new file mode 100644 index 00000000000..c009301ded8 --- /dev/null +++ b/src/mpl2/test/mpl2_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/mpl2 2 2 2 +Command counts match. diff --git a/src/mpl2/test/mpl2_man_tcl_check.py b/src/mpl2/test/mpl2_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/mpl2/test/mpl2_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/mpl2/test/mpl2_readme_msgs_check.ok b/src/mpl2/test/mpl2_readme_msgs_check.ok new file mode 100644 index 00000000000..cd7b603d405 --- /dev/null +++ b/src/mpl2/test/mpl2_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 2, Desc: 2, Syn: 2, Options: 2, Args: 2 +Info: 2, Warn: 6, Error: 21 diff --git a/src/mpl2/test/mpl2_readme_msgs_check.py b/src/mpl2/test/mpl2_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/mpl2/test/mpl2_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/mpl2/test/regression_tests.tcl b/src/mpl2/test/regression_tests.tcl index a89e0946a02..631372da658 100644 --- a/src/mpl2/test/regression_tests.tcl +++ b/src/mpl2/test/regression_tests.tcl @@ -1,3 +1,5 @@ record_tests { macro_only + mpl2_man_tcl_check + mpl2_readme_msgs_check } diff --git a/src/odb/README.md b/src/odb/README.md index 17bff93d033..56459c5a88f 100644 --- a/src/odb/README.md +++ b/src/odb/README.md @@ -1,9 +1,9 @@ # OpenDB -OpenDB is a design database to support tools for physical chip design. It -was originally developed by Athena Design Systems. Nefelus, Inc. acquired -the rights to the code and open-sourced it with BSD-3 license in 2019 to support the DARPA -OpenROAD project. +The OpenDB (`odb`) module in OpenROAD is a design database to support tools for physical +chip design. It was originally developed by Athena Design Systems. +Nefelus, Inc. acquired the rights to the code and open-sourced it with BSD-3 license +in 2019 to support the DARPA OpenROAD project. The structure of OpenDB is based on the text file formats LEF (library) and DEF (design) formats version 5.6. OpenDB supports a @@ -14,6 +14,12 @@ OpenDB is written in C++ 98 with standard library style iterators. The classes are designed to be fast enough to base an application on without having to copy them into application-specific structures. +## Commands + +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` ## Directory structure @@ -32,24 +38,7 @@ We are still working on documenting the APIs. We have over 1,800 objects and functions that we are still documenting (for both TCL and Python). **Contributions are very welcome in this effort**. Find starting points below. -### TCL - -After building successfully, run OpenDB Tcl shell using -`../../build/src/odb/src/swig/tcl/odbtcl`. An example usage: - -``` -set db [dbDatabase_create] -set lef_parser [new_lefin $db true] -set tech [lefin_createTech $lef_parser ./src/odb/test/data/gscl45nm.lef] -``` - -You can find examples on using the API from Tcl under `test/tcl/` directory. - -The full set of the Tcl commands exposed can be found under -`./build/src/swig/tcl/opendb_wrapper.cpp`. Search for `SWIG_prefix`. - - -### Python +## Python After building successfully, run `openroad -python` to enable the Python interpreter. You can find examples on using the API from Python under @@ -64,7 +53,7 @@ print(', '.join(dir(openroad))) print(', '.join(dir(odb))) ``` -### C++ +## C++ All public database classes are defined in `db.h`. These class definitions provide all functions for examining and modifying the database objects. The @@ -89,15 +78,327 @@ database to have exactly the same layout across save/restores. The database distance units are **nanometers** and use the type `uint`. +### Create Physical Cluster + +Description TBC. + +```tcl +create_physical_cluster cluster_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `cluster_name` | Name of cluster. | + + +### Create Child Physical Clusters + +Description TBC. + +```tcl +create_child_physical_clusters + [-top_module] +or +create_child_physical_clusters + [-modinst path] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `top_module` | TBC. | +| `-modinst` | TBC. | + +### Set NDR Layer Rule + +Description TBC. + +```tcl +set_ndr_layer_rule + tech + ndr + layerName + input + isSpacing +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `tech` | TBC. | +| `ndr` | TBC. | +| `values` | TBC. | +| `isSpacing` | TBC. | + +### Set NDR Rules + +Description TBC. + +```tcl +set_ndr_rules + tech + ndr + values + isSpacing +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `tech` | TBC. | +| `ndr` | TBC. | +| `layerName` | TBC. | +| `input` | TBC. | + +### Create NDR + +Description TBC. + +```tcl +create_ndr + -name name + [-spacing val] + [-width val] + [-via val] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-name` | TBC. | +| `-spacing` | TBC. | +| `-width` | TBC. | +| `-via` | TBC. | + +### Create Voltage Domain + +Description TBC. + +```tcl +create_voltage_domain + domain_name + -area {llx lly urx ury} +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-domain_name` | TBC. | +| `-area` | TBC. | + +### Delete Physical Cluster + +Description TBC. + +```tcl +delete_physical_cluster cluster_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `cluster_name` | TBC. | + +### Delete Voltage Domain + +Description TBC. + +```tcl +delete_voltage_domain domain_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `domain_name` | TBC. | + +### Assign Power Net + +Description TBC. + +```tcl +assign_power_net + -domain domain_name + -net snet_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-domain_name` | TBC. | +| `-net` | TBC. | + +### Assign Ground Net + +Description TBC. + +```tcl +assign_ground_net + -domain domain_name + -net snet_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-domain_name` | TBC. | +| `-net` | TBC. | + +### Add to Physical Cluster + +Description TBC. + +```tcl +add_to_physical_cluster + [-modinst path] + cluster_name +or +add_to_physical_cluster + [-inst inst_name] + cluster_name +or +add_to_physical_cluster + [-physical_cluster cluster_name] + cluster_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-modinst` | TBC. | +| `-inst` | TBC. | +| `-physical_cluster` | TBC. | +| `cluster_name` | TBC. | + +### Remove From Physical Cluster + +Description TBC. + +```tcl +remove_from_physical_cluster + [-parent_module module_name] + [-modinst modinst_name] + cluster_name +or +remove_from_physical_cluster + [-inst inst_name] + cluster_name +or +remove_from_physical_cluster + [-physical_cluster cluster_name] + cluster_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-parent_module` | TBC. | +| `-modinst` | TBC. | +| `-inst` | TBC. | +| `-physical_cluster` | TBC. | +| `-cluster_name` | TBC. | + +### Report Physical Clusters + +Description TBC. + +```tcl +report_physical_clusters +``` + +### Report Voltage Domains + +Description TBC. + +```tcl +report_voltage_domains +``` + +### Report Group + +Description TBC. + +```tcl +report_group group +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `group` | TBC. | + +### Write Guides + +This command writes global routing guides, which can be used as input +for global routing. + +Example: `write_guides route.guide`. + +```tcl +write_guides file_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `file_name` | Guide file name. | + +### Write Macro Placement + +This command writes macro placement. + +```tcl +write_macro_placement file_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `file_name` | Macro placement file name. | + + + + ## Example scripts +After building successfully, run OpenDB Tcl shell using +`../../build/src/odb/src/swig/tcl/odbtcl`. An example usage: + +``` +set db [dbDatabase_create] +set lef_parser [new_lefin $db true] +set tech [lefin_createTech $lef_parser ./src/odb/test/data/gscl45nm.lef] +``` + +You can find examples on using the API from Tcl under `test/tcl/` directory. + +The full set of the Tcl commands exposed can be found under +`./build/src/swig/tcl/opendb_wrapper.cpp`. Search for `SWIG_prefix`. + ## Regression tests -There are a set of regression tests in /test. +There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). -``` -./test/regression-tcl.sh -./test/regression-py.sh +Simply run the following script: + +```shell +./test/regression ``` ## Database Internals @@ -160,7 +461,7 @@ DRC on the whole chip). ## FAQs Check out -[GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/discussions/categories/q-a?discussions_q=category%3AQ%26A+fastroute+in%3Atitle) +[GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/discussions/categories/q-a?discussions_q=category%3AQ%26A+odb+in%3Atitle) about this tool. diff --git a/src/odb/include/odb/geom_boost.h b/src/odb/include/odb/geom_boost.h index 966fa20afff..2a5da178a33 100644 --- a/src/odb/include/odb/geom_boost.h +++ b/src/odb/include/odb/geom_boost.h @@ -35,6 +35,8 @@ #pragma once +#include +#include #include #include "odb/geom.h" @@ -83,6 +85,16 @@ struct boost::polygon::point_mutable_traits } }; +// Make odb's Point work with boost geometry + +BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(odb::Point, + int, + boost::geometry::cs::cartesian, + getX, + getY, + setX, + setY); + // Make odb's Rect work with boost polgyon template <> @@ -134,3 +146,65 @@ struct boost::polygon::rectangle_mutable_traits high(interval_vertical)); } }; + +// Make odb's Rect work with boost geometry. +// +// Unfortunately BOOST_GEOMETRY_REGISTER_BOX forces a bad API on the class +// and there is not _GET_SET version. Instead we have to go lower to the +// traits to adapt. + +namespace boost::geometry::traits { + +template <> +struct tag +{ + using type = box_tag; +}; + +template <> +struct point_type +{ + using type = odb::Point; +}; + +template +struct indexed_access +{ + using coordinate_type = int; + + static constexpr coordinate_type get(const odb::Rect& b) + { + return (Dimension == 0) ? b.xMin() : b.yMin(); + } + + static void set(odb::Rect& b, const int value) + { + if (Dimension == 0) { + b.set_xlo(value); + } else { + b.set_ylo(value); + } + } +}; + +template +struct indexed_access +{ + using coordinate_type = int; + + static constexpr coordinate_type get(const odb::Rect& b) + { + return (Dimension == 0) ? b.xMax() : b.yMax(); + } + + static void set(odb::Rect& b, const int value) + { + if (Dimension == 0) { + b.set_xhi(value); + } else { + b.set_yhi(value); + } + } +}; + +} // namespace boost::geometry::traits diff --git a/src/odb/src/db/odb.tcl b/src/odb/src/db/odb.tcl index eef0859db27..2cc85aad47d 100644 --- a/src/odb/src/db/odb.tcl +++ b/src/odb/src/db/odb.tcl @@ -1,7 +1,7 @@ - sta::define_cmd_args "create_physical_cluster" {cluster_name} proc create_physical_cluster { args } { + sta::parse_key_args "create_physical_cluster" args keys {} flags {} sta::check_argc_eq1 "create_physical_cluster" $args set cluster_name $args set db [ord::get_db] @@ -63,6 +63,9 @@ proc create_child_physical_clusters { args } { } } } + +sta::define_cmd_args "set_ndr_layer_rule" {tech ndr layerName input isSpacing};# checker off + proc set_ndr_layer_rule { tech ndr layerName input isSpacing} { set layer [$tech findLayer $layerName] if { $layer == "NULL" } { @@ -95,6 +98,9 @@ proc set_ndr_layer_rule { tech ndr layerName input isSpacing} { $rule setWidth $value } } + +sta::define_cmd_args "set_ndr_rules" {tech ndr values isSpacing};#checker off + proc set_ndr_rules { tech ndr values isSpacing } { if { [llength $values] == 1 } { # Omitting layers @@ -144,7 +150,10 @@ proc set_ndr_rules { tech ndr values isSpacing } { } } -sta::define_cmd_args "create_ndr" { -name name [-spacing val] [-width val] [-via val]} +sta::define_cmd_args "create_ndr" { -name name \ + [-spacing val] \ + [-width val] \ + [-via val]} proc create_ndr { args } { sta::parse_key_args "create_ndr" args keys {-name -spacing -width -via} flags {} @@ -225,7 +234,7 @@ proc create_voltage_domain { args } { $group setType VOLTAGE_DOMAIN } -sta::define_cmd_args "delete_physical_cluster" {cluster_name} +sta::define_cmd_args "delete_physical_cluster" {cluster_name};# checker off proc delete_physical_cluster { args } { sta::check_argc_eq1 "delete_physical_cluster" $args @@ -249,6 +258,7 @@ proc delete_physical_cluster { args } { sta::define_cmd_args "delete_voltage_domain" {domain_name} proc delete_voltage_domain { args } { + sta::parse_key_args "delete_voltage_domain" args keys {} flags {} sta::check_argc_eq1 "delete_voltage_domain" $args set domain_name $args set db [ord::get_db] @@ -431,7 +441,8 @@ proc remove_from_physical_cluster { args } { sta::define_cmd_args "report_physical_clusters" {} -proc report_physical_clusters {} { +proc report_physical_clusters { args } { + sta::parse_key_args "report_physical_clusters" args keys {} flags {} set db [ord::get_db] set chip [$db getChip] if { $chip == "NULL" } { @@ -449,7 +460,8 @@ proc report_physical_clusters {} { sta::define_cmd_args "report_voltage_domains" {} -proc report_voltage_domains {} { +proc report_voltage_domains { args } { + sta::parse_key_args "report_voltage_domains" args keys {} flags {} set db [ord::get_db] set chip [$db getChip] if { $chip == "NULL" } { @@ -465,6 +477,7 @@ proc report_voltage_domains {} { } } +sta::define_cmd_args "report_group" {group};# checker off proc report_group { group } { utl::report "[expr \"[$group getType]\" == \"PHYSICAL_CLUSTER\" ? \"Physical Cluster\": \"Voltage Domain\"]: [$group getName]" if { [$group hasBox] } { @@ -511,6 +524,7 @@ proc report_group { group } { sta::define_cmd_args "write_guides" { filename } proc write_guides { args } { + sta::parse_key_args "write_guides" args keys {} flags {} sta::check_argc_eq1 "write_guides" $args set filename $args set db [ord::get_db] @@ -525,6 +539,7 @@ proc write_guides { args } { sta::define_cmd_args "write_macro_placement" { file_name } proc write_macro_placement { args } { + sta::parse_key_args "write_macro_placement" args keys {} flags {} sta::check_argc_eq1 "write_macro_placement" $args set file_name $args set db [ord::get_db] diff --git a/src/odb/test/extract_utils.py b/src/odb/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/odb/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/odb/test/manpage.py b/src/odb/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/odb/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/odb/test/md_roff_compat.py b/src/odb/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/odb/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/odb/test/odb_man_tcl_check.ok b/src/odb/test/odb_man_tcl_check.ok new file mode 100644 index 00000000000..a077f21570e --- /dev/null +++ b/src/odb/test/odb_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/odb 13 13 17 +Command counts do not match. diff --git a/src/odb/test/odb_man_tcl_check.py b/src/odb/test/odb_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/odb/test/odb_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/odb/test/odb_readme_msgs_check.ok b/src/odb/test/odb_readme_msgs_check.ok new file mode 100644 index 00000000000..06260a07fe6 --- /dev/null +++ b/src/odb/test/odb_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 17, Desc: 17, Syn: 17, Options: 17, Args: 17 +Info: 60, Warn: 199, Error: 137 diff --git a/src/odb/test/odb_readme_msgs_check.py b/src/odb/test/odb_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/odb/test/odb_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/odb/test/regression_tests.tcl b/src/odb/test/regression_tests.tcl index 5ff9422150b..73d6426ab68 100644 --- a/src/odb/test/regression_tests.tcl +++ b/src/odb/test/regression_tests.tcl @@ -34,6 +34,8 @@ record_tests { gcd_abstract_lef_with_power abstract_origin write_macro_placement + odb_man_tcl_check + odb_readme_msgs_check } record_pass_fail_tests { diff --git a/src/pad/README.md b/src/pad/README.md index b77518d9a19..4b9fb6520a9 100644 --- a/src/pad/README.md +++ b/src/pad/README.md @@ -11,10 +11,16 @@ boundary of the chip and connect with either wirebond pads or a bump array. - Parameters without square brackets `-param2 param2` are required. ``` -### Placing Terminals +### Place IO Terminals In the case where the bond pads are integrated into the padcell, the IO terminals need to be placed. -To place a terminals on the padring +This command place terminals on the padring. + +Example usage: +``` +place_io_terminals u_*/PAD +place_io_terminals u_*/VDD +``` ```tcl place_io_terminals @@ -29,15 +35,15 @@ place_io_terminals | `-allow_non_top_layer` | Allow the terminal to be placed below the top layer. | | `inst_pins` | Instance pins to place the terminals on. | -#### Examples -``` -place_io_terminals u_*/PAD -place_io_terminals u_*/VDD -``` - ### Defining a Bump Array -To define a bump array. +This command defines a bump array. + +Example usage: + +``` +make_io_bump_array -bump BUMP -origin "200 200" -rows 14 -columns 14 -pitch "200 200" +``` ```tcl make_io_bump_array @@ -59,18 +65,21 @@ make_io_bump_array | `-columns` | Number of columns to create. | | `-pitch` | Pitch of the array. | | `-prefix` | Name prefix for the bump array. The default value is `BUMP_`. | -Example usage: -```tcl -make_io_bump_array -bump BUMP -origin "200 200" -rows 14 -columns 14 -pitch "200 200" -``` -### Removing Entire Bump Array +### Remove Entire Bump Array -To remove a bump array. +This command removes the entire bump array. + +Example usage: + +``` +remove_io_bump_array -bump BUMP +``` ```tcl -remove_io_bump_array -bump master +remove_io_bump_array + -bump master ``` #### Options @@ -79,18 +88,13 @@ remove_io_bump_array -bump master | ----- | ----- | | `-bump` | Name of the bump master. | -Example usage: - -```tcl -remove_io_bump_array -bump BUMP -``` - -### Removing a Single Bump Instance +### Remove a single Bump Instance -To remove a single bump instance. +This command removes a single bump instance. ```tcl -remove_io_bump instance_name +remove_io_bump + instance_name ``` #### Options @@ -99,9 +103,21 @@ remove_io_bump instance_name | ----- | ----- | | `instance_name` | Name of the bump. | -### Assigning a Net to a Bump +### Assign a net to IO Bump + +This command assigns a net to a bump instance. -To assign a net to a bump. +Example usage: + +``` +assign_io_bump -net p_ddr_addr_9_o BUMP_6_0 +assign_io_bump -net p_ddr_addr_8_o BUMP_6_2 +assign_io_bump -net DVSS BUMP_6_4 +assign_io_bump -net DVDD BUMP_7_3 +assign_io_bump -net DVDD -terminal u_dvdd/DVDD BUMP_8_3 +assign_io_bump -net p_ddr_addr_7_o BUMP_7_1 +assign_io_bump -net p_ddr_addr_6_o BUMP_7_0 +``` ```tcl assign_io_bump @@ -120,21 +136,16 @@ assign_io_bump | `-dont_route` | Flag to indicate that this bump should not be routed, only perform assignment. | | `instance` | Name of the bump. | -Example usage: +### Make IO Sites -```tcl -assign_io_bump -net p_ddr_addr_9_o BUMP_6_0 -assign_io_bump -net p_ddr_addr_8_o BUMP_6_2 -assign_io_bump -net DVSS BUMP_6_4 -assign_io_bump -net DVDD BUMP_7_3 -assign_io_bump -net DVDD -terminal u_dvdd/DVDD BUMP_8_3 -assign_io_bump -net p_ddr_addr_7_o BUMP_7_1 -assign_io_bump -net p_ddr_addr_6_o BUMP_7_0 -``` +This command defines an IO site for the pads to be placed into. -### Define IO Rows +Example usage: -Define an IO site for the pads to be placed into. +``` +make_io_sites -horizontal_site IOSITE_H -vertical_site IOSITE_V -corner_site IOSITE_C -offset 35 +make_io_sites -horizontal_site IOSITE_H -vertical_site IOSITE_V -corner_site IOSITE_C -offset 35 -rotation_horizontal R180 +``` ```tcl make_io_sites @@ -161,12 +172,6 @@ make_io_sites | `-rotation_corner` | Rotation to apply to the corner sites to ensure pads are placed correctly. The default value is `R0`. | | `-ring_index` | Used to specify the index of the ring in case of multiple rings. | -Example usage: - -```tcl -make_io_sites -horizontal_site IOSITE_H -vertical_site IOSITE_V -corner_site IOSITE_C -offset 35 -make_io_sites -horizontal_site IOSITE_H -vertical_site IOSITE_V -corner_site IOSITE_C -offset 35 -rotation_horizontal R180 -``` ### Remove IO Rows @@ -176,9 +181,9 @@ When the padring is complete, the following command can remove the IO rows to av remove_io_rows ``` -### Placing Corners +### Placing Corner Cells -To place the corner cells +This command places the corner cells. ```tcl place_corners @@ -195,7 +200,7 @@ place_corners Example usage: -```tcl +``` place_corners sky130_fd_io__corner_bus_overlay ``` @@ -203,6 +208,15 @@ place_corners sky130_fd_io__corner_bus_overlay To place a pad into the pad ring. +Example usage: + +``` +place_pad -row IO_SOUTH -location 280.0 {u_clk.u_in} +place_pad -row IO_SOUTH -location 360.0 -mirror {u_reset.u_in} +place_pad -master sky130_fd_io__top_ground_hvc_wpad -row IO_SOUTH -location 439.5 {u_vzz_0} +place_pad -master sky130_fd_io__top_power_hvc_wpad -row IO_SOUTH -location 517.5 {u_v18_0} +``` + ```tcl place_pad -row row_name @@ -222,19 +236,20 @@ place_pad | `-master` | Name of the instance master if the instance needs to be created. | | `name` | Name of the instance. | -Example usage: - -```tcl -place_pad -row IO_SOUTH -location 280.0 {u_clk.u_in} -place_pad -row IO_SOUTH -location 360.0 -mirror {u_reset.u_in} -place_pad -master sky130_fd_io__top_ground_hvc_wpad -row IO_SOUTH -location 439.5 {u_vzz_0} -place_pad -master sky130_fd_io__top_power_hvc_wpad -row IO_SOUTH -location 517.5 {u_v18_0} -``` ### Placing IO Filler Cells To place the IO filler cells. +Example usage: + +``` +place_io_fill -row IO_NORTH s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um +place_io_fill -row IO_SOUTH s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um +place_io_fill -row IO_WEST s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um +place_io_fill -row IO_EAST s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um +``` + ```tcl place_io_fill -row row_name @@ -250,15 +265,6 @@ place_io_fill | `-permit_overlaps` | Names of the masters for the IO filler cells that allow for overlapping. | | `masters` | Names of the masters for the IO filler cells. | -Example usage: - -```tcl -place_io_fill -row IO_NORTH s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um -place_io_fill -row IO_SOUTH s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um -place_io_fill -row IO_WEST s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um -place_io_fill -row IO_EAST s8iom0s8_com_bus_slice_10um s8iom0s8_com_bus_slice_5um s8iom0s8_com_bus_slice_1um -``` - ### Connecting Ring Signals Once the ring is complete, use the following command to connect the ring signals. @@ -267,10 +273,16 @@ Once the ring is complete, use the following command to connect the ring signals connect_by_abutment ``` -### Placing Wirebond Pads +### Place Wirebond Pads To place the wirebond pads over the IO cells. +Example usage: + +``` +place_bondpad -bond PAD IO_* +``` + ```tcl place_bondpad -bond master @@ -288,16 +300,19 @@ place_bondpad | `-rotation` | Rotation of the bondpad. | | `io_instances` | Names of the instances to add bond pads to. | +### Make False IO Site + +If the library does not contain sites for the IO cells, the following command can be used to add them. +This should not be used unless the sites are not in the library. + Example usage: -```tcl -place_bondpad -bond PAD IO_* +``` +make_fake_io_site -name IO_HSITE -width 1 -height 204 +make_fake_io_site -name IO_VSITE -width 1 -height 200 +make_fake_io_site -name IO_CSITE -width 200 -height 204 ``` -### Creating False IO Sites - -If the library does not contain sites for the IO cells, the following command can be used to add them. -This should not be used unless the sites are not in the library. ```tcl make_fake_io_site @@ -314,17 +329,10 @@ make_fake_io_site | `-width` | Width of the site (in microns). | | `-height` | Height of the site (in microns). | -Example usage: - -```tcl -make_fake_io_site -name IO_HSITE -width 1 -height 204 -make_fake_io_site -name IO_VSITE -width 1 -height 200 -make_fake_io_site -name IO_CSITE -width 200 -height 204 -``` ### Redistribution Layer Routing -To route the RDL for the bump arrays. +To route the Redistribution Layer (RDL) for the bump arrays. ```tcl rdl_route @@ -351,7 +359,7 @@ rdl_route | `-allow45` | Specifies that 45 degree routing is permitted. | | `nets` | Nets to route. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/ICeWall.cpp) or the [swig file](./src/pad.i). @@ -369,7 +377,7 @@ If you are a developer, you might find these useful. More details can be found i Example scripts for running ICeWall functions can be found in `./test`. -```tcl +``` ./test/assign_bumps.tcl ./test/bump_array_make.tcl ./test/bump_array_remove.tcl diff --git a/src/pad/src/pad.tcl b/src/pad/src/pad.tcl index da79da725ae..c4f213d4873 100644 --- a/src/pad/src/pad.tcl +++ b/src/pad/src/pad.tcl @@ -138,13 +138,14 @@ sta::define_cmd_args "make_io_sites" {-horizontal_site site \ [-rotation_horizontal rotation] \ [-rotation_vertical rotation] \ [-rotation_corner rotation] \ - [-ring_index index]} + [-ring_index index] +};# checker off proc make_io_sites {args} { sta::parse_key_args "make_io_sites" args \ keys {-horizontal_site -vertical_site -corner_site -offset -rotation \ -rotation_horizontal -rotation_vertical -rotation_corner -ring_index} \ - flags {} + flags {}; # checker off sta::check_argc_eq0 "make_io_sites" $args set index -1 @@ -206,8 +207,7 @@ sta::define_cmd_args "place_corners" {[-ring_index index] \ proc place_corners {args} { sta::parse_key_args "place_corners" args \ - keys {} \ - flags {} + keys {-ring_index} flags {} sta::check_argc_eq1 "place_corners" $args @@ -331,7 +331,8 @@ proc place_bondpad {args} { $offset_y } -sta::define_cmd_args "place_io_terminals" {inst_terms} +sta::define_cmd_args "place_io_terminals" {inst_terms + [-allow_non_top_layer]} proc place_io_terminals {args} { sta::parse_key_args "place_io_terminals" args \ @@ -346,9 +347,9 @@ proc place_io_terminals {args} { pad::place_terminals $iterms [info exists flags(-allow_non_top_layer)] } -sta::define_hidden_cmd_args "make_fake_io_site" {-name name \ - -width width \ - -height height} +sta::define_cmd_args "make_fake_io_site" {-name name \ + -width width \ + -height height} proc make_fake_io_site {args} { sta::parse_key_args "make_fake_io_site" args \ diff --git a/src/pad/test/extract_utils.py b/src/pad/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/pad/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/pad/test/manpage.py b/src/pad/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/pad/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/pad/test/md_roff_compat.py b/src/pad/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/pad/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/pad/test/pad_man_tcl_check.ok b/src/pad/test/pad_man_tcl_check.ok new file mode 100644 index 00000000000..6cb57746c10 --- /dev/null +++ b/src/pad/test/pad_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/pad 13 13 13 +Command counts match. diff --git a/src/pad/test/pad_man_tcl_check.py b/src/pad/test/pad_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/pad/test/pad_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/pad/test/pad_readme_msgs_check.ok b/src/pad/test/pad_readme_msgs_check.ok new file mode 100644 index 00000000000..35480008051 --- /dev/null +++ b/src/pad/test/pad_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 14, Desc: 14, Syn: 14, Options: 14, Args: 14 +Info: 18, Warn: 59, Error: 353 diff --git a/src/pad/test/pad_readme_msgs_check.py b/src/pad/test/pad_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/pad/test/pad_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/pad/test/regression_tests.tcl b/src/pad/test/regression_tests.tcl index 2d061a1e494..fa3a1246c78 100644 --- a/src/pad/test/regression_tests.tcl +++ b/src/pad/test/regression_tests.tcl @@ -28,4 +28,7 @@ record_tests { skywater130_caravel skywater130_coyote_tc + + pad_man_tcl_check + pad_readme_msgs_check } diff --git a/src/par/README.md b/src/par/README.md index 29eb23797e7..8c51d316af0 100644 --- a/src/par/README.md +++ b/src/par/README.md @@ -193,7 +193,9 @@ the end of each PM/FM/HER pass. - Parameters without square brackets `-param2 param2` are required. ``` -### Partition Netlist +### Partition Hypergraph Netlist + +This command performs hypergraph netlist partitioning. ```tcl triton_part_hypergraph @@ -201,6 +203,7 @@ triton_part_hypergraph -num_parts num_parts -balance_constraint balance_constraint [-base_balance base_balance] + [-scale_factor scale_factor] [-seed seed] [-vertex_dimension vertex_dimension] [-hyperedge_dimension hyperedge_dimension] @@ -239,6 +242,7 @@ triton_part_hypergraph | `-num_parts` | Number of partitions. The default value is `2`, and the allowed values are integers `[0, MAX_INT]`. | | `-balance_constraint` | Allowed imbalance between blocks. The default value is `1.0`, and the allowed values are floats. | | `-base_balance` | Tcl list of baseline imbalance between partitions. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | +| `-scale_factor` | KIV. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | | `-seed` | Random seed. The default value is `0`, and the allowed values are integers `[-MAX_INT, MAX_INT]`. | | `-vertex_dimension` | Number of vertices in the hypergraph. The default value is `1`, and the allowed values are integers `[0, MAX_INT]`. | | `-hyperedge_dimension` | Number of hyperedges in hypergraph. The default value is `1`, and the allowed values are integers `[0, MAX_INT]`. | @@ -272,6 +276,8 @@ triton_part_hypergraph ### Evaluate Hypergraph Partition +This command evaluates hypergraph partition. + ```tcl evaluate_hypergraph_solution -num_parts num_parts @@ -279,6 +285,7 @@ evaluate_hypergraph_solution -hypergraph_file hypergraph_file -solution_file solution_file [-base_balance base_balance] + [-scale_factor scale_factor] [-vertex_dimension vertex_dimension] [-hyperedge_dimension hyperedge_dimension] [-fixed_file fixed_file] @@ -298,6 +305,7 @@ evaluate_hypergraph_solution | `-hypergraph_file` | Path to hypergraph file. | | `-solution_file` | Path to solution file. | | `-base_balance` | Tcl list of baseline imbalance between partitions. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | +| `-scale_factor` | KIV. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | | `-fixed_file` | Path to fixed vertices constraint file. | | `-group_file` | Path to `stay together` attributes file. | | `-e_wt_factors` | Hyperedge weight factor. | @@ -306,11 +314,14 @@ evaluate_hypergraph_solution ### Partition Netlist +This command partitions the design netlist. Note that design must be loaded in memory. + ```tcl triton_part_design [-num_parts num_parts] [-balance_constraint balance_constraint] [-base_balance base_balance] + [-scale_factor scale_factor] [-seed seed] [-timing_aware_flag timing_aware_flag] [-top_n top_n] @@ -360,6 +371,7 @@ triton_part_design | `-num_parts` | Number of partitions. The default value is `2`, and the allowed values are integers `[0, MAX_INT]`. | | `-balance_constraint` | Allowed imbalance between blocks. The default value is `1.0`, and the allowed values are floats. | | `-base_balance` | Tcl list of baseline imbalance between partitions. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | +| `-scale_factor` | KIV. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | | `-seed` | Random seed. The default value is `1`, and the allowed values are integers `[-MAX_INT, MAX_INT]`. | | `-timing_aware_flag` | Enable timing-driven mode. The default value is `true`, and the allowed values are booleans. | | `-top_n` | Extract the top n critical timing paths. The default value is `1000`, and the allowed values are integers `[0, MAX_INT`. | @@ -401,13 +413,16 @@ triton_part_design | `-num_vertices_threshold_ilp` | Describes threshold $t$, the number of vertices used for integer linear programming (ILP) partitioning. if $n_{vertices} > t$, do not use ILP-based partitioning. The default value is `50`, and the allowed values are integers `[0, MAX_INT]`. | | `-global_net_threshold` | If the net is larger than this, it will be ignored by TritonPart. The default value is `1000`, and the allowed values are integers `[0, MAX_INT]`. | -### Evaluation Netlist Partition +### Evaluate Netlist Partition + +This command evaluates partition design solution. ```tcl evaluate_part_design_solution [-num_parts num_parts] [-balance_constraint balance_constraint] [-base_balance base_balance] + [-scale_factor scale_factor] [-timing_aware_flag timing_aware_flag] [-top_n top_n] [-fence_flag fence_flag] @@ -438,6 +453,7 @@ evaluate_part_design_solution | `-num_parts` | Number of partitions. The default value is `2`, and the allowed values are integers `[0, MAX_INT]`. | | `-balance_constraint` | Allowed imbalance between blocks. The default value is `1.0`, and the allowed values are floats. | | `-base_balance` | Tcl list of baseline imbalance between partitions. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | +| `-scale_factor` | KIV. The default value is `{1.0}`, and the allowed values are floats that sum up to `1.0`. | | `-timing_aware_flag` | Enable timing-driven mode. The default value is `true`, and the allowed values are booleans. | | `-top_n` | Extract the top n critical timing paths. The default value is `1000`, and the allowed values are integers `[0, MAX_INT]`. | | `-fence_flag ` | Consider fences in the partitioning. The default value is `false`, and the allowed values are booleans. | @@ -462,10 +478,13 @@ evaluate_part_design_solution ### Write Partition to Verilog +This command writes the partition result to verilog. + ```tcl write_partition_verilog [-port_prefix prefix] [-module_suffix suffix] + [-partitioning_id part_id] [file] ``` @@ -479,24 +498,31 @@ write_partition_verilog ### Read the Partition file +This command reads the partition file into design. + ```tcl read_partitioning -read_file name [-instance_map_file file_path] +``` +| Switch Name | Description | +| ----- | ----- | +| `-read_file` | Read partitioning file (usually with the extension `.part`). The file format must match the same format as the output of `write_partition_verilog`. | +| `-instance_map_file` | Instance mapping file. | ## Example Scripts -### How to partition a hypergraph in the way you would using hMETIS (min-cut partitioning) +## How to partition a hypergraph in the way you would using hMETIS (min-cut partitioning) -```tcl +``` triton_part_hypergraph -hypergraph_file des90.hgr -num_parts 5 -balance_constraint 2 -seed 2 ``` You can also check the provided example [here](./examples/min-cut-partitioning/run_openroad.tcl). -### How to perform the embedding-aware partitioning +## How to perform the embedding-aware partitioning -```tcl +``` set num_parts 2 set balance_constraint 2 set seed 0 @@ -516,9 +542,9 @@ triton_part_hypergraph -hypergraph_file $hypergraph_file -num_parts $num_parts You can find the provided example [here](./examples/embedding-aware-partitioning/run_placement_aware_flow.tcl). -### How to partition a netlist +## How to partition a netlist -```tcl +``` # set technology information set ALL_LEFS “list_of_lefs” set ALL_LIBS “list_of_libs” @@ -549,7 +575,7 @@ set timing_guardband true set part_design_solution_file "${design}_part_design.hgr.part.${num_parts}" ############################################################################################## -### TritonPart with slack progagation +## TritonPart with slack progagation ############################################################################################## puts "Start TritonPart with slack propagation" # call triton_part to partition the netlist @@ -579,4 +605,4 @@ Simply run the following script: ## License -BSD 3-Clause License. See [LICENSE](../../LICENSE) file. +BSD 3-Clause License. See [LICENSE](../../LICENSE) file. \ No newline at end of file diff --git a/src/par/src/partitionmgr.tcl b/src/par/src/partitionmgr.tcl index d1ffa4e40b1..ad1fcd282b1 100644 --- a/src/par/src/partitionmgr.tcl +++ b/src/par/src/partitionmgr.tcl @@ -981,7 +981,7 @@ proc evaluate_part_design_solution { args } { #-------------------------------------------------------------------- sta::define_cmd_args "write_partition_verilog" { \ - [-port_prefix prefix] [-module_suffix suffix] [file] + [-port_prefix prefix] [-module_suffix suffix] [-partitioning_id id] [file] } proc write_partition_verilog { args } { diff --git a/src/par/test/extract_utils.py b/src/par/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/par/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/par/test/manpage.py b/src/par/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/par/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/par/test/md_roff_compat.py b/src/par/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/par/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/par/test/par_man_tcl_check.ok b/src/par/test/par_man_tcl_check.ok new file mode 100644 index 00000000000..168f0994f36 --- /dev/null +++ b/src/par/test/par_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/par 6 6 6 +Command counts match. diff --git a/src/par/test/par_man_tcl_check.py b/src/par/test/par_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/par/test/par_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/par/test/par_readme_msgs_check.ok b/src/par/test/par_readme_msgs_check.ok new file mode 100644 index 00000000000..93ce4a0bec6 --- /dev/null +++ b/src/par/test/par_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 6, Desc: 6, Syn: 6, Options: 6, Args: 6 +Info: 13, Warn: 9, Error: 16 diff --git a/src/par/test/par_readme_msgs_check.py b/src/par/test/par_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/par/test/par_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/par/test/regression_tests.tcl b/src/par/test/regression_tests.tcl index 9ff31fb12ba..52b9a2fcc8a 100644 --- a/src/par/test/regression_tests.tcl +++ b/src/par/test/regression_tests.tcl @@ -1,4 +1,6 @@ record_tests { read_part partition_gcd + par_man_tcl_check + par_readme_msgs_check } diff --git a/src/pdn/README.md b/src/pdn/README.md index c187e476919..65830112f7c 100644 --- a/src/pdn/README.md +++ b/src/pdn/README.md @@ -8,10 +8,6 @@ applied to the design, such as layers to use, stripe width and spacing, then have the utility generate the actual metal straps. Grid policies can be defined over the stdcell area, and over areas occupied by macros. -```{seealso} -To work with UPF files, refer to [Read UPF Utility](../upf/README.md). -``` - ## Commands ```{note} @@ -31,6 +27,7 @@ pdngen [-ripup] [-report_only] [-failed_via_report file] + [-verbose] ``` #### Options @@ -42,13 +39,21 @@ pdngen | `-reset` | Reset the grid and domain specifications. | | `-ripup` | Ripup the existing power grid, as specified by the voltage domains. | | `-report_only` | Print the current specifications. | -| `-failed_via_report` | Generate a report file that can be viewed in the DRC viewer for all the failed vias (i.e., those that did not get built or were removed). | +| `-failed_via_report` | Generate a report file which can be viewed in the DRC viewer for all the failed vias (ie. those that did not get built or were removed). | -### Define Voltage Domains +### Define Voltage Domain Defines a named voltage domain with the names of the power and ground nets for a region. -This region must already exist in the floorplan before referencing it with the `set_voltage_domain` command. If the `-region` argument is not supplied, then the region is the entire core area of the design. +This region must already exist in the floorplan before referencing it with the `set_voltage_domain` command. If the `-region` argument is not supplied then region is the entire core area of the design. + +Example usage: + +``` +set_voltage_domain -power VDD -ground VSS +set_voltage_domain -name TEMP_ANALOG -region TEMP_ANALOG -power VIN -ground VSS +set_voltage_domain -region test_domain -power VDD -ground VSS -secondary_power VREG +``` ```tcl set_voltage_domain @@ -71,63 +76,54 @@ set_voltage_domain | `-secondary_power` | Specifies the name of the secondary power net for this voltage domain. | | `-switched_power` | Specifies the name of the switched power net for switched power domains. | -Example usage: +### Define Power Grids -```tcl -set_voltage_domain -power VDD -ground VSS -set_voltage_domain -name TEMP_ANALOG -region TEMP_ANALOG -power VIN -ground VSS -set_voltage_domain -region test_domain -power VDD -ground VSS -secondary_power VREG +```{warning} +`define_pdn_grid` is overloaded with two different signatures. Take note of the arguments when using this function! ``` -### Define Power Grid (General) - +- Method 1: General Usage Define the rules to describe a power grid pattern to be placed in the design. -```{warning} -`define_pdn_grid` is overloaded with two different signatures. Take note of the arguments when using this function! -``` +Example usage: -```tcl -define_pdn_grid - [-name name] - [-voltage_domain list_of_domain_names] - [-pins list_of_pin_layers] - [-starts_with POWER|GROUND] - [-starts_with POWER|GROUND] - [-obstructions list_of_layers] +``` +define_pdn_grid -name main_grid -pins {metal7} -voltage_domain {CORE TEMP_ANALOG} ``` -#### Options - -| Switch Name | Description | -| ----- | ----- | -| `-name` | The name to use when referring to this grid definition. | -| `-voltage_domain` | This grid's voltage domain name. Defaults to the last domain created. | -| `-pins` | List of layers where the power straps will be promoted to block pins. | -| `-starts_with` | Use `POWER` or `GROUND` for the first placed strap. Defaults to `GROUND`. | -| `-obstructions` | Layers to add routing blockages to avoid DRC violations. | +- Method 2: Macros +Define the rules for one or more macros. Example usage: -```tcl -define_pdn_grid -name main_grid -pins {metal7} -voltage_domain {CORE TEMP_ANALOG} +``` +define_pdn_grid -macro -name ram -orient {R0 R180 MX MY} -grid_over_pg_pins -starts_with POWER -pin_direction vertical +define_pdn_grid -macro -name rotated_rams -orient {E FE W FW} -grid_over_boundary -starts_with POWER -pin_direction horizontal ``` -### Define Power Grid (Macros) +- Method 3: Modify existing power domain +Modify pre-existing power domain. + +Example usage: + +``` +define_pdn_grid -name main_grid -existing +``` ```tcl define_pdn_grid - -macro - [-name name] + [-name name] + [-macro] + [-existing] + [-voltage_domains list_of_domain_names] [-grid_over_pg_pins|-grid_over_boundary] - [-voltage_domain list_of_domain_names] [-orient list_of_valid_orientations] [-instances list_of_instances] [-cells list_of_cells] [-default] [-halo list_of_halo_values] - [-pins list_of_pin_layers] - [-starts_with POWER|GROUND] + [-pins list_of_pin_layers] + [-starts_with POWER|GROUND] [-obstructions list_of_layers] [-power_switch_cell name] [-power_control signal_name] @@ -138,55 +134,34 @@ define_pdn_grid | Switch Name | Description | | ----- | ----- | -| `-macro` | The type of grid added as a macro. | -| `-name` | The name to use when referring to this grid definition. | -| `-grid_over_pg_pins`, `-grid_over_boundary` | Place the power grid over the power ground pins of the macro (default) or place the power grid over the entire macro. | -| `-voltage_domain` | Grid's voltage domain name. Defaults to the last domain created. | +| `-name` | Defines a name to use when referring to this grid definition. | +| `-voltage_domains` | Defines the name of the voltage domain for this grid (Default: Last domain created). | +| `-pins` | Defines a list of layers which where the power straps will be promoted to block pins. | +| `-starts_with` | Specifies whether the first strap placed will be POWER or GROUND (Default: GROUND). | +| `-obstructions` | Specify the layers to add routing blockages, in order to avoid DRC violations. | +| `-macro` | Defines the type of grid being added as a macro. | +| `-grid_over_pg_pins`, `-grid_over_boundary` | Place the power grid over the power ground pins of the macro. (Default True), or Place the power grid over the entire macro. | | `-orient` | For a macro, defines a set of valid orientations. LEF orientations (N, FN, S, FS, E, FE, W and FW) can be used as well as standard geometry orientations (R0, R90, R180, R270, MX, MY, MXR90 and MYR90). Macros with one of the valid orientations will use this grid specification. | | `-instances` | For a macro, defines a set of valid instances. Macros with a matching instance name will use this grid specification. | -| `-cells` | For a macro, defines a set of valid cells. Macros, which are instances of one of these cells, will use this grid specification. | +| `-cells` | For a macro, defines a set of valid cells. Macros which are instances of one of these cells will use this grid specification. | | `-default` | For a macro, specifies this is a default grid that can be overwritten. | -| `-halo` | Specifies the design's default minimum separation of selected macros from other cells. This is only used if the macro does not define halo values in the LEF description. If one value is specified, it will be used on all four sides; if two values are specified, the first will be applied to left/right sides, and the second will be applied to top/bottom sides; if four values are specified, then they are applied to left, bottom, right and top sides respectively (Default: 0). | -| `-pins` | Defines a list of layers where the power straps will be promoted to block pins. | -| `-starts_with` | Use `POWER` or `GROUND` for the first placed strap. Defaults to `GROUND`.| -| `-obstructions` | Specify the layers to add routing blockages in order to avoid DRC violations. | +| `-halo` | Specifies the default minimum separation of selected macros from other cells in the design. This is only used if the macro does not define halo values in the LEF description. If 1 value is specified it will be used on all 4 sides, if two values are specified, the first will be applied to left/right sides and the second will be applied to top/bottom sides, if 4 values are specified, then they are applied to left, bottom, right and top sides respectively (Default: 0). | +| `-obstructions` | Specify the layers to add routing blockages, in order to avoid DRC violations. | | `-power_switch_cell` | Defines the name of the coarse grain power switch cell to be used wherever the stdcell rail connects to the rest of the power grid. The mesh layers are associated with the unswitched power net of the voltage domain, whereas the stdcell rail is associated with the switched power net of the voltage domain. The placement of a power switch cell connects the unswitched power mesh to the switched power rail through a power switch defined by the `define_power_switch_cell` command. | | `-power_control` | Defines the name of the power control signal used to control the switching of the inserted power switches. | -| `-power_control_network` | Defines the structure of the power control signal network. Choose from STAR or DAISY. If STAR is specified, then the network is wired as a high-fanout net with the power control signal driving the power control pin on every power switch. If DAISY is specified, then the power switches are connected in a daisy-chain configuration - note, this requires that the power switch defined by the `define_power_switch_cell` command defines an acknowledge pin for the switch. | +| `-power_control_network` | Defines the structure of the power control signal network. Choose from STAR, or DAISY. If STAR is specified, then the network is wired as a high-fanout net with the power control signal driving the power control pin on every power switch. If DAISY is specified then the power switches are connected in a daisy-chain configuration - note, this requires that the power swich defined by the `define_power_switch_cell` command defines an acknowledge pin for the switch. | +| `-existing` | Flag to enable defining for existing routing solution. | -Example usage: -```tcl -define_pdn_grid -macro -name ram -orient {R0 R180 MX MY} -grid_over_pg_pins -starts_with POWER -pin_direction vertical -define_pdn_grid -macro -name rotated_rams -orient {E FE W FW} -grid_over_boundary -starts_with POWER -pin_direction horizontal -``` +### Power Switch Cell insertion -### Define Power Grid for an Existing Routing Solution - -```tcl -define_pdn_grid - -existing - [-name name] - [-obstructions list_of_layers] -``` - -#### Options - -| Switch Name | Description | -| ----- | ----- | -| `-existing` | Enable use of existing routing solution. | -| `-name` | The name to use when referring to this grid definition. Defaults to `existing_grid`. | -| `-obstructions` | The layers to add routing blockages in order to avoid DRC violations. | +Define a power switch cell that will be inserted into a power grid Example usage: -```tcl -define_pdn_grid -name main_grid -existing ``` - -### Define Power Switch Cell - -Define a power switch cell that will be inserted into a power grid. +define_power_switch_cell -name POWER_SWITCH -control SLEEP -switched_power VDD -power VDDG -ground VSS +``` ```tcl define_power_switch_cell @@ -204,20 +179,22 @@ define_power_switch_cell | ----- | ----- | | `-name` | The name of the power switch cell. | | `-control` | The name of the power control port of the power switch cell. | -| `-switched_power` | The pin's name that outputs the switched power net. | -| `-power` | The pin's name that connects to the unswitched power net. | -| `-ground` | The pin's name that connects to the ground net. | -| `-acknowledge` | The name of the output control signal of the power control switch if it has one. | +| `-switched_power` | Defines the name of the pin that outputs the switched power net. | +| `-power` | Defines the name of the pin that connects to the unswitched power net. | +| `-ground` | Defines the name of the pin that connects to the ground net. | +| `-acknowledge` | Defines the name of the output control signal of the power control switch if it has one. | -Example usage: +### Add PDN Straps/Stripes -```tcl -define_power_switch_cell -name POWER_SWITCH -control SLEEP -switched_power VDD -power VDDG -ground VSS -``` +Defines a pattern of power and ground stripes in a single layer to be added to a power grid. -### Add Stripes +Example usage: -Defines a pattern of power and ground stripes in a single layer to be added to a power grid. +``` +add_pdn_stripe -grid main_grid -layer metal1 -followpins +add_pdn_stripe -grid main_grid -layer metal2 -width 0.17 -followpins +add_pdn_stripe -grid main_grid -layer metal4 -width 0.48 -pitch 56.0 -offset 2 -starts_with GROUND +``` ```tcl add_pdn_stripe @@ -240,31 +217,80 @@ add_pdn_stripe | Switch Name | Description | | ----- | ----- | -| `-layer` | The layer name for these stripes. | -| `-grid` | The grid to which this stripe definition will be added. (Default: Last grid defined by `define_pdn_grid`). | -| `-width` | Value for the width of the stripe. | -| `-followpins` | Indicates that the stripe forms part of the stdcell rails, pitch and spacing are dictated by the stdcell rows, and the `-width` is not needed if it can be determined from the cells. | +| `-layer` | Specifies the name of the layer for these stripes. | +| `-grid` | Specifies the grid to which this stripe definition will be added. (Default: Last grid defined by `define_pdn_grid`). | +| `-width` | Value for the width of stripe. | +| `-followpins` | Indicates that the stripe forms part of the stdcell rails, pitch and spacing are dictated by the stdcell rows, the `-width` is not needed if it can be determined from the cells. | | `-extend_to_core_ring` | Extend the stripes to the core PG ring. | | `-pitch` | Value for the distance between each power/ground pair. | -| `-spacing` | Optional specification of the spacing between power/ground pairs within a single pitch defaults to `pitch / 2`. | +| `-spacing` | Optional specification of the spacing between power/ground pairs within a single pitch (Default: pitch / 2). | | `-offset` | Value for the offset of the stripe from the lower left corner of the design core area. | -| `-starts_with` | Use `POWER` or `GROUND` for the first placed strap. Defaults to `GROUND`. | +| `-starts_with` | Specifies whether the first strap placed will be POWER or GROUND (Default: grid setting). | | `-extend_to_boundary` | Extend the stripes to the boundary of the grid. | | `-snap_to_grid` | Snap the stripes to the defined routing grid. | | `-number_of_straps` | Number of power/ground pairs to add. | | `-nets` | Limit straps to just this list of nets. | -Example usage: +### Add Sroute Connect + +The `add_sroute_connect` command is employed for connecting pins located +outside of a specific power domain to the power ring, especially in cases where +multiple power domains are present. During `sroute`, multi-cut vias will be added +for new connections. The use of fixed vias from the technology file should be +specified for the connection using the `add_sroute_connect` command. The use +of max_rows and max_columns defines the row and column limit for the via stack. + +Example: +``` +add_sroute_connect -net "VIN" -outerNet "VDD" -layers {met1 met4} -cut_pitch {200 200} -fixed_vias {M3M4_PR_M} -metalwidths {1000 1000} -metalspaces {800} -ongrid {met3 met4} -insts "temp_analog_1.a_header_0" +``` ```tcl -add_pdn_stripe -grid main_grid -layer metal1 -followpins -add_pdn_stripe -grid main_grid -layer metal2 -width 0.17 -followpins -add_pdn_stripe -grid main_grid -layer metal4 -width 0.48 -pitch 56.0 -offset 2 -starts_with GROUND +add_sroute_connect + -layers list_of_2_layers + -cut_pitch pitch_value + [-net net] + [-outerNet outerNet] + [-fixed_vias list_of_vias] + [-max_rows rows] + [-max_columns columns] + [-metalwidths metalwidths] + [-metalspaces metalspaces] + [-ongrid ongrid_layers] + [-insts inst] ``` -### Add Rings +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-net` | The inner net where the power ring exists. | +| `-outerNet` | The outer net where instances/pins that need to get connected exist. | +| `-layers` | The metal layers for vertical stripes within inner power ring. | +| `-cut_pitch` | Distance between via cuts when the two layers are parallel, e.g., overlapping stdcell rails. (Default:200 200) | +| `-fixed_vias` | List of fixed vias to be used to form the via stack. | +| `-max_rows` | Maximum number of rows when adding arrays of vias. (Default:10) | +| `-max_columns` | Maximum number of columns when adding arrays of vias. (Default:10) | +| `-metalwidths` | Width for each metal layer. | +| `-metalspaces` | Spacing of each metal layer. | +| `-ongrid` | List of intermediate layers in a via stack to snap onto a routing grid. | +| `-insts` | List of all the instances that contain the pin that needs to get connected with power ring. (Default:nothing) | + +### Add PDN Ring + +The `add_pdn_ring` command is used to define power/ground rings around a grid region. +The ring structure is built using two layers that are orthogonal to each other. +A power/ground pair will be added above and below the grid using the horizontal +layer, with another power/ground pair to the left and right using the vertical layer. +Together these 4 pairs of power/ground stripes form a ring around the specified grid. +Power straps on these layers that are inside the enclosed region are extend to +connect to the ring. + +Example usage: -The `add_pdn_ring` command defines power/ground rings around a grid region. The ring structure is built using two layers that are orthogonal to each other. A power/ground pair will be added above and below the grid using the horizontal layer, with another power/ground pair to the left and right using the vertical layer. These four pairs of power/ground stripes form a ring around the specified grid. Power straps on these layers that are inside the enclosed region are extended to connect to the ring. +``` +add_pdn_ring -grid main_grid -layer {metal6 metal7} -widths 5.0 -spacings 3.0 -core_offset 5 +``` ```tcl add_pdn_ring @@ -280,6 +306,9 @@ add_pdn_ring [-connect_to_pad_layers layers] [-starts_with POWER|GROUND] [-nets list_of_nets] + [-ground_pads pads] + [-power_pads pads] + ``` #### Options @@ -296,18 +325,27 @@ add_pdn_ring | `-extend_to_boundary` | Extend the rings to the grid boundary. | | `-connect_to_pads` | The core side of the pad pins will be connected to the ring. | | `-connect_to_pad_layers` | Restrict the pad pins layers to this list. | -| `-starts_with` | Use `POWER` or `GROUND` for the first placed strap. Defaults to `GROUND`. | +| `-starts_with` | Specifies whether the first strap placed will be POWER or GROUND (Default: grid setting). | | `-nets` | Limit straps to just this list of nets. | -Example usage: +### Add PDN Connect + +The `add_pdn_connect` command is used to define which layers in the power grid are to be connected together. During power grid generation, vias will be added for overlapping power nets and overlapping ground nets. The use of fixed vias from the technology file can be specified or else via stacks will be constructed using VIARULEs. If VIARULEs are not available in the technology, then fixed vias must be used. + +Example usage: -```tcl -add_pdn_ring -grid main_grid -layer {metal6 metal7} -widths 5.0 -spacings 3.0 -core_offset 5 ``` +add_pdn_connect -grid main_grid -layers {metal1 metal2} -cut_pitch 0.16 +add_pdn_connect -grid main_grid -layers {metal2 metal4} +add_pdn_connect -grid main_grid -layers {metal4 metal7} -### Add Connections +add_pdn_connect -grid ram -layers {metal4 metal5} +add_pdn_connect -grid ram -layers {metal5 metal6} +add_pdn_connect -grid ram -layers {metal6 metal7} -The `add_pdn_connect` command is used to define which layers in the power grid are to be connected together. During power grid generation, vias will be added for overlapping power nets and overlapping ground nets. The use of fixed vias from the technology file can be specified or else via stacks will be constructed using VIARULEs. If VIARULEs are not available in the technology, then fixed vias must be used. +add_pdn_connect -grid rotated_rams -layers {metal4 metal6} +add_pdn_connect -grid rotated_rams -layers {metal6 metal7} +``` ```tcl add_pdn_connect @@ -328,7 +366,7 @@ add_pdn_connect | ----- | ----- | | `-layers` | Layers to be connected where there are overlapping power or overlapping ground nets. | | `-grid` | Specifies the name of the grid definition to which this connection will be added (Default: Last grid created by `define_pdn_grid`). | -| `-cut_pitch` | When the two layers are parallel, e.g., overlapping stdcell rails, specify the distance between via cuts. | +| `-cut_pitch` | When the two layers are parallel e.g. overlapping stdcell rails, specify the distance between via cuts. | | `-fixed_vias` | List of fixed vias to be used to form the via stack. | | `-dont_use_vias` | List or pattern of vias to not use to form the via stack. | | `-max_rows` | Maximum number of rows when adding arrays of vias. | @@ -336,24 +374,10 @@ add_pdn_connect | `-ongrid` | List of intermediate layers in a via stack to snap onto a routing grid. | | `-split_cuts` | Specifies layers to use split cuts on with an associated pitch, for example `{metal3 0.380 metal5 0.500}`. | -Example usage: - -```tcl -add_pdn_connect -grid main_grid -layers {metal1 metal2} -cut_pitch 0.16 -add_pdn_connect -grid main_grid -layers {metal2 metal4} -add_pdn_connect -grid main_grid -layers {metal4 metal7} - -add_pdn_connect -grid ram -layers {metal4 metal5} -add_pdn_connect -grid ram -layers {metal5 metal6} -add_pdn_connect -grid ram -layers {metal6 metal7} - -add_pdn_connect -grid rotated_rams -layers {metal4 metal6} -add_pdn_connect -grid rotated_rams -layers {metal6 metal7} -``` +### Repairing power grid vias after detailed routing -### Repairing Power Grid vias after Detailed Routing - -To remove vias which generate DRC violations after detailed placement and routing use `repair_pdn_vias`. +To remove vias which generate DRC violations after detailed placement +and routing use `repair_pdn_vias`. ```tcl repair_pdn_vias @@ -368,29 +392,29 @@ repair_pdn_vias | `-all` | Repair vias on all supply nets. | | `-net` | Repair only vias on the specified net. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](../src/PdnGen.cc) or the [swig file](PdnGen.i). | Command Name | Description | | ----- | ----- | | `name_cmp` | Compare 2 input strings `obj1` and `obj2` if they are equal. | -| `check_design_state` | Check if the design is loaded. | -| `get_layer` | Get the layer reference of the layer name. | +| `check_design_state` | Check if design is loaded. | +| `get_layer` | Get the layer reference of layer name. | | `get_voltage_domains` | Gets a Tcl list of power domains in design. | | `match_orientation` | Checks if a given orientation `orient` is within a list of orientations `orients`. | -| `get_insts` | Get the Tcl list of instances. | -| `get_masters` | Get the Tcl list of masters. | -| `get_one_to_two` | If a Tcl list has one element `{x}`, Tcl list `{x x}` is returned. If a Tcl list of two elements `{y y}`, list as is returned. Otherwise, for any other list lengths, an error is triggered. | -| `get_one_to_four` | Similar logic for the above function, except the logic only works for lists of length one, two, and four, respectively. All other list lengths trigger errors. | -| `get_obstructions` | Get the Tcl list of layers. | +| `get_insts` | Get Tcl list of instances. | +| `get_masters` | Get Tcl list of masters. | +| `get_one_to_two` | If a Tcl list has one element `{x}`, Tcl list `{x x}` is returned. If a Tcl list of two elements `{y y}`, list as is returned. Otherwise, for any other list lengths, error is triggered. | +| `get_one_to_four` | Similar logic for above function, except the logic only works for lists of length one, two and four respectively. All other list lengths triggers error. | +| `get_obstructions` | Get Tcl list of layers. | | `get_starts_with` | If value starts with `POWER`, return 1; else if value starts with `GROUND` return 0; else return error. | | `get_mterm` | Find master terminal. | -| `get_orientations` | Get the list of valid orientations. | +| `get_orientations` | Get list of valid orientations. | ## Example scripts -### Defining a SoC power grid with pads +## Defining a SoC power grid with pads ``` add_global_connection -net VDD -pin_pattern {^VDD$} -power @@ -420,52 +444,6 @@ add_pdn_connect -layers {metal9 metal10} pdngen ``` -### Sroute - -The `add_sroute_connect` command is employed for connecting pins located -outside of a specific power domain to the power ring, especially in cases where -multiple power domains are present. During `sroute`, multi-cut vias will be added -for new connections. The use of fixed vias from the technology file should be -specified for the connection using the `add_sroute_connect` command. The use -of max_rows and max_columns defines the row and column limit for the via stack. - -``` -add_sroute_connect - -layers list_of_2_layers - -cut_pitch pitch_value - [-net net] - [-outerNet outerNet] - [-fixed_vias list_of_vias] - [-max_rows rows] - [-max_columns columns] - [-metalwidths metalwidths] - [-metalspaces metalspaces] - [-ongrid ongrid_layers] - [-insts inst] -``` - -#### Options - -| Switch Name | Description | -| ----- | ----- | -| `-net` | The inner net where the power ring exists. | -| `-outerNet` | The outer net where instances/pins that need to get connected exist. | -| `-layers` | The metal layers for vertical stripes within inner power ring. | -| `-cut_pitch` | Distance between via cuts when the two layers are parallel, e.g., overlapping stdcell rails. (Default:200 200) | -| `-fixed_vias` | List of fixed vias to be used to form the via stack. | -| `-max_rows` | Maximum number of rows when adding arrays of vias. (Default:10) | -| `-max_columns` | Maximum number of columns when adding arrays of vias. (Default:10) | -| `-metalwidths` | Width for each metal layer. | -| `-metalspaces` | Spacing of each metal layer. | -| `-ongrid` | List of intermediate layers in a via stack to snap onto a routing grid. | -| `-insts` | List of all the instances that contain the pin that needs to get connected with power ring. (Default:nothing) | - -#### Examples - -``` -add_sroute_connect -net "VIN" -outerNet "VDD" -layers {met1 met4} -cut_pitch {200 200} -fixed_vias {M3M4_PR_M} -metalwidths {1000 1000} -metalspaces {800} -ongrid {met3 met4} -insts "temp_analog_1.a_header_0" - -``` ## Regression tests There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). @@ -490,4 +468,4 @@ Check out [GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/d ## License -BSD 3-Clause License. See [LICENSE](../../LICENSE) file. +BSD 3-Clause License. See [LICENSE](../../LICENSE) file. \ No newline at end of file diff --git a/src/pdn/src/pdn.tcl b/src/pdn/src/pdn.tcl index a229210c468..ec5f7e5e530 100644 --- a/src/pdn/src/pdn.tcl +++ b/src/pdn/src/pdn.tcl @@ -33,7 +33,8 @@ sta::define_cmd_args "pdngen" {[-skip_trim] \ [-reset] \ [-ripup] \ [-report_only] \ - [-failed_via_report file] + [-failed_via_report file] \ + [-verbose] } proc pdngen { args } { @@ -89,7 +90,7 @@ sta::define_cmd_args "set_voltage_domain" {-name domain_name \ proc set_voltage_domain {args} { sta::parse_key_args "set_voltage_domain" args \ - keys {-name -region -power -ground -secondary_power -switched_power} + keys {-name -region -power -ground -secondary_power -switched_power} flags {} sta::check_argc_eq0 "set_voltage_domain" $args @@ -182,7 +183,8 @@ sta::define_cmd_args "define_pdn_grid" {[-name ] \ [-obstructions ] \ [-power_switch_cell ] \ [-power_control ] \ - [-power_control_network (STAR|DAISY)]} + [-power_control_network (STAR|DAISY)] +};#checker off proc define_pdn_grid {args} { set is_macro 0 @@ -212,7 +214,7 @@ sta::define_cmd_args "define_power_switch_cell" {-name \ proc define_power_switch_cell {args} { sta::parse_key_args "define_power_switch_cell" args \ - keys {-name -control -acknowledge -power_switchable -power -ground} + keys {-name -control -acknowledge -power_switchable -power -ground} flags {} sta::check_argc_eq0 "define_power_switch_cell" $args @@ -273,7 +275,7 @@ sta::define_cmd_args "add_pdn_stripe" {[-grid grid_name] \ [-pitch pitch_value] \ [-spacing spacing_value] \ [-offset offset_value] \ - [-starts_width (POWER|GROUND)] + [-starts_with (POWER|GROUND)] [-extend_to_boundary] \ [-snap_to_grid] \ [-number_of_straps count] \ @@ -394,6 +396,8 @@ sta::define_cmd_args "add_pdn_ring" {[-grid grid_name] \ -spacings (spacing_value|list_of_spacing_values) \ [-core_offsets (offset_value|list_of_offset_values)] \ [-pad_offsets (offset_value|list_of_offset_values)] \ + [-ground_pads list_of_gnd_pads] \ + [-power_pads list_of_pwr_pads] \ [-add_connect] \ [-extend_to_boundary] \ [-connect_to_pads] \ @@ -854,7 +858,7 @@ proc define_pdn_grid { args } { sta::parse_key_args "define_pdn_grid" args \ keys {-name -voltage_domains -pins -starts_with -obstructions -power_switch_cell \ -power_control -power_control_network} \ - flags {} + flags {};# checker off sta::check_argc_eq0 "define_pdn_grid" $args pdn::check_design_state "define_pdn_grid" @@ -929,7 +933,7 @@ proc define_pdn_grid { args } { proc define_pdn_grid_existing { args } { sta::parse_key_args "define_pdn_grid" args \ keys {-name -obstructions} \ - flags {-existing} + flags {-existing};# checker off sta::check_argc_eq0 "define_pdn_grid" $args pdn::check_design_state "define_pdn_grid" @@ -951,7 +955,7 @@ proc define_pdn_grid_macro { args } { sta::parse_key_args "define_pdn_grid" args \ keys {-name -voltage_domains -orient -instances -cells -halo -pin_direction -starts_with \ -obstructions} \ - flags {-macro -grid_over_pg_pins -grid_over_boundary -default -bump} + flags {-macro -grid_over_pg_pins -grid_over_boundary -default -bump};# checker off sta::check_argc_eq0 "define_pdn_grid" $args pdn::check_design_state "define_pdn_grid" @@ -1520,4 +1524,4 @@ proc get_pdn_ground_nets {} { } } # namespace pdngen -} +} \ No newline at end of file diff --git a/src/pdn/test/extract_utils.py b/src/pdn/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/pdn/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/pdn/test/manpage.py b/src/pdn/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/pdn/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/pdn/test/md_roff_compat.py b/src/pdn/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/pdn/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/pdn/test/pdn_man_tcl_check.ok b/src/pdn/test/pdn_man_tcl_check.ok new file mode 100644 index 00000000000..43a6876d20c --- /dev/null +++ b/src/pdn/test/pdn_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/pdn 8 9 9 +Command counts do not match. diff --git a/src/pdn/test/pdn_man_tcl_check.py b/src/pdn/test/pdn_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/pdn/test/pdn_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/pdn/test/pdn_readme_msgs_check.ok b/src/pdn/test/pdn_readme_msgs_check.ok new file mode 100644 index 00000000000..204453d5e8b --- /dev/null +++ b/src/pdn/test/pdn_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 9, Desc: 9, Syn: 9, Options: 9, Args: 9 +Info: 3, Warn: 20, Error: 99 diff --git a/src/pdn/test/pdn_readme_msgs_check.py b/src/pdn/test/pdn_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/pdn/test/pdn_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/pdn/test/regression_tests.tcl b/src/pdn/test/regression_tests.tcl index 72e4d4ec0a4..6fdf5fa27b5 100644 --- a/src/pdn/test/regression_tests.tcl +++ b/src/pdn/test/regression_tests.tcl @@ -114,4 +114,7 @@ record_tests { sroute_test bpin_removal + + pdn_man_tcl_check + pdn_readme_msgs_check } diff --git a/src/ppl/README.md b/src/ppl/README.md index cc83206ee30..3e5eec74de4 100644 --- a/src/ppl/README.md +++ b/src/ppl/README.md @@ -50,7 +50,7 @@ and [hybrid bonding](https://www.3dincites.com/2018/04/hybrid-bonding-from-concept-to-commercialization/) for high density face-to-face interconnect. -### Set IO Pin Constraint +### Set IO Pin Constraints The `set_io_pin_constraint` command sets region constraints for pins according to the pin direction or the pin name. This command can be called multiple @@ -123,7 +123,7 @@ set_pin_length | `-hor_length` | The length (in microns) of the horizontal pins. | | `-ver_length` | The length (in microns) of the vertical pins. | -### Set Pin Extension +### Set Pin Length Extension The `set_pin_length_extension` command defines the an extension of the length of all vertical and horizontal pins. Note that this command may generate pins @@ -142,7 +142,7 @@ set_pin_length_extension | `-hor_extension` | The length (in microns) for the horizontal pins. | | `-ver_extension` | The length (in microns) for the vertical pins. | -### Set Pin Thick Multiplier +### Set Pin Thickness Multiplier The `set_pin_thick_multiplier` command defines a multiplier for the thickness of all vertical and horizontal pins. @@ -160,7 +160,7 @@ set_pin_thick_multiplier | `-hor_multiplier` | The thickness multiplier for the horizontal pins. | | `-ver_multiplier` | The thickness multiplier for the vertical pins. | -### Set Simulated Annealing Parameters +### Set Simulated Annealing The `set_simulated_annealing` command defines the parameters for simulated annealing pin placement. @@ -181,7 +181,25 @@ set_simulated_annealing | `-perturb_per_iter` | The number of perturbations per iteration. The default value is `0`, and the allowed values are integers `[0, MAX_INT]`. | | `-alpha` | The temperature decay factor. The default value is `0.985`, and the allowed values are floats `(0, 1]`. | -### Place Individual Pin +### Simulated Annealing Debug Mode + +The `simulated_annealing_debug` command allows you to debug the simulated +annealing pin placement with a pause mode. + +```tcl +simulated_annealing_debug + [-iters_between_paintings iters] + [-no_pause_mode no_pause_mode] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-iters_between_paintings` | Determines the number of iterations between updates. | +| `-no_pause_mode` | Print solver state every second based on `iters_between_paintings`. | + +### Place specific Pin The `place_pin` command places a specific pin in the specified location with the specified size. It is recommended that individual pins be placed before the `place_pins` command, @@ -196,6 +214,7 @@ place_pin -location {x y} [-pin_size {width height}] [-force_to_die_boundary] + [-placed_status] ``` #### Options @@ -208,9 +227,12 @@ place_pin | `-pin_size` | The width and height of the pin (in microns). | | `-force_to_die_boundary` | When this flag is enabled, the pin will be snapped to the nearest routing track, next to the die boundary. | -### Place All Pins +### Place all Pins + +The `place_pins` command places all pins together. Use the following command to perform pin placement: -Use the following command to perform pin placement: +Developer arguments: +- `-random`, `-random_seed` ```tcl place_pins @@ -257,10 +279,11 @@ microns to 60.5 microns, and the left edge from its beginning to 50 microns. ### Write Pin Placement -Use the following command to write a file with the pin placement in the format of multiple calls for the `place_pin` command: +The `write_pin_placement` command writes a file with the pin placement in the format of multiple calls for the `place_pin` command: ```tcl -write_pin_placement file_name +write_pin_placement + file_name ``` #### Options @@ -269,7 +292,7 @@ write_pin_placement file_name | ----- | ----- | | `file_name` | The name of the file with the pin placement. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/IOPlacer.cpp) or the [swig file](./src/IOPlacer.i). @@ -286,12 +309,11 @@ If you are a developer, you might find these useful. More details can be found i | `add_pins_to_constraint` | Add pins to constrained region. | | `add_pins_to_top_layer` | Add pins to top layer. | - ## Example scripts Example scripts of `ppl` running on a sample design of `gcd` as follows: -```tcl +``` ./test/gcd.tcl ``` diff --git a/src/ppl/src/IOPlacer.tcl b/src/ppl/src/IOPlacer.tcl index a658b4a2184..012352fbfe1 100644 --- a/src/ppl/src/IOPlacer.tcl +++ b/src/ppl/src/IOPlacer.tcl @@ -43,7 +43,7 @@ sta::define_cmd_args "define_pin_shape_pattern" {[-layer layer] \ proc define_pin_shape_pattern { args } { sta::parse_key_args "define_pin_shape_pattern" args \ - keys {-layer -x_step -y_step -region -size -pin_keepout} + keys {-layer -x_step -y_step -region -size -pin_keepout} flags {} sta::check_argc_eq0 "define_pin_shape_pattern" $args @@ -249,7 +249,10 @@ proc set_io_pin_constraint { args } { } +sta::define_cmd_args "clear_io_pin_constraints" {} + proc clear_io_pin_constraints {} { + sta::parse_key_args "clear_io_pin_constraints" args keys {} flags {} ppl::clear_constraints } @@ -259,7 +262,7 @@ sta::define_cmd_args "set_pin_length" {[-hor_length h_length]\ proc set_pin_length { args } { sta::parse_key_args "set_pin_length" args \ - keys {-hor_length -ver_length} + keys {-hor_length -ver_length} flags {} sta::check_argc_eq0 "set_pin_length" $args @@ -278,7 +281,7 @@ sta::define_cmd_args "set_pin_length_extension" {[-hor_extension h_ext]\ proc set_pin_length_extension { args } { sta::parse_key_args "set_pin_length_extension" args \ - keys {-hor_extension -ver_extension} + keys {-hor_extension -ver_extension} flags {} sta::check_argc_eq0 "set_pin_length_extension" $args @@ -297,7 +300,7 @@ sta::define_cmd_args "set_pin_thick_multiplier" {[-hor_multiplier h_mult]\ proc set_pin_thick_multiplier { args } { sta::parse_key_args "set_pin_thick_multiplier" args \ - keys {-hor_multiplier -ver_multiplier} + keys {-hor_multiplier -ver_multiplier} flags {} sta::check_argc_eq0 "set_pin_thick_multiplier" $args @@ -318,7 +321,7 @@ sta::define_cmd_args "set_simulated_annealing" {[-temperature temperature]\ proc set_simulated_annealing { args } { sta::parse_key_args "set_simulated_annealing" args \ - keys {-temperature -max_iterations -perturb_per_iter -alpha} + keys {-temperature -max_iterations -perturb_per_iter -alpha} flags {} set temperature 0 if {[info exists keys(-temperature)]} { @@ -349,7 +352,7 @@ proc set_simulated_annealing { args } { sta::define_cmd_args "simulated_annealing_debug" { [-iters_between_paintings iters] - [-no_pause_mode no_pause_mode] # Print solver state every second based on iters_between_paintings + [-no_pause_mode no_pause_mode] } proc simulated_annealing_debug { args } { @@ -370,7 +373,8 @@ sta::define_cmd_args "place_pin" {[-pin_name pin_name]\ [-layer layer]\ [-location location]\ [-pin_size pin_size]\ - [-force_to_die_boundary] + [-force_to_die_boundary]\ + [-placed_status] } proc place_pin { args } { @@ -431,6 +435,8 @@ proc place_pin { args } { sta::define_cmd_args "write_pin_placement" { file_name } proc write_pin_placement { args } { + sta::parse_key_args "write_pin_placement" args \ + keys {} flags {} set file_name $args ppl::write_pin_placement $file_name } diff --git a/src/ppl/test/extract_utils.py b/src/ppl/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/ppl/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/ppl/test/manpage.py b/src/ppl/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/ppl/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/ppl/test/md_roff_compat.py b/src/ppl/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/ppl/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/ppl/test/ppl_man_tcl_check.ok b/src/ppl/test/ppl_man_tcl_check.ok new file mode 100644 index 00000000000..94280ca3ef4 --- /dev/null +++ b/src/ppl/test/ppl_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/ppl 11 11 11 +Command counts match. diff --git a/src/ppl/test/ppl_man_tcl_check.py b/src/ppl/test/ppl_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/ppl/test/ppl_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/ppl/test/ppl_readme_msgs_check.ok b/src/ppl/test/ppl_readme_msgs_check.ok new file mode 100644 index 00000000000..5b17c5085ef --- /dev/null +++ b/src/ppl/test/ppl_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 11, Desc: 11, Syn: 11, Options: 11, Args: 11 +Info: 17, Warn: 17, Error: 66 diff --git a/src/ppl/test/ppl_readme_msgs_check.py b/src/ppl/test/ppl_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/ppl/test/ppl_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/ppl/test/regression_tests.tcl b/src/ppl/test/regression_tests.tcl index dee3581a45d..b2e247175f3 100644 --- a/src/ppl/test/regression_tests.tcl +++ b/src/ppl/test/regression_tests.tcl @@ -111,4 +111,6 @@ record_tests { write_pin_placement2 write_pin_placement3 write_pin_placement4 + ppl_man_tcl_check + ppl_readme_msgs_check } diff --git a/src/psm/README.md b/src/psm/README.md index 792cdbf6ca7..76a6a4321b4 100644 --- a/src/psm/README.md +++ b/src/psm/README.md @@ -25,6 +25,8 @@ Features: ### Analyze Power Grid +This command analyzes power grid. + ```tcl analyze_power_grid [-vsrc vsrc_file] @@ -57,8 +59,12 @@ analyze_power_grid ### Check Power Grid +This command checks power grid. + ```tcl -check_power_grid -net net_name +check_power_grid + [-net net_name] + [-error_file error_file] ``` #### Options @@ -66,9 +72,12 @@ check_power_grid -net net_name | Switch Name | Description | | ----- | ----- | | `-net` | Name of the net to analyze. Must be a power or ground net name. | +| `-error_file` | Name of the error file to save output to. | ### Write Spice Power Grid +This command writes the `spice` file for power grid. + ```tcl write_pg_spice [-vsrc vsrc_file] @@ -91,6 +100,8 @@ write_pg_spice ### Set PDNSim Net voltage +This command sets PDNSim net voltage. + ```tcl set_pdnsim_net_voltage [-net net_name] @@ -104,7 +115,7 @@ set_pdnsim_net_voltage | `-net` | Name of the net to analyze. It must be a power or ground net name. | | `-voltage` | Sets the voltage on a specific net. If this option is not given, the Liberty file's voltage value is obtained from operating conditions. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/pdnsim.cpp) or the [swig file](./src/pdnsim.i). @@ -116,7 +127,7 @@ If you are a developer, you might find these useful. More details can be found i Example scripts demonstrating how to run PDNSim on a sample design on `aes` as follows: -```tcl +``` ./test/aes_test_vdd.tcl ./test/aes_test_vss.tcl ``` diff --git a/src/psm/test/extract_utils.py b/src/psm/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/psm/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/psm/test/manpage.py b/src/psm/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/psm/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/psm/test/md_roff_compat.py b/src/psm/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/psm/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/psm/test/psm_man_tcl_check.ok b/src/psm/test/psm_man_tcl_check.ok new file mode 100644 index 00000000000..13d025dbb41 --- /dev/null +++ b/src/psm/test/psm_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/psm 4 4 4 +Command counts match. diff --git a/src/psm/test/psm_man_tcl_check.py b/src/psm/test/psm_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/psm/test/psm_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/psm/test/psm_readme_msgs_check.ok b/src/psm/test/psm_readme_msgs_check.ok new file mode 100644 index 00000000000..c5fb5ffe433 --- /dev/null +++ b/src/psm/test/psm_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 4, Desc: 4, Syn: 4, Options: 4, Args: 4 +Info: 10, Warn: 18, Error: 39 diff --git a/src/psm/test/psm_readme_msgs_check.py b/src/psm/test/psm_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/psm/test/psm_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/psm/test/regression_tests.tcl b/src/psm/test/regression_tests.tcl index d15175a0005..b45bd084dee 100644 --- a/src/psm/test/regression_tests.tcl +++ b/src/psm/test/regression_tests.tcl @@ -23,4 +23,6 @@ record_tests { zerosoc_pads_check_only zerosoc_pads_check_only_disconnected pad_connected_by_abutment + psm_man_tcl_check + psm_readme_msgs_check } diff --git a/src/rcx/README.md b/src/rcx/README.md index 05d3e4752d7..40d4eb52062 100644 --- a/src/rcx/README.md +++ b/src/rcx/README.md @@ -27,6 +27,8 @@ objects. Optionally, OpenRCX can generate a `.spef` file. ### Define Process Corner +This command defines proccess corner. + ```tcl define_process_corner [-ext_model_index index] @@ -82,6 +84,7 @@ in the database. write_spef [-net_id net_id] [-nets nets] + [-coordinates] filename ``` @@ -91,6 +94,7 @@ write_spef | ----- | ----- | | `-net_id` | Output the parasitics info for specific net IDs. | | `-nets` | Net name. | +| `coordinates` | Coordinates TBC. | | `filename` | Output filename. | ### Scale RC @@ -113,7 +117,7 @@ adjust_rc | `-cc_factor` | Scale factor for coupling capacitance. | | `-gndc_factor` | Scale factor for ground capacitance. | -### Comparing SPEF files +### Comparing different SPEF files The `diff_spef` command compares the parasitics in the reference database `.spef`. The output of this command is `diff_spef.out` @@ -232,6 +236,7 @@ write_rules [-dir dir] [-name name] [-pattern pattern] + [-db] ``` #### Options @@ -242,6 +247,7 @@ write_rules | `-dir` | Output file directory. | | `-name` | Name of rule. | | `-pattern` | Flag to write the pattern to rulefile (0/1). | +| `-db` | DB tbc. | ## Example scripts diff --git a/src/rcx/src/OpenRCX.tcl b/src/rcx/src/OpenRCX.tcl index 0ae97dbff38..b947cf0695a 100644 --- a/src/rcx/src/OpenRCX.tcl +++ b/src/rcx/src/OpenRCX.tcl @@ -38,7 +38,8 @@ sta::define_cmd_args "define_process_corner" { } proc define_process_corner { args } { - sta::parse_key_args "define_process_corner" args keys {-ext_model_index} + sta::parse_key_args "define_process_corner" args \ + keys {-ext_model_index} flags {} sta::check_argc_eq1 "define_process_corner" $args set ext_model_index 0 @@ -124,7 +125,9 @@ proc extract_parasitics { args } { sta::define_cmd_args "write_spef" { [-net_id net_id] - [-nets nets] filename } + [-nets nets] + [-coordinates] + filename } proc write_spef { args } { sta::parse_key_args "write_spef" args \ @@ -159,7 +162,8 @@ proc adjust_rc { args } { sta::parse_key_args "adjust_rc" args \ keys { -res_factor -cc_factor - -gndc_factor } + -gndc_factor } \ + flags {} set res_factor 1.0 if { [info exists keys(-res_factor)] } { @@ -284,6 +288,7 @@ proc bench_wires { args } { sta::define_cmd_args "bench_verilog" { filename } proc bench_verilog { args } { + sta::parse_key_args "bench_verilog" args keys {} flags{} sta::check_argc_eq1 "bench_verilog" $args rcx::bench_verilog $args } @@ -291,6 +296,7 @@ proc bench_verilog { args } { sta::define_cmd_args "bench_read_spef" { filename } proc bench_read_spef { args } { + sta::parse_key_args "bench_read_spef" args keys {} flags{} sta::check_argc_eq1 "bench_read_spef" $args rcx::read_spef $args } @@ -300,6 +306,7 @@ sta::define_cmd_args "write_rules" { [-dir dir] [-name name] [-pattern pattern] + [-db] } proc write_rules { args } { diff --git a/src/rcx/test/extract_utils.py b/src/rcx/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/rcx/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/rcx/test/manpage.py b/src/rcx/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/rcx/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/rcx/test/md_roff_compat.py b/src/rcx/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/rcx/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/rcx/test/rcx_man_tcl_check.ok b/src/rcx/test/rcx_man_tcl_check.ok new file mode 100644 index 00000000000..cdc57491838 --- /dev/null +++ b/src/rcx/test/rcx_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/rcx 9 9 9 +Command counts match. diff --git a/src/rcx/test/rcx_man_tcl_check.py b/src/rcx/test/rcx_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/rcx/test/rcx_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/rcx/test/rcx_readme_msgs_check.ok b/src/rcx/test/rcx_readme_msgs_check.ok new file mode 100644 index 00000000000..0429e78c089 --- /dev/null +++ b/src/rcx/test/rcx_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 9, Desc: 9, Syn: 9, Options: 9, Args: 9 +Info: 90, Warn: 63, Error: 28 diff --git a/src/rcx/test/rcx_readme_msgs_check.py b/src/rcx/test/rcx_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/rcx/test/rcx_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/rcx/test/regression_tests.tcl b/src/rcx/test/regression_tests.tcl index 7070cc45fae..ea6b873109e 100644 --- a/src/rcx/test/regression_tests.tcl +++ b/src/rcx/test/regression_tests.tcl @@ -5,6 +5,8 @@ record_tests { gcd 45_gcd names + rcx_man_tcl_check + rcx_readme_msgs_check } record_pass_fail_tests { rcx_unit_test diff --git a/src/rmp/README.md b/src/rmp/README.md index 2381135bc92..54d27e8106a 100644 --- a/src/rmp/README.md +++ b/src/rmp/README.md @@ -18,30 +18,26 @@ by the user as per the interface described below. - Parameters without square brackets `-param2 param2` are required. ``` +### Restructure + Restructuring can be done in two modes: area or delay. -### Area Mode +- Method 1: Area Mode +Example: `restructure -liberty_file ckt.lib -target area -tielo_pin ABC -tiehi_pin DEF` -```tcl -restructure - -liberty_file liberty_file - -target area - -tielo_pin tielo_pin_name - -tiehi_pin tiehi_pin_name - -work_dir workdir_name -``` - -### Timing Mode +- Method 2: Timing Mode +Example: `restructure -liberty_file ckt.lib -target delay -tielo_pin ABC -tiehi_pin DEF -slack_threshold 1 -depth_threshold 2` ```tcl restructure - -liberty_file liberty_file - -target timing - -slack_threshold slack_val - -depth_threshold depth_threshold - -tielo_pin tielo_pin_name - -tiehi_pin tiehi_pin_name - -work_dir workdir_name + [-slack_threshold slack_val] + [-depth_threshold depth_threshold] + [-target area|delay] + [-abc_logfile logfile] + [-liberty_file liberty_file] + [-tielo_port tielo_pin_name] + [-tiehi_port tiehi_pin_name] + [-work_dir work_dir] ``` #### Options @@ -54,13 +50,14 @@ restructure | `-depth_threshold` | Specifies the path depth above which a timing path would be considered for restructuring. The default value is `16`, and the allowed values are `[0, MAX_INT]`. | | `-tielo_pin` | Tie cell pin that can drive constant zero. The format is `/`. | | `-tiehi_pin` | Tie cell pin that can drive constant one. The format is `/`. | +| `-abc_logfile` | Output file to save abc logs to. | | `-work_dir` | Name of the working directory for temporary files. If not provided, `run` directory would be used. | ## Example scripts Example scripts on running `rmp` for a sample design of `gcd` as follows: -```tcl +``` ./test/gcd_restructure.tcl ``` diff --git a/src/rmp/src/rmp.tcl b/src/rmp/src/rmp.tcl index 97eb462cf7b..85eff675d64 100644 --- a/src/rmp/src/rmp.tcl +++ b/src/rmp/src/rmp.tcl @@ -56,6 +56,7 @@ sta::define_cmd_args "restructure" { \ [-slack_threshold slack]\ [-depth_threshold depth]\ [-target area|timing]\ + [-abc_logfile logfile]\ [-liberty_file liberty_file]\ [-tielo_port tielow_port]\ [-tiehi_port tiehigh_port]\ diff --git a/src/rmp/test/extract_utils.py b/src/rmp/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/rmp/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/rmp/test/manpage.py b/src/rmp/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/rmp/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/rmp/test/md_roff_compat.py b/src/rmp/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/rmp/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/rmp/test/regression_tests.tcl b/src/rmp/test/regression_tests.tcl index e9c1e5b3fff..2f0a8ccf84c 100644 --- a/src/rmp/test/regression_tests.tcl +++ b/src/rmp/test/regression_tests.tcl @@ -9,4 +9,6 @@ record_tests { blif_reader blif_reader_const blif_reader_sequential + rmp_man_tcl_check + rmp_readme_msgs_check } diff --git a/src/rmp/test/rmp_man_tcl_check.ok b/src/rmp/test/rmp_man_tcl_check.ok new file mode 100644 index 00000000000..53856c8d948 --- /dev/null +++ b/src/rmp/test/rmp_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/rmp 1 1 1 +Command counts match. diff --git a/src/rmp/test/rmp_man_tcl_check.py b/src/rmp/test/rmp_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/rmp/test/rmp_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/rmp/test/rmp_readme_msgs_check.ok b/src/rmp/test/rmp_readme_msgs_check.ok new file mode 100644 index 00000000000..3c8e366ada5 --- /dev/null +++ b/src/rmp/test/rmp_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 9, Warn: 5, Error: 10 diff --git a/src/rmp/test/rmp_readme_msgs_check.py b/src/rmp/test/rmp_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/rmp/test/rmp_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/rsz/README.md b/src/rsz/README.md index a741f484ef5..907a55e10dc 100644 --- a/src/rsz/README.md +++ b/src/rsz/README.md @@ -19,14 +19,21 @@ delay of routing wires. Separate values can be specified for clock and data nets with the `-signal` and `-clock` flags. Without either `-signal` or `-clock` the resistance and capacitance for clocks and data nets are set. +``` +# Either run +set_wire_rc -clock ... -signal ... -layer ... + +# Or +set_wire_rc -resistance ... -capacitance ... +``` + ```tcl set_wire_rc [-clock] [-signal] + [-data] + [-corner corner] [-layer layer_name] - -or -set_wire_rc [-resistance res] [-capacitance cap] ``` @@ -100,7 +107,14 @@ or a list of cell names (`wildcards` allowed). For example, `DLY*` says do not use cells with names that begin with `DLY` in all libraries. ```tcl -set_dont_use lib_cells +set_dont_use lib_cells +``` + +### Unset Don't Use + +The `unset_dont_use` command reverses the `set_dont_use` command. + +```tcl unset_dont_use lib_cells ``` @@ -110,7 +124,14 @@ The `set_dont_touch` command prevents the resizer commands from modifying instances or nets. ```tcl -set_dont_touch instances_nets +set_dont_touch instances_nets +``` + +### Unset Don't Touch + +The `unset_dont_touch` command reverse the `set_dont_touch` command. + +```tcl unset_dont_touch instances_nets ``` @@ -127,6 +148,7 @@ buffer_ports [-inputs] [-outputs] [-max_utilization util] + [-buffer_cell buf_cell] ``` #### Options @@ -146,6 +168,14 @@ in buffering nets. remove_buffers ``` +### Balance Row Usage + +Command description pending. + +```tcl +balance_row_usage +``` + ### Repair Design The `repair_design` command inserts buffers on nets to repair max slew, max @@ -183,6 +213,7 @@ of the tie high/low cell. ```tcl repair_tie_fanout [-separation dist] + [-max_fanout fanout] [-verbose] lib_port ``` @@ -212,10 +243,13 @@ repair_timing [-recover_power percent_of_paths_with_slack] [-setup_margin setup_margin] [-hold_margin hold_margin] + [-slack_margin slack_margin] + [-libraries libs] [-allow_setup_violations] [-skip_pin_swap] [-skip_gate_cloning] [-repair_tns tns_end_percent] + [-max_passes passes] [-max_utilization util] [-max_buffer_percent buffer_percent] [-verbose] @@ -246,7 +280,9 @@ this option be used with global routing based parasitics. The `clock_tree_synthesis` command inserts a clock tree in the design but may leave a long wire from the clock input pin to the clock tree -root buffer. The `repair_clock_nets` command inserts buffers in the +root buffer. + +The `repair_clock_nets` command inserts buffers in the wire from the clock input pin to the clock root buffer. ```tcl @@ -295,7 +331,7 @@ report_floating_nets | ----- | ----- | | `-verbose` | Print the net names. | -### Useful Developer Commands +## Useful Developer Commands If you are a developer, you might find these useful. More details can be found in the [source file](./src/Resizer.cc) or the [swig file](./src/Resizer.i). @@ -318,7 +354,7 @@ If you are a developer, you might find these useful. More details can be found i A typical `resizer` command file (after a design and Liberty libraries have been read) is shown below. -```tcl +``` read_sdc gcd.sdc set_wire_rc -layer metal2 @@ -336,7 +372,7 @@ repair_timing Note that OpenSTA commands can be used to report timing metrics before or after resizing the design. -```tcl +``` set_wire_rc -layer metal2 report_checks report_tns diff --git a/src/rsz/src/Resizer.tcl b/src/rsz/src/Resizer.tcl index b6a43897de0..97876692df2 100644 --- a/src/rsz/src/Resizer.tcl +++ b/src/rsz/src/Resizer.tcl @@ -124,7 +124,7 @@ proc set_layer_rc {args} { } } -sta::define_cmd_args "set_wire_rc" {[-clock] [-signal]\ +sta::define_cmd_args "set_wire_rc" {[-clock] [-signal] [-data]\ [-layer layer_name]\ [-resistance res]\ [-capacitance cap]\ @@ -235,12 +235,14 @@ proc estimate_parasitics { args } { sta::define_cmd_args "set_dont_use" {lib_cells} proc set_dont_use { args } { + sta::parse_key_args "set_dont_use" args keys {} flags {} set_dont_use_cmd "set_dont_use" $args 1 } sta::define_cmd_args "unset_dont_use" {lib_cells} proc unset_dont_use { args } { + sta::parse_key_args "unset_dont_use" args keys {} flags {} set_dont_use_cmd "unset_dont_use" $args 0 } @@ -254,12 +256,14 @@ proc set_dont_use_cmd { cmd cmd_args dont_use } { sta::define_cmd_args "set_dont_touch" {nets_instances} proc set_dont_touch { args } { + sta::parse_key_args "set_dont_touch" args keys {} flags {} set_dont_touch_cmd "set_dont_touch" $args 1 } sta::define_cmd_args "unset_dont_touch" {nets_instances} proc unset_dont_touch { args } { + sta::parse_key_args "unset_dont_touch" args keys {} flags {} set_dont_touch_cmd "unset_dont_touch" $args 0 } @@ -275,7 +279,8 @@ proc set_dont_touch_cmd { cmd cmd_args dont_touch } { } sta::define_cmd_args "buffer_ports" {[-inputs] [-outputs]\ - [-max_utilization util]} + [-max_utilization util]\ + [-buffer_cell buf_cell]} proc buffer_ports { args } { sta::parse_key_args "buffer_ports" args \ @@ -302,6 +307,7 @@ proc buffer_ports { args } { sta::define_cmd_args "remove_buffers" {} proc remove_buffers { args } { + sta::parse_key_args "remove_buffers" args keys {} flags {} sta::check_argc_eq0 "remove_buffers" $args rsz::remove_buffers_cmd } @@ -309,6 +315,7 @@ proc remove_buffers { args } { sta::define_cmd_args "balance_row_usage" {} proc balance_row_usage { args } { + sta::parse_key_args "balance_row_usage" args keys {} flags {} sta::check_argc_eq0 "balance_row_usage" $args rsz::balance_row_usage_cmd } @@ -355,11 +362,15 @@ proc repair_clock_nets { args } { sta::define_cmd_args "repair_clock_inverters" {} proc repair_clock_inverters { args } { + sta::parse_key_args "repair_clock_inverters" args keys {} flags {} sta::check_argc_eq0 "repair_clock_inverters" $args rsz::repair_clk_inverters_cmd } -sta::define_cmd_args "repair_tie_fanout" {lib_port [-separation dist] [-verbose]} +sta::define_cmd_args "repair_tie_fanout" {lib_port\ + [-separation dist]\ + [-max_fanout fanout]\ + [-verbose]} proc repair_tie_fanout { args } { sta::parse_key_args "repair_tie_fanout" args keys {-separation -max_fanout} \ @@ -390,13 +401,17 @@ proc repair_tie_fanout { args } { # -max_passes is for developer debugging so intentionally not documented # in define_cmd_args -sta::define_cmd_args "repair_timing" {[-setup] [-hold] [-recover_power percent_of_paths_with_slack]\ +sta::define_cmd_args "repair_timing" {[-setup] [-hold]\ + [-recover_power percent_of_paths_with_slack]\ [-setup_margin setup_margin]\ [-hold_margin hold_margin]\ + [-slack_margin slack_margin]\ + [-libraries libs]\ [-allow_setup_violations]\ [-skip_pin_swap]\ - [-skip_gate_cloning)]\ + [-skip_gate_cloning]\ [-repair_tns tns_end_percent]\ + [-max_passes passes]\ [-max_buffer_percent buffer_percent]\ [-max_utilization util] \ [-verbose]} @@ -487,16 +502,17 @@ proc repair_timing { args } { sta::define_cmd_args "report_design_area" {} -proc report_design_area {} { +proc report_design_area { args } { + sta::parse_key_args "report_design_area" args keys {} flags {} set util [format %.0f [expr [rsz::utilization] * 100]] set area [sta::format_area [rsz::design_area] 0] utl::report "Design area ${area} u^2 ${util}% utilization." } -sta::define_cmd_args "report_floating_nets" {[-verbose] [> filename] [>> filename]} +sta::define_cmd_args "report_floating_nets" {[-verbose] [> filename] [>> filename]};# checker off sta::proc_redirect report_floating_nets { - sta::parse_key_args "report_floating_nets" args keys {} flags {-verbose} + sta::parse_key_args "report_floating_nets" args keys {} flags {-verbose};# checker off set verbose [info exists flags(-verbose)] set floating_nets [rsz::find_floating_nets] @@ -524,12 +540,12 @@ sta::proc_redirect report_floating_nets { utl::metric_int "timing__drv__floating__pins" $floating_pin_count } -sta::define_cmd_args "report_long_wires" {count [> filename] [>> filename]} +sta::define_cmd_args "report_long_wires" {count [> filename] [>> filename]};# checker off sta::proc_redirect report_long_wires { global sta_report_default_digits - sta::parse_key_args "report_long_wires" args keys {-digits} flags {} + sta::parse_key_args "report_long_wires" args keys {-digits} flags {};# checker off set digits $sta_report_default_digits if { [info exists keys(-digits)] } { diff --git a/src/rsz/test/extract_utils.py b/src/rsz/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/rsz/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/rsz/test/manpage.py b/src/rsz/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/rsz/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/rsz/test/md_roff_compat.py b/src/rsz/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/rsz/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/rsz/test/regression_tests.tcl b/src/rsz/test/regression_tests.tcl index 9837b9159d7..1c8e361b490 100644 --- a/src/rsz/test/regression_tests.tcl +++ b/src/rsz/test/regression_tests.tcl @@ -109,4 +109,6 @@ record_tests { repair_hold9_verbose set_dont_touch1 set_dont_use1 + rsz_man_tcl_check + rsz_readme_msgs_check } diff --git a/src/rsz/test/rsz_man_tcl_check.ok b/src/rsz/test/rsz_man_tcl_check.ok new file mode 100644 index 00000000000..50dcb44e586 --- /dev/null +++ b/src/rsz/test/rsz_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/rsz 16 16 17 +Command counts do not match. diff --git a/src/rsz/test/rsz_man_tcl_check.py b/src/rsz/test/rsz_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/rsz/test/rsz_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/rsz/test/rsz_readme_msgs_check.ok b/src/rsz/test/rsz_readme_msgs_check.ok new file mode 100644 index 00000000000..64d62d3e267 --- /dev/null +++ b/src/rsz/test/rsz_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 17, Desc: 17, Syn: 17, Options: 17, Args: 17 +Info: 35, Warn: 21, Error: 26 diff --git a/src/rsz/test/rsz_readme_msgs_check.py b/src/rsz/test/rsz_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/rsz/test/rsz_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/sta b/src/sta index 073ff2e8c8c..95276f87311 160000 --- a/src/sta +++ b/src/sta @@ -1 +1 @@ -Subproject commit 073ff2e8c8c5469996fed1236f801b0b14064b0c +Subproject commit 95276f87311b63092b8502832f24f784cc516128 diff --git a/src/stt/README.md b/src/stt/README.md new file mode 100644 index 00000000000..d36cdd0b344 --- /dev/null +++ b/src/stt/README.md @@ -0,0 +1,75 @@ +# Rectilinear Steiner Tree + +The steiner tree (`stt`) module in OpenROAD constructs steiner trees used in +the global routing (`grt`) module. Documentation is current under construction. + +## Commands + +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` + +### Set Routing Alpha + +This command sets routing alphas for a given net `net_name`. + +By default the global router uses heuristic rectilinear Steiner minimum +trees (RSMTs) as an initial basis to construct route guides. An RSMT +tries to minimize the total wirelength needed to connect a given set +of pins. The Prim-Dijkstra heuristic is an alternative net topology +algorithm that supports a trade-off between total wirelength and maximum +path depth from the net driver to its loads. The `set_routing_alpha` +command enables the Prim/Dijkstra algorithm and sets the alpha parameter +used to trade-off wirelength and path depth. Alpha is between 0.0 +and 1.0. When alpha is 0.0 the net topology minimizes total wirelength +(i.e. capacitance). When alpha is 1.0 it minimizes longest path between +the driver and loads (i.e., maximum resistance). Typical values are +0.4-0.8. You can call it multiple times for different nets. + +Example: `set_routing_alpha -net clk 0.3` sets the alpha value of 0.3 for net *clk*. + +```tcl +set_routing_alpha + [-net net_name] + [-min_fanout fanout] + [-min_hpwl hpwl] + [-clock_nets] + alpha +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-net` | Net name. | +| `-min_fanout` | Set the minimum number for fanout. | +| `-min_hpwl` | Set the minimum half-perimetere wirelength (microns). | +| `-clock_nets` | If this flag is set to True, only clock nets will be modified. | +| `alpha` | Float between 0 and 1 describing the trade-off between wirelength and path depth. | + +## Example scripts + +## Regression tests + +There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). + +Simply run the following script: + +```shell +./test/regression +``` + +## Limitations + +## FAQs + +Check out [GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/discussions/categories/q-a?discussions_q=category%3AQ%26A+stt+in%3Atitle) +about this tool. + +## References + +## License + +BSD 3-Clause License. See [LICENSE](LICENSE) file. + diff --git a/src/stt/test/extract_utils.py b/src/stt/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/stt/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/stt/test/manpage.py b/src/stt/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/stt/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/stt/test/md_roff_compat.py b/src/stt/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/stt/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/stt/test/regression_tests.tcl b/src/stt/test/regression_tests.tcl index 1178615a002..c7df3e4b7ce 100644 --- a/src/stt/test/regression_tests.tcl +++ b/src/stt/test/regression_tests.tcl @@ -6,4 +6,6 @@ record_tests { pd1 pd2 pd_gcd + stt_man_tcl_check + stt_readme_msgs_check } diff --git a/src/stt/test/stt_man_tcl_check.ok b/src/stt/test/stt_man_tcl_check.ok new file mode 100644 index 00000000000..1b804d932f5 --- /dev/null +++ b/src/stt/test/stt_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/stt 1 1 1 +Command counts match. diff --git a/src/stt/test/stt_man_tcl_check.py b/src/stt/test/stt_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/stt/test/stt_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/stt/test/stt_readme_msgs_check.ok b/src/stt/test/stt_readme_msgs_check.ok new file mode 100644 index 00000000000..fa9fe94a58e --- /dev/null +++ b/src/stt/test/stt_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 0, Warn: 0, Error: 5 diff --git a/src/stt/test/stt_readme_msgs_check.py b/src/stt/test/stt_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/stt/test/stt_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/tap/README.md b/src/tap/README.md index 10638890c13..60f88b2a2c9 100644 --- a/src/tap/README.md +++ b/src/tap/README.md @@ -11,11 +11,26 @@ Tapcell and endcap insertion. ### Add Tapcell/Endcap +This command inserts tapcells or endcaps. + +The figures below show two examples of tapcell insertion. When only the +`-tapcell_master` and `-endcap_master` masters are given, the tapcell placement +is similar to Figure 1. When the remaining masters are give, the tapcell +placement is similar to Figure 2. + +| | | +|:--:|:--:| +| Figure 1: Tapcell insertion representation | Figure 2: Tapcell insertion around macro representation | + + ```tcl tapcell [-tapcell_master tapcell_master] + [-tap_prefix tap_prefix] [-endcap_master endcap_master] + [-endcap_prefix endcap_prefix] [-distance dist] + [-disallow_one_site_gaps] [-halo_width_x halo_x] [-halo_width_y halo_y] [-tap_nwin2_master tap_nwin2_master] @@ -28,9 +43,8 @@ tapcell [-cnrcap_nwout_master cnrcap_nwout_master] [-incnrcap_nwin_master incnrcap_nwin_master] [-incnrcap_nwout_master incnrcap_nwout_master] - [-tap_prefix tap_prefix] - [-endcap_prefix endcap_prefix] [-tbtie_cpp tbtie_cpp] + [-endcap_cpp endcap_cpp] [-no_cell_at_top_bottom] ``` @@ -39,8 +53,11 @@ tapcell | Switch Name | Description | | ----- | ----- | | `-tapcell_master` | Master used as a tapcell. | +| `-tap_prefix` | Prefix for the tapcell instances. The default value is `TAP_`. | | `-endcap_master` | Master used as an endcap. | +| `-endcap_prefix` | Prefix for the endcaps instances. The default value is `PHY_`. | | `-distance` | Distance (in microns) between each tapcell in the checkerboard. | +| `-disallow_one_site_gaps` | KIV. | | `-halo_width_x` | Horizontal halo size (in microns) around macros during cut rows. | | `-halo_width_y` | Vertical halo size (in microns) around macros during cut rows. | | `-tap_nwintie_master` | Master cell placed at the top and bottom of|macros and the core area according the row orientation. | @@ -53,21 +70,13 @@ tapcell | `-incnrcap_nwout_master` | Master cell placed at the corners of macros, according the row orientation. | | `-cnrcap_nwin_master` | Macro cell placed at the corners the core area according the row orientation. | | `-cnrcap_nwout_master` | Macro cell placed at the corners the core area according the row orientation. | -| `-tap_prefix` | Prefix for the tapcell instances. The default value is `TAP_`. | -| `-endcap_prefix` | Prefix for the endcaps instances. The default value is `PHY_`. | | `-tbtie_cpp` | Option is deprecated. | +| `-endcap_cpp` | Option is deprecated. | | `-no_cell_at_top_bottom` | Option is deprecated. | -The figures below show two examples of tapcell insertion. When only the -`-tapcell_master` and `-endcap_master` masters are given, the tapcell placement -is similar to Figure 1. When the remaining masters are give, the tapcell -placement is similar to Figure 2. +### Cut Rows -| | | -|:--:|:--:| -| Figure 1: Tapcell insertion representation | Figure 2: Tapcell insertion around macro representation | - -### Only cutting rows +This command cuts rows. ```tcl cut_rows @@ -134,7 +143,9 @@ place_endcaps | `-top_edge` | List of masters for the top row endcaps. (overrides `-endcap_horizontal`). | | `-bottom_edge` | List of masters for the bottom row endcaps. (overrides `-endcap_horizontal`). | -### Only adding tapcells cells +### Only adding Tapcells + +This command is used for tapcell placement only. ```tcl place_tapcells @@ -152,6 +163,8 @@ place_tapcells ### Remove Tapcells/Endcaps +This command is used for removing tapcells or endcaps based on their prefix. + ```tcl tapcell_ripup -tap_prefix tap_prefix @@ -169,7 +182,7 @@ tapcell_ripup You can find script examples for both 45nm and 14nm in `./etc/scripts` -```tcl +``` ./etc/scripts/example_14nm.tcl ./etc/scripts/example_45nm.tcl ``` diff --git a/src/tap/src/tapcell.tcl b/src/tap/src/tapcell.tcl index 1c29a7407b5..6f9e83d9d0c 100644 --- a/src/tap/src/tapcell.tcl +++ b/src/tap/src/tapcell.tcl @@ -53,6 +53,7 @@ sta::define_cmd_args "tapcell" {[-tapcell_master tapcell_master]\ [-incnrcap_nwin_master incnrcap_nwin_master]\ [-incnrcap_nwout_master incnrcap_nwout_master]\ [-tbtie_cpp tbtie_cpp]\ + [-endcap_cpp endcap_cpp]\ [-no_cell_at_top_bottom]\ } @@ -228,7 +229,7 @@ sta::define_cmd_args "tapcell_ripup" {[-tap_prefix tap_prefix]\ # This will remove the tap cells and endcaps to tapcell can be rerun with new parameters proc tapcell_ripup { args } { - sta::parse_key_args "tapcell" args \ + sta::parse_key_args "tapcell_ripup" args \ keys {-tap_prefix -endcap_prefix} \ flags {} @@ -357,11 +358,11 @@ sta::define_cmd_args "place_tapcells" { proc place_tapcells {args } { - sta::parse_key_args "place_boundary_cells" args \ + sta::parse_key_args "place_tapcells" args \ keys {-master -distance} \ flags {} - sta::check_argc_eq0 "place_boundary_cells" $args + sta::check_argc_eq0 "place_tapcells" $args set dist -1 if { [info exists keys(-distance)] } { diff --git a/src/tap/test/extract_utils.py b/src/tap/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/tap/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/tap/test/manpage.py b/src/tap/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/tap/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/tap/test/md_roff_compat.py b/src/tap/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/tap/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/tap/test/regression_tests.tcl b/src/tap/test/regression_tests.tcl index 4e4c1ae9595..eed300908ef 100644 --- a/src/tap/test/regression_tests.tcl +++ b/src/tap/test/regression_tests.tcl @@ -20,4 +20,6 @@ record_tests { gcd_sky130_separate jpeg_gf180 aes_gf180 + tap_man_tcl_check + tap_readme_msgs_check } diff --git a/src/tap/test/tap_man_tcl_check.ok b/src/tap/test/tap_man_tcl_check.ok new file mode 100644 index 00000000000..c37fe8b2621 --- /dev/null +++ b/src/tap/test/tap_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/tap 5 5 5 +Command counts match. diff --git a/src/tap/test/tap_man_tcl_check.py b/src/tap/test/tap_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/tap/test/tap_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/tap/test/tap_readme_msgs_check.ok b/src/tap/test/tap_readme_msgs_check.ok new file mode 100644 index 00000000000..9254f35b001 --- /dev/null +++ b/src/tap/test/tap_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 5, Desc: 5, Syn: 5, Options: 5, Args: 5 +Info: 5, Warn: 4, Error: 9 diff --git a/src/tap/test/tap_readme_msgs_check.py b/src/tap/test/tap_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/tap/test/tap_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/upf/README.md b/src/upf/README.md index ba1d6b25897..0c50faa97b6 100644 --- a/src/upf/README.md +++ b/src/upf/README.md @@ -27,6 +27,8 @@ read_upf ### Create Power Domain +This command creates power domain for a group of modules. + ```tcl create_power_domain [-elements elements] @@ -42,6 +44,9 @@ create_power_domain ### Create Logic Port +This command creates logic port. Direction must be specified from: +`in`, `out`, `inout`. + ```tcl create_logic_port [-direction direction] @@ -57,6 +62,8 @@ create_logic_port ### Create Power Switch +This command creates power switch. + ```tcl create_power_switch [-domain domain] @@ -78,7 +85,9 @@ create_power_switch | `-on_state` | One of {`state_name`, `input_supply_port`, `boolean_expression`}. | | `name` | Power switch name. | -### Create or Update Isolation Strategy +### Create or Update Isolation Strategy + +This command creates or update isolation strategy. ```tcl set_isolation @@ -107,6 +116,8 @@ set_isolation ### Set Interface cell +This command sets the interface cell. + ```tcl use_interface_cell [-domain domain] @@ -124,6 +135,8 @@ use_interface_cell ### Set Domain Area +This command sets the power domain area. + ```tcl set_domain_area domain_name @@ -140,6 +153,8 @@ set_domain_area ### Map existing power switch +This command maps existing power switch. + ```tcl map_power_switch [-switch_name_list switch_name_list] @@ -155,11 +170,107 @@ map_power_switch | `-lib_cells` | A list of library cells that could be mapped to the power switch | | `-port_map` | A map that associates model ports defined by create_power_switch to logical ports | +### Set Level Shifter + +This command sets level shifter. +Options coming soon. + +```tcl +set_level_shifter + [-domain domain] \ + [-elements elements] \ + [-exclude_elements exclude_elements] \ + [-source source] \ + [-sink sink] \ + [-use_functional_equivalence use_functional_equivalence] \ + [-applies_to applies_to] \ + [-applies_to_boundary applies_to_boundary] \ + [-rule rule] \ + [-threshold threshold] \ + [-no_shift] \ + [-force_shift] \ + [-location location] \ + [-input_supply input_supply] \ + [-output_supply output_supply] \ + [-internal_supply internal_supply] \ + [-name_prefix name_prefix] \ + [-name_suffix name_suffix] \ + [-instance instance] \ + [-update] \ + [-use_equivalence use_equivalence] \ + name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-domain` | | +| `-elements` | | +| `-exclude_elements` | | +| `-source` | | +| `-sink` | | +| `-use_functional_equivalence` | | +| `-applies_to` | | +| `-applies_to_boundary` | | +| `-rule` | | +| `-threshold` | | +| `-no_shift` | | +| `-force_shift` | | +| `-location` | | +| `-input_supply` | | +| `-output_supply` | | +| `-internal_supply` | | +| `-name_prefix` | | +| `-name_suffix` | | +| `-instance` | | +| `-update` | | +| `-use_equivalence` | | +| `name` | | + +### Set Domain Voltage + +This command sets the voltage of a power domain. + +```tcl +set_domain_voltage + [-domain domain] \ + [-voltage voltage] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-domain` | Power domain name. | +| `-voltage` | Power domain voltage. The allowed values are `float`, default value is `0.0`. | + +### Set Level Shifter Cell + +This command sets the library cell used for level shifter. + +```tcl +set_level_shifter_cell + [-level_shifter level_shifter] \ + [-cell_name cell_name] \ + [-input_port input_port] \ + [-output_port output_port] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-level_shifter` | KIV. | +| `-cell_name` | KIV. | +| `-input_port` | KIV. | +| `-output_port` | KIV. | + ## Example scripts Example script demonstrating how to run `upf` related commands can be found here: -```tcl +``` ./test/upf_test.tcl ``` diff --git a/src/upf/src/upf.tcl b/src/upf/src/upf.tcl index 283c57af115..f912e98d996 100644 --- a/src/upf/src/upf.tcl +++ b/src/upf/src/upf.tcl @@ -340,7 +340,7 @@ proc map_power_switch { args } { upf::check_block_exists sta::parse_key_args "map_power_switch" args \ - keys {switch_name_list -lib_cells -port_map} flags {} + keys {-switch_name_list -lib_cells -port_map} flags {} sta::check_argc_eq1 "map_power_switch" $args diff --git a/src/upf/test/extract_utils.py b/src/upf/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/upf/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/upf/test/manpage.py b/src/upf/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/upf/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/upf/test/md_roff_compat.py b/src/upf/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/upf/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/upf/test/regression_tests.tcl b/src/upf/test/regression_tests.tcl index e479311ba42..8722bd909b2 100644 --- a/src/upf/test/regression_tests.tcl +++ b/src/upf/test/regression_tests.tcl @@ -1,4 +1,6 @@ record_tests { levelshifter write + upf_man_tcl_check + upf_readme_msgs_check } diff --git a/src/upf/test/upf_man_tcl_check.ok b/src/upf/test/upf_man_tcl_check.ok new file mode 100644 index 00000000000..b3cfea12f0f --- /dev/null +++ b/src/upf/test/upf_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/upf 12 11 11 +Command counts do not match. diff --git a/src/upf/test/upf_man_tcl_check.py b/src/upf/test/upf_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/upf/test/upf_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/upf/test/upf_readme_msgs_check.ok b/src/upf/test/upf_readme_msgs_check.ok new file mode 100644 index 00000000000..1cacfd10222 --- /dev/null +++ b/src/upf/test/upf_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 11, Desc: 11, Syn: 11, Options: 11, Args: 11 +Info: 0, Warn: 45, Error: 9 diff --git a/src/upf/test/upf_readme_msgs_check.py b/src/upf/test/upf_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/upf/test/upf_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/src/utl/CMakeLists.txt b/src/utl/CMakeLists.txt index be9a032e21e..66658c5c697 100644 --- a/src/utl/CMakeLists.txt +++ b/src/utl/CMakeLists.txt @@ -40,6 +40,7 @@ find_package(Boost 1.78 REQUIRED) swig_lib(NAME utl NAMESPACE utl I_FILE src/Logger.i + SCRIPTS src/Utl.tcl SWIG_INCLUDES ${PROJECT_SOURCE_DIR}/src ) @@ -109,6 +110,12 @@ target_compile_definitions(utl target_link_libraries(utl PUBLIC utl_lib + PRIVATE + OpenSTA +) + +messages( + TARGET utl ) ############################################################ diff --git a/src/utl/README.md b/src/utl/README.md new file mode 100644 index 00000000000..d79e78d0213 --- /dev/null +++ b/src/utl/README.md @@ -0,0 +1,62 @@ +# Utilities + +The utility module contains the `man` command. + +## Commands + +```{note} +- Parameters in square brackets `[-param param]` are optional. +- Parameters without square brackets `-param2 param2` are required. +``` + +### Man + +This can be used for a range of commands in different levels as follows: +- Level 1: Top-level openroad command (e.g. `man openroad`) +- Level 2: Individual module commands (e.g. `man clock_tree_synthesis`) +- Level 3: Info, error, warning messages (e.g. `man CTS-0001`) + +```tcl +man + name + [-manpath manpath] + [-no_query] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-name` | Name of the command/message to query. | +| `-manpath` | Include optional path to manpage pages (e.g. ~/OpenROAD/docs/cat). | +| `-no_query` | This flag determines whether you wish to see all of the man output at once. Default value is `False`, which shows a buffered output. | + +## Example scripts + +## Regression tests + +There are a set of regression tests in `./test`. For more information, refer to this [section](../../README.md#regression-tests). + +Simply run the following script: + +```shell +./test/regression +``` + +## Limitations + +## FAQs + +Check out +[GitHub discussion](https://github.com/The-OpenROAD-Project/OpenROAD/discussions/categories/q-a?discussions_q=category%3AQ%26A+utl) about this tool. + +## References + +## Authors + +MAN command is written by Jack Luar with guidance from members of the OpenROAD team, +including: Cho Moon, Matt Liberty. + +## License + +BSD 3-Clause License. See [LICENSE](../../LICENSE) file. diff --git a/src/utl/src/MakeLogger.cpp b/src/utl/src/MakeLogger.cpp index 942ee7999d9..f632eb29929 100644 --- a/src/utl/src/MakeLogger.cpp +++ b/src/utl/src/MakeLogger.cpp @@ -37,8 +37,13 @@ #include +#include "sta/StaMain.hh" #include "utl/Logger.h" +namespace sta { +extern const char* utl_tcl_inits[]; +} + extern "C" { extern int Utl_Init(Tcl_Interp* interp); } @@ -56,6 +61,7 @@ void initLogger(Logger* logger, Tcl_Interp* tcl_interp) { // Define swig TCL commands. Utl_Init(tcl_interp); + sta::evalTclInit(tcl_interp, sta::utl_tcl_inits); } -} // namespace ord +} // namespace ord \ No newline at end of file diff --git a/src/utl/src/Utl.tcl b/src/utl/src/Utl.tcl new file mode 100644 index 00000000000..341315ea008 --- /dev/null +++ b/src/utl/src/Utl.tcl @@ -0,0 +1,169 @@ +############################################################################### +## +## BSD 3-Clause License +## +## Copyright (c) 2024, The Regents of the University of California +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, this +## list of conditions and the following disclaimer. +## +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and#or other materials provided with the distribution. +## +## * Neither the name of the copyright holder nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +# This code contains the Tcl-native implementation of man command. + +global MAN_PATH +set MAN_PATH "" + +sta::define_cmd_args "man" { name\ + [-manpath manpath]\ + [-no_query]} +proc man { args } { + sta::parse_key_args "man" args \ + keys {-manpath} flags {-no_query} + + set name [lindex $args 0] + + # check the default man path based on executable path + set exec_output [info nameofexecutable] + + # Check if the output contains 'build/src' + if {[string match "*build/src*" $exec_output]} { + set executable_path [file normalize [file dirname [info nameofexecutable]]] + set man_path [file normalize [file dirname [file dirname $executable_path]]] + set DEFAULT_MAN_PATH [file join $man_path "docs" "cat"] + } else { + set DEFAULT_MAN_PATH "/usr/local/share/man/cat" + } + + global MAN_PATH + if { [info exists keys(-manpath) ]} { + set MAN_PATH $keys(-manpath) + if { [utl::check_valid_man_path $MAN_PATH] == false } { + puts "Using default manpath." + set MAN_PATH $DEFAULT_MAN_PATH + } + } else { + set MAN_PATH $DEFAULT_MAN_PATH + } + + set no_query 0 + if { [info exists flags(-no_query) ]} { + set no_query 1 + } + + #set MAN_PATH [utl::get_input] + #if { [utl::check_valid_man_path $MAN_PATH] == false } { + # puts "Using default manpath." + # set MAN_PATH $DEFAULT_MAN_PATH + #} + + set man_path $MAN_PATH + set man_sections {} + foreach man_section {cat1 cat2 cat3} { + lappend man_sections "$man_path/$man_section" + } + set man_found false + foreach man_section $man_sections { + set length [string length $man_section] + # Get suffix for man section + set man_suffix [string range $man_section [expr {$length - 1}] $length] + # Replace all "::" with "_" + set name1 [string map { "::" "_" } $name] + append name1 ".$man_suffix" + set man_file [file join $man_section $name1] + if { [file exists $man_file] } { + set man_found true + set file_handle [open $man_file r] + set content [read $file_handle] + close $file_handle + + # Display content + set lines [split $content "\n"] + set num_lines [llength $lines] + set page_size 40 + + for {set i 0} {$i < $num_lines} {incr i $page_size} { + set page [lrange $lines $i [expr {$i + $page_size - 1}]] + puts [join $page "\n"] + + # Ask user to continue or quit + if {!$no_query && [llength $lines] > $page_size} { + puts -nonewline "---\nPress 'q' to quit or any other key to continue: \n---" + flush stdout; + set input [gets stdin] + if {$input == "q"} { + break + } + } + } + } + } + + if { $man_found == false } { + utl::error UTL 100 "Man page for $name is not found." + } +} + +namespace eval utl { + +proc get_input { } { + # Get the relative path from the user + puts "Please enter an optional relative path to the cat folders:" + flush stdout + gets stdin relative_path + + # Convert the relative path to an absolute path + set absolute_path [file join [pwd] $relative_path] + + # Display the absolute path + puts "Absolute Path: $absolute_path" + if { [utl::check_valid_path $absolute_path] } { + return $absolute_path + } + return "" +} + +proc check_valid_path { path } { + if {[file isdirectory $path]} { + return true + } else { + puts "Invalid path, please retry." + return false + } +} + +proc check_valid_man_path { path } { + if {[file isdirectory "$path/cat1"]} { + return true + } else { + puts "Invalid man path, please retry." + return false + } +} + +# utl namespace end +} \ No newline at end of file diff --git a/src/utl/test/extract_utils.py b/src/utl/test/extract_utils.py new file mode 120000 index 00000000000..3a16235d508 --- /dev/null +++ b/src/utl/test/extract_utils.py @@ -0,0 +1 @@ +../../../docs/src/scripts/extract_utils.py \ No newline at end of file diff --git a/src/utl/test/manpage.py b/src/utl/test/manpage.py new file mode 120000 index 00000000000..c39859549f2 --- /dev/null +++ b/src/utl/test/manpage.py @@ -0,0 +1 @@ +../../../docs/src/scripts/manpage.py \ No newline at end of file diff --git a/src/utl/test/md_roff_compat.py b/src/utl/test/md_roff_compat.py new file mode 120000 index 00000000000..43530e421eb --- /dev/null +++ b/src/utl/test/md_roff_compat.py @@ -0,0 +1 @@ +../../../docs/src/scripts/md_roff_compat.py \ No newline at end of file diff --git a/src/utl/test/regression_tests.tcl b/src/utl/test/regression_tests.tcl index 23077baa345..93d46772b96 100644 --- a/src/utl/test/regression_tests.tcl +++ b/src/utl/test/regression_tests.tcl @@ -3,6 +3,8 @@ record_tests { test_error test_suppress_message test_metrics + utl_man_tcl_check + utl_readme_msgs_check #test_error_exception } diff --git a/src/utl/test/save_ok b/src/utl/test/save_ok new file mode 120000 index 00000000000..4f5b707628d --- /dev/null +++ b/src/utl/test/save_ok @@ -0,0 +1 @@ +../../../test/save_ok \ No newline at end of file diff --git a/src/utl/test/utl_man_tcl_check.ok b/src/utl/test/utl_man_tcl_check.ok new file mode 100644 index 00000000000..87d738035ef --- /dev/null +++ b/src/utl/test/utl_man_tcl_check.ok @@ -0,0 +1,3 @@ +Tool Dir Help count Proc count Readme count +./src/utl 1 1 1 +Command counts match. diff --git a/src/utl/test/utl_man_tcl_check.py b/src/utl/test/utl_man_tcl_check.py new file mode 100644 index 00000000000..09bfed91e0d --- /dev/null +++ b/src/utl/test/utl_man_tcl_check.py @@ -0,0 +1,67 @@ +import os +import glob +import re +from extract_utils import extract_tcl_code, extract_help, extract_proc + +# Test objective: Make sure similar output in all three: help, proc, and readme +path = os.getcwd() +module_dir = os.path.dirname(path) +module = os.path.basename(module_dir) + +or_home = os.path.dirname(os.path.dirname(os.path.dirname(path))) +os.chdir(or_home) + +help_dict, proc_dict, readme_dict = {}, {}, {} + +# Directories to exclude (according to md_roff_compat) +exclude = ["sta"] +include = ["./src/odb/src/db/odb.tcl"] + +for path in glob.glob("./src/*/src/*tcl") + include: + # exclude all dirs other than the current dir. + if module not in path: continue + + # exclude these dirs which are not compiled in man (do not have readmes). + tool_dir = os.path.dirname(os.path.dirname(path)) + if module not in tool_dir: continue + if "odb" in tool_dir: tool_dir = './src/odb' + if not os.path.exists(f"{tool_dir}/README.md"): continue + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + + # special handling for pad, since it has 3 Tcls. + if "ICeWall" in path or "PdnGen" in path: continue + + with open(path) as f: + # Help patterns + content = f.read() + matches = extract_help(content) + help_dict[tool_dir] = len(matches) + + # Proc patterns + matches = extract_proc(content) + proc_dict[tool_dir] = len(matches) + +for path in glob.glob("./src/*/README.md"): + # exclude all dirs other than the current dir. + if module not in path: continue + + if re.search(f".*{'|'.join(e for e in exclude)}.*", path): continue + tool_dir = os.path.dirname(path) + + # for gui, filter out the gui:: for separate processing + results = [x for x in extract_tcl_code(open(path).read()) if "gui::" not in x] + readme_dict[tool_dir] = len(results) + + # for pad, remove `make_fake_io_site` because it is a hidden cmd arg + if 'pad' in tool_dir: readme_dict[tool_dir] -= 1 + +print("Tool Dir".ljust(20), "Help count".ljust(15), "Proc count".ljust(15), "Readme count") + +for path in help_dict: + h,p,r = help_dict[path], proc_dict[path], readme_dict[path] + print(path.ljust(20), + str(h).ljust(15), + str(p).ljust(15), + str(r)) + if h == p == r: print("Command counts match.") + else: print("Command counts do not match.") \ No newline at end of file diff --git a/src/utl/test/utl_readme_msgs_check.ok b/src/utl/test/utl_readme_msgs_check.ok new file mode 100644 index 00000000000..a911dce0718 --- /dev/null +++ b/src/utl/test/utl_readme_msgs_check.ok @@ -0,0 +1,3 @@ +README.md +Names: 1, Desc: 1, Syn: 1, Options: 1, Args: 1 +Info: 1, Warn: 2, Error: 7 diff --git a/src/utl/test/utl_readme_msgs_check.py b/src/utl/test/utl_readme_msgs_check.py new file mode 100644 index 00000000000..5f2920c1a42 --- /dev/null +++ b/src/utl/test/utl_readme_msgs_check.py @@ -0,0 +1,19 @@ +import os +import sys +from md_roff_compat import man2_translate, man3_translate + +# Test objective: Check man2/man3 items parsed. + +cur_dir = os.getcwd() +doc_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(cur_dir))), + "docs" + ) +save_dir = os.path.join(cur_dir, "results/docs") +os.makedirs(save_dir, exist_ok=True) + +readme_path = os.path.join(cur_dir, "../README.md") +messages_path = os.path.join(cur_dir, "../messages.txt") + +man2_translate(readme_path, save_dir) +man3_translate(messages_path, save_dir) \ No newline at end of file diff --git a/test/regression.tcl b/test/regression.tcl index 888574f42e3..33d7b2f9d3e 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -567,7 +567,7 @@ proc save_defok { test } { proc result_lang { test } { global test_langs - if {[lsearch $test_langs($test) tcl]} { + if {[lsearch $test_langs($test) tcl] != -1} { return tcl } return [lindex $test_langs($test) 0]