├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ ├── deploy_docs.yml │ ├── python-publish.yml │ └── test-python.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── bin └── sp ├── docs ├── Makefile ├── UtUnitTest.rst ├── autobuild.bat ├── autobuild.sh ├── make.bat ├── requirements.txt └── source │ ├── _static │ ├── LICENSE │ ├── css │ │ └── custom.css │ ├── environments.drawio │ ├── environments.png │ ├── logo │ │ ├── vectorstock_39262313.zip │ │ ├── virtue_banner.png │ │ ├── virtue_banner_with_tagline.png │ │ └── virtue_logo.png │ ├── plugins │ │ └── softworks │ │ │ └── virtue_softworks.py │ ├── virtue_menu.png │ └── virtuoso.yml │ ├── _templates │ └── version.html │ ├── conf.py │ ├── development │ ├── getting_started.rst │ └── index.rst │ ├── index.rst │ ├── overview │ ├── cli.rst │ ├── conda.rst │ ├── index.rst │ ├── install.rst │ ├── packaging │ │ ├── index.rst │ │ ├── modules.rst │ │ ├── package_registration.rst │ │ └── skill_packages.rst │ ├── release_notes.rst │ ├── skill_guidelines.rst │ ├── skillbridge.rst │ ├── standard_library.rst │ ├── testing_framework.rst │ └── toml.rst │ └── reference │ ├── cli.rst │ ├── index.rst │ └── skill_api │ ├── Geo.rst │ ├── List.rst │ ├── Str.rst │ └── index.rst ├── environment.yml ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── scripts ├── build └── setup ├── tests ├── Toml │ ├── simple_key_value.toml │ ├── simple_key_value_write.toml │ ├── simple_key_value_write_list.toml │ ├── simple_table.toml │ ├── simple_table_write.toml │ ├── simple_table_write_association_list.toml │ ├── test_simple_key_value.ils │ └── test_simple_table.ils ├── __init__.py ├── data_types │ ├── test_Dpl.ils │ ├── test_Lcv.ils │ ├── test_List.ils │ ├── test_Path.ils │ ├── test_Str.ils │ └── test_Time.ils ├── run_skill_tests.py ├── run_tests.ils ├── skill_environment │ ├── __init__.py │ └── test_api.py ├── skill_package │ ├── __init__.py │ ├── pyproject.toml │ ├── test_metadata.py │ ├── test_module.ils │ └── test_skill_packages_data.py ├── test │ ├── test_TestFixtures.ils │ └── test_lint.ils └── test_cli.py ├── virtue ├── ConfigUtility.py ├── Geo.ils ├── GeoPoints.ils ├── HdbConfig.ils ├── MaeExport.ils ├── MaeMaestro.ils ├── SchUtility.py ├── Skillbridge.ils ├── Toml.ils ├── __init__.py ├── calculator │ ├── VrtAnalog2Decimal.il │ ├── VrtAnalog2DecimalBit.il │ ├── VrtAnalog2Hex.il │ └── VrtDecimal2Hex.il ├── cdslib.py ├── cli.py ├── data_types │ ├── Dpl.ils │ ├── Lcv.ils │ ├── List.ils │ ├── Path.ils │ ├── Str.ils │ └── Time.ils ├── plugins │ ├── __init__.py │ ├── hookspecs.py │ ├── lib.py │ └── plugin_manager.py ├── skill_environment │ ├── __init__.py │ ├── api.py │ ├── cli.py │ └── init_scripts.py ├── skill_package │ ├── Module.ils │ ├── Package.ils │ ├── __init__.py │ ├── metadata.py │ └── metadata_data.py ├── skillbridge.py ├── test │ ├── Test.ils │ ├── TestFile.ils │ ├── TestFixtures.ils │ ├── TestFunction.ils │ └── TestSuite.ils ├── virtue.cdsLibMgr.il ├── virtue.cdsinit.ils └── virtue_dev_work │ └── README.md └── virtuoso.yml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/python-3/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster 4 | ARG VARIANT="3.10-bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} 6 | 7 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 8 | ARG NODE_VERSION="none" 9 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 10 | 11 | # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. 12 | # COPY requirements.txt /tmp/pip-tmp/ 13 | # RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ 14 | # && rm -rf /tmp/pip-tmp 15 | 16 | # [Optional] Uncomment this section to install additional OS packages. 17 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 18 | # && apt-get -y install --no-install-recommends 19 | 20 | # [Optional] Uncomment this line to install global node packages. 21 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 22 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/python-3 3 | { 4 | "name": "Python 3", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "context": "..", 8 | "args": { 9 | // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 10 | // Append -bullseye or -buster to pin to an OS version. 11 | // Use -bullseye variants on local on arm64/Apple Silicon. 12 | "VARIANT": "3.8-bullseye", 13 | // Options 14 | "NODE_VERSION": "none" 15 | } 16 | }, 17 | 18 | // Configure tool-specific properties. 19 | "customizations": { 20 | // Configure properties specific to VS Code. 21 | "vscode": { 22 | // Set *default* container specific settings.json values on container create. 23 | "settings": { 24 | "python.defaultInterpreterPath": "/usr/local/bin/python", 25 | "python.linting.enabled": true, 26 | "python.linting.pylintEnabled": true, 27 | "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", 28 | "python.formatting.blackPath": "/usr/local/py-utils/bin/black", 29 | "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", 30 | "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", 31 | "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", 32 | "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", 33 | "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", 34 | "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", 35 | "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" 36 | }, 37 | 38 | // Add the IDs of extensions you want installed when the container is created. 39 | "extensions": [ 40 | "ms-python.python", 41 | "ms-python.vscode-pylance", 42 | "herbertagosto.skill", 43 | "metamorphosis.skill", 44 | "timonwong.shellcheck", 45 | "cschleiden.vscode-github-actions", 46 | "GitHub.vscode-pull-request-github", 47 | "mhutchie.git-graph", 48 | "ms-vscode-remote.vscode-remote-extensionpack", 49 | "bungcip.better-toml", 50 | "lextudio.restructuredtext" 51 | ] 52 | } 53 | }, 54 | 55 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 56 | // "forwardPorts": [], 57 | 58 | // Use 'postCreateCommand' to run commands after the container is created. 59 | // "postCreateCommand": "pip3 install --user -r requirements.txt", 60 | 61 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 62 | "remoteUser": "vscode", 63 | "features": { 64 | "git": "os-provided", 65 | "github-cli": "latest" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/deploy_docs.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: build and deploy docs 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "main" branch 8 | push: 9 | branches: [ "main" ] 10 | pull_request: 11 | branches: [ "main" ] 12 | release: 13 | types: [published] 14 | workflow_dispatch: 15 | inputs: 16 | deploy: 17 | description: 'deploy?' 18 | required: true 19 | default: 'false' 20 | type: choice 21 | options: 22 | - 'true' 23 | - 'false' 24 | 25 | jobs: 26 | build: 27 | runs-on: ubuntu-latest 28 | env: 29 | SPHINX_GITHUB_CHANGELOG_TOKEN: ${{ github.token }} 30 | 31 | steps: 32 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 33 | - uses: actions/checkout@v3 34 | - uses: ammaraskar/sphinx-action@master 35 | with: 36 | docs-folder: "docs/" 37 | - uses: actions/upload-pages-artifact@v1 38 | with: 39 | path: docs/build/html 40 | deploy: 41 | needs: build 42 | if: (github.event.action == 'published') || (inputs.deploy == 'true') 43 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 44 | permissions: 45 | pages: write # to deploy to Pages 46 | id-token: write # to verify the deployment originates from an appropriate source 47 | 48 | # Deploy to the github-pages environment 49 | environment: 50 | name: github-pages 51 | url: ${{ steps.deployment.outputs.page_url }} 52 | 53 | # Specify runner + deployment step 54 | runs-on: ubuntu-latest 55 | steps: 56 | - name: Deploy to GitHub Pages 57 | id: deployment 58 | uses: actions/deploy-pages@v1 59 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.10' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install flit 33 | pip install -r requirements.txt 34 | - name: Build package 35 | run: make build 36 | - name: Publish package 37 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 38 | with: 39 | user: __token__ 40 | password: ${{ secrets.PYPI_API_TOKEN }} 41 | - name: Upload a Build Artifact 42 | uses: actions/upload-artifact@v3.1.0 43 | with: 44 | path: dist/* 45 | -------------------------------------------------------------------------------- /.github/workflows/test-python.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Test Python 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up Python 3.10 23 | uses: actions/setup-python@v3 24 | with: 25 | python-version: "3.10" 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Lint with flake8 32 | run: | 33 | # stop the build if there are Python syntax errors or undefined names 34 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 35 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 36 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Taskfile.yml 2 | scripts/release 3 | 4 | rds 5 | 6 | # SKILL 7 | **/.skillide.* 8 | *.ils~ 9 | *.il~ 10 | test/ 11 | 12 | # Virtue environment initialization scripts 13 | virtue/virtue-environment.cdsinit.ils 14 | virtue/virtue-environment.data.reg 15 | virtue/virtue-environment.cdsLibMgr.il 16 | 17 | # docs setup 18 | docs/source/_static/src 19 | docs/source/_static/conda-recipe 20 | docs/source/_static/tests 21 | 22 | # Byte-compiled / optimized / DLL files 23 | __pycache__/ 24 | *.py[cod] 25 | *$py.class 26 | 27 | # C extensions 28 | *.so 29 | 30 | # Distribution / packaging 31 | .Python 32 | build/ 33 | develop-eggs/ 34 | dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | lib/ 39 | lib64/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | wheels/ 44 | pip-wheel-metadata/ 45 | share/python-wheels/ 46 | *.egg-info/ 47 | .installed.cfg 48 | *.egg 49 | MANIFEST 50 | 51 | # PyInstaller 52 | # Usually these files are written by a python script from a template 53 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 54 | *.manifest 55 | *.spec 56 | 57 | # Installer logs 58 | pip-log.txt 59 | pip-delete-this-directory.txt 60 | 61 | # Unit test / coverage reports 62 | htmlcov/ 63 | .tox/ 64 | .nox/ 65 | .coverage 66 | .coverage.* 67 | .cache 68 | nosetests.xml 69 | coverage.xml 70 | *.cover 71 | *.py,cover 72 | .hypothesis/ 73 | .pytest_cache/ 74 | 75 | # Translations 76 | *.mo 77 | *.pot 78 | 79 | # Django stuff: 80 | *.log 81 | local_settings.py 82 | db.sqlite3 83 | db.sqlite3-journal 84 | 85 | # Flask stuff: 86 | instance/ 87 | .webassets-cache 88 | 89 | # Scrapy stuff: 90 | .scrapy 91 | 92 | # Sphinx documentation 93 | docs/_build/ 94 | 95 | # PyBuilder 96 | target/ 97 | 98 | # Jupyter Notebook 99 | .ipynb_checkpoints 100 | 101 | # IPython 102 | profile_default/ 103 | ipython_config.py 104 | 105 | # pyenv 106 | .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 116 | __pypackages__/ 117 | 118 | # Celery stuff 119 | celerybeat-schedule 120 | celerybeat.pid 121 | 122 | # SageMath parsed files 123 | *.sage.py 124 | 125 | # Environments 126 | .env 127 | .venv 128 | env/ 129 | venv/ 130 | ENV/ 131 | env.bak/ 132 | venv.bak/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.0.1 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: end-of-file-fixer 9 | - id: check-added-large-files 10 | - id: check-merge-conflict 11 | - id: check-ast 12 | - id: debug-statements 13 | #- id: no-commit-to-branch 14 | #- repo: local 15 | # hooks: 16 | # - id: pylint 17 | # name: pylint 18 | # entry: pylint 19 | # language: system 20 | # types: [python] 21 | # args: 22 | # [ 23 | # "-rn", # Only display messages 24 | # ] 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "herbertagosto.skill", 4 | "ms-python.python", 5 | "timonwong.shellcheck", 6 | "github.vscode-pull-request-github", 7 | "mhutchie.git-graph", 8 | "ms-vscode-remote.vscode-remote-extensionpack", 9 | "bungcip.better-toml", 10 | "lextudio.restructuredtext" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Current File", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal", 13 | "justMyCode": true 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.defaultInterpreterPath": "/space/${env:USER}/envs/mambaforge/envs/virtue-dev/bin/python", 3 | "editor.rulers": [79], 4 | "[skill]": { 5 | "editor.insertSpaces": true, 6 | "editor.tabSize": 2 7 | }, 8 | "python.testing.pytestEnabled": true, 9 | "python.testing.pytestArgs": ["tests"], 10 | "python.linting.mypyEnabled": true, 11 | "python.analysis.typeCheckingMode": "basic" 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Cascode Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "" 3 | @echo "Build a Virtue package or install Virtue for development" 4 | @echo "" 5 | @echo "Build Packages" 6 | @echo " 'make build' will build both a wheel and sdist" 7 | @echo " 'make build-wheel' will build just a wheel" 8 | @echo " 'make build-sdist' will build just a sdist" 9 | @echo " 'make upload-virtuoso-env' will upload the virtuoso environment" 10 | @echo " definition file to our anaconda cloud" 11 | @echo "Install" 12 | @echo " 'make install-conda-dev' will install a conda development environment" 13 | @echo "Build documentation" 14 | @echo " 'make auto-docs' will continuosly rebuild the docs" 15 | @echo " as they are updated" 16 | @echo " 'make docs' will build the documentation once" 17 | @echo "Other tasks" 18 | @echo " 'make clean' will clean up the work area" 19 | @echo "" 20 | 21 | SHELL = /bin/tcsh 22 | .PHONY: help clean \ 23 | build \ 24 | install-dev \ 25 | docs docs-single \ 26 | install-conda 27 | .ONESHELL: 28 | 29 | install-dev: 30 | mamba env create -f environment.yml 31 | conda activate ids-dev 32 | pip install --no-deps -e . 33 | 34 | build: 35 | exec scripts/build 36 | 37 | release: 38 | make clean 39 | make build 40 | anaconda upload -u cascode-labs virtuoso.yml \ 41 | --description 'A \ 42 | [Virtue environment](https://www.cascode-labs.org/virtue/) \ 43 | for automating IC design' 44 | 45 | clean: 46 | rm -rf dist 47 | 48 | docs: 49 | cd docs; ./autobuild.sh 50 | 51 | docs-single: 52 | cd docs; make html 53 | 54 | install-conda: 55 | wget "https://github.com/conda-forge/miniforge/releases/latest/download/Mambaforge-Linux-x86_64.sh" 56 | exec Mambaforge-Linux-x86_64.sh 57 | conda install -n base mamba anaconda-client 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Virtue](docs/source/_static/logo/virtue_banner_with_tagline.png "Virtue") 2 | 3 | [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/cascode-labs/virtue?include_prereleases)](https://github.com/cascode-labs/virtue/releases/latest) 4 | [![Conda](https://img.shields.io/conda/v/conda-forge/virtue?label=conda-forge)](https://anaconda.org/conda-forge/virtue) 5 | [![PyPI](https://img.shields.io/pypi/v/virtue-skill)](https://pypi.org/project/virtue-skill/) 6 | [![GitHub issues](https://img.shields.io/github/issues/cascode-labs/virtue)](https://github.com/cascode-labs/virtue/issues) 7 | [![PyPI - License](https://img.shields.io/pypi/l/virtue-skill)](https://choosealicense.com/licenses/mit/) 8 | 9 | A SKILL and Python Framework for automating IC design in 10 | [Cadence Virtuoso](https://www.cadence.com/en_US/home/tools/custom-ic-analog-rf-design/circuit-design.html) 11 | with the following goals: 12 | 13 | 1. Bring the capabilities of skill to Python so (ideally) you don't have 14 | to write skill code to do EDA in Python 15 | 2. In those cases where you do need to write skill, make it pythonic 16 | 17 | 18 | ## Projects Built with Virtue 19 | 20 | - [Softworks](https://github.com/cascode-labs/softworks): 21 | Software and documentation view types in the Cadence Virtuoso IC design environment. 22 | - [Data-panels](https://github.com/cascode-labs/data-panels): 23 | Export rich data reports from simulation results to pptx slides and 24 | xlsx tables 25 | - [Morpheus](https://github.com/cascode-labs/morpheus): 26 | Generate Maestro test benches in a standard way compatible with an associated 27 | data-panels report 28 | 29 | > [YouTube Video Explanation](https://www.youtube.com/watch?v=5GJbBLpHo6s&t=1s) 30 | 31 | ## Features 32 | 33 | - A [SKILL standard library](https://www.cascode-labs.org/virtue/reference/skill_api/index.html) of "batteries included" modules 34 | - A [SKILL test framework](https://www.cascode-labs.org/virtue/overview/testing_framework.html) modeled after [pytest](https://docs.pytest.org/en/7.1.x/) 35 | - A [SKILL TOML config file reader and writer](https://www.cascode-labs.org/virtue/overview/toml.html) 36 | for the [TOML standard](https://toml.io) 37 | - A SKILL package manager 38 | - Define [SKILL++ modules](https://www.cascode-labs.org/virtue/overview/packaging/modules.html) 39 | - Import modules into a SKILL++ lexical scope using the top-level "VrtImport" table 40 | - Create [SKILL++ packages](https://www.cascode-labs.org/virtue/overview/packaging/skill_packages.html) 41 | - SKILL environment manager using 42 | [Conda](https://docs.conda.io/en/latest/) or 43 | [Pip](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) 44 | Python environments 45 | - Seamless execution of SKILL from Python using 46 | [SkillBridge](https://unihd-cag.github.io/skillbridge/) 47 | 48 | ## Example SKILL++ Package 49 | 50 | ```scheme 51 | let((Str 52 | (module_description "String functions") 53 | (Module VrtImport['Module]) 54 | ) 55 | Str = let(() 56 | 57 | procedure(emptyp(in "g") 58 | "Checks if the input is an empty string 59 | @param Any type of object to be checked 60 | @return A boolean, 't if it is an empty string, otherwise nil" 61 | stringp(in) && strlen(in) == 0) 62 | 63 | procedure(str2bool(input_string "t") 64 | "Converts a case-insensitive 'TRUE' or 'FALSE' string to a boolean 65 | ('t / nil) If it is not a boolean, the string is returned." 66 | if(stringp(input_string) && (upperCase(input_string) == "TRUE") then 67 | 't 68 | else if(stringp(input_string) && (upperCase(input_string) == "FALSE") then 69 | nil 70 | else 71 | error("%s is not a boolean, must be \"TRUE\" or \"FALSE\" 72 | (case insensitive)" input_string) 73 | )) 74 | ) 75 | 76 | list(nil 77 | 'emptyp emptyp 78 | 'str2bool str2bool 79 | )) 80 | 81 | Module->New('Str Str 82 | ?package VrtImport['Virtue] 83 | ?description module_description) 84 | 85 | ) 86 | ``` 87 | 88 | ## Example Test Script 89 | 90 | Note the package imports at the top 91 | 92 | ``` scheme 93 | let(((Str VrtImport['Str]) 94 | (Test VrtImport['Test]) 95 | (Virtue VrtImport['Virtue]) 96 | ) 97 | 98 | procedure(Test_emptyp() 99 | assert(Str->emptyp("")) 100 | assert(!Str->emptyp("test")) 101 | ) 102 | 103 | procedure(Test_str2bool() 104 | assert(Str->str2bool("true")) 105 | assert(Str->str2bool("TRUE")) 106 | assert(!Str->str2bool("false")) 107 | ) 108 | 109 | procedure(Test_str2bool_error() 110 | assert(!errset(Str->str2bool("Nothing"))) 111 | ) 112 | 113 | Test->RunFile(list(nil 114 | 'Test_emptyp Test_emptyp 115 | 'Test_str2bool Test_str2bool 116 | 'Test_str2bool_error Test_str2bool_error 117 | ) 118 | ?filepath Virtue->GetCurrentFilePath() 119 | ) 120 | 121 | ) 122 | ``` 123 | 124 | Prints out the following when ran in the CIW: 125 | 126 | ``` sh 127 | FILE: /path/to/file/test_Str.ils 128 | passed: Test_emptyp 129 | passed: Test_str2bool 130 | passed: Test_str2bool_error 131 | 3 / 3 tests passed 132 | ``` 133 | 134 | ## Installation 135 | 136 | Virtue requires Python >= 3.7 and can be installed using several methods: 137 | 138 | - Conda 139 | - Pip 140 | - From source 141 | 142 | See the 143 | [installation instructions in the documentation](https://www.cascode-labs.org/virtue/overview/index.html#installation) 144 | for detailed instructions. 145 | -------------------------------------------------------------------------------- /bin/sp: -------------------------------------------------------------------------------- 1 | #!/bin/tcsh 2 | # svp: start virtue project 3 | 4 | # Display the help when no arguments are provided 5 | if ($# == 0) then 6 | svp --help 7 | exit 0 8 | endif 9 | 10 | # Get the directory of this script 11 | #set rootdir = `dirname $0` 12 | #set abs_rootdir = `cd $rootdir && pwd` 13 | 14 | # Defaults 15 | set conda_env="" 16 | set dev="false" 17 | set no_skill="false" 18 | set VIEWPRJ_CMD="false" 19 | 20 | # Parse input options 21 | set opts=(`getopt -s tcsh -o cdhon:p:v -l code,dev,help,no,name,prefix,view -- $argv:q`) 22 | if ($? != 0) then 23 | echo "Terminating..." >/dev/stderr 24 | exit 1 25 | endif 26 | # Now we do the eval part. As the result is a list, we need braces. But they 27 | # must be quoted, because they must be evaluated when the eval is called. 28 | # The 'q` stops doing any silly substitutions. 29 | eval set argv=\($opts:q\) 30 | 31 | while (1) 32 | switch($1:q) 33 | case -c: 34 | case --code: 35 | echo "code option set" 36 | setenv SP_START_CODE "TRUE" 37 | shift 38 | breaksw 39 | case -n: 40 | case --name: 41 | echo "named conda environment set to $2:q" 42 | set conda_env=\`$2:q\' 43 | shift ; shift 44 | breaksw 45 | case --o: 46 | case --no: 47 | echo "no option set, will not load skill environment (-o / --no)" 48 | set no_skill="true" ; shift 49 | breaksw 50 | case -p: 51 | case --prefix: 52 | echo "conda environment prefix set to $2:q" 53 | set VIRTUE_CONDA_PREFIX=$2 ; shift ; shift 54 | breaksw 55 | case -v: 56 | case --view: 57 | echo "Opening virtuoso session in view mode (viewprj)" 58 | set VIEWPRJ_CMD="true" ; shift 59 | breaksw 60 | case -h: 61 | case --help: 62 | echo "" 63 | echo "Start Project (sp)" 64 | echo " sp [Options] [prj_name]" 65 | echo " Starts a cadence project in a Virtue SKILL environment" 66 | echo " Example:" 67 | echo " sp sky58270_mk5872ASM_CR" 68 | echo " Options:" 69 | echo " -c, --code: Starts vs code in the project's code workspace" 70 | echo " instead of running virtuoso" 71 | echo " -h, --help: Display this help message" 72 | echo " -O, --no: Don't load any SKILL environment," 73 | echo " just start the project" 74 | echo " -n, --name: Uses the conda environment specified by the name given" 75 | echo " If both -n and -p are specified then -p is used" 76 | echo " -p, --prefix: Uses the conda environment specified by the prefix given" 77 | echo " If both -n and -p are specified then -p is used" 78 | echo " -v, --view: Opens the virtuoso project as read-only, if supported" 79 | echo "" 80 | exit 0 81 | case --: 82 | shift 83 | break 84 | default: 85 | echo "Internal error!" ; exit 1 86 | endsw 87 | end 88 | 89 | if ($#argv == 0) then 90 | echo "error: You must specify the project name as the last argument or -h / --help" 91 | endif 92 | 93 | setenv PRJ_NAME $1:q 94 | set argv = () 95 | 96 | if (${VIEWPRJ_CMD} == "false") then 97 | cdsprj ${PRJ_NAME} 98 | else 99 | viewprj ${PRJ_NAME} 100 | endif 101 | 102 | # Setup Virtue SKILL environment 103 | if ( $?CONDA_PREFIX && (-d ${CONDA_PREFIX}/lib/skill/virtue ) ) then 104 | setenv VIRTUE_CONDA_PREFIX ${CONDA_PREFIX} 105 | setenv VIRTUE_SKILL_PREFIX ${CONDA_PREFIX}/lib/skill 106 | echo "Using active Conda environment:\n ${CONDA_PREFIX}\n" 107 | else if ( $?VIRTUE_CONDA_PREFIX ) then 108 | setenv VIRTUE_SKILL_PREFIX ${VIRTUE_CONDA_PREFIX}/lib/skill 109 | else if ( ! $?VIRTUE_SKILL_PREFIX ) then 110 | echo 'No VIRTUE SKILL environment found, please select a virtue skill\n 111 | environment by setting $VIRTUE_SKILL_PREFIX, activating a Conda \n 112 | environment containing the virtue package before calling "sp", or \n 113 | setting $VIRTUE_CONDA_PREFIX to the prefix of a conda environment \n 114 | containing Virtue.' 115 | exit 1 116 | endif 117 | echo "Loading skill environment:\n ${CONDA_PREFIX}" 118 | 119 | echo "activating conda" 120 | if ( -d "$VIRTUE_CONDA_PREFIX" ) then 121 | # Required in tcsh scripts, similar to the interactive "conda activate" 122 | source ${VIRTUE_CONDA_PREFIX}/etc/profile.d/conda.csh 123 | conda activate $VIRTUE_CONDA_PREFIX 124 | conda info 125 | echo " Done, Activated conda environment" 126 | else if ( $?VIRTUE_CONDA_PREFIX ) then 127 | echo ' No Conda evironment found at' 128 | echo ' $VIRTUE_CONDA_PREFIX = ' $VIRTUE_CONDA_PREFIX 129 | exit 1 130 | endif 131 | 132 | 133 | if ( $?SP_START_CODE ) then 134 | mkdir -p ../../${PRJ_NAME}_code 135 | cd ../../${PRJ_NAME}_code 136 | code . 137 | else 138 | if ( -r "${VIRTUE_SKILL_PREFIX}/virtue/virtue-environment.ils" && ${no_skill} == "false" ) then 139 | echo "Starting Virtuoso with Virtue SKILL env initialization script:" 140 | echo " ${VIRTUE_SKILL_PREFIX}/virtue/virtue-environment.ils" 141 | icfb -replay "${VIRTUE_SKILL_PREFIX}/virtue/virtue-environment.ils" 142 | else if ( ${no_skill} == "true" ) then 143 | echo "Virtue SKILL environment disabled with --no" 144 | echo " Starting Virtuoso with no Virtue SKILL environment initialized" 145 | icfb 146 | endif 147 | endif 148 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help clean Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | cp -rf ../virtue ./source/_static/src 21 | cp -f ../LICENSE ./source/_static/LICENSE 22 | cp -rf ../tests ./source/_static/tests 23 | cp -rf ../virtuoso.yml ./source/_static/virtuoso.yml 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | 26 | clean: 27 | rm -rf ./source/_static/src 28 | rm -f ./source/_static/LICENSE 29 | rm -rf ./source/_static/tests 30 | rm -rf ./source/_static/virtuoso.yml 31 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 32 | -------------------------------------------------------------------------------- /docs/UtUnitTest.rst: -------------------------------------------------------------------------------- 1 | Unit Testing (UtUnitTest) 2 | ------------------------- 3 | 4 | A unit testing framework for the Cadence SKILL language. It was inspired by a 5 | `Cadence blog post series `_. 6 | 7 | References 8 | ^^^^^^^^^^^^^^ 9 | 10 | - `SKILL Development Reference `_ 11 | - `SKILL IDE User Guide `_ 12 | 13 | Built-in Functions: 14 | 15 | - ``sklint(...)`` 16 | SKILL code linter 17 | 18 | .. dropdown:: Details 19 | 20 | | sklint( 21 | | [ ?file tl_inputFileName ] 22 | | [ ?context t_contextName ] 23 | | [ ?outputFile t_outputFileName ] 24 | | [ ?ignoreGroups l_ignoreGroups ] 25 | | [ ?globals l_globals ] 26 | | [ ?depends l_depends ] 27 | | [ ?rulesFile t_rulesFile ] 28 | | [ ?ignores l_ignoresMessageList ] 29 | | [ ?checkNlambda g_checkNlambda ] 30 | | [ ?noPrintLog g_noPrintLog ] 31 | | [ ?useGlobalIgnores g_useGlobalIgnores ] 32 | | [ ?useGlobalRulesFileList g_useGlobalRulesFileList ] 33 | | [ ?useDisableMessages g_useDisableMessages ] 34 | | [ ?checkCdsFuncs g_checkCdsFuncs ] 35 | | [ ?checkPvtFuncs g_checkPvtFuncs ] 36 | | [ ?checkPubFuncs g_checkPubFuncs ] 37 | | [ ?prefixes l_prefixList ] 38 | | [ ?checkCdsPrefixes g_checkCdsPrefixes ] 39 | | [ ?checkFuncPrefixes g_checkFuncPrefixes ] 40 | | [ ?tabulate g_tabulate ] 41 | | [ ?skPath t_skPath ] 42 | | [ ?codeVersion t_release ] 43 | | ) 44 | 45 | UtUnitTest Class 46 | ^^^^^^^^^^^^^^^^ 47 | 48 | Methods: 49 | 50 | - ``UtTest->new(name)`` 51 | Instantiates UtUnitTest with the given name 52 | 53 | - ``UtAssertTrue((obj UtUnitTest) val)`` 54 | Asserts that the statement is true 55 | 56 | - ``UtRun((obj UtUnitTest) @key (verbose t))`` 57 | Run all test methods. A test method is any method that starts with ``test_``. 58 | 59 | Macros: 60 | 61 | - ``UtDefTest(@key project name class @rest body)`` 62 | Skips defining the test if the current project does not match the 63 | project name specified 64 | Replaces the call to defmethod for the test. 65 | 66 | - ``UtAssertTest(expr @key ident @rest printf_style_args)`` 67 | | arguments: 68 | | expr - an expression to evaluate, asserting that it does not return nil 69 | | ?ident ident - specifies an optional identifier which will be printed with [%L] in 70 | | the output if the assertion fails. This will help you identify the 71 | | exact assertion that failed when scanning a testing log file. 72 | | printf_style_args - additional printed information which will be output if the 73 | | assertion fails. 74 | 75 | - ``UtAssertFail (expression)`` 76 | 77 | UtSuite Class 78 | ^^^^^^^^^^^^^ 79 | 80 | Methods: 81 | 82 | - ``UtTest->newSuite()`` 83 | Instantiates UtSuite 84 | - ``UtSuiteRunByPath ((obj UtSuite) path @key (verbose t))`` 85 | Run all test files in the folder path given. 86 | A test file is any file with a "test_" prefix 87 | Args: 88 | Path: The path to a folder containing the test files to ran. (string or skyPath) 89 | 90 | 91 | Source Code 92 | ^^^^^^^^^^^ 93 | 94 | .. dropdown:: UtUnitTest.ils sourcecode 95 | 96 | .. literalinclude:: _static/src/UtUnitTest.ils 97 | :language: none 98 | :linenos: 99 | -------------------------------------------------------------------------------- /docs/autobuild.bat: -------------------------------------------------------------------------------- 1 | call conda activate "C:\Users\mayberc\Anaconda3\envs\virtue-build-docs" 2 | 3 | REM Rebuild to start with a clean slate 4 | call make.bat clean 5 | call make.bat html 6 | 7 | start chrome http://127.0.0.1:8000 8 | call sphinx-autobuild source build/html 9 | 10 | call conda deactivate 11 | -------------------------------------------------------------------------------- /docs/autobuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Initial Build 4 | make clean 5 | make html 6 | firefox "http://127.0.0.1:8000" & 7 | sphinx-autobuild --port 8005 source build/html 8 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | -r ../requirements.txt 2 | # Docs 3 | sphinx == 4.5.0 4 | pydata-sphinx-theme == 0.9.0 5 | sphinx-panels == 0.6.0 6 | sphinx_copybutton == 0.5.0 7 | sphinx-autobuild == 2021.3.14 8 | sphinx-github-changelog == 1.2.0 9 | sphinx-sitemap == 2.2.0 10 | pygments == 2.12.0 11 | sphinx-click == 4.3.0 12 | -------------------------------------------------------------------------------- /docs/source/_static/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Cascode Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* Make each footer item in-line so they stack horizontally instead of vertically */ 2 | .footer-item { 3 | display: inline-block; 4 | } 5 | 6 | /* Add a separating border line for all but the last item */ 7 | .footer-item:not(:last-child) { 8 | border-right: 1px solid var(--pst-color-text-base); 9 | margin-right: .5em; 10 | padding-right: .5em; 11 | } 12 | -------------------------------------------------------------------------------- /docs/source/_static/environments.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/source/_static/environments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/environments.png -------------------------------------------------------------------------------- /docs/source/_static/logo/vectorstock_39262313.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/logo/vectorstock_39262313.zip -------------------------------------------------------------------------------- /docs/source/_static/logo/virtue_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/logo/virtue_banner.png -------------------------------------------------------------------------------- /docs/source/_static/logo/virtue_banner_with_tagline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/logo/virtue_banner_with_tagline.png -------------------------------------------------------------------------------- /docs/source/_static/logo/virtue_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/logo/virtue_logo.png -------------------------------------------------------------------------------- /docs/source/_static/plugins/softworks/virtue_softworks.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | from pathlib import Path 3 | from importlib.resources import files 4 | import virtue 5 | import softworks 6 | 7 | 8 | def virtue_data_reg_paths() -> Tuple[Path]: 9 | return ( 10 | files(softworks) / "python" / "SdmPy.data.reg", 11 | files(softworks) / "skill" / "SdmSkill.data.reg", 12 | files(softworks) / "pptx" / "SdmPptx.data.reg", 13 | files(softworks) / "xlsx" / "SdmXlsx.data.reg", 14 | files(softworks) / "pdf" / "SdmPdf.data.reg", 15 | files(softworks) / "html" / "SdmHtml.data.reg", 16 | ) 17 | 18 | @virtue.hookimpl 19 | def virtue_register_skill_package(): 20 | return { 21 | "python_package_name": "softworks", 22 | "skill_package_name": "Softworks", 23 | "cdsinit_paths": (files(softworks) / "softworks.cdsinit.ils"), 24 | "cdslibmgr_paths": (files(softworks) / "softworks.cdsLibMgr.il"), 25 | "cds_dev_libraries": { 26 | "virtue_dev_project": (files(virtue) / "softworks_dev_work"), 27 | }, 28 | "data_reg_paths": virtue_data_reg_paths(), 29 | } 30 | -------------------------------------------------------------------------------- /docs/source/_static/virtue_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/docs/source/_static/virtue_menu.png -------------------------------------------------------------------------------- /docs/source/_static/virtuoso.yml: -------------------------------------------------------------------------------- 1 | # Virtue development environment 2 | name: virtuoso 3 | 4 | channels: 5 | - conda-forge 6 | - defaults 7 | 8 | dependencies: 9 | - virtue 10 | - softworks 11 | - skillbridge = 1.2.18 12 | - python=3.9 13 | - git 14 | - conda 15 | - mamba 16 | - pip 17 | -------------------------------------------------------------------------------- /docs/source/_templates/version.html: -------------------------------------------------------------------------------- 1 | 2 | Virtue v{{ version }} 3 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath( '../..')) 16 | # sys.path.append(os.path.relpath()) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | import virtue 21 | 22 | project = 'virtue' 23 | copyright = '2020, Cascode-labs' 24 | author = 'Curtis Mayberry' 25 | 26 | # The full version, including alpha/beta/rc tags 27 | release = virtue.__version__ 28 | print('Release version={num}'.format(num=release)) 29 | version = release 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # By default Sphinx expects the master doc to be at contents, but ours is index. 34 | master_doc = 'index' 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be 37 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 38 | # ones. 39 | extensions = [ 40 | 'sphinx_copybutton', 41 | 'sphinx_design', 42 | "sphinx_github_changelog", 43 | 'sphinx_sitemap', 44 | 'sphinx.ext.autosectionlabel', 45 | 'sphinx.ext.autodoc', 46 | 'sphinx_click', 47 | ] 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ['_templates'] 51 | 52 | # List of patterns, relative to source directory, that match files and 53 | # directories to ignore when looking for source files. 54 | # This pattern also affects html_static_path and html_extra_path. 55 | exclude_patterns = [] 56 | 57 | source_suffix = ['.rst'] 58 | 59 | language = "en" 60 | 61 | # -- Options for HTML output ------------------------------------------------- 62 | 63 | # The theme to use for HTML and HTML Help pages. See the documentation for 64 | # a list of builtin themes. 65 | # 66 | html_theme = "pydata_sphinx_theme" 67 | 68 | # Add any paths that contain custom static files (such as style sheets) here, 69 | # relative to this directory. They are copied after the builtin static files, 70 | # so a file named "default.css" will overwrite the builtin "default.css". 71 | html_static_path = ['_static'] 72 | html_logo = "_static/logo/virtue_logo.png" 73 | 74 | panels_add_bootstrap_css = False 75 | 76 | html_theme_options = { 77 | "icon_links": [ 78 | { 79 | # Label for this link 80 | "name": "GitHub", 81 | # URL where the link will redirect 82 | "url": "https://github.com/cascode-labs/virtue", # required 83 | # Icon class (if "type": "fontawesome"), or path to local image (if "type": "local") 84 | "icon": "fab fa-github-square", 85 | # The type of image to be used (see below for details) 86 | "type": "fontawesome", 87 | }, 88 | { 89 | "name": "Latest-release", 90 | "url": "https://github.com/cascode-labs/virtue/releases/latest", # required 91 | "icon": "fas fa-tag", 92 | "type": "fontawesome", 93 | } 94 | ], 95 | "external_links": [ 96 | {"name": "cascode-labs", "url": "http://www.cascode-labs.org/"}, 97 | ], 98 | "logo": { 99 | "text": "Virtue", 100 | }, 101 | "footer_items": ["version", "copyright", "sphinx-version"], 102 | "show_nav_level": 2, 103 | "use_edit_page_button": True, 104 | "google_analytics_id": "G-80SSZFFV2B", 105 | } 106 | 107 | html_context = { 108 | # "github_url": "https://github.com", # or your GitHub Enterprise interprise 109 | "github_user": "cascode-labs", 110 | "github_repo": "virtue", 111 | "github_version": "main", 112 | "doc_path": "docs/source", 113 | } 114 | 115 | html_baseurl = 'http://www.cascode-labs.org/virtue/' 116 | 117 | html_css_files = [ 118 | 'css/custom.css', 119 | ] 120 | -------------------------------------------------------------------------------- /docs/source/development/getting_started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | ================ 3 | 4 | I would recommend using vs code to develop virtue. It has many helpful 5 | extensions, including a skill code extension for syntax highlighting. 6 | 7 | 1. Make sure you have 8 | `mambaforge `_ 9 | (`conda `_) installed. If not you can install it: 10 | 11 | .. code-block:: bash 12 | :caption: Setup mambaforge 13 | 14 | make install-conda-dev 15 | 16 | 2. Create a new Conda development environment with all the Virtue dependencies 17 | installed 18 | 19 | .. code-block:: bash 20 | :linenos: 21 | :caption: create a conda development environment 22 | 23 | conda env create -f environment.yml 24 | 25 | 4. Install virtue as an editable package. 26 | 27 | .. code-block:: bash 28 | :linenos: 29 | :lineno-start: 2 30 | :caption: Install virtue as an editable package 31 | 32 | conda activate virtue-dev 33 | pip install --no-deps -e . 34 | 35 | 36 | 4. Initiailize the virtue SKILL environement by following the standard 37 | installation instructions. 38 | 39 | .. code-block:: bash 40 | :linenos: 41 | :lineno-start: 5 42 | :caption: Initialize the environment 43 | 44 | virtue env init 45 | 46 | 6. Install pre-commit git hooks 47 | 48 | .. code-block:: bash 49 | :linenos: 50 | :lineno-start: 7 51 | :caption: create a conda development environment 52 | 53 | pre-commit install 54 | 55 | 7. If you're using VS Code as your IDE, then install the extensions recommended 56 | by the workspace. 57 | 58 | Writing Documentation 59 | ---------------------- 60 | 61 | The documentation is built using the 62 | `Sphinx static site generator `_ 63 | and the 64 | `pydata theme `_. 65 | 66 | We use sphinx-autobuild to automatically rebuild the documentation whenever it 67 | changes. To start the auto-build, open a new terminal in the project repo root 68 | directory and start it by calling the following. If you're using a 69 | vs code integrated terminal then a box will popup asking you to open the 70 | auto-built docs in a web browser. Otherwise navigate to the indicated 71 | URL. 72 | 73 | .. code-block:: 74 | :linenos: 75 | :lineno-start: 5 76 | :caption: Start the docs' auto-build 77 | 78 | make auto-docs 79 | 80 | To rebuild the documentation a single time: 81 | 82 | .. code-block:: 83 | :linenos: 84 | :lineno-start: 5 85 | :caption: Build the docs' once 86 | 87 | make docs 88 | -------------------------------------------------------------------------------- /docs/source/development/index.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Development 3 | =========== 4 | 5 | Thank you for your interest in contributing to Virtue. If you have a feature 6 | you'd be interested in seeing added to Virtue, please start by describing it 7 | in the issue tracker. Others may have experience with the issue and can help 8 | determine the best course of action. 9 | 10 | If a feature needs to be added, then a pull request can be created. 11 | Please follow the guidelines so we can efficiently merge your contibutions. 12 | Once the pull-request is ready, it will need to be reviewed by at least one 13 | member of cascod-labs. 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | :caption: Contents: 18 | :hidden: 19 | 20 | getting_started 21 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. IDS-skill documentation master file, created by 2 | sphinx-quickstart on Thu Oct 15 16:36:40 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. image:: _static/logo/virtue_banner_with_tagline.png 7 | 8 | .. image:: https://img.shields.io/github/v/release/cascode-labs/virtue?include_prereleases 9 | :alt: GitHub release (latest by date including pre-releases) 10 | :target: https://github.com/cascode-labs/virtue/releases/latest 11 | .. image:: https://img.shields.io/conda/v/conda-forge/virtue?label=conda-forge 12 | :alt: Conda 13 | :target: https://anaconda.org/conda-forge/virtue 14 | .. image:: https://img.shields.io/pypi/v/virtue-skill 15 | :alt: PyPI 16 | :target: https://pypi.org/project/virtue-skill/ 17 | .. image:: https://img.shields.io/github/issues/cascode-labs/virtue 18 | :alt: GitHub issues 19 | :target: https://github.com/cascode-labs/virtue/issues 20 | .. image:: https://img.shields.io/pypi/l/virtue-skill 21 | :alt: PyPI - License 22 | :target: https://choosealicense.com/licenses/mit/ 23 | 24 | | Virtue is a SKILL and Python Framework for automating IC design in `Cadence Virtuoso `_ with the following goals: 25 | 26 | 1. Bring the capabilities of skill to Python so (ideally) you don't have 27 | to write skill code to do EDA in Python 28 | 2. In those cases where you do need to write skill, make it pythonic 29 | 30 | 31 | Projects built with Virtue 32 | -------------------------------- 33 | 34 | - `Softworks `_: 35 | Software and documentation view types in the Cadence Virtuoso IC design 36 | environment. 37 | - `Data-panels `_: 38 | Export rich data reports from simulation results to pptx slides and 39 | xlsx tables 40 | - `Morpheus `_: 41 | Generate Maestro test benches in a standard way compatible with an associated 42 | data-panels report 43 | 44 | .. admonition:: YouTube Video Explanation 45 | 46 | I explain Virtue as a part of the Silicon Austria Labs (SAL) bootcamp: 47 | `YouTube Video Explanation `_ 48 | 49 | Features 50 | -------- 51 | 52 | - A SKILL code packaging system 53 | 54 | - Simple SKILL package installation from the Python environment 55 | - Define :ref:`SKILL++ modules` 56 | - Create :ref:`SKILL++ packages` 57 | - Import modules into a SKILL++ environment using the top-level "Import" table 58 | 59 | - A :ref:`SKILL test framework` modeled after `pytest `_ 60 | - A :ref:`SKILL standard library` of "batteries included" modules 61 | - A :ref:`SKILL TOML config file reader and writer ` 62 | for the `TOML standard `_ 63 | - SKILL environment manager using 64 | `Conda `_ or 65 | `Pip `_ 66 | - Seamless execution of SKILL from Python using 67 | `SkillBridge `_ 68 | 69 | .. toctree:: 70 | :maxdepth: 2 71 | :caption: Contents: 72 | :hidden: 73 | 74 | overview/index 75 | reference/index 76 | development/index 77 | 78 | 79 | Indices and tables 80 | ================== 81 | 82 | * :ref:`genindex` 83 | * :ref:`modindex` 84 | * :ref:`search` 85 | -------------------------------------------------------------------------------- /docs/source/overview/cli.rst: -------------------------------------------------------------------------------- 1 | Command Line Interface (CLI) 2 | ============================= 3 | 4 | The CLI allows for initialization of a Python environment 5 | (:code:`virtue env install`) and inspection 6 | of the Virtue packages in an active Python environment 7 | (:code:`virtue env info`, :code:`virtue env list`). 8 | 9 | See the cli help (:code:`virtue --help`) or the 10 | :ref:`Command Line Interface (CLI) Reference` for more details 11 | on the interface. 12 | -------------------------------------------------------------------------------- /docs/source/overview/conda.rst: -------------------------------------------------------------------------------- 1 | Conda Package Manager 2 | ====================== 3 | 4 | `Conda `_ is a package, environment and 5 | dependency manager. It can be used to create a Conda environement containing 6 | Virtue SKILL++ packages, along with any version of Python and packages from any 7 | language, to easily create an IC design environment. 8 | 9 | Getting started with Conda 10 | --------------------------- 11 | 12 | You can find instructions on how setup a design automation environment 13 | on the Viper-forge site. 14 | 15 | Then all you have to do is activate your shell and start virtuoso. Either 16 | start it the way you normally do or you can use the "sp" command provided by 17 | Virtue. 18 | 19 | Viper-forge is a project to add design automation Conda packages to the 20 | conda-forge channel. (Conda package repository) 21 | 22 | A Virtue skill environment will be created inside a conda 23 | environment with Virtue installed in it. Then if the conda environment 24 | can be activated and virtuoso can be started 25 | 26 | Build a SKILL Conda Package 27 | --------------------------- 28 | 29 | A Conda recipe must be created to build 30 | a conda package 31 | 32 | `Conda-build `_ and 33 | `Boa `_ are tools for building Conda 34 | packages. 35 | A `Conda recipe `_ 36 | provides the instructions on how to build a conda package. The conda package 37 | metadata will be defined in a 38 | `meta.yaml file `_. 39 | For example this is the meta.yaml file for Virtue: 40 | 41 | .. dropdown:: Virtue meta.yaml 42 | 43 | .. literalinclude:: ../_static/conda-recipe/meta.yaml 44 | :language: yaml+jinja 45 | :linenos: 46 | 47 | A SKILL package recipe will need a 48 | `build script `_, build.sh, 49 | in order to include a `Virtue SKILL++ package` as a part of the Conda package. 50 | The build script needs to install the package's source code to the project's 51 | folder in the Conda environment's Virtue SKILL environment located at 52 | "$CONDA_PREFIX/libs/skill/". In the example build script below, from the 53 | Virtue recipe, the first section copies the package source code into the 54 | Conda environment's SKILL environment and then the second defines some 55 | activation scripts that are sourced when a Conda environment is activated. 56 | 57 | .. dropdown:: Virtue build.sh 58 | 59 | .. literalinclude:: ../_static/conda-recipe/build.sh 60 | :language: bash 61 | :linenos: 62 | -------------------------------------------------------------------------------- /docs/source/overview/index.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Overview 3 | ======== 4 | 5 | .. image:: ../_static/logo/virtue_banner_with_tagline.png 6 | 7 | .. image:: https://img.shields.io/github/v/release/cascode-labs/virtue?include_prereleases 8 | :alt: GitHub release (latest by date including pre-releases) 9 | :target: https://github.com/cascode-labs/virtue/releases/latest 10 | .. image:: https://img.shields.io/conda/v/conda-forge/virtue?label=conda-forge 11 | :alt: Conda 12 | :target: https://anaconda.org/conda-forge/virtue 13 | .. image:: https://img.shields.io/pypi/v/virtue-skill 14 | :alt: PyPI 15 | :target: https://pypi.org/project/virtue-skill/ 16 | .. image:: https://img.shields.io/github/issues/cascode-labs/virtue 17 | :alt: GitHub issues 18 | :target: https://github.com/cascode-labs/virtue/issues 19 | .. image:: https://img.shields.io/pypi/l/virtue-skill 20 | :alt: PyPI - License 21 | :target: https://choosealicense.com/licenses/mit/ 22 | 23 | | Virtue is a SKILL and Python Framework for automating IC design in `Cadence Virtuoso `_ with the following goals: 24 | 25 | 1. Bring the capabilities of skill to Python so (ideally) you don't have 26 | to write skill code to do EDA in Python 27 | 2. In those cases where you do need to write skill, make it pythonic 28 | 29 | .. admonition:: YouTube Video Explanation 30 | 31 | I explain Virtue as a part of the Silicon Austria Labs (SAL) bootcamp: 32 | `YouTube Video Explanation `_ 33 | 34 | 35 | Projects built with Virtue 36 | -------------------------------- 37 | 38 | - `Softworks `_: 39 | Software and documentation view types in the Cadence Virtuoso IC design 40 | environment. 41 | - `Data-panels `_: 42 | Export rich data reports from simulation results to pptx slides and 43 | xlsx tables 44 | - `Morpheus `_: 45 | Generate Maestro test benches in a standard way compatible with an associated 46 | data-panels report 47 | 48 | Features 49 | -------- 50 | 51 | - A SKILL code packaging system 52 | 53 | - Simple SKILL package installation from the Python environment 54 | - Define :ref:`SKILL++ modules` 55 | - Create :ref:`SKILL++ packages` 56 | - Import modules into a SKILL++ environment using the top-level "VrtImport" table 57 | 58 | - A :ref:`SKILL test framework` modeled after `pytest `_ 59 | - A :ref:`SKILL standard library` of "batteries included" modules 60 | - A :ref:`SKILL TOML config file reader and writer ` 61 | for the `TOML standard `_ 62 | - SKILL environment manager using 63 | `Conda `_ or 64 | `Pip `_ 65 | - Seamless execution of SKILL from Python using 66 | `SkillBridge `_ 67 | 68 | License 69 | ------- 70 | 71 | Virtue is licensed under an MIT license. 72 | 73 | .. dropdown:: MIT License 74 | 75 | .. literalinclude:: ../_static/LICENSE 76 | :linenos: 77 | 78 | Other Open Source SKILL Projects 79 | -------------------------------- 80 | 81 | - `skillbridge Python-SKILL interface `_ 82 | - `SKILL_Tools Utilities for working with Cadence's SKILL/SKILL++ `_ 83 | - `cdsgit Git design manager `_ 84 | - `cdsdm Git design manager `_ 85 | 86 | .. toctree:: 87 | :maxdepth: 2 88 | :caption: Contents: 89 | :hidden: 90 | 91 | install 92 | cli 93 | packaging/index 94 | testing_framework 95 | skill_guidelines 96 | standard_library 97 | conda 98 | toml 99 | skillbridge 100 | release_notes 101 | -------------------------------------------------------------------------------- /docs/source/overview/install.rst: -------------------------------------------------------------------------------- 1 | Install 2 | ======== 3 | 4 | I would recommend using Conda to install Virtue and any related packages. 5 | This will install both virtue and Python into a virtual environments and makes 6 | it easy to use different versions of Python in each Conda environment. 7 | 8 | .. tab-set:: 9 | 10 | .. tab-item:: Label1Conda **(recommended)** 11 | 12 | 0. Install mambaforge if some form of conda or mamba isn't already installed 13 | using the `mambaforge installer `_. 14 | 15 | 1. Create a new environment named "virtuoso". 16 | 17 | This can be done using the environment definition from 18 | `our anaconda cloud `_: 19 | 20 | .. code-block:: bash 21 | :linenos: 22 | :lineno-start: 1 23 | 24 | conda env create cascode-labs/virtuoso 25 | 26 | **OR** 27 | 28 | If you want to edit the packages to be installed you can download 29 | the virtuoso environment definition file, 30 | `virtuoso.yml, <../_static/virtuoso.yml>`_, and create the env 31 | from the downloaded file: 32 | 33 | .. code-block:: bash 34 | :linenos: 35 | :lineno-start: 1 36 | 37 | conda env create -f virtuoso.yml 38 | 39 | 2. Activate the newly created Conda environment and then 40 | install the Virtue SKILL environment into its Python environment. 41 | 42 | 43 | .. code-block:: bash 44 | :linenos: 45 | :lineno-start: 2 46 | 47 | conda activate virtuoso 48 | virtue install 49 | 50 | 51 | .. tab-item:: Pip / venv 52 | 53 | You can install Virtue using pip from the `virtue-skill PyPi package `_ 54 | 55 | 0. You'll need to have Python and pip installed and it's recommended to create 56 | a new new virtual environment for virtuoso before installing virtue. 57 | 58 | 1. Install Virtue using Pip. skillbridge and softworks are both optional 59 | recommendations to be installed with virtue. 60 | 61 | .. code-block:: bash 62 | 63 | # Remember to activate your virtual environment first 64 | pip install virtue-skill skillbridge softworks 65 | virtue install 66 | 67 | .. tab-item:: Source 68 | 69 | 1. You'll need to have either Conda or Python and pip installed. It's 70 | recommended to create a new Conda or venv virtual environment for virtuoso 71 | before installing virtue. 72 | 73 | 2. Clone the repo from GitHub 74 | 75 | 3. Pip install from source: 76 | 77 | For a editable development installation that will include local updates: 78 | 79 | .. code-block:: bash 80 | :linenos: 81 | :lineno-start: 1 82 | 83 | # Remember to activate your virtual environment first 84 | pip install -e . 85 | virtue install 86 | 87 | **OR** 88 | 89 | For a read-only installation: 90 | 91 | .. code-block:: bash 92 | :linenos: 93 | :lineno-start: 1 94 | 95 | 96 | # Remember to activate your virtual environment first 97 | pip install . 98 | virtue install 99 | 100 | Then Follow the instructions to add the Virtue SKILL environment initialization 101 | scripts to your Virtuoso initialization scripts. 102 | Each script will need to be initialized in a different way in your Virtuoso 103 | environment. See ":ref:`Install the Library Manager Customizations`" and 104 | ":ref:`Install the View Type Registry`" sections for more details and hints. 105 | 106 | You can also just load the "virtue.init.ils" from the CIW window to enable only 107 | the main skill code for just the current session. This also doesn't enable 108 | the data registry required to define custom view types and doesn't enable the 109 | library manager customizations. 110 | 111 | .. code-block:: lisp 112 | 113 | load("/path/to/repo/virtue/virtue/virtue.init.ils") 114 | 115 | 116 | Reminder: The following will change your top-level interpreter to SKILL++ 117 | if you want to test it out interactively in SKILL++: 118 | 119 | .. code-block:: lisp 120 | 121 | toplevel('ils) 122 | 123 | .. _install-library-manager-customizations: 124 | 125 | Install the Library Manager Customizations 126 | -------------------------------------------- 127 | 128 | Library customizations for all packages installed in a Virtue SKILL environment 129 | can be loaded by adding the following code to the "cdsLibMgr.il" file in the 130 | current working directory, a user's home directory, or a site installation 131 | directory from the 132 | `setup.loc `_ 133 | list. You should also follow the instructions for 134 | `loading multiple cdsLibMgr.il files `_, 135 | especially when existing site customizations must be loaded in addition to the 136 | user's. 137 | 138 | .. code-block:: lisp 139 | :linenos: 140 | :caption: Load virtue-environment.cdsLibMgr.il 141 | :name: install_cdsLibMgr-il 142 | 143 | when(getShellEnvVar("VIRTUE_SKILL_PREFIX") 144 | && isFile(strcat(env(VIRTUE_SKILL_PREFIX) "/virtue/virtue-environment.cdsLibMgr.il")) 145 | printf("virtue-environment.cdsLibMgr.il...\n") 146 | loadi(strcat(env(VIRTUE_SKILL_PREFIX) "/virtue/virtue-environment.cdsLibMgr.il")) 147 | ) 148 | 149 | .. _install_view_types: 150 | 151 | Install the View Type Registry 152 | ------------------------------- 153 | 154 | Custom cell view types must be included in a 155 | `data.reg data registry file `_ 156 | before starting Virtuoso. 157 | 158 | You can add the following SOFTINCLUDE line to a data.reg file in the current working, home, or 159 | $CDS_SITE directory. You should also 160 | `setup support for multiple files `_ 161 | if it's not already setup. Each package 162 | in the Virtue SKILL environment will then add itself to the environment's 163 | data.reg. 164 | 165 | .. code-block:: 166 | 167 | SOFTINCLUDE $VIRTUE_SKILL_PREFIX/virtue.data.reg; 168 | -------------------------------------------------------------------------------- /docs/source/overview/packaging/index.rst: -------------------------------------------------------------------------------- 1 | Packaging 2 | ========= 3 | 4 | A Virtue package is a Python package which contains a SKILL package. 5 | The skill code is installed into a Python environment using either Conda or 6 | Pip. Then a SKILL environment is created within the Python environment using 7 | the "virtue env install" command. 8 | 9 | Virtue packages can then contain SKILL moduels which organize SKILL code. 10 | Then these modules can be accessed from the global "VrtImport" table 11 | without cluttering the global namespace. This system allows many modules 12 | to be defined without naming collisions. It also allows new tools to be 13 | easily added to the SKILL environment. Each module consists of a SKILL 14 | table which can be defined directly as a table or as a DPL. 15 | 16 | .. toctree:: 17 | :caption: Contents: 18 | :hidden: 19 | 20 | package_registration 21 | modules 22 | skill_packages 23 | -------------------------------------------------------------------------------- /docs/source/overview/packaging/modules.rst: -------------------------------------------------------------------------------- 1 | *************** 2 | SKILL++ Modules 3 | *************** 4 | 5 | Virtue SKILL++ modules ares SKILL tables that organize closely related code 6 | without defining any global symbols. They allow it to share private functions 7 | and variables between functions and avoid name collisions between modules. 8 | Modules can then be imported into a local lexical SKILL++ environment from 9 | the module VrtImport table without affecting the top-level environment. 10 | A module is saved as a table but can be created as a DPL or a table. 11 | 12 | Import a Module 13 | ------------------- 14 | 15 | A module can be imported into the lexical environment of a SKILL++ let 16 | statement by defining a local variable to a module from the Package VrtImport 17 | table. 18 | 19 | Define a DPL Module 20 | ------------------- 21 | 22 | A module can be created as a decomposed property list (DPL), similar to the 23 | `"SKILL++ packages" defined in the user manual `_. 24 | Public functions are defined as part of the DPL property list. Private module 25 | variables are defined in the variable list of the named let function. 26 | Functions defined within the package's let statement but aren't in the DPL 27 | property list are local. The public variables of the DPL are added to the 28 | module's table. 29 | 30 | example: 31 | 32 | .. dropdown:: Lcv.ils SKILL++ Module 33 | 34 | .. literalinclude:: ../../_static/src/data_types/Lcv.ils 35 | :language: scheme 36 | :linenos: 37 | -------------------------------------------------------------------------------- /docs/source/overview/packaging/package_registration.rst: -------------------------------------------------------------------------------- 1 | ****************************** 2 | Virtue Package Registration 3 | ****************************** 4 | 5 | Virtue uses `Pluggy `_ 6 | to register Virtue packages and include their SKILL packages in the 7 | Virtue SKILL environment. A package can register itself as a Virtue SKILL 8 | plugin by registering a dictionary by implementing a hook. 9 | 10 | Softworks Example 11 | ----------------- 12 | 13 | Softworks is registered as a Virtue SKILL package with the following steps 14 | 15 | 1. Include all your SKILL code under your top-level Python module. 16 | 17 | 2. Add a Virtue entry-point in your "pyproject.toml" (recommended) or 18 | "setup.py" files which points to the module containing the hook 19 | implementation. 20 | 21 | .. code-block:: yaml 22 | :linenos: 23 | :caption: pyproject.toml 24 | 25 | [project.entry-points.virtue] 26 | softworks = "softworks.virtue_softworks" 27 | 28 | 3. Add a Virtue hook implementation to register the Virtue SKILL package. 29 | The hook must be defined in the module selected in the entry-point. 30 | 31 | .. dropdown:: Softwork's Virtue Package registration hook implementation 32 | 33 | .. literalinclude:: ../../_static/plugins/softworks/virtue_softworks.py 34 | :language: python 35 | :linenos: 36 | :caption: softworks.virtue_softworks 37 | 38 | Reference 39 | --------- 40 | 41 | .. dropdown:: Virtue Registration Dictionary Entry Definitions 42 | 43 | A SKILL package can define the following entries in its registration 44 | dictionary: 45 | 46 | .. autoclass:: virtue.skill_package.metadata_data.SKillPackageMetadata 47 | :members: 48 | 49 | .. autoclass:: virtue.skill_package.metadata_data.SKillPackageOptionalMetadata 50 | :members: 51 | 52 | .. dropdown:: Virtue Registration Function Definition 53 | 54 | .. autofunction:: virtue.plugins.hookspecs.virtue_register_skill_package 55 | 56 | Virtue defines a subset of these for itself: 57 | 58 | .. dropdown:: Virtue's own hook implementations 59 | 60 | .. literalinclude:: ../../_static/src/plugins/lib.py 61 | :language: python 62 | :linenos: 63 | -------------------------------------------------------------------------------- /docs/source/overview/packaging/skill_packages.rst: -------------------------------------------------------------------------------- 1 | ***************** 2 | SKILL++ Packages 3 | ***************** 4 | 5 | A SKILL++ package is a set of related SKILL++ modules defined as part of a 6 | project. Normally each project contains a single SKILL++ package. 7 | 8 | A Virtue SKILL++ conda package can : 9 | * Automatically initialize it's SKILL Code 10 | * Customize the library manager 11 | * Register custom view types with the data registry 12 | * Add OA libraries to the library manager 13 | 14 | Initialize SKILL 15 | ---------------- 16 | 17 | Each SKILL++ package should have an initialization file which loads and 18 | initializes all of the project's skill code. The init file should follow be 19 | named ".cdsinit.ils" (SKILL++) or 20 | ".init.il" (SKILL) by convention. It should be located 21 | in the top-level source code directory. This init file will then be loaded by 22 | Virtue automatically once virtue is installed. 23 | 24 | The virtue-environment.ils script initializes each package in the Virtue 25 | SKILL environment containing it. 26 | The Virtue package is initialized first, followed by all 27 | the initialization scripts of all the other packages. The 28 | initialization scripts are loaded from the location registerd with Virtue. 29 | 30 | Customize the Library Manager 31 | ----------------------------- 32 | 33 | A Virtue SKILL++ package can customize the Virtuoso library manager to 34 | customize its menus and settings. See the installation instructions for how 35 | the library customization environment initialization script can be called. 36 | 37 | 38 | A library manager initialization script can be included in the source code 39 | directory. It's file name should end in "cdsLibMgr.il". This file can contain 40 | calls to any of the 41 | `library manager SKILL functions `_. 42 | These functions **cannot** be called from the main SKILL initialization script 43 | which is loaded in the top-level SKILL environment. 44 | 45 | A project can add additional entries in the Virtue library manager menu 46 | which can be referenced by name, "**VirtueMenu**". 47 | If more than one item is needed for a project, it should be added as a 48 | sub-menu. Links to documentation for other projects can also be added to the 49 | "**VirtueHelpMenu**". 50 | 51 | .. image:: ../../_static/virtue_menu.png 52 | :alt: Virtue library manager menu 53 | 54 | see :ref:`Install the Library Manager Customizations` for installation information. 55 | 56 | Examples: 57 | 58 | See `Library Manager customization example file `_ 59 | from Cadence online support for examples. 60 | 61 | .. dropdown:: Virtue's virtue.cdsLibMgr.il script 62 | 63 | .. literalinclude:: ../../_static/src/virtue.cdsLibMgr.il 64 | :language: lisp 65 | :linenos: 66 | 67 | Data Registry Customization 68 | --------------------------- 69 | 70 | A virtue conda package can customize the Virtuoso data registry to include 71 | custom view types in the library manager. The package should then register the 72 | location of it with Virtue using the plugin hooks. 73 | Virtue will then add this as a SOFTINCLUDE in its data.reg along with all the 74 | other packages in the same Python environment with data.reg files registered 75 | so only the virtue data.reg needs to be installed in your environment. 76 | 77 | See the :ref:`Install View Types ` section for 78 | environment setup instructions. 79 | 80 | Cadence Libraries 81 | ----------------- 82 | 83 | **Still under development** 84 | 85 | A Conda SKILL package can add libraries to Virtuoso by registering them with 86 | Virtue. 87 | -------------------------------------------------------------------------------- /docs/source/overview/release_notes.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============== 3 | 4 | .. changelog:: 5 | :github: https://github.com/cascode-labs/virtue/releases 6 | :pypi: https://pypi.org/project/virtue-skill/ 7 | :changelog-url: http://www.cascode-labs.org/virtue/overview/release_notes.html 8 | -------------------------------------------------------------------------------- /docs/source/overview/skill_guidelines.rst: -------------------------------------------------------------------------------- 1 | SKILL Code guidelines 2 | ====================== 3 | 4 | SKILL Code Expectations 5 | ----------------------- 6 | 7 | The following expectations are set to ensure the quality and consistency of 8 | Virtue SKILL package's code. 9 | 10 | - SKILL code should be written using c-style function calls rather than 11 | lisp-style. 12 | - Each file should contain less than 200 lines of code. 13 | - Applications with gui elements should seperate the gui code from the 14 | underlying functionality. The functionality should have a function that allows 15 | it to be called programmatically. This make it easy to test and reuse the 16 | code. 17 | - Public module functions should be documented with :ref:`SKILL Docstrings`. 18 | - Every module should have a set of unit tests demonstrating its functionality. 19 | These should be included in the tests/ folder. 20 | 21 | SKILL Training 22 | -------------- 23 | 24 | - `Writing Good SKILL Code Video `_ 25 | by Andrew Beckett 26 | 27 | - `Cadence online training `_ 28 | 29 | SKILL Docstrings 30 | ---------------- 31 | 32 | Docstrings should be used to document public functions 33 | 34 | They should follow the following format which is compatible with the 35 | SKILL function browser: 36 | 37 | .. code-block:: lisp 38 | :linenos: 39 | :caption: Example SKILL docstring 40 | 41 | procedure(exampleFunction(cellview) 42 | "Does something very important. 43 | @brief A shorter description 44 | @param cellview The cellview object it will act on. 45 | @return importantList A list of important strings" 46 | let((localVar) 47 | ... 48 | )) 49 | -------------------------------------------------------------------------------- /docs/source/overview/skillbridge.rst: -------------------------------------------------------------------------------- 1 | Skillbridge 2 | ============ 3 | 4 | Virtue includes support for the 5 | `skillbridge `_ 6 | library for running SKILL code in a Virtuoso session from Python. 7 | It communicates between SKILL and Python using IPC and a TCP port. 8 | 9 | Menu 10 | ----- 11 | 12 | Skillbridge can be started from the Virtue menu in the library manager. 13 | Once it is running, it can be shutdown by toggling the same menu item. 14 | 15 | .. image:: ../_static/virtue_menu.png 16 | :alt: Virtue library manager menu 17 | 18 | Workspace ID 19 | ------------ 20 | 21 | Virtue also provides a workspace ID that can be used to create a unique 22 | workspace for each user and project. 23 | 24 | The workspace ID is based on environment variables and is defined as 25 | 26 | workspace_id = "${PRJ_ID}-${USER}" 27 | 28 | Where $PRJ_ID is the name of the current project. 29 | 30 | -------------------------------------------------------------------------------- /docs/source/overview/standard_library.rst: -------------------------------------------------------------------------------- 1 | SKILL Standard Library 2 | ====================== 3 | 4 | Virtue includes a standard library of functions that extend Virtuoso's library 5 | of built-in functions. 6 | 7 | See the :doc:`../reference/skill_api/index` page and the 8 | `source code `_ 9 | for more details. 10 | -------------------------------------------------------------------------------- /docs/source/overview/testing_framework.rst: -------------------------------------------------------------------------------- 1 | SKILL Test Framework 2 | ==================== 3 | 4 | The Test module defines a testing framework modelled after the Python 5 | `PyTest `_ tool. 6 | 7 | - The built-in assert is used for testing 8 | - test function names start with 'test\_' 9 | - Test setup fixtures are defined with input parameters. 10 | - Additional Test setup fixtures can be added to the Test->Fixtures table. 11 | 12 | The Test->Run function should be called at the end of each test script so 13 | the tests run when the script is loaded. Each test case is defined as a function that uses the built-in 14 | assert statement to test the code. A function is considered passing if it 15 | doesn't throw an error. 16 | 17 | .. dropdown:: Example test script 18 | :open: 19 | 20 | .. code-block:: scheme 21 | :linenos: 22 | 23 | let(((Str VrtImport['Str]) 24 | (Test VrtImport['Test]) 25 | (Virtue VrtImport['Virtue]) 26 | ) 27 | 28 | procedure(Test_emptyp() 29 | assert(Str->emptyp("")) 30 | assert(!Str->emptyp("test")) 31 | ) 32 | 33 | procedure(Test_str2bool() 34 | assert(Str->str2bool("true")) 35 | assert(Str->str2bool("TRUE")) 36 | assert(!Str->str2bool("false")) 37 | ) 38 | 39 | procedure(Test_str2bool_error() 40 | assert(!errset(Str->str2bool("Nothing"))) 41 | ) 42 | 43 | Test->RunFile(list(nil 44 | 'Test_emptyp Test_emptyp 45 | 'Test_str2bool Test_str2bool 46 | 'Test_str2bool_error Test_str2bool_error 47 | ) 48 | ?filepath Virtue->GetCurrentFilePath() 49 | ) 50 | 51 | ) 52 | 53 | The Test->RunDirectory(test_dir_path) function will run all the test scripts 54 | in a directory and report the results of them all. 55 | 56 | .. dropdown:: run_tests.ils 57 | :open: 58 | 59 | .. literalinclude:: ../_static/tests/run_tests.ils 60 | :language: scheme 61 | :linenos: 62 | 63 | The testing framework also includes test setup fixtures which can be called 64 | by adding the name of the fixture function to a test case: 65 | 66 | .. dropdown:: TestFixtures.ils 67 | :open: 68 | 69 | .. literalinclude:: ../_static/tests/test/test_TestFixtures.ils 70 | :language: scheme 71 | :linenos: 72 | 73 | Additional test fixtures can be added by adding them to the Test->Fixtures 74 | table: 75 | 76 | .. dropdown:: TestFixtures.ils 77 | :open: 78 | 79 | .. literalinclude:: ../_static/src/test/TestFixtures.ils 80 | :language: scheme 81 | :linenos: 82 | -------------------------------------------------------------------------------- /docs/source/overview/toml.rst: -------------------------------------------------------------------------------- 1 | TOML Config Files 2 | ----------------- 3 | 4 | Virtue supports the reading and writing of 5 | `TOML config files `_ 6 | which are easy to use and human-readable. 7 | 8 | Currently Virtue supports basic TOML files including key-value pairs and 9 | tables. But it doesn't currently support the whole specification and is 10 | missong some features such as in-line tables. 11 | 12 | Read a ".toml" file into a SKILL Table using the following function: 13 | 14 | .. code-block:: lisp 15 | :linenos: 16 | :caption: TOML read function signature 17 | 18 | Toml->ReadFile(filePath "t") 19 | 20 | Write a ".toml" file from a SKILL table or association list input: 21 | 22 | .. code-block:: lisp 23 | :linenos: 24 | :caption: TOML write function signature 25 | 26 | Toml->WriteFile(filePath input "tg") 27 | -------------------------------------------------------------------------------- /docs/source/reference/cli.rst: -------------------------------------------------------------------------------- 1 | Command Line Interface (CLI) Reference 2 | ====================================== 3 | 4 | .. click:: virtue.cli:typer_click_object 5 | :prog: virtue 6 | :nested: full 7 | -------------------------------------------------------------------------------- /docs/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Reference 3 | ========= 4 | 5 | Reference materials for Virtue's command-line interface, SKILL API, and 6 | Python API. 7 | 8 | .. toctree:: 9 | :caption: Contents: 10 | :hidden: 11 | 12 | cli 13 | skill_api/index 14 | -------------------------------------------------------------------------------- /docs/source/reference/skill_api/Geo.rst: -------------------------------------------------------------------------------- 1 | Geometry (Geo) Functions 2 | ------------------------- 3 | A geometry function module. 4 | 5 | **Point Functions:** 6 | 7 | * ptOffset 8 | * ptNeg 9 | * ptProduct 10 | * ptProductScalar 11 | * ptProductVector 12 | * ptDistance 13 | * ptInBoxp 14 | * ptNearestBoxSide 15 | * ptSnapToGrid 16 | 17 | **bBox Functions:** 18 | 19 | * bBoxOffset 20 | * bBoxSnapToGrid 21 | * bBoxFromSize 22 | * bBoxSize 23 | 24 | **Other Functions:** 25 | 26 | * figFlattenOverlaps 27 | * mosFlatten 28 | 29 | 30 | .. dropdown:: Geo.ils sourcecode 31 | 32 | .. literalinclude:: ../../_static/src/Geo.ils 33 | :language: scheme 34 | :linenos: 35 | -------------------------------------------------------------------------------- /docs/source/reference/skill_api/List.rst: -------------------------------------------------------------------------------- 1 | List Functions 2 | --------------- 3 | A list manipulation module. 4 | 5 | **Functions**: 6 | 7 | - ensure 8 | - setDiff 9 | - unique 10 | - getSlot 11 | - assocKeys 12 | 13 | .. dropdown:: List.ils sourcecode 14 | 15 | .. literalinclude:: ../../_static/src/data_types/List.ils 16 | :language: scheme 17 | :linenos: 18 | -------------------------------------------------------------------------------- /docs/source/reference/skill_api/Str.rst: -------------------------------------------------------------------------------- 1 | String (Str) Functions 2 | ----------------------- 3 | 4 | A string manipulation module. 5 | 6 | .. dropdown:: Str.ils sourcecode 7 | 8 | .. literalinclude:: ../../_static/src/data_types/Str.ils 9 | :language: scheme 10 | :linenos: 11 | -------------------------------------------------------------------------------- /docs/source/reference/skill_api/index.rst: -------------------------------------------------------------------------------- 1 | SKILL API 2 | ========= 3 | 4 | A standard library of SKILL and SKILL++ modules. They are included directly 5 | in the package VrtImport table for convenience. 6 | 7 | Cadence Reference Manuals 8 | -------------------------- 9 | 10 | Available on Cadence online support: 11 | 12 | - `Language Manuals `_ 13 | - `Language Reference `_ 14 | - `Language User Guide `_ 15 | - `Development Reference `_ 16 | - `IDE User Guide `_ 17 | 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | :caption: Modules: 22 | :hidden: 23 | 24 | List 25 | Str 26 | Geo.rst 27 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | # Virtue development environment 2 | name: virtue-dev 3 | 4 | channels: 5 | - conda-forge 6 | - defaults 7 | 8 | dependencies: 9 | - python=3.9 10 | - make = 4.3 11 | - conda 12 | - conda-build 13 | - conda-verify 14 | - mamba 15 | - boa 16 | - anaconda-client 17 | - git 18 | - pytest = 7.1.2 19 | - pylint = 2.14.4 20 | - mypy = 0.961 21 | - pre-commit = 2.20.0 22 | # Runtime 23 | - typer = 0.6.1 24 | - rich = 12.5.1 25 | - pluggy = 1.0.0 26 | - toml = 0.10.2 27 | # Optional Runtime 28 | - skillbridge = 1.2.18 29 | # Build 30 | - flit = 3.7.1 31 | - pip = 22.2.2 32 | - pyinstaller = 5.11.0 33 | # Documentation 34 | - sphinx = 4.5.0 35 | - pydata-sphinx-theme = 0.9.0 36 | - sphinx-panels = 0.6.0 37 | - sphinx-copybutton = 0.5.0 38 | - sphinx-autobuild = 2021.3.14 39 | - sphinx-github-changelog = 1.2.0 40 | - esbonio = 0.13.1 41 | - sphinx-sitemap = 2.2.0 42 | - sphinx-click = 4.3.0 43 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core>=3.2"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "virtue-skill" 7 | dependencies = [ 8 | "typer >= 0.6.1, < 1.0.0", 9 | "rich >= 12.5.1, < 13.0.0", 10 | "pluggy >= 1.0.0, < 2.0.0", 11 | "toml == 0.10.2", 12 | ] 13 | authors = [ 14 | {name="Curtis Mayberry", email="Curtisma3@gmail.com"}, 15 | ] 16 | maintainers = [ 17 | {name="Curtis Mayberry", email="Curtisma3@gmail.com"}, 18 | ] 19 | readme = "README.md" 20 | license = {file="LICENSE"} 21 | requires-python = ">=3.7" 22 | classifiers = [ 23 | "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", 24 | "Topic :: Scientific/Engineering", 25 | "Programming Language :: Python :: 3 :: Only", 26 | "Programming Language :: Python :: 3.8", 27 | "Programming Language :: Python :: 3.9", 28 | "Programming Language :: Python :: 3.10", 29 | "Programming Language :: Other Scripting Engines", 30 | "Programming Language :: Other", 31 | "License :: OSI Approved :: MIT License", 32 | "Operating System :: POSIX :: Linux", 33 | "Topic :: Software Development", 34 | "Topic :: Software Development :: Libraries", 35 | "Topic :: Software Development :: Libraries :: Python Modules", 36 | "Topic :: Software Development :: Testing", 37 | "Topic :: Software Development :: Testing :: Unit", 38 | ] 39 | keywords = [ 40 | "circuit design", 41 | "design automation", 42 | "Cadence", 43 | "virtuoso", 44 | "SKILL", 45 | "Python" 46 | ] 47 | dynamic = ["version", "description"] 48 | 49 | [project.urls] 50 | "Home" = "http://www.cascode-labs.org/virtue/" 51 | "Documentation" = "http://www.cascode-labs.org/virtue/" 52 | "Source" = "https://github.com/cascode-labs/virtue" 53 | 54 | [project.scripts] 55 | virtue = "virtue.cli:app" 56 | 57 | 58 | [tool.flit.module] 59 | name = "virtue" 60 | 61 | [project.optional-dependencies] 62 | optional = ["softworks", "skillbridge"] 63 | dev = [ 64 | "pre-commit == 2.20.0", 65 | # Build 66 | "flit == 3.7.1", 67 | "pip == 22.2.2", 68 | "pyinstaller == 5.11.0", 69 | # Testing 70 | "pytest >=7.1.2", 71 | "pylint >= 2.14.4", 72 | "ruff >= 0.3.0", 73 | # Docs 74 | "pydata-sphinx-theme == 0.15.2", 75 | "sphinx_design == 0.5.0", 76 | "sphinx_copybutton == 0.5.2", 77 | "sphinx-autobuild == 2024.2.4", 78 | "sphinx-github-changelog == 1.2.1", 79 | "sphinx-sitemap == 2.2.0", 80 | "sphinx-click == 5.1.0", 81 | ] 82 | 83 | [tool.virtue.build] 84 | build_dirpath = "dist/skill_build" 85 | source_dirpath = "virtue" 86 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | -e /workspaces/virtue 3 | pip == 22.1.2 4 | flit == 3.7.1 5 | pytest == 7.1.2 6 | pylint == 2.14.4 7 | mypy == 0.961 8 | pre-commit == 2.20.0 9 | # Docs 10 | sphinx == 4.5.0 11 | pydata-sphinx-theme == 0.9.0 12 | sphinx-panels == 0.6.0 13 | sphinx_copybutton == 0.5.0 14 | sphinx-autobuild == 2021.3.14 15 | sphinx-github-changelog == 1.2.0 16 | sphinx-sitemap == 2.2.0 17 | sphinx-click == 4.3.0 18 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | typer == 0.6.1 2 | rich == 12.5.1 3 | pluggy == 1.0.0 4 | skillbridge == 1.2.18 5 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | VERSION=$(virtue --version) 5 | DIST_PATH="dist/$VERSION" 6 | echo "Building IDS-skill" 7 | echo " building version $VERSION" 8 | rm -rf "$DIST_PATH" 9 | mkdir -p "$DIST_PATH/bin" 10 | ln -sf "$VERSION" dist/latest 11 | 12 | # Copy in SKILL Code 13 | cp -rf virtue "$DIST_PATH/" 14 | ln -s "virtue/virtue.cdsinit.ils" "$DIST_PATH/virtue.cdsinit.ils" 15 | ln -s "virtue/virtue.cdsLibMgr.il" "$DIST_PATH/virtue.cdsLibMgr.il" 16 | 17 | # Build Python package 18 | flit build 19 | mkdir "$DIST_PATH/pkgs" 20 | mv "dist/virtue_skill-${VERSION:1}.tar.gz" "$DIST_PATH/pkgs/" 21 | mv "dist/virtue_skill-${VERSION:1}-py3-none-any.whl" "$DIST_PATH/pkgs/" 22 | # Build executable 23 | pyinstaller -F -n virtue virtue/cli.py 24 | mv dist/virtue "$DIST_PATH/bin/" 25 | 26 | echo " build saved to $DIST_PATH" 27 | echo " build complete!" -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # (Re)Setup a new development environment 5 | rm -rf ./.venv 6 | # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/rds/prod/tools/python/3.10.7/lib 7 | /rds/prod/tools/python/3.10.7/bin/python3 -m venv .venv 8 | source .venv/bin/activate 9 | pip install -e .[dev] 10 | -------------------------------------------------------------------------------- /tests/Toml/simple_key_value.toml: -------------------------------------------------------------------------------- 1 | key = "value" 2 | bare_key1 = "value1" 3 | bare-key2 = "value2" 4 | 1234 = "value" 5 | -------------------------------------------------------------------------------- /tests/Toml/simple_key_value_write.toml: -------------------------------------------------------------------------------- 1 | bare-key2 = "value2" 2 | 1234 = "value" 3 | bare_key1 = "value1" 4 | key = "value" 5 | -------------------------------------------------------------------------------- /tests/Toml/simple_key_value_write_list.toml: -------------------------------------------------------------------------------- 1 | key = "value" 2 | bare_key1 = "value1" 3 | bare-key2 = "value2" 4 | 1234 = "value" 5 | -------------------------------------------------------------------------------- /tests/Toml/simple_table.toml: -------------------------------------------------------------------------------- 1 | [table-1] 2 | key1 = "some string" 3 | key2 = 123 4 | 5 | [table-2] 6 | key1 = "another string" 7 | key2 = 456 8 | -------------------------------------------------------------------------------- /tests/Toml/simple_table_write.toml: -------------------------------------------------------------------------------- 1 | [table-2] 2 | key2 = 456 3 | key1 = "another string" 4 | [table-1] 5 | key2 = 123 6 | key1 = "some string" 7 | -------------------------------------------------------------------------------- /tests/Toml/simple_table_write_association_list.toml: -------------------------------------------------------------------------------- 1 | [table-1] 2 | key2 = 123 3 | key1 = "some string" 4 | [table-2] 5 | key2 = 456 6 | key1 = "another string" 7 | -------------------------------------------------------------------------------- /tests/Toml/test_simple_key_value.ils: -------------------------------------------------------------------------------- 1 | let(((List VrtImport['Toml]) 2 | (Test VrtImport['Test]) 3 | ) 4 | 5 | 6 | procedure(Test_KeyValueStrings() 7 | let((table) 8 | table = Toml->ReadFile("/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_key_value.toml") 9 | ;println(table) 10 | ;println("keys:") 11 | ;println(table[?]) 12 | assert(table["key"] == "value") 13 | assert(table["bare_key1"] == "value1") 14 | assert(table["bare-key2"] == "value2") 15 | assert(table["1234"] == "value") 16 | )) 17 | 18 | procedure(Test_write_kv_table() 19 | let((filePath table table_dut) 20 | filePath = "/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_key_value_write.toml" 21 | table = makeTable(filePath) 22 | table["key"] = "value" 23 | table["bare_key1"] = "value1" 24 | table["bare-key2"] = "value2" 25 | table[1234] = "value" 26 | Toml->WriteFile(filePath table) 27 | table_dut = Toml->ReadFile(filePath) 28 | assert(table_dut["key"] == "value") 29 | assert(table_dut["bare_key1"] == "value1") 30 | assert(table_dut["bare-key2"] == "value2") 31 | assert(table_dut["1234"] == "value") 32 | )) 33 | 34 | procedure(Test_write_association_list() 35 | let((filePath assocList table) 36 | filePath = "/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_key_value_write_list.toml" 37 | assocList = list( 38 | list("key" "value") 39 | list("bare_key1" "value1") 40 | list("bare-key2" "value2") 41 | list(1234 "value") 42 | ) 43 | Toml->WriteFile(filePath assocList) 44 | table = Toml->ReadFile(filePath) 45 | assert(table["key"] == "value") 46 | assert(table["bare_key1"] == "value1") 47 | assert(table["bare-key2"] == "value2") 48 | assert(table["1234"] == "value") 49 | )) 50 | 51 | Test->RunFile(list(nil 52 | 'Test_KeyValueStrings Test_KeyValueStrings 53 | 'Test_write_kv_table Test_write_kv_table 54 | 'Test_write_association_list Test_write_association_list 55 | ) 56 | ?filepath VrtImport['Virtue]->GetCurrentFilePath() 57 | ) 58 | 59 | ) 60 | -------------------------------------------------------------------------------- /tests/Toml/test_simple_table.ils: -------------------------------------------------------------------------------- 1 | let(((List VrtImport['Toml]) 2 | (Test VrtImport['Test]) 3 | ) 4 | 5 | procedure(Test_read_table() 6 | let((table table1 table2) 7 | table = Toml->ReadFile("/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_table.toml") 8 | println(table) 9 | println("keys:") 10 | println(table[?]) 11 | assert(tablep(table["table-1"])) 12 | table1=table["table-1"] 13 | assert(table1["key1"] == "some string") 14 | assert(table1["key2"] == 123) 15 | assert(tablep(table["table-2"])) 16 | table2=table["table-2"] 17 | assert(table2["key1"] == "another string") 18 | assert(table2["key2"] == 456) 19 | )) 20 | 21 | procedure(Test_write_table() 22 | let((filePath table table_dut table1 table2) 23 | filePath = "/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_table_write.toml" 24 | table = makeTable(filePath) 25 | table1 = makeTable("table-1") 26 | table["table-1"] = table1 27 | table1["key1"] = "some string" 28 | table1["key2"] = 123 29 | table2 = makeTable("table-2") 30 | table["table-2"] = table2 31 | table2["key1"] = "another string" 32 | table2["key2"] = 456 33 | Toml->WriteFile(filePath table) 34 | table_dut = Toml->ReadFile(filePath) 35 | table1 = table_dut["table-1"] 36 | assert(tablep(table1)) 37 | assert(table1["key1"] == "some string") 38 | assert(table1["key2"] == 123) 39 | table2 = table_dut["table-2"] 40 | assert(tablep(table2)) 41 | assert(table2["key1"] == "another string") 42 | assert(table2["key2"] == 456) 43 | )) 44 | 45 | procedure(Test_table_write_association_list() 46 | let((filePath assocList table_dut table1 table2) 47 | filePath = "/prj/ids_dev/work_libs/mayberc/IDS-skill/tests/toml/simple_table_write_association_list.toml" 48 | table1 = makeTable("table-1") 49 | table1["key1"] = "some string" 50 | table1["key2"] = 123 51 | table2 = makeTable("table-2") 52 | table2["key1"] = "another string" 53 | table2["key2"] = 456 54 | assocList = list( 55 | list("table-1" table1) 56 | list("table-2" table2)) 57 | Toml->WriteFile(filePath assocList) 58 | table_dut = Toml->ReadFile(filePath) 59 | table1 = table_dut["table-1"] 60 | assert(tablep(table1)) 61 | assert(table1["key1"] == "some string") 62 | assert(table1["key2"] == 123) 63 | table2 = table_dut["table-2"] 64 | assert(tablep(table2)) 65 | assert(table2["key1"] == "another string") 66 | assert(table2["key2"] == 456) 67 | )) 68 | 69 | Test->RunFile(list(nil 70 | 'Test_read_table Test_read_table 71 | 'Test_write_table Test_write_table 72 | 'Test_table_write_association_list Test_table_write_association_list 73 | ) 74 | ?filepath VrtImport['Virtue]->GetCurrentFilePath() 75 | ) 76 | 77 | ) 78 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/tests/__init__.py -------------------------------------------------------------------------------- /tests/data_types/test_Dpl.ils: -------------------------------------------------------------------------------- 1 | let(((Dpl VrtImport['Dpl]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | (example_dpl list(nil 'key1 "value1" 'key2 "value2")) 5 | (example_dpl2 list(nil 'key3 "value3" 'key4 "value4")) 6 | ) 7 | 8 | procedure(test_PropertyTable() 9 | let((table) 10 | table = Dpl->PropertyTable(example_dpl) 11 | assert(tablep(table)) 12 | assert(length(table) == 2) 13 | assert(table['key1] == "value1") 14 | assert(table['key2] == "value2") 15 | )) 16 | 17 | procedure(test_Cat() 18 | let((ans) 19 | ans = Dpl->Cat(example_dpl example_dpl2) 20 | assert(listp(ans)) 21 | assert(length(ans) == 4) 22 | assert(ans->key1 == "value1") 23 | assert(ans->key2 == "value2") 24 | assert(ans->key3 == "value3") 25 | assert(ans->key4 == "value4") 26 | )) 27 | 28 | procedure(test_lint_Dpl() 29 | assert(sklint( 30 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/virtue/data_types/Dpl.ils" 31 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_Dpl.txt" 32 | ?prefixes '(Vrt VrtImport) 33 | )) 34 | ) 35 | 36 | procedure(test_lint_test_Dpl() 37 | createDirHier("/prj/ids_dev/work_libs/mayberc/skill/virtue/test/data_types") 38 | assert(sklint( 39 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/tests/data_types/test_Dpl.ils" 40 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_test_Dpl.txt" 41 | ?prefixes '(Vrt VrtImport) 42 | )) 43 | ) 44 | 45 | Test->RunFile(list(nil 46 | 47 | 'test_PropertyTable test_PropertyTable 48 | 'test_Cat test_Cat 49 | 'test_lint_Dpl test_lint_Dpl 50 | 'test_lint_test_Dpl test_lint_test_Dpl 51 | ) 52 | ?filepath Virtue->GetCurrentFilePath() 53 | ) 54 | 55 | ) 56 | -------------------------------------------------------------------------------- /tests/data_types/test_Lcv.ils: -------------------------------------------------------------------------------- 1 | let(((Lcv VrtImport['Lcv]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | ) 5 | 6 | ; Fixtures 7 | 8 | procedure(lcv_list() 9 | list("lib_name" "cell_name" "view_name") 10 | ) 11 | 12 | procedure(lcvh_list() 13 | list("lib_name" "cell_name" "view_name" "history_name") 14 | ) 15 | 16 | Test->Fixtures['lcv_list] = lcv_list 17 | Test->Fixtures['lcvh_list] = lcvh_list 18 | 19 | ; Tests 20 | 21 | procedure(Test_lib(lcv_list, lcvh_list) 22 | assert(Lcv->lib(lcv_list) == "lib_name") 23 | assert(Lcv->lib(lcvh_list) == "lib_name") 24 | ) 25 | 26 | procedure(Test_cell(lcv_list, lcvh_list) 27 | assert(Lcv->cell(lcv_list) == "cell_name") 28 | assert(Lcv->cell(lcvh_list) == "cell_name") 29 | ) 30 | 31 | procedure(Test_view(lcv_list, lcvh_list) 32 | assert(Lcv->view(lcv_list) == "view_name") 33 | assert(Lcv->view(lcvh_list) == "view_name") 34 | ) 35 | 36 | procedure(Test_history(lcvh_list) 37 | assert(Lcv->view(lcvh_list) == "history_name") 38 | ) 39 | 40 | procedure(Test_lint_Lcv() 41 | assert(sklint( 42 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/virtue/data_types/Lcv.ils" 43 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_Lcv.txt" 44 | ?prefixes '(Vrt VrtImport) 45 | )) 46 | ) 47 | 48 | procedure(Test_lint_Test_Lcv() 49 | assert(sklint( 50 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/tests/data_types/test_Lcv.ils" 51 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_test_Lcv.txt" 52 | ?prefixes '(Vrt VrtImport) 53 | )) 54 | ) 55 | 56 | 57 | Test->RunFile(list(nil 58 | 'Test_lib Test_lib 59 | 'Test_cell Test_cell 60 | 'Test_view Test_view 61 | 'Test_history Test_history 62 | 'Test_lint_Lcv Test_lint_Lcv 63 | 'Test_lint_Test_Lcv Test_lint_Test_Lcv 64 | ) 65 | ?filepath Virtue->GetCurrentFilePath() 66 | ) 67 | 68 | ) 69 | -------------------------------------------------------------------------------- /tests/data_types/test_List.ils: -------------------------------------------------------------------------------- 1 | let(((List VrtImport['List]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | ) 5 | 6 | procedure(Test_ensure() 7 | assert(List->ensure('(1 2 3)) == '(1 2 3)) ; already a list 8 | assert(List->ensure("testing false") == '("testing false")) ; a string 9 | assert(List->ensure(1) == '(1)) ; an integer 10 | assert(List->ensure('()) == '()) ; empty list 11 | ) 12 | 13 | procedure(Test_setDiff() 14 | assert(List->setDiff('(1 2 3) '(2 3)) == '(1)) ; integers 15 | assert(List->setDiff('(1 2 3) '(1 2 3)) == '()) 16 | assert(List->setDiff('("A" "B" "CD" "E") '("A" "CD")) == '("B" "E")) ; strings 17 | assert(List->setDiff('("A" "B" "CD" "E") '("A" "B" "CD" "E")) == '()) 18 | ) 19 | 20 | procedure(Test_uniqueList() 21 | assert(List->uniqueList('(1 2 3 2 3)) == '(3 2 1)) ; integers 22 | assert(List->uniqueList('(1 2 3)) == '(3 2 1)) 23 | assert(List->uniqueList('("A" "B" "CD" "E" "A" "CD")) == '("B" "CD" "E" "A")) ; strings 24 | assert(List->uniqueList('("hi" "hello" "hello")) == '("hi" "hello")) 25 | assert(List->uniqueList('("hi" "hello")) == '("hi" "hello")) 26 | assert(List->uniqueList('("hi" "hi" "hello" "hello")) == '("hi" "hello")) 27 | assert(List->uniqueList('("hi" "hi" "hello" "hello" "" "")) == '("hi" "" "hello")) ; multiple empty strings 28 | ) 29 | 30 | procedure(Test_uniqueListOrdered() 31 | assert(List->uniqueListOrdered('(1 2 3 2 3)) == '(1 2 3)) ; integers 32 | assert(List->uniqueListOrdered('(1 2 3)) == '(1 2 3)) 33 | assert(List->uniqueListOrdered('("A" "B" "CD" "E" "A" "CD")) == '("A" "B" "CD" "E")) ; strings 34 | assert(List->uniqueListOrdered('("hi" "hello" "hello")) == '("hi" "hello")) 35 | assert(List->uniqueListOrdered('("hi" "hello")) == '("hi" "hello")) 36 | assert(List->uniqueListOrdered('("hi" "hi" "hello" "hello")) == '("hi" "hello")) 37 | assert(List->uniqueListOrdered('("hi" "hi" "hello" "hello" "" "")) == '("hi" "hello" "")) ; multiple empty strings 38 | ) 39 | 40 | procedure(Test_assocKeys() 41 | assert(List->assocKeys('((1 "A") '(2 "B") '(3 "C"))) == '(1 2 3)) 42 | assert(List->assocKeys('((1 "A"))) == '(1)) 43 | assert(!List->assocKeys(nil)) 44 | ) 45 | 46 | procedure(Test_lint_List() 47 | assert(sklint( 48 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/virtue/data_types/List.ils" 49 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_List.txt" 50 | ?prefixes '(Vrt VrtImport) 51 | )) 52 | ) 53 | 54 | procedure(Test_lint_test_List() 55 | assert(sklint( 56 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/tests/data_types/test_List.ils" 57 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/test/lint_report_test_List.txt" 58 | ?prefixes '(Vrt VrtImport) 59 | )) 60 | ) 61 | 62 | Test->RunFile(list(nil 63 | 'Test_ensure Test_ensure 64 | 'Test_setDiff Test_setDiff 65 | 'Test_uniqueList Test_uniqueList 66 | 'Test_uniqueListOrdered Test_uniqueListOrdered 67 | 'Test_assocKeys Test_assocKeys 68 | 'Test_lint_List Test_lint_List 69 | 'Test_lint_test_List Test_lint_test_List 70 | ) 71 | ?filepath Virtue->GetCurrentFilePath() 72 | ) 73 | 74 | ) 75 | -------------------------------------------------------------------------------- /tests/data_types/test_Path.ils: -------------------------------------------------------------------------------- 1 | let(((Path VrtImport['Path]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | ) 5 | 6 | ; Fixtures 7 | ; -------- 8 | 9 | procedure(directory_path() 10 | Path->Concat("/absolute" "path" "to" "a" "directory") 11 | ) 12 | 13 | procedure(file_path() 14 | Path->Concat("/absolute" "path" "to" "a" "file.txt") 15 | ) 16 | 17 | Test->Fixtures['directory_path] = directory_path 18 | Test->Fixtures['file_path] = file_path 19 | 20 | ; Tests 21 | ; ----- 22 | 23 | procedure(Test_PathConcat() 24 | assert(Path->Concat("/absolute" "path" "to" "a" "directory") == 25 | "/absolute/path/to/a/directory") 26 | assert(Path->Concat("" "absolute" "path" "to" "a" "directory") == 27 | "/absolute/path/to/a/directory") 28 | assert(Path->Concat("/absolute" "path" "to" "a" "file.txt") == 29 | "/absolute/path/to/a/file.txt") 30 | ) 31 | 32 | procedure(Test_PathCat() 33 | assert(Path->Cat("/absolute" "path" "to" "a" "directory") == 34 | "/absolute/path/to/a/directory") 35 | assert(Path->Cat("" "absolute" "path" "to" "a" "directory") == 36 | "/absolute/path/to/a/directory") 37 | assert(Path->Cat("/absolute" "path" "to" "a" "file.txt") == 38 | "/absolute/path/to/a/file.txt") 39 | ) 40 | 41 | procedure(Test_FileName(file_path directory_path) 42 | assert(Path->FileName(file_path) == "file.txt") 43 | assert(Path->FileName(directory_path) == "directory") 44 | ) 45 | 46 | procedure(Test_Folder(file_path directory_path) 47 | assert(Path->FileName(file_path) == "/absolute/path/to/a") 48 | assert(Path->FileName(directory_path) == "/absolute/path/to/a") 49 | ) 50 | 51 | procedure(Test_RemoveExtension(file_path) 52 | assert(Path->RemoveExtension(file_path) == "/absolute/path/to/a/file") 53 | assert(Path->RemoveExtension(directory_path) == "/absolute/path/to/a/directory") 54 | assert(Path->RemoveExtension("file.ext") == "file") 55 | ) 56 | 57 | procedure(Test_UpdateExtension(file_path) 58 | assert(Path->UpdateExtension(file_path "new") == "/absolute/path/to/a/file.new") 59 | assert(Path->UpdateExtension(directory_path "new") == "/absolute/path/to/a/directory.new") 60 | assert(Path->UpdateExtension("file.ext" "new") == "file.new") 61 | ) 62 | 63 | procedure(Test_FileExtension(file_path) 64 | assert(Path->FileExtension(file_path) == "txt") 65 | assert(Path->FileExtension(directory_path) == "") 66 | assert(Path->FileExtension("file.ext") == "ext") 67 | assert(Path->FileExtension("file.myext") == "myext") 68 | ) 69 | 70 | procedure(Test_lint_Path(lint_settings) 71 | assert(sklint( 72 | ?file Path->Cat(lint_settings->testsDir 73 | "data_types/Path.ils") 74 | ?outputFile Path->Cat(lint_settings->outputDir 75 | "lint_report_Path.txt") 76 | ?prefixes lint_settings->prefixes 77 | )) 78 | ) 79 | 80 | procedure(Test_lint_Test_Path(lint_settings) 81 | assert(sklint( 82 | ?file Path->Cat(lint_settings->testsDir 83 | "data_types/test_Path.ils") 84 | ?outputFile Path->Cat(lint_settings->outputDir 85 | "lint_report_test_Path.txt") 86 | ?prefixes lint_settings->prefixes 87 | )) 88 | ) 89 | 90 | Test->RunFile(list(nil 91 | 'Test_PathConcat Test_PathConcat 92 | 'Test_PathCat Test_PathCat 93 | 'Test_FileName Test_FileName 94 | 'Test_Folder Test_Folder 95 | 'Test_RemoveExtension Test_RemoveExtension 96 | 'Test_UpdateExtension Test_UpdateExtension 97 | 'Test_FileExtension Test_FileExtension 98 | 'Test_lint_Path Test_lint_Path 99 | 'Test_lint_Test_Path Test_lint_Test_Path 100 | ) 101 | ?filepath Virtue->GetCurrentFilePath() 102 | ) 103 | 104 | ) 105 | -------------------------------------------------------------------------------- /tests/data_types/test_Str.ils: -------------------------------------------------------------------------------- 1 | let(((Str VrtImport['Str]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | ) 5 | 6 | procedure(Test_emptyp() 7 | assert(Str->emptyp("")) 8 | assert(!Str->emptyp("test")) 9 | ) 10 | 11 | procedure(Test_split() 12 | assert(Str->split("Hi,how,,you" ",") == '("Hi" "how" "" "you")) 13 | assert(Str->split("Hi,how,are,you" ",") == '("Hi" "how" "are" "you")) 14 | assert(Str->split("Hi how you" " ") == '("Hi" "how" "" "you")) 15 | assert(Str->split("Hi how are you" " ") == '("Hi" "how" "are" "you")) 16 | ) 17 | 18 | procedure(Test_trimWhiteSpace() 19 | assert(Str->trimWhiteSpace(" Hi everyone") == "Hi everyone") 20 | assert(Str->trimWhiteSpace(" Hi everyone ") == "Hi everyone") 21 | assert(Str->trimWhiteSpace("Hi everyone ") == "Hi everyone") 22 | assert(Str->trimWhiteSpace("Hi everyone") == "Hi everyone") 23 | assert(!("Hi everyone " == "Hi everyone")) 24 | ) 25 | 26 | procedure(Test_str2bool() 27 | assert(Str->str2bool("true")) 28 | assert(Str->str2bool("True")) 29 | assert(Str->str2bool("TRUE")) 30 | assert(!Str->str2bool("false")) 31 | assert(!Str->str2bool("False")) 32 | assert(!Str->str2bool("FALSE")) 33 | ) 34 | 35 | procedure(Test_str2bool_error() 36 | assert(!errset(Str->str2bool("Nothing"))) 37 | ) 38 | 39 | procedure(Test_convertNumber() 40 | assert(Str->convertNumber("5.0") == 5.0) 41 | assert(Str->convertNumber("5") == 5) 42 | assert(floatp(Str->convertNumber("5.0"))) 43 | assert(integerp(Str->convertNumber("5"))) 44 | ) 45 | 46 | procedure(Test_convert() 47 | assert(Str->convert("true")) 48 | assert(!Str->convert("false")) 49 | assert(Str->convert("5") == 5) 50 | assert(integerp(Str->convert("5"))) 51 | assert(floatp(Str->convert("5.0"))) 52 | assert(Str->convert("5.0") == 5) 53 | assert(Str->convert("5") == 5.0) 54 | ) 55 | 56 | procedure(Test_num2str() 57 | assert(Str->num2str(5) == "5") 58 | assert(Str->num2str(5.0) == "5.000000") 59 | assert(Str->num2str(4.5) == "4.500000") 60 | assert(Str->num2str(10) == "10") 61 | ) 62 | 63 | procedure(Test_bool2str() 64 | assert(Str->bool2str('t) == "TRUE") 65 | assert(Str->bool2str(nil) == "FALSE") 66 | assert(Str->bool2str("test") == "TRUE") 67 | assert(!(Str->bool2str('("test")) == "FALSE")) 68 | ) 69 | 70 | procedure(Test_startsWith() 71 | assert(Str->startsWith("HiMan" "Hi")) 72 | assert(Str->startsWith("HiMan" "Hi") == "Man") 73 | assert(!Str->startsWith("HiMan" "Man")) 74 | assert(!Str->startsWith("HiManly" "High")) 75 | assert(Str->startsWith("HiManly" "H")) 76 | ) 77 | 78 | procedure(Test_endsWith() 79 | assert(Str->endsWith("HiMan" "Man")) 80 | assert(Str->endsWith("HiMan" "Man") == "Hi") 81 | assert(!Str->endsWith("HiManly" "Man")) 82 | assert(!Str->endsWith("HiManly" "Hi")) 83 | ) 84 | 85 | procedure(Test_prefixp() 86 | assert(Str->prefixp("preFix" "pre")) 87 | assert(!Str->prefixp("notpreFix" "pre")) 88 | assert(!Str->prefixp("no" "pre")) 89 | ) 90 | 91 | procedure(Test_lint_Str(lint_settings) 92 | assert(lint_settings->RunLint("../virtue/Str.ils")) 93 | ) 94 | 95 | procedure(Test_lint_test_Str(lint_settings) 96 | assert(lint_settings->RunLint("data_types/test_Str.ils")) 97 | ) 98 | 99 | Test->RunFile(list(nil 100 | 'Test_emptyp Test_emptyp 101 | 'Test_split Test_split 102 | 'Test_trimWhiteSpace Test_trimWhiteSpace 103 | 'Test_str2bool Test_str2bool 104 | 'Test_str2bool_error Test_str2bool_error 105 | 'Test_convertNumber Test_convertNumber 106 | 'Test_convert Test_convert 107 | 'Test_num2str Test_num2str 108 | 'Test_bool2str Test_bool2str 109 | 'Test_startsWith Test_startsWith 110 | 'Test_endsWith Test_endsWith 111 | 'Test_prefixp Test_prefixp 112 | 'Test_lint_Str Test_lint_Str 113 | 'Test_lint_test_Str Test_lint_test_Str 114 | ) 115 | ?filepath Virtue->GetCurrentFilePath() 116 | ) 117 | 118 | ) 119 | -------------------------------------------------------------------------------- /tests/data_types/test_Time.ils: -------------------------------------------------------------------------------- 1 | let(((Time VrtImport['Time]) 2 | (Test VrtImport['Test]) 3 | (Virtue VrtImport['Virtue]) 4 | (example_time "Feb 23 10:54:12 2024") 5 | ) 6 | 7 | procedure(Test_IntTimeTable() 8 | let(((timeTable Time->IntTimeTable())) 9 | assert(timeTable["string"] == example_time) 10 | assert(timeTable["year"] == 2024) 11 | assert(timeTable["month"] == 2) 12 | assert(timeTable["day"] == 23) 13 | assert(timeTable["hour"] == 10) 14 | assert(timeTable["min"] == 54) 15 | assert(timeTable["sec"] == 12) 16 | assert(timeTable["weekday"] == 5) 17 | assert(timeTable["yearday"] == 54) 18 | assert(timeTable["isdst"] == 0) 19 | )) 20 | 21 | procedure(Test_IsoDateString() 22 | assert(IsoDateString(example_time) 23 | == "2024-02-23") 24 | ) 25 | 26 | procedure(Test_IsoTimeString() 27 | assert(IsoTimeString(example_time) 28 | == "10:54:12") 29 | ) 30 | 31 | procedure(Test_CurrentDatetimeIsoUTC() 32 | let(((value Time->CurrentDatetimeIsoUTC())) 33 | assert(stringp(value)) 34 | assert(strlen(value) == 19) 35 | )) 36 | 37 | procedure(Test_lint_Time(lint_settings) 38 | assert(lint_settings->RunLint("../virtue/data_types/Time.ils")) 39 | ) 40 | 41 | procedure(Test_lint_test_Time(lint_settings) 42 | assert(lint_settings->RunLint("data_types/test_Time.ils")) 43 | ) 44 | 45 | 46 | procedure(Test_DateStringIsoUTC() 47 | let(((value Time->DateStringIsoUTC())) 48 | assert(stringp(value)) 49 | assert(strlen(value) == 10) 50 | )) 51 | 52 | procedure(Test_TimeStringIsoUTC() 53 | let(((value Time->TimeStringIsoUTC())) 54 | assert(stringp(value)) 55 | assert(strlen(value) == 8) 56 | )) 57 | 58 | Test->RunFile(list(nil 59 | 'Test_IntTimeTable Test_IntTimeTable 60 | 'Test_IsoDateString Test_IsoDateString 61 | 'Test_IsoTimeString Test_IsoTimeString 62 | 'Test_CurrentDatetimeIsoUTC Test_CurrentDatetimeIsoUTC 63 | 'Test_lint_Time Test_lint_Time 64 | 'Test_lint_test_Time Test_lint_test_Time 65 | 'Test_DateStringIsoUTC Test_DateStringIsoUTC 66 | 'Test_TimeStringIsoUTC Test_TimeStringIsoUTC 67 | ) 68 | ?filepath Virtue->GetCurrentFilePath() 69 | ) 70 | 71 | ) 72 | -------------------------------------------------------------------------------- /tests/run_skill_tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | Run the SKILL tests from Python. Makes it easy to run them from a 3 | python IDE. Make sure the skillbridge server is running first. 4 | """ 5 | 6 | from pathlib import Path 7 | from skillbridge import Workspace, Symbol 8 | 9 | if __name__ == "__main__": 10 | tests_path = Path(__file__).parent.resolve() 11 | run_tests_path = tests_path / "run_tests.ils" 12 | src_init_path = tests_path /".." / "virtue" / "virtue.init.ils" 13 | ws = Workspace.open('ids_dev-mayberc') 14 | skill_import = ws.__.VrtImport 15 | test_pkg = skill_import[Symbol("Test")] 16 | print(f"\nRunning tests in:\n {run_tests_path}") 17 | run_dir_tests = test_pkg[8] 18 | result = run_dir_tests(str(tests_path)) 19 | function_count = result["FunctionCount"]() 20 | pass_count = result["FunctionPassCount"]() 21 | if result["pass"]: 22 | print(f"All {pass_count} tests passed! ") 23 | else: 24 | print(("Some tests failed:\n" 25 | f" {pass_count} passed out of {function_count}\n")) 26 | -------------------------------------------------------------------------------- /tests/run_tests.ils: -------------------------------------------------------------------------------- 1 | let((dir_path 2 | (Test VrtImport['Test]) 3 | (Path VrtImport['Path]) 4 | (Virtue VrtImport['Virtue]) 5 | ) 6 | 7 | 8 | dir_path = Virtue->GetCurrentFileDirectory() 9 | dir_path = Path->Cat(dir_path) 10 | 11 | ; Shared Test Fixtures 12 | ; -------------------- 13 | 14 | procedure(lint_settings() 15 | "Test" 16 | let(((outputDir simplifyFilename(Path->Cat(dir_path "../test"))) 17 | (prefixes '(Vrt VrtImport)) 18 | ) 19 | 20 | 21 | createDirHier(Path->Cat(outputDir "data_types")) 22 | 23 | procedure(RunLint(relativeFilePath) 24 | "Run lint on a file relative to the tests directory" 25 | ; funcall is necessary since sklint is an nlambda function and 26 | ; selectively evaluates the args in the function rather than 27 | ; within the lexical env before the function call. 28 | funcall(sklint 29 | ?file Path->Cat(dir_path relativeFilePath) 30 | ?outputFile Path->Cat(outputDir strcat( 31 | "lint_report_" 32 | Path->RemoveExtension(Path->FileName(relativeFilePath)) 33 | ".txt")) 34 | ?prefixes prefixes 35 | ) 36 | ) 37 | 38 | list(nil 39 | 'RunLint RunLint 40 | 'outputDir outputDir 41 | 'prefixes prefixes 42 | 'testsDir dir_path 43 | ) 44 | )) 45 | 46 | Test->Fixtures['lint_settings] = lint_settings 47 | 48 | Test->RunDirectory(Path->Cat(dir_path "data_types")) 49 | ;Test->RunDirectory(dir_path) 50 | ) 51 | -------------------------------------------------------------------------------- /tests/skill_environment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/tests/skill_environment/__init__.py -------------------------------------------------------------------------------- /tests/skill_environment/test_api.py: -------------------------------------------------------------------------------- 1 | from virtue.skill_environment import api, init_scripts 2 | 3 | 4 | def test_api_info_python_version(): 5 | info_data = api.info() 6 | assert info_data["python version"][0] == 3 7 | 8 | def test_api_info_install_paths(): 9 | info_data = api.info() 10 | assert "python version" in info_data 11 | 12 | def test_api_list_packages(): 13 | packages = api.list_packages() 14 | assert "virtue-skill" in packages 15 | assert "version" in packages["virtue-skill"] 16 | 17 | def test_api_init(): 18 | env_init_file_paths = init_scripts.init() 19 | assert ".cdsinit" in env_init_file_paths 20 | assert "cdsLibMgr.il" in env_init_file_paths 21 | assert "data.reg" in env_init_file_paths 22 | assert env_init_file_paths[".cdsinit"] is not None 23 | assert env_init_file_paths["cdsLibMgr.il"] is not None 24 | -------------------------------------------------------------------------------- /tests/skill_package/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/tests/skill_package/__init__.py -------------------------------------------------------------------------------- /tests/skill_package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core>=3.2"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "virtue-skill" 7 | dependencies = [ 8 | "typer >= 0.6.1, < 1.0.0", 9 | "rich >= 12.5.1, < 13.0.0", 10 | "pluggy >= 1.0.0, < 2.0.0", 11 | ] 12 | authors = [ 13 | {name="Curtis Mayberry", email="Curtisma3@gmail.com"}, 14 | ] 15 | maintainers = [ 16 | {name="Curtis Mayberry", email="Curtisma3@gmail.com"}, 17 | ] 18 | readme = "README.md" 19 | license = {file="LICENSE"} 20 | requires-python = ">=3.7" 21 | classifiers = [ 22 | "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", 23 | "Topic :: Scientific/Engineering", 24 | "Programming Language :: Python :: 3 :: Only", 25 | "Programming Language :: Python :: 3.8", 26 | "Programming Language :: Python :: 3.9", 27 | "Programming Language :: Python :: 3.10", 28 | "Programming Language :: Other Scripting Engines", 29 | "Programming Language :: Other", 30 | "License :: OSI Approved :: MIT License", 31 | "Operating System :: POSIX :: Linux", 32 | "Topic :: Software Development", 33 | "Topic :: Software Development :: Libraries", 34 | "Topic :: Software Development :: Libraries :: Python Modules", 35 | "Topic :: Software Development :: Testing", 36 | "Topic :: Software Development :: Testing :: Unit", 37 | ] 38 | keywords = [ 39 | "circuit design", 40 | "design automation", 41 | "Cadence", 42 | "virtuoso", 43 | "SKILL", 44 | "Python" 45 | ] 46 | dynamic = ["version", "description"] 47 | 48 | [project.urls] 49 | "Home" = "http://www.cascode-labs.org/virtue/" 50 | "Documentation" = "http://www.cascode-labs.org/virtue/" 51 | "Source" = "https://github.com/cascode-labs/virtue" 52 | 53 | [project.scripts] 54 | virtue = "virtue.cli:app" 55 | 56 | 57 | [tool.flit.module] 58 | name = "virtue" 59 | 60 | [project.optional-dependencies] 61 | optional = ["softworks", "skillbridge"] 62 | test = [ 63 | "pytest >=7.1.2", 64 | "pylint >= 2.14.4", 65 | "mypy >= 0.961", 66 | ] 67 | dev = ["pre-commit == 2.20.0"] 68 | doc = [ 69 | "sphinx == 4.5.0", 70 | "pydata-sphinx-theme == 0.9.0", 71 | "sphinx-panels == 0.6.0", 72 | "sphinx_copybutton == 0.5.0", 73 | "sphinx-autobuild == 2021.3.14", 74 | "sphinx-github-changelog == 1.2.0", 75 | "sphinx-sitemap == 2.2.0", 76 | "sphinx-click == 4.3.0", 77 | ] 78 | 79 | [tool.virtue.build] 80 | build_dirpath = "dist/skill_build" 81 | source_dirpath = "virtue" 82 | -------------------------------------------------------------------------------- /tests/skill_package/test_metadata.py: -------------------------------------------------------------------------------- 1 | from virtue.skill_package.metadata import read_metadata 2 | from tests import skill_package 3 | from importlib.resources import path 4 | 5 | def test_read_metadata(): 6 | with path(skill_package, "pyproject.toml") as toml_file: 7 | metadata = read_metadata(toml_file) 8 | assert metadata["project"]["name"] == "virtue-skill" 9 | -------------------------------------------------------------------------------- /tests/skill_package/test_module.ils: -------------------------------------------------------------------------------- 1 | let((dut_package 2 | (Module VrtImport['Module]) 3 | (Test VrtImport['Test]) 4 | ) 5 | 6 | procedure(ModuleCat(package string_arg) 7 | strcat(Module->some_string string_arg) 8 | ) 9 | 10 | dut_package = list(nil 11 | 'some_string "A") 12 | 13 | putprop(dut_package Module->Method(ModuleCat dut_package) 'ModuleCat) 14 | 15 | procedure(test_Method() 16 | assert(dut_Module->ModuleCat("B") == "AB") 17 | ) 18 | 19 | test_function = Test->NewFunction('test_Method test_Method) 20 | test_function->Run() 21 | test_function->ReportResult() 22 | ) 23 | -------------------------------------------------------------------------------- /tests/skill_package/test_skill_packages_data.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from virtue.skill_package.metadata \ 4 | import skill_packages_metadata, metadata 5 | 6 | 7 | def test_virtue_package_data(): 8 | data = skill_packages_metadata() 9 | assert isinstance(data, dict) 10 | assert "virtue-skill" in data 11 | assert data["virtue-skill"]["skill_package_name"] == "Virtue" 12 | 13 | @pytest.mark.parametrize("metadata_key", 14 | [("cdsinit_paths"), ("cdslibmgr_paths"), ("cds_dev_libraries"), 15 | ("python_package_name"), ("skill_package_name")]) 16 | def test_metadata(metadata_key): 17 | data = metadata(metadata_key) 18 | assert isinstance(data, list) 19 | assert len(data) > 0 20 | -------------------------------------------------------------------------------- /tests/test/test_TestFixtures.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (Virtue VrtImport['Virtue]) 3 | ) 4 | 5 | procedure(test_TempDirectory(TempDirectory) 6 | assert(isDir(TempDirectory)) 7 | ) 8 | 9 | Test->RunFile(list(nil 10 | 'test_TempDirectory test_TempDirectory 11 | ) 12 | ?filepath Virtue->GetCurrentFilePath() 13 | ) 14 | 15 | ) 16 | -------------------------------------------------------------------------------- /tests/test/test_lint.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (Virtue VrtImport['Virtue]) 3 | ) 4 | 5 | procedure(test_lint_Str() 6 | assert(sklint( 7 | ?file "/prj/ids_dev/work_libs/mayberc/skill/virtue/virtue/Str.ils" 8 | ?outputFile "/prj/ids_dev/work_libs/mayberc/skill/virtue/tests/lint_report.txt" 9 | ?prefixes '(Vrt VrtImport) 10 | )) 11 | ) 12 | 13 | Test->RunFile(list(nil 14 | 'test_lint_Str test_lint_Str 15 | ) 16 | ?filepath Virtue->GetCurrentFilePath() 17 | ) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | from typer.testing import CliRunner 2 | 3 | import virtue 4 | from virtue.cli import app 5 | 6 | runner = CliRunner() 7 | 8 | 9 | def test_cli_version(): 10 | result = runner.invoke(app, ["--version"]) 11 | assert result.exit_code == 0 12 | assert f"v{virtue.__version__}" in result.stdout 13 | 14 | def test_cli_info(): 15 | result = runner.invoke(app, ["env", "info"]) 16 | assert result.exit_code == 0 17 | assert result.stdout != "" 18 | 19 | def test_cli_list(): 20 | result = runner.invoke(app, ["env", "list"]) 21 | assert result.exit_code == 0 22 | assert "Virtue Packages" in result.stdout 23 | assert "Python Package" in result.stdout 24 | assert "SKILL Package" in result.stdout 25 | assert "virtue-skill" in result.stdout 26 | assert "Virtue" in result.stdout 27 | assert f"{virtue.__version__}" in result.stdout 28 | -------------------------------------------------------------------------------- /virtue/ConfigUtility.py: -------------------------------------------------------------------------------- 1 | def create_config_view(workspace, config_lcv, top_cell_lcv, library_list, view_list, stop_list): 2 | """Creates a config view in a given lib-cell-view. Sets the top cell lib-cell-view based on given. Sets the config 3 | library list, view list, and stop list in the global binding section. 4 | 5 | Args: 6 | workspace (Workspace): A open Skillbridge Workspace object, connected to a specified project virtuoso session. 7 | config_lcv (list(str str str)): A list containing the library, cell, and view name of where the config will 8 | be created. 9 | top_cell_lcv (list(str str str)): A list containing the library, cell, and view name of the Top Cell in the 10 | created config. 11 | library_list (str): String of library names separated by spaces. 12 | view_list (str): String of view names separated by spaces. 13 | stop_list (str): String of stops separated by spaces. 14 | 15 | Returns: 16 | (bool) Returns true is successful, false otherwise 17 | """ 18 | return workspace['HdbCreateConfigView'](config_lcv, top_cell_lcv, library_list, view_list, stop_list) 19 | -------------------------------------------------------------------------------- /virtue/Geo.ils: -------------------------------------------------------------------------------- 1 | /***************************************************************** 2 | * * 3 | * Layout Geometry Module * 4 | * * 5 | * Handles layout geometries such as shapes, points, and * 6 | * bounding boxes * 7 | * * 8 | * * Points are handled by functions starting with pt * 9 | * * Bounding boxes are handled by functions starting with * 10 | * bBox * 11 | * * Figures are handled by functions starting with fig * 12 | * * Mosaics are handled by functions starting with mos * 13 | * * 14 | *****************************************************************/ 15 | 16 | let((Geo 17 | (module_description "Geometry functions") 18 | ) 19 | 20 | Geo = let(() 21 | 22 | ;;;;;;;;;; 23 | ; Points ; 24 | ;;;;;;;;;; 25 | ; Built-in Functions: 26 | ; * isLocation 27 | ; * xCoord 28 | ; * yCoord 29 | ; * dbTransformPoint 30 | ; * dbTransformPointList 31 | ; * dbConcatTransform 32 | 33 | procedure(ptOffset(P offset "ll") 34 | "Offset a point or sum 2 points" 35 | list(xCoord(P)+xCoord(offset) 36 | yCoord(P)+yCoord(offset)) 37 | ) 38 | 39 | procedure(ptNeg(P "l") 40 | "Negates both the x and Y of the given point" 41 | list(-xCoord(P) -yCoord(P)) 42 | ) 43 | 44 | procedure(ptProduct(P Multiplier "lg") 45 | "Multiplies the point, P, by the given Multiplier 46 | 47 | @param P The point as a (X,Y) list 48 | @param Multiplier Either a scalar to multiply with both the X and Y or 49 | an (X,Y) list containing seperate multipliers for each coordinate" 50 | cond( 51 | (floatp(Multiplier) || integerp(Multiplier) 52 | ptProductScalar(P Multiplier)) 53 | (isLocation(Multiplier) 54 | ptProductVector(P Multiplier)) 55 | ('t 56 | error("Multiplier type is not supported"))) 57 | ) 58 | 59 | procedure(ptProductScalar(P Multiplier "ln") 60 | "Multiplys the point, P, by the given scalar Multiplier 61 | 62 | @param P The point as a (X,Y) list 63 | @param Multiplier A scalar to multiply with both the X and Y" 64 | list(xCoord(P)*Multiplier yCoord(P)*Multiplier) 65 | ) 66 | 67 | procedure(ptProductVector(P Multiplier "ll") 68 | "Multiplys the point, P, by the given scalar Multiplier 69 | 70 | @param P The point as a (X,Y) list 71 | @param Multiplier An (X,Y) list containing seperate multipliers for each 72 | coordinate" 73 | list(xCoord(P)*xCoord(Multiplier) yCoord(P)*yCoord(Multiplier)) 74 | ) 75 | 76 | procedure(ptDistance(P1 P2 "ll") 77 | "Calculates the distance between 2 points." 78 | sqrt(expt(xCoord(P2) - xCoord(P1) 2) + expt(yCoord(P2) - yCoord(P1) 2))) 79 | 80 | procedure( ptInBoxp( point box "ll") 81 | "Checks if the point is inside the bounding box or on the box edge. 82 | 83 | @return inside_box a boolean" 84 | xCoord(point) >= leftEdge(box)&& 85 | xCoord(point) <= rightEdge(box) && 86 | yCoord(point) >= bottomEdge(box) && 87 | yCoord(point) <= topEdge(box) 88 | ) 89 | 90 | procedure( ptNearestBoxSide(point box "ll") 91 | "Finds the nearest side of the box to the reference point 92 | @return side A string, 'left', 'right', 'top', or 'bottom'" 93 | let( (x1 y1 x2 y2 x y d1 d2 d3 d4 minDist side) 94 | 95 | x1 = caar( box ) 96 | y1 = cadar( box ) 97 | x2 = caadr( box ) 98 | y2 = cadadr( box ) 99 | 100 | x = xCoord( point ) 101 | y = yCoord( point ) 102 | 103 | if(ptInBoxp(point box) then 104 | d1 = x - x1 ; left distance 105 | d2 = y - y1 ; bottom distance 106 | d3 = x2 - x ; right distance 107 | d4 = y2 - y ; top distance 108 | 109 | minDist = min( d1 d2 d3 d4 ) 110 | cond( 111 | ( minDist == d1 side = "left") 112 | ( minDist == d2 side = "bottom") 113 | ( minDist == d3 side = "right") 114 | ( minDist == d4 side = "top") 115 | ) 116 | else 117 | ; point is outside the box. 118 | ; A diagnol line between each corner and the center splits the sides 119 | cond( 120 | ( x > max(x1 x2) ; To the right of the box 121 | side = "right" 122 | cond( 123 | ( y > max(y1 y2) && 124 | (y - max(y1 y2)) > (x - max(x1 x2)) 125 | side = "top" 126 | ) 127 | ( y < min(y1 y2) && 128 | (min(y1 y2) - y) > (x - max(x1 x2)) 129 | side = "bottom" 130 | ) 131 | ) 132 | ) 133 | ( x < min(x1 x2) ; To the left of the box 134 | side = "left" 135 | cond( 136 | ( y > max(y1 y2) && 137 | (y - max(y1 y2)) > (min(x1 x2) - x) 138 | side = "top" 139 | ) 140 | ( y < min(y1 y2) && 141 | (min(y1 y2) - y) > (min(x1 x2) - x) 142 | side = "bottom" 143 | ) 144 | ) 145 | ) 146 | ( t ; straight above or below the box 147 | cond( 148 | ( y > max(y1 y2) side = "top" ) 149 | ( y < min(y1 y2) side = "bottom" ) 150 | ) 151 | ) 152 | ) 153 | ) 154 | side 155 | )) 156 | 157 | procedure(ptSnapToGrid( pt grid @optional noLarger) 158 | "Snap the point to the grid" 159 | numSnapToGrid(xCoord(pt) grid noLarger): 160 | numSnapToGrid(yCoord(pt) grid noLarger) 161 | ) 162 | 163 | 164 | procedure(numSnapToGrid( number grid @optional noLarger) 165 | "Snap a number to the grid" 166 | let((out) 167 | if( noLarger then 168 | out = fix(abs(number)/grid) * grid 169 | else 170 | out = round(abs(number)/grid) * grid 171 | ) 172 | when(negativep(number) ; negative number 173 | out = -out 174 | ) 175 | )) 176 | 177 | 178 | ;;;;;;;;;;;;;;;;;; 179 | ; Bounding Boxes ; 180 | ;;;;;;;;;;;;;;;;;; 181 | ; Built-in Functions: 182 | ; * isBBox 183 | ; * dbTransformBBox 184 | ; * lowerLeft 185 | ; * upperRight 186 | ; * centerBox 187 | ; * leftEdge 188 | ; * rightEdge 189 | ; * bottomEdge 190 | ; * topEdge 191 | 192 | procedure(bBoxOffset(box offset) 193 | "Offset the given bounding box" 194 | list(ptOffset(lowerLeft(box) offset) 195 | ptOffset(upperRight(box) offset)) 196 | ) 197 | 198 | procedure(bBoxSnapToGrid(box grid) 199 | "Snap the bBox to the grid" 200 | list(ptSnapToGrid(lowerLeft(box) grid) 201 | ptSnapToGrid(upperRight(box) grid)) 202 | ) 203 | 204 | procedure(bBoxFromSize(size "l") 205 | "Returns the bounding box with the origin at the center from the size 206 | of the bounding box. 207 | @param size: A (x,y) list of the size of the bounding box." 208 | list(list(-xCoord(size)/2 -yCoord(size)/2) 209 | list( xCoord(size)/2 yCoord(size)/2)) 210 | ) 211 | 212 | procedure(bBoxSize(box "l") 213 | "Calculate the Size of the bounding box 214 | @param box A bounding box list 215 | @return size Size of the bounding box as a (x,y) list" 216 | list(rightEdge(box)-leftEdge(box) 217 | topEdge(box)-bottomEdge(box)) 218 | ) 219 | 220 | ;;;;;;;;;;;;;;;;;;; 221 | ; Figure Overlaps ; 222 | ;;;;;;;;;;;;;;;;;;; 223 | ; Built-in Functions: 224 | ; * dbProduceOverlap 225 | ; * dbGetOverlaps 226 | ; * dbGetTrueOverlaps 227 | 228 | procedure(figFlattenOverlaps(obj "R") 229 | "Flattens a hierarchial list of dbObjects returned by the figure overlap 230 | functions i.e. dbGetOverlaps, etc 231 | @param obj A hierarchial list of obects returned by dbGetOverlaps" 232 | let((out) 233 | if( atom(obj) && dbobjectp(obj) then 234 | ncons(obj) 235 | else 236 | ;mapcan( 'figFlattenOverlaps obj ) 237 | out = list() 238 | foreach(singleObj obj 239 | out=append(out figFlattenOverlaps(singleObj)) 240 | ) 241 | out 242 | ) 243 | )) 244 | 245 | ;;;;;;;;;;; 246 | ; Mosaics ; 247 | ;;;;;;;;;;; 248 | 249 | procedure(mosFlatten(cv) 250 | "Flattens all the mosaics up to 5 levels deep in the cell's hierarchy 251 | @param cv Cellview object 252 | @return 't" 253 | foreach( inst cv~>mosaics 254 | leFlattenInst(inst 5) 255 | ) 256 | 't 257 | ) 258 | 259 | list(nil 260 | 'ptOffset ptOffset 261 | 'ptNeg ptNeg 262 | 'ptProduct ptProduct 263 | 'ptDistance ptDistance 264 | 'ptInBoxp ptInBoxp 265 | 'ptNearestBoxSide ptNearestBoxSide 266 | 'ptSnapToGrid ptSnapToGrid 267 | 'bBoxOffset bBoxOffset 268 | 'bBoxSnapToGrid bBoxSnapToGrid 269 | 'bBoxFromSize bBoxFromSize 270 | 'bBoxSize bBoxSize 271 | 'figFlattenOverlaps figFlattenOverlaps 272 | 'mosFlatten mosFlatten 273 | ) 274 | ) 275 | 276 | VrtImport['Module]->New('Geo 277 | ?module Geo 278 | ?package VrtImport['Virtue] 279 | ?description module_description) 280 | ) 281 | -------------------------------------------------------------------------------- /virtue/GeoPoints.ils: -------------------------------------------------------------------------------- 1 | ; Point Geometry Module 2 | ; ------------------------- 3 | ; Built-in Point Functions: 4 | ; * isLocation 5 | ; * xCoord 6 | ; * yCoord 7 | ; * dbTransformPoint 8 | ; * dbTransformPointList 9 | ; * dbConcatTransform 10 | 11 | let(((Geo VrtImport('Geo)) 12 | (Dpl VrtImport('Dpl)) 13 | ) 14 | 15 | procedure(ptOffset(P offset "ll") 16 | "Offset a point or sum 2 points" 17 | list(xCoord(P)+xCoord(offset) 18 | yCoord(P)+yCoord(offset)) 19 | ) 20 | 21 | procedure(ptNeg(P "l") 22 | "Negates both the x and Y of the given point" 23 | list(-xCoord(P) -yCoord(P)) 24 | ) 25 | 26 | procedure(ptProduct(P Multiplier "lg") 27 | "Multiplies the point, P, by the given Multiplier 28 | 29 | @param P The point as a (X,Y) list 30 | @param Multiplier Either a scalar to multiply with both the X and Y or 31 | an (X,Y) list containing seperate multipliers for each coordinate" 32 | cond( 33 | (floatp(Multiplier) || integerp(Multiplier) 34 | ptProductScalar(P Multiplier)) 35 | (isLocation(Multiplier) 36 | ptProductVector(P Multiplier)) 37 | ('t 38 | error("Multiplier type is not supported"))) 39 | ) 40 | 41 | procedure(ptProductScalar(P Multiplier "ln") 42 | "Multiplys the point, P, by the given scalar Multiplier 43 | 44 | @param P The point as a (X,Y) list 45 | @param Multiplier A scalar to multiply with both the X and Y" 46 | list(xCoord(P)*Multiplier yCoord(P)*Multiplier) 47 | ) 48 | 49 | procedure(ptProductVector(P Multiplier "ll") 50 | "Multiplys the point, P, by the given scalar Multiplier 51 | 52 | @param P The point as a (X,Y) list 53 | @param Multiplier An (X,Y) list containing seperate multipliers for each 54 | coordinate" 55 | list(xCoord(P)*xCoord(Multiplier) yCoord(P)*yCoord(Multiplier)) 56 | ) 57 | 58 | procedure(ptDistance(P1 P2 "ll") 59 | "Calculates the distance between 2 points." 60 | sqrt(expt(xCoord(P2) - xCoord(P1) 2) + expt(yCoord(P2) - yCoord(P1) 2))) 61 | 62 | procedure( ptInBoxp( point box "ll") 63 | "Checks if the point is inside the bounding box or on the box edge. 64 | 65 | @return inside_box a boolean" 66 | xCoord(point) >= leftEdge(box)&& 67 | xCoord(point) <= rightEdge(box) && 68 | yCoord(point) >= bottomEdge(box) && 69 | yCoord(point) <= topEdge(box) 70 | ) 71 | 72 | procedure( ptNearestBoxSide(point box "ll") 73 | "Finds the nearest side of the box to the reference point 74 | @return side A string, 'left', 'right', 'top', or 'bottom'" 75 | let((x1 y1 x2 y2 x y d1 d2 d3 d4 minDist side) 76 | 77 | x1 = caar( box ) 78 | y1 = cadar( box ) 79 | x2 = caadr( box ) 80 | y2 = cadadr( box ) 81 | 82 | x = xCoord( point ) 83 | y = yCoord( point ) 84 | 85 | if( Geo->ptInBoxp(point box) then 86 | d1 = x - x1 ; left distance 87 | d2 = y - y1 ; bottom distance 88 | d3 = x2 - x ; right distance 89 | d4 = y2 - y ; top distance 90 | 91 | minDist = min( d1 d2 d3 d4 ) 92 | cond( 93 | ( minDist == d1 side = "left") 94 | ( minDist == d2 side = "bottom") 95 | ( minDist == d3 side = "right") 96 | ( minDist == d4 side = "top") 97 | ) 98 | else 99 | ; point is outside the box. 100 | ; A diagnol line between each corner and the center splits the sides 101 | cond( 102 | ( x > max(x1 x2) ; To the right of the box 103 | side = "right" 104 | cond( 105 | ( y > max(y1 y2) && 106 | (y - max(y1 y2)) > (x - max(x1 x2)) 107 | side = "top" 108 | ) 109 | ( y < min(y1 y2) && 110 | (min(y1 y2) - y) > (x - max(x1 x2)) 111 | side = "bottom" 112 | ) 113 | ) 114 | ) 115 | ( x < min(x1 x2) ; To the left of the box 116 | side = "left" 117 | cond( 118 | ( y > max(y1 y2) && 119 | (y - max(y1 y2)) > (min(x1 x2) - x) 120 | side = "top" 121 | ) 122 | ( y < min(y1 y2) && 123 | (min(y1 y2) - y) > (min(x1 x2) - x) 124 | side = "bottom" 125 | ) 126 | ) 127 | ) 128 | ( t ; straight above or below the box 129 | cond( 130 | ( y > max(y1 y2) side = "top" ) 131 | ( y < min(y1 y2) side = "bottom" ) 132 | ) 133 | ) 134 | ) 135 | ) 136 | side 137 | )) 138 | 139 | procedure(ptSnapToGrid( pt grid @optional noLarger) 140 | "Snap the point to the grid" 141 | numSnapToGrid(xCoord(pt) grid noLarger): 142 | numSnapToGrid(yCoord(pt) grid noLarger) 143 | ) 144 | 145 | 146 | procedure(numSnapToGrid( number grid @optional noLarger) 147 | "Snap a number to the grid" 148 | let((out) 149 | if( noLarger then 150 | out = fix(abs(number)/grid) * grid 151 | else 152 | out = round(abs(number)/grid) * grid 153 | ) 154 | when(negativep(number) ; negative number 155 | out = -out 156 | ) 157 | )) 158 | 159 | Geo = Dpl->Cat(Geo list(nil 160 | 'ptOffset ptOffset 161 | 'ptNeg ptNeg 162 | 'ptProduct ptProduct 163 | 'ptDistance ptDistance 164 | 'ptInBoxp ptInBoxp 165 | 'ptNearestBoxSide ptNearestBoxSide 166 | 'ptSnapToGrid ptSnapToGrid 167 | )) 168 | ) 169 | -------------------------------------------------------------------------------- /virtue/HdbConfig.ils: -------------------------------------------------------------------------------- 1 | let((Hdb 2 | (module_description "Hierarchy database functions for config views") 3 | (Module VrtImport['Module]) 4 | ) 5 | 6 | Hdb = let(() 7 | 8 | procedure(CreateConfigView(configLCV topCellLCV libraryList viewList stopList) 9 | "Creates a config view in a given lib-cell-view. Sets the top cell 10 | lib-cell-view. Sets the config library list, view list, and stop list in 11 | the global binding section. 12 | 13 | @brief Creates a config view 14 | @param configLCV (list(str str str)): A list containing the library, cell, and view name of where the config will be created. 15 | @param topCellLCV (list(str str str)): A list containing the library, cell, and view name of the Top Cell in the created config 16 | @param libraryList (str): String of library names separated by spaces. 17 | @param viewList (str): String of view names separated by spaces. 18 | @param stopList(str): String of stops separated by spaces. 19 | @return 't if successful, nil otherwise" 20 | prog((configId result) 21 | configId = hdbOpen(nth(0 configLCV) nth(1 configLCV) nth(2 configLCV) "w") 22 | unless(result return(nil)) 23 | result = hdbSetTopCellViewName(configId nth(0 topCellLCV) nth(1 topCellLCV) nth(2 topCellLCV)) 24 | unless(result return(nil)) 25 | result = hdbSetDefaultLibListString(configId libraryList) 26 | unless(result return(nil)) 27 | result = hdbSetDefaultViewListString(configId viewList) 28 | unless(result return(nil)) 29 | result = hdbSetDefaultStopListString(configId stopList) 30 | result = hdbSave(configId) 31 | unless(result return(nil)) 32 | result = hdbClose(configId) 33 | return(result) 34 | )) 35 | 36 | list(nil 37 | 'CreateConfigView CreateConfigView 38 | )) 39 | 40 | Module->New('Hdb 41 | ?module Hdb 42 | ?package VrtImport['Virtue] 43 | ?description module_description) 44 | ) 45 | -------------------------------------------------------------------------------- /virtue/MaeExport.ils: -------------------------------------------------------------------------------- 1 | ; Exports information about a Maestro view 2 | let((MaeExport 3 | (Module VrtImport['Module]) 4 | (Toml VrtImport['Toml]) 5 | ) 6 | 7 | MaeExport = let(() 8 | 9 | procedure(ExportMaestroHistoryInfoTable(sessionName hsdb filePath "t") 10 | let(() 11 | Toml->Write(BuildMaestroHistoryInfoTable(sessionName hsdb) 12 | filePath) 13 | )) 14 | 15 | procedure(BuildMaestroHistoryInfoTable(sessionName historyEntry "tx") 16 | let((table) 17 | table = makeTable(strcat("test_bench_info_" sessionName)) 18 | table["history"] = BuildHistoryInfoTable(historyEntry) 19 | table["maestro"] = BuildMaestroInfoTable(sessionName) 20 | table["maestro.tests"] = BuildAllTestInfoTables(sessionName) 21 | table 22 | )) 23 | 24 | procedure(BuildHistoryInfoTable(historyEntry "gt") 25 | let((table) 26 | table = makeTable("history") 27 | table["name"] = axlGetHistoryName(historyEntry) 28 | table 29 | )) 30 | 31 | procedure(BuildMaestroInfoTable(sessionName "t") 32 | let((table) 33 | table = makeTable(strcat("maestro_info_" sessionName)) 34 | table["library"] = axlGetSessionLibName(sessionName) 35 | table["cell"] = axlGetSessionCellName(sessionName) 36 | table["view"] = axlGetSessionViewName(sessionName) 37 | table 38 | )) 39 | 40 | procedure(TestInfoTable(sessionName testName "tt") 41 | let((table config test) 42 | "Reads a test's info into a table" 43 | table = makeTable(testName) 44 | test = maeGetTestSession(testName ?session sessionName) 45 | table["design_library"] = asiGetDesignLibName(test) 46 | table["design_cell"] = asiGetDesignCellName(test) 47 | table["design_view"] = asiGetDesignViewName(test) 48 | if(hdbIsConfig(table["design_library"] table["design_cell"] table["design_view"]) then 49 | table["schematic_library"] = table["design_library"] 50 | table["schematic_cell"] = table["design_cell"] 51 | table["schematic_view"] = table["design_view"] 52 | config = hdbOpen(table["design_library"] table["design_cell"] table["design_view"] "r") 53 | table["config_library"] = hdbGetTopLibName(config) 54 | table["config_cell"] = hdbGetTopCellName(config) 55 | table["config_view"] = hdbGetTopViewName(config) 56 | else 57 | table["schematic_library"] = table["design_library"] 58 | table["schematic_cell"] = table["design_cell"] 59 | table["schematic_view"] = table["design_view"] 60 | ) 61 | table 62 | )) 63 | 64 | procedure(BuildAllTestInfoTables(sessionName) 65 | let((table tests) 66 | tests = maeGetSetup(?typeName "tests" 67 | ?enabled t 68 | ?session sessionName) 69 | table = makeTable("tests") 70 | foreach(test tests 71 | table[test] = TestInfoTable(sessionName test) 72 | ) 73 | table 74 | )) 75 | 76 | list(nil 77 | 'ExportMaestroHistoryInfoTable ExportMaestroHistoryInfoTable 78 | 'BuildMaestroHistoryInfoTable BuildMaestroHistoryInfoTable 79 | )) 80 | 81 | Module->New('MaeExport 82 | ?module MaeExport 83 | ?package VrtImport['Virtue]) 84 | ) 85 | -------------------------------------------------------------------------------- /virtue/MaeMaestro.ils: -------------------------------------------------------------------------------- 1 | let((Maestro 2 | (Module VrtImport['Module]) 3 | ) 4 | 5 | ; Skill Functions for Maestro views 6 | Maestro = let(() 7 | 8 | procedure(ExportCellImage(lib cell view filepath) let((win (out nil)) 9 | win = geOpen(?lib lib ?cell cell ?view view ?mode "r") 10 | when(win 11 | out = hiExportImage( 12 | ?fileName filepath 13 | ?window win 14 | ?exportRegion 'entireDesign 15 | ?bgColor "white" 16 | ?fgColor "black" 17 | ?colorType 'biColor 18 | ?windowProps list( list("drawAxesOn" nil) list("drawGridOn" nil)) 19 | ?width 2048) 20 | hiCloseWindow(win) 21 | ) 22 | out 23 | )) 24 | 25 | ; Returns the test's design LCV (Library, Cell, View) information 26 | ; If the view is a config, then it also has the LCV of the top-level cell of the config. 27 | procedure(GetTestDesign(sessionName testName) let((test out config) 28 | "Returns the test's design LCV (Library, Cell, View) information. 29 | If the view is a config, then it also has the LCV of the top-level cell of the config." 30 | test = maeGetTestSession(testName ?session sessionName) 31 | out = list(asiGetDesignLibName(test) asiGetDesignCellName(test) asiGetDesignViewName(test)) 32 | ; Find the config view 33 | if(hdbIsConfig(car(out) cadr(out) caddr(out)) then 34 | config = hdbOpen(car(out) cadr(out) caddr(out) "r") 35 | out = append(out list(hdbGetTopLibName(config) hdbGetTopCellName(config) hdbGetTopViewName(config))) 36 | hdbClose(config) 37 | ) 38 | out 39 | )) 40 | 41 | 42 | 43 | procedure(GetEnabledAnalysisNames(testSession) let((out) 44 | foreach(analysis asiGetEnabledAnalysisList(testSession) 45 | out = append1(out symbolToString(asiGetAnalysisName(analysis))) 46 | ) 47 | out 48 | )) 49 | 50 | list(nil 51 | 'ExportCellImage ExportCellImage 52 | 'GetTestDesign GetTestDesign 53 | 'GetEnabledAnalysisNames GetEnabledAnalysisNames 54 | ) 55 | ) 56 | 57 | Module->New('Maestro 58 | ?module Maestro 59 | ?package VrtImport['Virtue]) 60 | ) 61 | -------------------------------------------------------------------------------- /virtue/SchUtility.py: -------------------------------------------------------------------------------- 1 | def find_all_nets_at_heir_level(workspace, hier_path, top_lib, top_cell, top_view="schematic"): 2 | """Get list of nets at a certrain hierarchical level in the given lib-cell-veiw. 3 | 4 | Args: 5 | workspace (Workspace): A open Skillbridge Workspace object, connected to a specified project virtuoso session. 6 | hier_path (str): The hierarchical level path to search for nets. (example: '/XDUT/' or '/' or '/XDUT/XDUT/') 7 | top_lib (str): The top library name of the view being parsed. 8 | top_cell (str): The top cell name of the view being parsed. 9 | top_view (:obj:`str`, optional): The top view name of the view being parsed. Defaults to 'schematic'. 10 | 11 | Returns: 12 | list: List of nets found in the given top lib-cell-view at the hierarchical level. 13 | """ 14 | return workspace['SchFindAllNetsAtHierLevel'](hier_path, topLib=top_lib, topCell=top_cell, topView=top_view) 15 | -------------------------------------------------------------------------------- /virtue/Skillbridge.ils: -------------------------------------------------------------------------------- 1 | let((Skillbridge 2 | (module_description "Skillbridge interface")) 3 | 4 | Skillbridge = let(() 5 | procedure(GetID() 6 | strcat( getShellEnvVar("PROJ_ID") "-" getShellEnvVar("USER")) 7 | ) 8 | 9 | procedure(IsInstalled() 10 | procedurep(fboundp('pyStartServer)) 11 | ) 12 | 13 | procedure(StartPythonServer() 14 | "Start the Skillbridge Python server" 15 | let(((server_id GetID())) 16 | if(IsInstalled() then 17 | if( pyStartServer(?id server_id) then 18 | fprintf(poport, "Skill Bridge server started successfully, id = '%s'.\n" server_id) 19 | t 20 | else 21 | fprintf(poport, "Skill Bridge server was unable to be started, id = '%s'\n." server_id) 22 | nil 23 | ) 24 | else 25 | error("Cannot start the Skillbridge server. It is not installed.") 26 | ) 27 | )) 28 | 29 | procedure(KillPythonServer() 30 | ; Add id correspond to the project that is open 31 | pyKillServer() 32 | ) 33 | 34 | procedure(TogglePythonServerLmgrCallback(menu_item_name _lib _cell _view _file _cat) 35 | let(((menu_item lmgrGetObject(menu_item_name)) server_running) 36 | server_running = cadr(assoc("state" menu_item)) 37 | if(!server_running then 38 | KillPythonServer() 39 | menu_item->state = 't 40 | lmgrSetObject(menu_item_name list(list("label" "Start Skillbridge Server"))) 41 | else 42 | StartPythonServer() 43 | menu_item->state = nil 44 | lmgrSetObject(menu_item_name list(list("label" "Kill Skillbridge Server"))) 45 | ) 46 | )) 47 | 48 | procedure(ReloadPythonServer() 49 | pyReloadServer()) 50 | 51 | procedure(ShowLog(@optional length "x") 52 | if(length then 53 | pyShowLog(length) 54 | else 55 | pyShowLog() 56 | ) 57 | ) 58 | 59 | procedure(RunPythonScript(@rest rest_args) 60 | apply(pyRunScript rest_args) 61 | ) 62 | 63 | procedure(OpenDocumentation() 64 | system("firefox https://unihd-cag.github.io/skillbridge/ &") 65 | ) 66 | 67 | ; Callback to open the SkyVer help from the library manager window 68 | procedure(OpenDocumentationLmgrCallback(_menuItem _lib _cell _view _file _cat) 69 | OpenDocumentation() 70 | ) 71 | 72 | procedure(Initialize() 73 | unless(IsInstalled() 74 | lmgrSensitizeMenuItems('("SkillBridgeToggle") nil)) 75 | ) 76 | 77 | list(nil 78 | 'Initialize Initialize 79 | 'TogglePythonServerLmgrCallback TogglePythonServerLmgrCallback 80 | 'OpenDocumentationLmgrCallback OpenDocumentationLmgrCallback 81 | 'IsInstalled IsInstalled 82 | )) 83 | 84 | VrtImport['Module]->New('Skillbridge 85 | ?module Skillbridge 86 | ?package VrtImport['Virtue] 87 | ?description module_description) 88 | Skillbridge->Initialize() 89 | 90 | ) 91 | 92 | ; Global functions required for library manager 93 | VrtSkillbridgeTogglePythonServerLmgrCallback = 94 | VrtImport['Skillbridge]->TogglePythonServerLmgrCallback 95 | VrtSkillbridgeOpenDocumentationLmgrCallback = 96 | VrtImport['Skillbridge]->OpenDocumentationLmgrCallback 97 | -------------------------------------------------------------------------------- /virtue/Toml.ils: -------------------------------------------------------------------------------- 1 | let((Toml 2 | (module_description "Reads and writes 3 | Tom's Obvious Markup Language (.toml) configuration 4 | files. https://toml.io/en/") 5 | (Str VrtImport['Str]) 6 | (Module VrtImport['Module]) 7 | ) 8 | 9 | Toml = let(() 10 | 11 | ; Reads a toml file and returns it's contents as a SKILL table 12 | procedure(ReadFile(filePath "t") 13 | "Reads a toml file and returns its contents as a SKILL table 14 | @brief Reads in a TOML file as a table 15 | @param filePath The path to the file 16 | @return toml_table A table with the top-level entries of the toml file." 17 | prog((root_table currentTable inputPort line name) 18 | unless(isFile(filePath) 19 | return()) 20 | 21 | root_table = makeTable(filePath) 22 | currentTable = root_table 23 | 24 | inputPort = infile(filePath) 25 | while(gets(line, inputPort) 26 | line = car(parseString(line "#")) ; Strip line comments 27 | line = Str->trimWhiteSpace(line) 28 | cond( 29 | (blankstrp(line) nil) ; empty or comment-only line 30 | (TableName(line) 31 | name = TableName(line) 32 | currentTable=makeTable(name) 33 | root_table[name]=currentTable 34 | ) 35 | (GetKey(line) 36 | currentTable[GetKey(line)] = GetValue(line)) 37 | )) 38 | return(root_table) 39 | )) 40 | 41 | procedure(TableName(line) 42 | "returns either the table name or nil if the line is not a table declaration line ([table_name])" 43 | if((substring(line 1 1) == "[") && (substring(line length(line) 1) == "]") then 44 | substring(line 2 length(line)-2) 45 | else 46 | nil 47 | )) 48 | 49 | procedure(GetKey(line) 50 | prog((key_value_list) 51 | key_value_list = parseString(line "=") 52 | if(length(key_value_list) == 2 then 53 | return(Str->trimWhiteSpace(car(key_value_list))) 54 | else 55 | return()) 56 | )) 57 | 58 | procedure(GetValue(line) 59 | let((value) 60 | value = Str->trimWhiteSpace(cadr(parseString(line "="))) 61 | when((substring(value 1 1) == "\"") && (substring(value length(value) 1) == "\"") 62 | value = substring(value 2 length(value)-2)) 63 | value = Str->convertNumber(value) 64 | value 65 | )) 66 | 67 | procedure(WriteFile(filePath input "tg") 68 | "Writes a table or association list to a toml file. 69 | @param filePath The path where the toml file will be saved 70 | @param input A table or association list to be written to the toml file" 71 | let((file) 72 | file = outfile(filePath) 73 | cond( 74 | (tablep(input) 75 | PrintTable(file input)) 76 | (listp(input) 77 | PrintAssocList(file input)) 78 | ) 79 | close(file) 80 | )) 81 | 82 | procedure(PrintTable(file table @key (name nil)) 83 | when(name 84 | fprintf(file "\n[%s]\n" name)) 85 | foreach( key table 86 | PrintKey(file key table[key] ?name name) 87 | ) 88 | ) 89 | 90 | procedure(PrintAssocList(file assocList) 91 | let((key value) 92 | foreach( item assocList 93 | key = car(item) 94 | value = cadr(item) 95 | PrintKey(file key value) 96 | ) 97 | )) 98 | 99 | procedure(PrintKey(file key value @key name) 100 | let(((subtables nil) subtable_name) 101 | cond( 102 | (stringp(key) && !tablep(value) 103 | fprintf(file "%s = %L\n" key value)) 104 | (!tablep(value) 105 | fprintf(file "%L = %L\n" key value)) 106 | (tablep(value) 107 | subtables = append1(subtables list(key value))) 108 | ) 109 | when(subtables 110 | foreach(subtableItem subtables 111 | if(name then 112 | subtable_name = sprintf(nil "%s.%s" name car(subtableItem)) 113 | else 114 | subtable_name = car(subtableItem) 115 | ) 116 | PrintTable(file value ?name subtable_name) 117 | )) 118 | )) 119 | 120 | list(nil 121 | 'ReadFile ReadFile 122 | 'WriteFile WriteFile 123 | )) 124 | 125 | Module->New('Toml 126 | ?module Toml 127 | ?package VrtImport['Virtue] 128 | ?description module_description) 129 | ) 130 | -------------------------------------------------------------------------------- /virtue/__init__.py: -------------------------------------------------------------------------------- 1 | """Cadence Virtuoso SKILL and Python library""" 2 | import pluggy 3 | 4 | from .SchUtility import * 5 | from .ConfigUtility import * 6 | from .skill_package.metadata_data import SKillPackageMetadata 7 | 8 | __version__ = '0.7.0' 9 | 10 | hookimpl = pluggy.HookimplMarker("virtue") 11 | -------------------------------------------------------------------------------- /virtue/calculator/VrtAnalog2Decimal.il: -------------------------------------------------------------------------------- 1 | procedure(VrtAnalog2DecimalBit(in) 2 | "Converts an analog input into a digital 1 or 0 in decimal format." 3 | if(in > 0.9 then 4 | 1 5 | else 6 | 0 7 | ) 8 | ) 9 | 10 | procedure(VrtAnalog2Decimal(@rest inputs) 11 | "Converts a set of analog bits into a digital decimal number" 12 | let(((out_decimal 0) (n 0)) 13 | inputs = reverse(inputs) 14 | for(n 0 (length(inputs)-1) 15 | out_decimal = out_decimal + (2**n) * VrtAnalog2DecimalBit(nth(n inputs)) 16 | ) 17 | out_decimal 18 | )) 19 | 20 | procedure(VrtDecimal2Hex(input @optional (prefix 't)) 21 | "Converts a decimal between 0 and 15 into a hex digit (string) 22 | When the optional prefix input is true (default) then '0x ' will be prepended to the hex digit. 23 | A space is included between '0x' and the hex output since otherwise " 24 | let((out_hex) 25 | if(input < 16 then 26 | out_hex = upperCase(sprintf(nil "%x" input)) 27 | else 28 | out_hex = upperCase(sprintf(nil "%x" (input-mod(input 16))/16 )) 29 | out_hex = strcat(out_hex upperCase(sprintf(nil "%x" mod(input 16)))) 30 | ) 31 | if(prefix then 32 | ; A sm 33 | out_hex = strcat("0x " out_hex) 34 | ) 35 | out_hex 36 | )) 37 | 38 | procedure(VrtAnalog2Hex(@rest inputs) 39 | "Converts 4 analog bits into a hex digit. (string) 40 | When the optional prefix input is true (default) then 0x will be prepended to the hex digit." 41 | VrtDecimal2Hex(apply('VrtAnalog2Decimal inputs)) 42 | ) -------------------------------------------------------------------------------- /virtue/calculator/VrtAnalog2DecimalBit.il: -------------------------------------------------------------------------------- 1 | procedure(VrtAnalog2DecimalBit(in) 2 | "Converts an analog input into a digital 1 or 0 in decimal format. 3 | The theshold for a 1 is > 0.9v" 4 | if(in > 0.9 then 5 | 1 6 | else 7 | 0 8 | ) 9 | ) 10 | 11 | ;;; GUI builder information ;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ocnmRegGUIBuilder( 13 | '(nil 14 | function VrtAnalog2DecimalBit 15 | name VrtAnalog2DecimalBit 16 | description "Converts an analog input into a digital 1 or 0 in decimal format." 17 | category ("Special Functions" "Digital") 18 | analysis (nil 19 | general (nil 20 | args (signal) 21 | signals (nil 22 | signal (nil 23 | prompt "Signal" 24 | tooltip "Signal to convert to a digital integer") 25 | ) 26 | inputrange t 27 | ) 28 | ) 29 | outputs (result) 30 | )) 31 | -------------------------------------------------------------------------------- /virtue/calculator/VrtAnalog2Hex.il: -------------------------------------------------------------------------------- 1 | procedure(VrtAnalog2DecimalBit(in) 2 | "Converts an analog input into a digital 1 or 0 in decimal format." 3 | if(in > 0.9 then 4 | 1 5 | else 6 | 0 7 | ) 8 | ) 9 | 10 | procedure(VrtAnalog2Decimal(@rest inputs) 11 | "Converts a set of analog bits into a digital decimal number" 12 | let(((out_decimal 0) (n 0)) 13 | inputs = reverse(inputs) 14 | for(n 0 (length(inputs)-1) 15 | out_decimal = out_decimal + (2**n) * VrtAnalog2DecimalBit(nth(n inputs)) 16 | ) 17 | out_decimal 18 | )) 19 | 20 | procedure(VrtDecimal2Hex(input @optional (prefix 't)) 21 | "Converts a decimal between 0 and 15 into a hex digit (string) 22 | When the optional prefix input is true (default) then '0x ' will be prepended to the hex digit. 23 | A space is included between '0x' and the hex output since otherwise " 24 | let((out_hex) 25 | if(input < 16 then 26 | out_hex = upperCase(sprintf(nil "%x" input)) 27 | else 28 | out_hex = upperCase(sprintf(nil "%x" (input-mod(input 16))/16 )) 29 | out_hex = strcat(out_hex upperCase(sprintf(nil "%x" mod(input 16)))) 30 | ) 31 | if(prefix then 32 | ; A sm 33 | out_hex = strcat("0x " out_hex) 34 | ) 35 | out_hex 36 | )) 37 | 38 | procedure(VrtAnalog2Hex(@rest inputs) 39 | "Converts 4 analog bits into a hex digit. (string) 40 | When the optional prefix input is true (default) then 0x will be prepended to the hex digit." 41 | VrtDecimal2Hex(apply('VrtAnalog2Decimal inputs)) 42 | ) -------------------------------------------------------------------------------- /virtue/calculator/VrtDecimal2Hex.il: -------------------------------------------------------------------------------- 1 | procedure(VrtAnalog2DecimalBit(in) 2 | "Converts an analog input into a digital 1 or 0 in decimal format." 3 | if(in > 0.9 then 4 | 1 5 | else 6 | 0 7 | ) 8 | ) 9 | 10 | procedure(VrtAnalog2Decimal(@rest inputs) 11 | "Converts a set of analog bits into a digital decimal number" 12 | let(((out_decimal 0) (n 0)) 13 | inputs = reverse(inputs) 14 | for(n 0 (length(inputs)-1) 15 | out_decimal = out_decimal + (2**n) * VrtAnalog2DecimalBit(nth(n inputs)) 16 | ) 17 | out_decimal 18 | )) 19 | 20 | procedure(VrtDecimal2Hex(input @optional (prefix 't)) 21 | "Converts a decimal between 0 and 15 into a hex digit (string) 22 | When the optional prefix input is true (default) then '0x ' will be prepended to the hex digit. 23 | A space is included between '0x' and the hex output since otherwise " 24 | let((out_hex) 25 | if(input < 16 then 26 | out_hex = upperCase(sprintf(nil "%x" input)) 27 | else 28 | out_hex = upperCase(sprintf(nil "%x" (input-mod(input 16))/16 )) 29 | out_hex = strcat(out_hex upperCase(sprintf(nil "%x" mod(input 16)))) 30 | ) 31 | if(prefix then 32 | ; A sm 33 | out_hex = strcat("0x " out_hex) 34 | ) 35 | out_hex 36 | )) 37 | 38 | procedure(VrtAnalog2Hex(@rest inputs) 39 | "Converts 4 analog bits into a hex digit. (string) 40 | When the optional prefix input is true (default) then 0x will be prepended to the hex digit." 41 | VrtDecimal2Hex(apply('VrtAnalog2Decimal inputs)) 42 | ) 43 | 44 | procedure(VrtAnalog2Hex_noprefix(@rest inputs) 45 | "Converts 4 analog bits into a hex digit. (string) 46 | When the optional prefix input is true (default) then 0x will be prepended to the hex digit. 47 | Warning!: inputing 8 0s gives '0' rather than '00' which can be a problem when the number of 48 | bits needs to be maintained such as when using this to process the lower 8 bits of a number that is more than 8 bits long" 49 | Decimal2Hex(apply('Analog2Decimal inputs) nil) 50 | ) -------------------------------------------------------------------------------- /virtue/cdslib.py: -------------------------------------------------------------------------------- 1 | def include_cdslib(cds_path, include_file_path, soft=True): 2 | """ 3 | Includes another cds.lib file in the current cds.lib file. 4 | 5 | :param cds_path: Path where the file is t 6 | :param lib_dict: A dictionary with the name of the library as the key and the path as the value. 7 | :return: True if successful 8 | """ 9 | if not soft: 10 | line = "SOFTINCLUDE" + include_file_path 11 | else: 12 | line = "INCLUDE" + include_file_path 13 | with open(cds_path,"w") as file: 14 | if _contains_line(file, line): 15 | return False 16 | else: 17 | print(line, file=file) 18 | return True 19 | 20 | def add_library(cds_path, library_name, library_path): 21 | """ 22 | Adds a library to the cds.lib 23 | 24 | :param cds_path: Path to the cds.lib file 25 | :param library_name: Name of the library to be added to the cds.lib file 26 | :param library_path: Path to the library to be added to the cds.lib file 27 | :return: True if successful 28 | """ 29 | with open(cds_path,"w") as file: 30 | if _contains_library_name(file, library_name): 31 | return False 32 | else: 33 | print("DEFINE %s %s\n", library_name, library_path, file=file) 34 | return True 35 | 36 | def _contains_line(file, line): 37 | """ 38 | Checks if the file contains the specified line. 39 | 40 | :return: True if the file contains the specified line 41 | """ 42 | line = line.strip() 43 | for file_line in file: 44 | file_line = file_line.strip() 45 | if file_line == line: 46 | return True 47 | else: 48 | return False 49 | 50 | def _contains_library_name(file, library_name): 51 | """ 52 | Checks if the file contains the specified line. 53 | :return: True if the file contains the specified line 54 | """ 55 | for line in file: 56 | line = line.strip() 57 | line = line.split() 58 | if len(line) == 3: 59 | if line[1] == library_name: 60 | return True 61 | else: 62 | return False 63 | -------------------------------------------------------------------------------- /virtue/cli.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import typer 4 | from rich import print 5 | from rich.console import Console 6 | 7 | import virtue 8 | import virtue.skill_environment.cli 9 | 10 | app = typer.Typer() 11 | console = Console() 12 | 13 | app.add_typer(virtue.skill_environment.cli.app, name="env") 14 | 15 | 16 | def _version_callback(value: bool): 17 | if value: 18 | print(f"v{virtue.__version__}") 19 | raise typer.Exit() 20 | 21 | @app.callback() 22 | def main( 23 | version: Optional[bool] = typer.Option( 24 | None, "--version", callback=_version_callback, is_eager=True, 25 | help="""Prints the semantic version number prepended with 'v' 26 | and exit""" 27 | ), 28 | ) -> None: 29 | """Virtue command line interface""" 30 | pass 31 | 32 | typer_click_object = typer.main.get_command(app) 33 | 34 | if __name__ == "__main__": 35 | app() 36 | -------------------------------------------------------------------------------- /virtue/data_types/Dpl.ils: -------------------------------------------------------------------------------- 1 | let((Dpl 2 | (module_description "Disembodied property list functions") 3 | (Module VrtImport['Module]) 4 | ) 5 | Dpl = let(() 6 | 7 | procedure(Cat(dpl_1 dpl_2 "ll") 8 | "Appends the data (car) and property lists of dpl_1 and dpl_2." 9 | cons(append(car(dpl_1) car(dpl_2)) append(cdr(dpl_1) cdr(dpl_2))) 10 | ) 11 | 12 | procedure(PropertyTable(input_dpl "l") 13 | "Convert a DPL to an association table" 14 | let((property_list (table makeTable("dpl properties"))) 15 | property_list = cdr(input_dpl) 16 | 17 | while(property_list 18 | table[car(property_list)] = cadr(property_list) 19 | property_list = cddr(property_list) 20 | ) 21 | table 22 | )) 23 | 24 | list(nil 25 | 'PropertyTable PropertyTable 26 | 'Cat Cat 27 | ) 28 | ) 29 | 30 | Module->New('Dpl 31 | ?module Dpl 32 | ?package VrtImport['Virtue] 33 | ?description module_description) 34 | ) 35 | -------------------------------------------------------------------------------- /virtue/data_types/Lcv.ils: -------------------------------------------------------------------------------- 1 | let((Lcv 2 | (module_description 3 | "Select the lib, cell, view or history name from an ordered list of them.") 4 | ) 5 | Lcv = let(() 6 | 7 | procedure(lib(lcv_list "l") 8 | "Get the library name frim a lcv (lib, cell, view) list" 9 | car(lcv_list) 10 | ) 11 | procedure(cell(lcv_list "l") 12 | "Get the cell name frim a lcv (lib, cell, view) list" 13 | cadr(lcv_list) 14 | ) 15 | 16 | procedure(view(lcv_list "l") 17 | "Get the view name frim a lcv (lib, cell, view) list" 18 | caddr(lcv_list) 19 | ) 20 | 21 | procedure(history(lcvh_list "l") 22 | "Get the history name frim a lcvh (lib, cell, view, history) list" 23 | cadddr(lcvh_list) 24 | ) 25 | 26 | list(nil 27 | 'lib lib 28 | 'cell cell 29 | 'view view 30 | 'history history 31 | )) 32 | 33 | VrtImport['Module]->New('Lcv 34 | ?module Lcv 35 | ?package VrtImport['Virtue] 36 | ?description module_description) 37 | ) 38 | -------------------------------------------------------------------------------- /virtue/data_types/List.ils: -------------------------------------------------------------------------------- 1 | let((List 2 | (module_description "List functions") 3 | (Module VrtImport['Module]) 4 | ) 5 | 6 | List = let(() 7 | 8 | procedure(ensure(input "g") 9 | "Ensures the input is a list, making it a list if it isn't one already." 10 | unless(listp(input) 11 | input = list(input)) 12 | input 13 | ) 14 | 15 | procedure(setDiff(plusList minusList "ll") 16 | "Returns plusList with the items in minusList removed" 17 | let(() 18 | foreach(item minusList 19 | plusList = remove(item plusList) 20 | ) 21 | plusList 22 | )) 23 | 24 | procedure(uniqueList(input "l") 25 | "Returns a list of items of the unique elements from the input list 26 | It doesn't guarantee the order of the elements but is much faster than the ordered version. " 27 | let(((table makeTable('uniq_table))) 28 | foreach(item input 29 | table[item] = 't 30 | ) 31 | table->? 32 | )) 33 | 34 | procedure(uniqueListOrdered(in "l") 35 | "Returns a list of items of the unique elements from the input list 36 | while keeping the order between the first unique elements" 37 | let((out noAdd) 38 | out = '() 39 | foreach(item in 40 | noAdd = member(item out) 41 | unless(noAdd 42 | out = append(out list(item)) 43 | ) 44 | ) 45 | out 46 | )) 47 | 48 | ;;;;;;;;;;;;;;;;;;;;;; 49 | ; Obj Instance Lists ; 50 | ;;;;;;;;;;;;;;;;;;;;;; 51 | 52 | procedure(getSlot(objList slotName "ls") 53 | "Returns a list of the value of the given slot of each instance in the list 54 | 55 | @param objList A list of skill++ object instances 56 | @param slotName A symbol of the slot name 57 | " 58 | let((out) 59 | foreach(obj objList 60 | out = tconc(out slotValue(obj slotName)) 61 | ) 62 | car(out) 63 | )) 64 | 65 | ;;;;;;;;;;;;;;;;;;;;; 66 | ; Association Lists ; 67 | ;;;;;;;;;;;;;;;;;;;;; 68 | 69 | procedure(assocKeys(assocList "l") 70 | "Returns all the keys in the specified association list" 71 | mapcar( 'car assocList) 72 | ) 73 | 74 | list(nil 75 | 'ensure ensure 76 | 'setDiff setDiff 77 | 'uniqueList uniqueList 78 | 'uniqueListOrdered uniqueListOrdered 79 | 'getSlot getSlot 80 | 'assocKeys assocKeys 81 | ) 82 | ) 83 | 84 | Module->New('List 85 | ?module List 86 | ?package VrtImport['Virtue] 87 | ?description module_description) 88 | ) 89 | -------------------------------------------------------------------------------- /virtue/data_types/Path.ils: -------------------------------------------------------------------------------- 1 | let((Path 2 | (module_description "String file and directory path functions") 3 | (Module VrtImport['Module]) 4 | ) 5 | 6 | Path = let(() 7 | 8 | procedure(Concat(@rest pathParts "t") 9 | "Concatenates each of the string partial paths into a full path by 10 | concatenating them with file seperators,'/', between them" 11 | buildString(pathParts "/") 12 | ) 13 | 14 | procedure(CopyCallback(_menu lib cell view _file _cat) 15 | "Library manager callback for copying a library, cell, or view path from 16 | a context menu" 17 | when(lib||cell||view 18 | hiSetClipboard( ddGetObj(lib cell view)~>readPath)) 19 | ) 20 | 21 | procedure(FileName(path "t") 22 | "Returns the file name of the path, including the extension" 23 | car(last(parseString(path "/"))) 24 | ) 25 | 26 | procedure(Folder(path "t") 27 | "Returns the path to the folder containing the path object" 28 | let((pieces fileName folderPath) 29 | pieces = parseString(path "/") 30 | fileName = car(last(pieces)) 31 | folderPath = buildString(remq(fileName pieces) "/") 32 | when(substring(path 1 1) == "/" 33 | folderPath = strcat("/" folderPath)) 34 | folderPath 35 | )) 36 | 37 | procedure(RemoveExtension(path "t") 38 | "Removes the current file extension if the file currently has one." 39 | let((currentExt) 40 | currentExt = FileExtension(path) 41 | if(currentExt then 42 | path = buildString(list(Folder(path) 43 | substring(FileName(path) 1 44 | length(FileName(path))-length(currentExt)-1) 45 | ) 46 | "/") 47 | ) 48 | path 49 | )) 50 | 51 | procedure(UpdateExtension(path extension "tt") 52 | "Update the file extension to the provided value. Appends the extension if 53 | one is not included. The last item in the path is considered the file name. 54 | @param extension The new file extension to be used. It shouldn't include the '.'" 55 | RemoveExtension(path) 56 | strcat(path "." extension) 57 | ) 58 | 59 | procedure(FileExtension(path "t") 60 | "Returns the file extension of the path" 61 | let((fileNameParts) 62 | fileNameParts = parseString(FileName(path) ".") 63 | if(length(fileNameParts) > 1 then 64 | car(last(fileNameParts)) 65 | else 66 | nil) 67 | )) 68 | 69 | procedure(Exists(path "t") 70 | "Checks if the file or directory of the path exists. 71 | Alias for the built-in 'isFileName' function" 72 | isFileName(path) 73 | ) 74 | 75 | procedure(ExistsFile(path "t") 76 | "Checks if the path exists and is a file, not a directory. 77 | Alias for the built-in 'isFile' function." 78 | isFile(path) 79 | ) 80 | 81 | list(nil 82 | 'Concat Concat 83 | 'Cat Concat 84 | 'FileName FileName 85 | 'Folder Folder 86 | 'RemoveExtension RemoveExtension 87 | 'UpdateExtension UpdateExtension 88 | 'FileExtension FileExtension 89 | 'Exists Exists 90 | 'ExistsFile ExistsFile 91 | 'FileExists ExistsFile 92 | 'CopyCallback CopyCallback 93 | ) 94 | ) 95 | 96 | Module->New('Path 97 | ?module Path 98 | ?package VrtImport['Virtue] 99 | ?description module_description) 100 | 101 | ) 102 | 103 | ; Global functions required for library manager 104 | VrtPathCopyCallback = VrtImport['Path]->CopyCallback 105 | -------------------------------------------------------------------------------- /virtue/data_types/Str.ils: -------------------------------------------------------------------------------- 1 | let((Str 2 | (module_description "String functions") 3 | (Module VrtImport['Module]) 4 | ) 5 | Str = let(() 6 | 7 | procedure(emptyp(in "g") 8 | "Checks if the input is an empty string" 9 | stringp(in) && strlen(in) == 0) 10 | 11 | 12 | procedure(split(in delim "tt") 13 | "Similar to parseString, except multiple delim are not ignored. 14 | Empty delimiter fields are returned as empty strings." 15 | let(((out '()) (inTmp in) nextC) 16 | while(nextC = nindex(inTmp,delim) 17 | out = append1(out substring(inTmp 1 nextC-1)) 18 | inTmp = substring(index(inTmp delim) 2) 19 | ) 20 | append1(out inTmp) 21 | )) 22 | 23 | procedure(trimWhiteSpace(in "t") 24 | "Trims both leading and trailing whitespace from the input string" 25 | let((pcreLeadingWhiteSpace pcreTrailingWhiteSpace) 26 | pcreLeadingWhiteSpace = pcreCompile( "\\A\\s+" ) 27 | pcreTrailingWhiteSpace = pcreCompile( "\\s+\\Z" ) 28 | in = pcreReplace(pcreLeadingWhiteSpace in "" 0 ) 29 | pcreReplace(pcreTrailingWhiteSpace in "" 0 ) 30 | )) 31 | 32 | procedure(str2bool(input_string "t") 33 | "Converts a case-insensitive 'TRUE' or 'FALSE' string to a boolean 34 | ('t / nil) If it is not a boolean, the string is returned." 35 | if(stringp(input_string) && (upperCase(input_string) == "TRUE") then 36 | 't 37 | else if(stringp(input_string) && (upperCase(input_string) == "FALSE") then 38 | nil 39 | else 40 | error("%s is not a boolean, must be \"TRUE\" or \"FALSE\" 41 | (case insensitive)" input_string) 42 | )) 43 | ) 44 | 45 | procedure(convertNumber(in "t") 46 | "Converts a string to a floating point number if it contains only 47 | numbers and a decimal point. Converts a string to an integer if it only 48 | contains numbers. Otherwise if it is non-numeric it returns the original 49 | string." 50 | let((splitString) 51 | splitString = parseString(in ".") 52 | if(((length(splitString) == 2) && atoi(car(splitString)) && atoi(cadr(splitString))) then 53 | atof(in) 54 | else if(((length(splitString) == 1) && atoi(in)) then 55 | atoi(in) 56 | else 57 | in 58 | )) 59 | )) 60 | 61 | procedure(convert(in "t") 62 | "Same as converNumber except it also handles boolean strings. see str2bool" 63 | let((val) 64 | val = convertNumber(in) 65 | when(stringp(val) 66 | val = str2bool(val)) 67 | val 68 | )) 69 | 70 | procedure(num2str(number "n") 71 | "Converts a number to a string. The number can be an integer 72 | or a floating point number. Returns the input unchanged 73 | if it is not an number." 74 | cond( 75 | (integerp(number) sprintf(nil "%d" number)) 76 | (floatp(number) sprintf(nil "%f" number)) 77 | (t number)) 78 | ) 79 | 80 | procedure(bool2str(in "g") 81 | "Converts a boolean to a string. If the input is nil, it 82 | returns 'FALSE', anything else returns 'TRUE'" 83 | if(in then 84 | "TRUE" 85 | else 86 | "FALSE") 87 | ) 88 | 89 | procedure(startsWith(string prefix "tt") 90 | "Checks if the input string begins with the given prefix string. 91 | Returns the suffix if it does end with the given prefix" 92 | if(strncmp(string prefix strlen(prefix)) == 0 then 93 | substring(string strlen(prefix)+1 strlen(string)) 94 | else 95 | nil) 96 | ) 97 | 98 | procedure(endsWith(string suffix "tt") 99 | "Checks if the input string ends with the given suffix string. 100 | Returns the prefix if it does end with the given suffix" 101 | if(length(suffix) > length(string) then 102 | nil 103 | else if(substring(string -length(suffix) length(suffix)) == suffix then 104 | substring(string 1 length(string)-length(suffix)) 105 | else 106 | nil 107 | )) 108 | ) 109 | 110 | procedure(prefixp(string prefix "tt") 111 | "Checks if the input string starts with the given prefix. 112 | It is case sensitive." 113 | if(strlen(string) >= strlen(prefix) && 114 | substring(string 1 strlen(prefix)) == prefix then 115 | 't 116 | else 117 | nil 118 | ) 119 | ) 120 | 121 | list(nil 122 | 'emptyp emptyp 123 | 'split split 124 | 'trimWhiteSpace trimWhiteSpace 125 | 'convertNumber convertNumber 126 | 'convert convert 127 | 'str2bool str2bool 128 | 'num2str num2str 129 | 'bool2str bool2str 130 | 'startsWith startsWith 131 | 'endsWith endsWith 132 | 'prefixp prefixp 133 | )) 134 | 135 | Module->New('Str 136 | ?module Str 137 | ?package VrtImport['Virtue] 138 | ?description module_description) 139 | 140 | ) 141 | -------------------------------------------------------------------------------- /virtue/data_types/Time.ils: -------------------------------------------------------------------------------- 1 | let((Time 2 | (module_description "Time functions") 3 | (Module VrtImport['Module]) 4 | (Str VrtImport['Str]) 5 | ) 6 | Time = let(() 7 | 8 | procedure(IntTimeTable(@optional (timeString getCurrentTime()) "t") 9 | "A table with the local date and time broken out into integer parameters" 10 | let(((timeTable makeTable(timeString)) 11 | (time timeToTm(stringToTime(timeString)))) 12 | timeTable["string"] = timeString ; The original input as a string 13 | timeTable["year"] = 1900 + time->tm_year ; year (int) 14 | timeTable["month"] = 1 + time->tm_mon ; month [1, 12] (int) 15 | timeTable["day"] = time->tm_mday ; day of the month: [1, 31] (int) 16 | timeTable["hour"] = time->tm_hour ; hours after midnight: [0, 23] (int) 17 | timeTable["min"] = time->tm_min ; minutes after the hour: [0, 59] (int) 18 | timeTable["sec"] = time->tm_sec ; seconds after the minute: [0, 61] (int) 19 | timeTable["weekday"] = time->tm_wday ; days since Sunday: [0, 6] (int) 20 | timeTable["yearday"] = time->tm_yday ; days since January: [0, 365] (int) 21 | timeTable["isdst"] = time->tm_isdst ; daylight saving time flag: <0,0,>0 22 | ; timeTable["zone"] = system("date +'%Z") ; TODO: Time Zone 23 | timeTable 24 | )) 25 | 26 | procedure(IsoDateString(@optional (timeString getCurrentTime()) "t") 27 | "The date in the local time zone as a string in ISO 8601 format, YYYY-MM-DD" 28 | let(((time IntTimeTable(timeString))) 29 | sprintf(nil "%.4d-%.2d-%.2d" time["year"] time["month"] time["day"]) 30 | )) 31 | 32 | procedure(IsoTimeString(@optional (timeString getCurrentTime()) "t") 33 | "The time in the local time zone as a string in ISO 8601 format, 'hh:mm:ss'" 34 | let(((time IntTimeTable(timeString))) 35 | sprintf(nil "%.2d:%.2d:%.2d" time["hour"] time["min"] time["sec"]) 36 | )) 37 | 38 | 39 | procedure(DateStringIsoUTC(@optional (isoTimeString CurrentDatetimeIsoUTC()) "t") 40 | "The date in the UTC time zone. 41 | Returns a string in ISO 8601 format, YYYY-MM-DD" 42 | substring(isoTimeString 1 10) 43 | ) 44 | 45 | procedure(TimeStringIsoUTC(@optional (isoTimeString CurrentDatetimeIsoUTC()) "t") 46 | "The time in the UTC time zone. 47 | Returns a string in ISO 8601 format, hh:mm:ss" 48 | substring(isoTimeString 12 8) 49 | ) 50 | 51 | procedure(CurrentDatetimeIsoUTC() 52 | "The UTC datetime as a string in ISO 8601 format, 'YYYY-MM-DD hh:mm:ss' 53 | A space seperates the date and time" 54 | let((child_id out) 55 | child_id = ipcBeginProcess("date --utc +'%Y-%m-%d %T'") 56 | out = Str->trimWhiteSpace(ipcReadProcess(child_id 1)) 57 | out 58 | )) 59 | 60 | list(nil 61 | 'IntTimeTable IntTimeTable 62 | 'IsoDateString IsoDateString 63 | 'IsoTimeString IsoTimeString 64 | 'CurrentDatetimeIsoUTC CurrentDatetimeIsoUTC 65 | 'DateStringIsoUTC DateStringIsoUTC 66 | 'TimeStringIsoUTC TimeStringIsoUTC 67 | )) 68 | 69 | Module->New('Time 70 | ?module Time 71 | ?package VrtImport['Virtue] 72 | ?description module_description) 73 | ) 74 | -------------------------------------------------------------------------------- /virtue/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/virtue/plugins/__init__.py -------------------------------------------------------------------------------- /virtue/plugins/hookspecs.py: -------------------------------------------------------------------------------- 1 | import pluggy 2 | from virtue.skill_package.metadata_data import SKillPackageMetadata 3 | 4 | hookspec = pluggy.HookspecMarker("virtue") 5 | 6 | 7 | @hookspec 8 | def virtue_register_skill_package() -> SKillPackageMetadata: # type: ignore 9 | """ Registers a skill package with Virtue. 10 | 11 | The skill package will be loaded in Virtue's skill environment. 12 | 13 | returns: 14 | A dict containing the required package metadata keys from the 15 | SKillPackageMetadata TypedDict and, optionally, any of the 16 | keys from SKillPackageOptionalMetadata TypedDict. 17 | """ 18 | -------------------------------------------------------------------------------- /virtue/plugins/lib.py: -------------------------------------------------------------------------------- 1 | from importlib.resources import files 2 | import virtue 3 | 4 | 5 | @virtue.hookimpl 6 | def virtue_register_skill_package(): 7 | return { 8 | "python_package_name": "virtue-skill", 9 | "skill_package_name": "Virtue", 10 | "cdsinit_paths": (files(virtue) / "virtue.cdsinit.ils"), 11 | "cdslibmgr_paths": (files(virtue) / "virtue.cdsLibMgr.il"), 12 | "cds_dev_libraries": { 13 | "virtue_dev_work": (files(virtue) / "virtue_dev_work"), 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /virtue/plugins/plugin_manager.py: -------------------------------------------------------------------------------- 1 | import pluggy 2 | from virtue.plugins import lib, hookspecs 3 | 4 | def get_plugin_manager(): 5 | pm = pluggy.PluginManager("virtue") 6 | pm.add_hookspecs(hookspecs) 7 | pm.load_setuptools_entrypoints("virtue") 8 | pm.register(lib) 9 | return pm 10 | 11 | plugin_manager = get_plugin_manager() 12 | -------------------------------------------------------------------------------- /virtue/skill_environment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/virtue/skill_environment/__init__.py -------------------------------------------------------------------------------- /virtue/skill_environment/api.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os import environ 3 | from importlib.resources import files 4 | from pathlib import Path 5 | from typing import Any, Dict, Optional 6 | import virtue 7 | from virtue.skill_package.metadata \ 8 | import metadata, skill_packages_metadata 9 | 10 | def info() -> Dict[str, Any]: 11 | data = { 12 | "python version": sys.version_info, 13 | } 14 | if("CONDA_PREFIX" in environ): 15 | data["Conda Env Prefix"] = environ["CONDA_PREFIX"] # type: ignore 16 | datareg_path = _get_data_reg_env_script_path() 17 | if datareg_path is not None: 18 | data["data.reg"] = datareg_path 19 | return data 20 | 21 | def list_packages() -> Dict[str,Any]: 22 | python_packages = skill_packages_metadata() 23 | return python_packages 24 | 25 | def _get_data_reg_env_script_path() -> Optional[Path]: 26 | """The path to the SKILL environment initialization script""" 27 | if len(metadata("data_reg_paths")) > 0: 28 | return Path(str(files(virtue) / "virtue-environment.data.reg")) 29 | else: 30 | return None 31 | -------------------------------------------------------------------------------- /virtue/skill_environment/cli.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from pathlib import Path 3 | import typer 4 | from rich import print 5 | from rich.console import Console 6 | from rich.table import Table 7 | from rich.syntax import Syntax 8 | from rich.padding import Padding 9 | from rich.markup import escape 10 | import toml 11 | 12 | from virtue.skill_environment import init_scripts, api 13 | 14 | app = typer.Typer() 15 | console = Console() 16 | 17 | 18 | @app.command() 19 | def info(): 20 | """Prints information on the virtue environment""" 21 | info_dict = api.info() 22 | table = Table(show_header=False, title="[bold green]Virtue Environment[/bold green]") 23 | 24 | if"Conda Env Prefix" in info_dict: 25 | table.add_row("Conda Env Prefix", info_dict["Conda Env Prefix"]) 26 | 27 | major = info_dict["python version"][0] 28 | minor = info_dict["python version"][1] 29 | patch = info_dict["python version"][2] 30 | python_version = f"{major}.{minor}.{patch}" 31 | table.add_row("Python version", python_version) 32 | 33 | print("") 34 | console.print(Padding(table,(0,2))) 35 | _print_install_script_table(init_scripts.script_paths()) 36 | 37 | 38 | @app.command("list") 39 | def list_packages( 40 | verbose: bool = typer.Option(False, "--verbose","-v", help= 41 | "Include additional package data such as init script paths"), 42 | export_toml: bool = typer.Option(False, "--toml","-t", help= 43 | "Print the full package list in TOML format"), 44 | export_python: bool = typer.Option(False, "--python","-p", help= 45 | "Prints the full package list in dict format") 46 | ) -> None: 47 | """List the Virtue packages""" 48 | 49 | packages = api.list_packages() 50 | if export_toml: 51 | print(escape(toml.dumps(packages))) 52 | elif export_python: 53 | print(packages) 54 | else: 55 | _print_package_table(packages, verbose) 56 | 57 | 58 | def _print_package_table(packages: dict, verbose: bool): 59 | column_names = { 60 | "python_package_name": "Python Package", 61 | "skill_package_name": "SKILL Package", 62 | "version": "Version" 63 | } 64 | if verbose: 65 | column_names.update({ 66 | "cdsinit_paths": ".cdsinit", 67 | "cdslibmgr_paths": "cdsLibMgr.il", 68 | "data_reg_paths": "data.reg", 69 | }) 70 | display_names = column_names.values() 71 | table = Table(*display_names, 72 | title ="\n:package: [bold green]Virtue Packages[/bold green]", 73 | title_style="") 74 | """for column_header in display_names: 75 | if column_header in [".cdsinit"]: 76 | table.add_column(column_header,justify="center") 77 | else: 78 | table.add_column(column_header)""" 79 | for package in packages.values(): 80 | package_filtered = {key: str(package[key]) for \ 81 | key in column_names.keys() if key in package} 82 | table.add_row(*package_filtered.values()) 83 | console.print(table) 84 | 85 | @app.command() 86 | def init(): 87 | """Initialializes the Virtue SKILL environment by creating the 88 | Virtuoso initialization files in the Virtue folder of the current 89 | Python environment.""" 90 | init_file_paths = init_scripts.init() 91 | _print_install_script_table(init_file_paths) 92 | _print_install_cdsinit(init_file_paths) 93 | _print_install_datareg(init_file_paths) 94 | _print_install_cdslibmgr(init_file_paths) 95 | 96 | def _print_install_script_table(init_file_paths: Dict[str,Path]): 97 | print("\nSKILL environment initialization scripts installed:") 98 | table = Table("Script", "Path") 99 | for script_name, script_path in init_file_paths.items(): 100 | if script_path is not None: 101 | table.add_row(script_name, str(script_path)) 102 | else: 103 | table.add_row(script_name, 104 | "Not Applicable: No Virtue packages require it") 105 | print(Padding(table,(0,2))) 106 | print("") 107 | 108 | def _print_install_cdsinit(init_file_paths: Dict[str,Path]): 109 | init_path = init_file_paths[".cdsinit"] 110 | print(("Add the following to your Virtuoso initialization file " 111 | "(e.g. .cdsinit):")) 112 | code = ("; Initialize the Virtue SKILL environment\n" 113 | f"when(isFile(\"{init_path}\")\n" 114 | f" loadi(\"{init_path}\"))") 115 | console.print(Padding(Syntax(code,"scheme"),(1,2))) 116 | 117 | def _print_install_datareg(init_file_paths: Dict[str,Path]): 118 | data_reg_path = init_file_paths["data.reg"] 119 | if data_reg_path is not None: 120 | print("Add the following to your data.reg file:") 121 | code = ("// Initialize the Virtue SKILL environment data registry\n" 122 | f"SOFTINCLUDE {data_reg_path};") 123 | console.print(Padding(Syntax(code,"scheme"),(1,2))) 124 | 125 | def _print_install_cdslibmgr(init_file_paths: Dict[str,Path]): 126 | init_path = init_file_paths["cdsLibMgr.il"] 127 | print(("Add the following to your Virtuoso library manager " 128 | "initialization file (e.g. cdsLibMgr.il):")) 129 | code = ( 130 | "; Initialize the Virtue SKILL environment in the library manager\n" 131 | f"when(isFile(\"{init_path}\")\n" 132 | f" loadi(\"{init_path}\"))") 133 | console.print(Padding(Syntax(code,"scheme"),(1,2))) 134 | 135 | typer_click_object = typer.main.get_command(app) 136 | 137 | if __name__ == "__main__": 138 | app() 139 | -------------------------------------------------------------------------------- /virtue/skill_environment/init_scripts.py: -------------------------------------------------------------------------------- 1 | from importlib.resources import files 2 | from pathlib import Path 3 | from typing import Dict, Optional 4 | 5 | import virtue 6 | from virtue.skill_package.metadata import metadata 7 | 8 | 9 | def init() -> Dict[str,Optional[Path]]: 10 | """Initialializes the Virtue SKILL environment by creating the 11 | Virtuoso initialization files in the Virtue folder of the current 12 | Python environment. 13 | 14 | Returns: 15 | A dictionary with the paths to the created initialization files. 16 | """ 17 | init_paths = script_paths() 18 | _install_env_cdsinit_script(init_paths[".cdsinit"]) 19 | _install_env_cdslibmgr_script(init_paths["cdsLibMgr.il"]) 20 | if init_paths["data.reg"] is not None: 21 | _install_data_reg_init_script(init_paths["data.reg"]) 22 | return init_paths 23 | 24 | def _get_data_reg_env_script_path() -> Optional[Path]: 25 | """The path to the SKILL environment initialization script""" 26 | if len(metadata("data_reg_paths")) > 0: 27 | return Path(str(files(virtue) / "virtue-environment.data.reg")) 28 | else: 29 | return None 30 | 31 | def _install_env_cdsinit_script(filepath: Path) -> None: 32 | with filepath.open("w") as file: 33 | file.write(( 34 | "printf(\"\\n---------------------------------------------------" 35 | "--------------\\n\")\n" 36 | "printf(\"Initializing Virtue skill environment\\n\")\n" 37 | f"printf(\" loading {filepath}\\n\")\n\n" 38 | "let((init_files)\n" 39 | " init_files = '(\n" 40 | )) 41 | for path in metadata("cdsinit_paths"): 42 | file.write(f" \"{path}\"\n") 43 | file.write( 44 | (" )\n" 45 | " foreachs(file init_files\n" 46 | " loadi(file))\n" 47 | ")\n" 48 | "printf(\" Done, initialized Virtue SKILL environment\\n\")\n" 49 | "printf(\"------------------------------------------------------" 50 | "-----------\\n\\n\")") 51 | ) 52 | 53 | def _install_data_reg_init_script(filepath: Path) -> None: 54 | data_reg_paths = metadata("data_reg_paths") 55 | if len(data_reg_paths) > 0: 56 | with filepath.open("w") as file: 57 | for path in data_reg_paths: 58 | if isinstance(path,str) or isinstance(path, Path): 59 | path = str(path) 60 | file.write(f"SOFTINCLUDE {path};\n") 61 | elif isinstance(path, tuple) or isinstance(path, list): 62 | for path_item in path: 63 | path_item = str(path_item) 64 | file.write(f"SOFTINCLUDE {path_item};\n") 65 | 66 | def _install_env_cdslibmgr_script(filepath: Path) -> None: 67 | with filepath.open("w") as file: 68 | file.write(( 69 | "printf(\"Initializing Virtue library manager environment\\n\")\n" 70 | f"printf(\" loading {filepath}\\n\")\n\n" 71 | "let((init_files)\n" 72 | " init_files = '(\n" 73 | )) 74 | for path in metadata("cdslibmgr_paths"): 75 | file.write(f" \"{path}\"\n") 76 | file.write( 77 | (" )\n" 78 | " foreachs(file init_files\n" 79 | " loadi(file))\n" 80 | ")\n" 81 | "printf(\" Done, initialized IDS library manager environment" 82 | "\\n\")\n") 83 | ) 84 | 85 | def script_paths() -> Dict[str, Optional[Path]]: 86 | return { 87 | ".cdsinit": 88 | Path(str(files(virtue) / "virtue-environment.cdsinit.ils")), 89 | "cdsLibMgr.il": 90 | Path(str(files(virtue) / "virtue-environment.cdsLibMgr.il")), 91 | "data.reg": _get_data_reg_env_script_path(), 92 | } 93 | -------------------------------------------------------------------------------- /virtue/skill_package/Module.ils: -------------------------------------------------------------------------------- 1 | let((Module 2 | (module_description "Virtue SKILL module tables") 3 | ) 4 | 5 | Module = let(() 6 | 7 | procedure(IsModule(input "g") 8 | "returns 't if the input is a Virtue module" 9 | tablep(input) && tablep(input['__metadata__]) 10 | ) 11 | 12 | procedure(New(module_symbol 13 | @key module package (description "") "sggt") 14 | "Create a new SKILL module. 15 | @param module_symbol A symbol of the module name 16 | @param module The existing module table or a decomposed property list (DPL) 17 | of functions to include as public functions. 'theEnvironment' can be 18 | provided to add add all the functions in the environment. 19 | @param package The top-level package which defines this module 20 | @brief Create a new SKILL module." 21 | let((module_table metadata) 22 | 23 | cond( 24 | (tablep(module) 25 | module_table = module) 26 | ((listp(module) || !module) 27 | module_table = makeTable(strcat(symbolToString(module_symbol) " module")) 28 | when(listp(module) 29 | AddFunctionsDpl(module_table module))) 30 | ('t error("A module must be a DPL or a table")) 31 | ) 32 | 33 | ; Add metadata 34 | metadata = makeTable(strcat(symbolToString(module_symbol) " module metadata")) 35 | metadata['symbol] = module_symbol 36 | when(listp(module) 37 | metadata['module_dpl] = module) 38 | when(symbolp(package) 39 | package = VrtImport[package]) 40 | when(package 41 | metadata['package] = package) 42 | metadata['description] = description 43 | module_table['__metadata__] = metadata 44 | 45 | VrtImport[module_symbol] = module_table 46 | module_table 47 | )) 48 | 49 | procedure(LoadList(module_filepaths @key (root_path "") continue_on_error) 50 | "loads a list of filepaths." 51 | let((load_function) 52 | if(continue_on_error then 53 | load_function = loadi 54 | else 55 | load_function = load) 56 | foreach(filepath module_filepaths 57 | load_function(strcat(root_path "/" filepath)) 58 | ) 59 | )) 60 | 61 | procedure(Method(method module_dpl "ul") 62 | let((new_method) 63 | procedure(new_method(@rest rest_args "g") 64 | apply(method cons(module_dpl rest_args)) 65 | ) 66 | new_method 67 | )) 68 | 69 | procedure(AddMethod(object_dpl method_symbol method "lsu") 70 | putprop(object_dpl Module->Method(method object_dpl) method_symbol) 71 | ) 72 | 73 | procedure(GetModuleMetadata(module "g") 74 | "Returns a table of metadata about the module." 75 | if(tablep(module) then 76 | module['__metadata__] 77 | else 78 | error("Input module must be a table") 79 | ) 80 | ) 81 | 82 | procedure(AddFunction(module function_symbol function) 83 | "Deprecated, use AddItem instead. Adds a function to the module." 84 | putprop(module function function_symbol) 85 | ) 86 | 87 | procedure(AddItem(module item_symbol item) 88 | "Add an item to the module table with the given item_symbol 89 | as the key. The item can be of any type, for example a function or 90 | submodule." 91 | putprop(module item item_symbol) 92 | ) 93 | 94 | procedure(AddFunctionsDpl(module functions_dpl "gl") 95 | let((property_list) 96 | property_list = cdr(functions_dpl) 97 | while((length(property_list) >= 2) 98 | AddFunction(module car(property_list) cadr(property_list)) 99 | property_list = cddr(property_list) 100 | ) 101 | )) 102 | 103 | list(nil 104 | 'New New 105 | 'IsModule IsModule 106 | 'LoadList LoadList 107 | 'Method Method 108 | 'AddMethod AddMethod 109 | 'AddFunction AddFunction 110 | 'AddItem AddItem 111 | 'AddFunctionsDpl AddFunctionsDpl 112 | ) 113 | ) 114 | 115 | Module->New('Module 116 | ?module Module 117 | ?package VrtImport['Virtue] 118 | ?description module_description) 119 | 120 | ) 121 | -------------------------------------------------------------------------------- /virtue/skill_package/Package.ils: -------------------------------------------------------------------------------- 1 | let((Package 2 | (module_description "Registers a SKILL project with Virtue") 3 | ) 4 | 5 | Package = let(((PackageModules makeTable("virtue package modules"))) 6 | 7 | procedure(New(module project_init_dir_path "gt") 8 | "Adds a set of useful variables about the project to the 9 | package's data_table." 10 | let((package_table config_filepaths config_filepath) 11 | 12 | module['__metadata__]['project_root_path] = project_init_dir_path 13 | register_package(module) 14 | module 15 | )) 16 | 17 | procedure(register_package(module "g") 18 | 19 | PackageModules[module['__metadata__]['symbol]] = module 20 | Packages[module['__metadata__]['symbol]] = module 21 | ) 22 | 23 | procedure(GetPackageMetadata(package_in "g") 24 | "Returns the package's metadata table." 25 | package_in['__metadata__] 26 | ) 27 | 28 | list(nil 29 | 'New New 30 | 'PackageModules PackageModules 31 | 'GetPackageMetadata GetPackageMetadata 32 | ) 33 | ) 34 | 35 | VrtImport['Module]->New('Package 36 | ?module Package 37 | ?package VrtImport['Virtue] 38 | ?description module_description) 39 | 40 | ) 41 | -------------------------------------------------------------------------------- /virtue/skill_package/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cascode-labs/virtue/5a80ab7a1d117e8a54a387c211720adc4b46c408/virtue/skill_package/__init__.py -------------------------------------------------------------------------------- /virtue/skill_package/metadata.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Union 2 | from pathlib import Path 3 | from importlib.metadata import metadata as python_metadata 4 | from virtue.plugins.plugin_manager import plugin_manager 5 | from virtue.skill_package.metadata_data import SKillPackageMetadata 6 | import toml 7 | 8 | 9 | def skill_packages_metadata()->Dict[str, SKillPackageMetadata]: 10 | """Provides metadata for each registerd Virtue skill package 11 | 12 | Returns: 13 | A dictionary containing a SkillPackageData dictionary for each 14 | registered skill package with each skill package's associated 15 | python package name as the key. 16 | """ 17 | skill_packages_data_list = \ 18 | plugin_manager.hook.virtue_register_skill_package() # type: ignore 19 | packages = {} 20 | for skill_package_data_dict in skill_packages_data_list: 21 | python_package_name = skill_package_data_dict['python_package_name'] 22 | python_package_metadata = python_metadata(python_package_name) 23 | packages[python_package_name] = skill_package_data_dict 24 | packages[python_package_name]["version"] = python_package_metadata["version"] 25 | return packages 26 | 27 | 28 | def metadata(data_key): 29 | """Returns a list of the data with the given key from each 30 | SKILL package's metadata dictionary""" 31 | packages = skill_packages_metadata() 32 | output = [] 33 | for package_data in packages.values(): 34 | if data_key in package_data: 35 | output.append(package_data[data_key]) 36 | return output 37 | 38 | def read_metadata(project_toml_filepath: Union[str, Path]) -> dict: 39 | return toml.load(str(project_toml_filepath)) 40 | -------------------------------------------------------------------------------- /virtue/skill_package/metadata_data.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Tuple, Dict, TypedDict 3 | 4 | 5 | class SKillPackageOptionalMetadata(TypedDict, total=False): 6 | """Optional SKILL package metadata""" 7 | 8 | cdsinit_paths: Tuple[Path] 9 | """Paths to .cdsinit initialization files which will be loaded into the 10 | main SKILL environment.""" 11 | data_reg_paths: Tuple[Path] 12 | """Paths to data.reg initialization files which will register tools and view types""" 13 | cdslibmgr_paths: Tuple[Path] 14 | """Paths to cdsLibMgr.il initialization files which will be loaded 15 | by the library manager to customize its menus and options.""" 16 | cds_libraries: Dict[str, Path] 17 | """Specifies OA libraries to be included in the skill 18 | environment's cds.lib. A dictionary with OA library names as the keys and 19 | Path objects to them for their values. 20 | """ 21 | cds_dev_libraries: Dict[str, Path] 22 | """Specifies OA libraries to be included in a development environment's 23 | cds.lib. A dictionary with OA library names as the keys and 24 | Path objects to them for their values. 25 | """ 26 | 27 | 28 | class SKillPackageMetadata(SKillPackageOptionalMetadata, total=True): 29 | """Required package metadata""" 30 | python_package_name: str 31 | "Name of the python package registering the skill package." 32 | skill_package_name: str 33 | "Name of the skill package." 34 | -------------------------------------------------------------------------------- /virtue/skillbridge.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def get_workspace_id(project_name=None, user_name=None): 5 | """ Gets the workspace id in the format expected by the Skill side of Skillbridge connection. 6 | 7 | Args: 8 | project_name(:obj:`str`, optional): Name of project that will a skill bridge workspace will connect to. 9 | Defaults to the value of 'PROJ_ID' environment variable. 10 | user_name(:obj:`str`, optional): Name of user the skill bridge connection workspace will be connected to. 11 | Defaults to the value of 'USER' environment variable. 12 | 13 | Returns: 14 | str: A workspace id in the format '{project_name}-{user_name}. 15 | 16 | Raises: 17 | ValueError: An error occurred if the project name or username is not provided and the associated environement 18 | variable does not exits. 19 | """ 20 | project_name = get_current_project_name() if not project_name else project_name 21 | user_name = get_current_user_name() if not user_name else user_name 22 | if not project_name or not user_name: 23 | raise ValueError("Need to provide project name and username or be in a project workarea with 'PROJ_ID' and " 24 | "'USER' environemnt variables set.") 25 | return "{prj}-{user}".format(prj=project_name, user=user_name) 26 | 27 | 28 | def get_current_user_name(): 29 | """Gets current user's username from 'USER' environment variable.""" 30 | return os.getenv('USER') 31 | 32 | 33 | def get_current_project_name(): 34 | """Gets current user's username from 'PROJ_ID' environment variable.""" 35 | return os.getenv('PROJ_ID') 36 | -------------------------------------------------------------------------------- /virtue/test/Test.ils: -------------------------------------------------------------------------------- 1 | let((Test 2 | (Virtue VrtImport['Virtue]) 3 | (Module VrtImport['Module]) 4 | (module_description "SKILL Test framework modeled after PyTest") 5 | ) 6 | 7 | Test = let((current_suite) 8 | 9 | procedure(Initialize() 10 | let((root_path subpackages) 11 | root_path = Virtue->GetCurrentFileDirectory() 12 | subpackages = list( 13 | "TestFunction.ils" 14 | "TestFile.ils" 15 | "TestSuite.ils" 16 | "TestFixtures.ils" 17 | ) 18 | Module->LoadList(subpackages ?root_path root_path) 19 | )) 20 | 21 | list(nil 22 | 'Initialize Initialize 23 | 'current_suite current_suite 24 | ) 25 | ) 26 | 27 | Module->New('Test 28 | ?module Test 29 | ?package Virtue 30 | ?description module_description) 31 | Test->Initialize() 32 | ) 33 | -------------------------------------------------------------------------------- /virtue/test/TestFile.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (Module VrtImport['Module]) 3 | ) 4 | 5 | procedure(NewFile(path @optional test_functions_dpl "tl") 6 | let((test_file) 7 | test_file = list(nil 8 | 'path path 9 | 'functions nil 10 | 'functions_tconc nil 11 | 'AddTestFunctions AddTestFunctions 12 | 'ReportResult ReportResult 13 | ) 14 | putprop(test_file Module->Method(AddTestFunctions test_file) 'AddTestFunctions) 15 | putprop(test_file Module->Method(ReportResult test_file) 'ReportResult) 16 | putprop(test_file Module->Method(Run test_file) 'Run) 17 | putprop(test_file Module->Method(FunctionCount test_file) 'FunctionCount) 18 | putprop(test_file Module->Method(FunctionPassCount test_file) 'FunctionPassCount) 19 | test_file->AddTestFunctions(test_functions_dpl) 20 | test_file 21 | )) 22 | 23 | procedure(Run(test_file "l") 24 | printf("Running File: %s \n" test_file->path) 25 | foreachs(test_function test_file->functions 26 | test_function->Run() 27 | ) 28 | printf(" Done Running File: %s \n" test_file->path) 29 | ) 30 | 31 | procedure(RunFile(test_functions_dpl @key (filepath "") "lt") 32 | let(((current_suite Test->GetCurrentSuite())) 33 | test_file = NewFile(filepath test_functions_dpl) 34 | test_file->Run() 35 | when(current_suite 36 | current_suite->AddFile(test_file)) 37 | unless(current_suite 38 | test_file->ReportResult()) 39 | test_file 40 | )) 41 | 42 | procedure(ReportResult(test_file "l") 43 | let((function_count pass_count) 44 | printf("FILE: %s\n" test_file->path) 45 | foreachs(test_function test_file->functions 46 | printf(" ") 47 | test_function->ReportResult() 48 | ) 49 | function_count = test_file->FunctionCount() 50 | pass_count = test_file->FunctionPassCount() 51 | printf("%n / %n tests passed\n" pass_count function_count) 52 | if(pass_count == function_count then 53 | printf("\nALL TESTS PASS!\n\n") 54 | else 55 | printf("\n%n TESTS FAILED!\n\n" function_count - pass_count)) 56 | 57 | )) 58 | 59 | procedure(AddTestFunctions(test_file test_functions_dpl "ll") 60 | when(test_functions_dpl 61 | let((functions_tconc) 62 | functions_tconc = test_file->functions_tconc 63 | test_functions_dpl = cdr(test_functions_dpl) 64 | while(test_functions_dpl 65 | functions_tconc = tconc(functions_tconc 66 | Test->NewFunction(car(test_functions_dpl) cadr(test_functions_dpl))) 67 | test_functions_dpl = cddr(test_functions_dpl) 68 | ) 69 | test_file->functions_tconc = functions_tconc 70 | test_file->functions = car(functions_tconc) 71 | ) 72 | ) 73 | 't 74 | ) 75 | 76 | procedure(FunctionCount(test_file "l") 77 | length(test_file->functions) 78 | ) 79 | 80 | procedure(FunctionPassCount(test_file "l") 81 | let(((pass_count 0) test_function) 82 | foreachs(test_function test_file->functions 83 | when(test_function->pass 84 | pass_count = pass_count + 1) 85 | ) 86 | pass_count 87 | )) 88 | 89 | Module->AddFunction(Test 'NewFile NewFile) 90 | Module->AddFunction(Test 'RunFile RunFile) 91 | ) 92 | -------------------------------------------------------------------------------- /virtue/test/TestFixtures.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (PathConcat VrtImport['Path]->Concat) 3 | (Fixtures makeTable("Test Fixtures")) 4 | ) 5 | procedure(TempDirectory(@optional (path_prefix "virtue") "t") 6 | let((temp_filepath) 7 | temp_filepath = makeTempFileName(PathConcat(getTempDir() path_prefix)) 8 | createDirHier(temp_filepath) 9 | temp_filepath 10 | )) 11 | 12 | Fixtures['TempDirectory] = TempDirectory 13 | putprop(Test Fixtures 'Fixtures) 14 | ) 15 | -------------------------------------------------------------------------------- /virtue/test/TestFunction.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (Module VrtImport['Module]) 3 | ) 4 | 5 | procedure(NewFunction(symbol function_object "su") 6 | let((test_function) 7 | test_function = list(nil 8 | 'symbol symbol 9 | 'function function_object 10 | ; Results 11 | 'ran nil 12 | 'pass nil 13 | 'error_details nil 14 | ) 15 | putprop(test_function Module->Method(Run test_function) 'Run) 16 | putprop(test_function Module->Method(ReportResult test_function) 'ReportResult) 17 | test_function 18 | )) 19 | 20 | procedure(get_fixture_values(test_function "l") 21 | let((function_args fixture_values) 22 | function_args = arglist(test_function->function) 23 | foreach(fixture_symbol function_args 24 | fixture_function = Test->Fixtures[fixture_symbol] 25 | when(fixture_function == 'unbound 26 | error("The fixture function '%s' is not defined in the Test->Fixtures table" fixture_symbol)) 27 | fixture_values = append1(fixture_values fixture_function()) 28 | ) 29 | fixture_values 30 | )) 31 | 32 | procedure(Run(test_function "l") 33 | let((pass function_arg_values ran error_details) 34 | pass = errset(begin( 35 | function_arg_values = get_fixture_values(test_function) 36 | if(function_arg_values then 37 | ran = errset(apply(test_function->function function_arg_values)) 38 | else 39 | ran = errset(test_function->function())) 40 | )) 41 | unless((pass || ran) 42 | error_details = errset.errset 43 | ) 44 | when(ran 45 | ran = 't) 46 | test_function->ran = ran 47 | if(pass then 48 | test_function->pass = pass 49 | else 50 | test_function->pass = nil) 51 | test_function->error_details = error_details 52 | test_function 53 | )) 54 | 55 | procedure(ReportResult(test_function "l") 56 | let(((pass_message "failed")) 57 | when(test_function->pass 58 | pass_message = "passed") 59 | 60 | printf("%s: %s\n" pass_message test_function->symbol ) 61 | when(test_function->error_details 62 | printf(" %s\n" caar(last(test_function->error_details))) 63 | ) 64 | )) 65 | 66 | putprop(Test NewFunction 'NewFunction) 67 | ) 68 | -------------------------------------------------------------------------------- /virtue/test/TestSuite.ils: -------------------------------------------------------------------------------- 1 | let(((Test VrtImport['Test]) 2 | (Str VrtImport['Str]) 3 | (Module VrtImport['Module]) 4 | (Path VrtImport['Path]) 5 | current_suite 6 | ) 7 | 8 | procedure(RunDirectory(directory_path @optional (recurse nil) "tg") 9 | "Run a directory of tests. Loads each SKILL or SKILL++ file with a 'Test_' 10 | prefix and reports the results of each. 11 | 12 | @brief Run a directory of test files with each containing a call to Test->RunFile 13 | @param directory_path Path to a directory of SKILL test scripts 14 | @return A test suite object" 15 | let((files file file_path test_files success suite) 16 | unless(recurse 17 | ResetSuite() 18 | current_suite = NewSuite(?path directory_path) 19 | ) 20 | files = getDirFiles(directory_path) 21 | files = remove("run_tests.ils" files) 22 | files = remove("." files) 23 | files = remove(".." files) 24 | when(files 25 | foreach(file files 26 | file_path = Path->Concat(directory_path file) 27 | when(Str->endsWith(file ".il") || 28 | Str->endsWith(file ".ils") && 29 | (Str->prefixp(file "test_") || Str->prefixp(file "Test_")) 30 | loadi(file_path) 31 | ) 32 | when(isDir(file_path) 33 | RunDirectory(file_path 't) 34 | ) 35 | ) 36 | ) 37 | unless(recurse 38 | current_suite->ReportResult() 39 | suite = current_suite 40 | Test->ResetSuite() 41 | ) 42 | suite 43 | )) 44 | 45 | procedure(NewSuite( @key path "t") 46 | let((suite) 47 | suite = list(nil 48 | 'path path 49 | 'files nil 50 | 'pass 't 51 | 'ResetSuite ResetSuite 52 | ) 53 | Module->AddMethod(suite 'AddFile AddFile) 54 | Module->AddMethod(suite 'ReportResult ReportResult) 55 | Module->AddMethod(suite 'FunctionCount FunctionCount) 56 | Module->AddMethod(suite 'FunctionPassCount FunctionPassCount) 57 | suite 58 | )) 59 | 60 | procedure(AddFile(suite file "ll") 61 | ;suite->files = cons(file suite->files) 62 | suite->files = append1(suite->files file) 63 | ) 64 | 65 | procedure(ReportResult(suite "l") 66 | let((function_count pass_count) 67 | printf("RAN SUITE: %s\n" suite->path) 68 | foreachs(file suite->files 69 | printf(" ") 70 | file->ReportResult() 71 | ) 72 | function_count = suite->FunctionCount() 73 | pass_count = suite->FunctionPassCount() 74 | printf("%n / %n suite tests passed\n" pass_count function_count) 75 | if(pass_count == function_count then 76 | printf("\nALL SUITE TESTS PASS!\n\n") 77 | else 78 | printf("\n%n SUITE TESTS FAILED!\n\n" function_count - pass_count)) 79 | pass_count == function_count 80 | )) 81 | 82 | procedure(FunctionCount(suite "l") 83 | let(((function_count 0)) 84 | foreachs(file suite->files 85 | function_count = function_count + file->FunctionCount() 86 | ) 87 | function_count 88 | )) 89 | 90 | procedure(FunctionPassCount(suite "l") 91 | let(((pass_count 0)) 92 | foreachs(file suite->files 93 | pass_count = pass_count + file->FunctionPassCount() 94 | ) 95 | pass_count 96 | )) 97 | 98 | procedure(ResetSuite() 99 | current_suite = nil 100 | ) 101 | 102 | procedure(GetCurrentSuite() 103 | current_suite) 104 | 105 | putprop(Test RunDirectory 'RunDirectory) 106 | putprop(Test ResetSuite 'ResetSuite) 107 | putprop(Test GetCurrentSuite 'GetCurrentSuite) 108 | ) 109 | -------------------------------------------------------------------------------- /virtue/virtue.cdsLibMgr.il: -------------------------------------------------------------------------------- 1 | printf("Initializing Virtue menu items\n") 2 | 3 | ;Popup Menu for libraries, cells, and views 4 | lmgrCreateMenuItem( "CopyPath" "simple" 5 | '(("label" "Copy Path" ) 6 | ("callback" ("VrtPathCopyCallback")) )) 7 | lmgrAddMenuItems( "editCascade" "LCVlcv" '("CopyPath") ) 8 | 9 | ; Library Manager Virtue Menu 10 | lmgrCreateMenu("VirtueMenu" '(("label" "Virtue"))) 11 | 12 | lmgrCreateMenuItem( "SkillBridgeToggle" "toggle" 13 | '(("label" "Start SkillBridge Server" ) 14 | ("callback" ("VrtSkillbridgeTogglePythonServerLmgrCallback")) )) 15 | lmgrAddMenuItems("VirtueMenu" "" '("SkillBridgeToggle")) 16 | unless(getShellEnvVar("VIRTUE_SKILLBRIDGE") 17 | lmgrSensitizeMenuItems('("SkillBridgeToggle") nil) 18 | ) 19 | 20 | lmgrCreateMenuItem("HelpSeperator" "separator" nil) 21 | 22 | lmgrCreateMenu("VirtueHelpMenu" '(("label" "Help"))) 23 | lmgrCreateMenuItem("VirtueHelp" "simple" 24 | '(("label" "Virtue Help" ) 25 | ("callback" ("VrtOpenDocumentationLmgrCallback")) )) 26 | lmgrCreateMenuItem( "SkillBridgeHelp" "simple" 27 | '(("label" "SkillBridge Help" ) 28 | ("callback" ("VrtSkillbridgeOpenDocumentationLmgrCallback")) )) 29 | lmgrAddMenuItems("VirtueHelpMenu" "" '("VirtueHelp" "SkillBridgeHelp")) 30 | lmgrAddMenuItems("VirtueMenu" "" '("VirtueHelpMenu")) 31 | 32 | lmgrAddMenuItems("menuBar" "L" '("VirtueMenu")) 33 | 34 | printf("Done, Initialized Virtue menu items\n") 35 | -------------------------------------------------------------------------------- /virtue/virtue.cdsinit.ils: -------------------------------------------------------------------------------- 1 | ; Define the iport table as a global in both SKILL and SKILL++ 2 | inSkill(VrtImport = makeTable("Virtue package import table containing just the latest version of each package")) 3 | importSkillVar(VrtImport) 4 | inSkill(Packages = makeTable("Virtue package import table containing all package versions")) 5 | importSkillVar(Packages) 6 | 7 | let((Virtue) 8 | 9 | Virtue = let(((module_description "SKILL++ Library and package manager") 10 | ) 11 | 12 | procedure(GetCurrentFilePath() 13 | "Gets the path to the skill file currently being loaded" 14 | simplifyFilename(get_filename(piport)) 15 | ) 16 | 17 | procedure(GetCurrentFileDirectory() 18 | "Gets the path to the directory containing the skill file currently being 19 | loaded" 20 | let((file_path) 21 | file_path = GetCurrentFilePath() 22 | strcat("/" buildString(reverse(cdr(reverse(parseString(file_path "/")))) "/")) 23 | )) 24 | 25 | procedure(OpenDocumentation() 26 | system("firefox http://www.cascode-labs.org/virtue/ &") 27 | ) 28 | 29 | procedure(OpenDocumentationLmgrCallback(_menuItem _lib _cell _view _file _cat) 30 | "Callback to open the Virtue help from the library manager window" 31 | OpenDocumentation() 32 | ) 33 | 34 | procedure(Initialize() 35 | let((dir_path preinit_packages Module Project virtue_packages) 36 | printf("\nInitializing Virtue SKILL++ Library\n") 37 | 38 | dir_path = GetCurrentFileDirectory() 39 | 40 | loadi(strcat(dir_path "/skill_package/Module.ils")) 41 | Module = VrtImport['Module] 42 | Virtue = Module->New('Virtue 43 | ?module VrtImport['Virtue] 44 | ?package VrtImport['Virtue] 45 | ?description module_description) 46 | 47 | preinit_packages = list( 48 | "data_types/Str.ils" 49 | "data_types/Path.ils" 50 | "Toml.ils" 51 | "skill_package/Package.ils" 52 | ) 53 | Module->LoadList(preinit_packages 54 | ?root_path dir_path 55 | ?continue_on_error 't) 56 | VrtImport['Package]->New(Virtue dir_path) 57 | 58 | virtue_packages = list( 59 | "data_types/List.ils" 60 | "test/Test.ils" 61 | "Geo.ils" 62 | "data_types/Dpl.ils" 63 | "data_types/Lcv.ils" 64 | "data_types/Time.ils" 65 | ; "Sch.ils" 66 | "MaeMaestro.ils" 67 | "MaeExport.ils" 68 | "HdbConfig.ils" 69 | "Skillbridge.ils" 70 | ) 71 | Module->LoadList(virtue_packages 72 | ?root_path dir_path 73 | ?continue_on_error 't) 74 | printf(" Initialized Virtue SKILL++ Library\n") 75 | )) 76 | 77 | list(nil 78 | 'VrtImport VrtImport 79 | 'Initialize Initialize 80 | 'GetCurrentFilePath GetCurrentFilePath 81 | 'GetCurrentFileDirectory GetCurrentFileDirectory 82 | 'OpenDocumentation OpenDocumentation 83 | 'OpenDocumentationLmgrCallback OpenDocumentationLmgrCallback 84 | )) 85 | 86 | VrtImport['Virtue] = Virtue 87 | Virtue->Initialize() 88 | ) 89 | 90 | ; Global functions required for library manager 91 | VrtOpenDocumentationLmgrCallback = 92 | VrtImport['Virtue]->OpenDocumentationLmgrCallback 93 | -------------------------------------------------------------------------------- /virtue/virtue_dev_work/README.md: -------------------------------------------------------------------------------- 1 | # Virtue Development Virtuoso Project 2 | -------------------------------------------------------------------------------- /virtuoso.yml: -------------------------------------------------------------------------------- 1 | # Virtue development environment 2 | name: virtuoso 3 | 4 | channels: 5 | - conda-forge 6 | - defaults 7 | 8 | dependencies: 9 | - virtue 10 | - softworks 11 | - skillbridge = 1.2.18 12 | - python=3.9 13 | - git 14 | - conda 15 | - mamba 16 | - pip 17 | --------------------------------------------------------------------------------