├── .flake8 ├── .github └── workflows │ ├── lint-check.yaml │ ├── mypy-check.yaml │ └── test-precheck.yaml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── caravel_template ├── caravel.lyp ├── dump_pic.rb └── klayoutrc ├── categories.json ├── cells.json ├── cells.py ├── config.py ├── config_utils.py ├── configure.py ├── def ├── analog │ ├── magic_init_project.tcl │ ├── tt_analog_1x2.def │ ├── tt_analog_1x2_3v3.def │ ├── tt_analog_2x2.def │ └── tt_analog_2x2_3v3.def ├── tt_block_1x1_pg.def ├── tt_block_1x2_pg.def ├── tt_block_2x2_pg.def ├── tt_block_3x2_pg.def ├── tt_block_3x4_pg.def ├── tt_block_4x2_pg.def ├── tt_block_4x4_pg.def ├── tt_block_5x4_pg.def ├── tt_block_6x2_pg.def └── tt_block_8x2_pg.def ├── discord_bot.py ├── docs ├── CREDITS.md ├── PINOUT.md ├── doc_header.md.mustache ├── doc_template.md.mustache ├── pics │ ├── efabless.png │ ├── tt-qfn-64.svg │ └── ttlogo.png ├── project_header.md ├── project_preview.md.mustache └── shuttle_index_header.md.mustache ├── documentation.py ├── fpga ├── tt_fpga_top.pcf └── tt_fpga_top.v ├── gds_compare.py ├── git_utils.py ├── ihp ├── categories.json ├── cells.json ├── def │ ├── tt_block_1x1_pgvdd.def │ ├── tt_block_1x2_pgvdd.def │ ├── tt_block_2x1_pgvdd.def │ ├── tt_block_2x2_pgvdd.def │ ├── tt_block_3x1_pgvdd.def │ ├── tt_block_3x2_pgvdd.def │ ├── tt_block_4x1_pgvdd.def │ ├── tt_block_4x2_pgvdd.def │ ├── tt_block_6x1_pgvdd.def │ ├── tt_block_6x2_pgvdd.def │ ├── tt_block_8x1_pgvdd.def │ └── tt_block_8x2_pgvdd.def └── tile_sizes.yaml ├── logo.py ├── logo ├── font │ ├── LICENSE │ └── UbuntuSansMono.ttf ├── tt_logo.png ├── tt_logo.svg ├── tt_logo_bottom.lef ├── tt_logo_bottom.v ├── tt_logo_top.gds ├── tt_logo_top.lef └── tt_logo_top.v ├── markdown_utils.py ├── precheck ├── .gitignore ├── default.nix ├── klayout_tools.py ├── magic_drc.tcl ├── pin_check.py ├── precheck.py ├── precheck_failure.py ├── pyproject.toml ├── reports │ └── .gitignore ├── requirements.txt ├── tech-files │ ├── README.txt │ ├── nwell_urpm.drc │ ├── pin_label_purposes_overlapping_drawing.rb.drc │ ├── sg13g2_mr.lydrc │ ├── sky130A_mr.drc │ ├── update.sh │ └── zeroarea.rb.drc ├── tech_data.py └── test_precheck.py ├── project.py ├── project_info.py ├── pyproject.toml ├── reharden.py ├── requirements.txt ├── rom.py ├── rom ├── .gitignore ├── config.json ├── config_ihp.json └── tt_um_chip_rom.v ├── shuttle.py ├── shuttle_index.py ├── testing ├── README.md ├── lib │ └── testutils │ │ ├── __init__.py │ │ └── truthtable.py └── src-tpl │ ├── Makefile │ ├── test.py │ └── test_wokwi.v ├── tile_sizes.py ├── tile_sizes.yaml ├── tt_annotate.py └── tt_tool.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | extend-ignore = E203,E501,F541 3 | -------------------------------------------------------------------------------- /.github/workflows/lint-check.yaml: -------------------------------------------------------------------------------- 1 | name: Python Lint 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | lint-check: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v2 15 | 16 | - name: Install pre-commit 17 | run: pip install pre-commit 18 | 19 | - name: Check code with pre-commit 20 | run: pre-commit run --all-files 21 | -------------------------------------------------------------------------------- /.github/workflows/mypy-check.yaml: -------------------------------------------------------------------------------- 1 | name: Mypy Type Checking 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | type-check: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v2 15 | 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: '3.11' 20 | 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 25 | 26 | - name: Install mypy 27 | run: | 28 | pip install mypy 29 | 30 | - name: Install type packages 31 | run: | 32 | yes | mypy . --install-types 33 | continue-on-error: true 34 | 35 | - name: Run mypy 36 | run: | 37 | mypy cells.py config.py configure.py documentation.py git_utils.py markdown_utils.py project.py rom.py shuttle.py --check-untyped-defs 38 | -------------------------------------------------------------------------------- /.github/workflows/test-precheck.yaml: -------------------------------------------------------------------------------- 1 | name: Precheck Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | test-precheck: 10 | runs-on: ubuntu-latest 11 | env: 12 | PDK_ROOT: /home/runner/pdk 13 | PDK_VERSION: dd7771c384ed36b91a25e9f8b314355fc26561be 14 | 15 | steps: 16 | - name: Check out code 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: '3.11' 23 | cache: 'pip' 24 | 25 | - name: Install dependencies 26 | working-directory: precheck 27 | run: pip install -r requirements.txt 28 | 29 | - name: Install Sky130 PDK 30 | uses: TinyTapeout/volare-action@v2 31 | with: 32 | pdk_name: sky130 33 | pdk_version: ${{ env.PDK_VERSION }} 34 | pdk_root: ${{ env.PDK_ROOT }} 35 | 36 | - name: Install Nix 37 | uses: rikhuijzer/cache-install@v1.1.4 38 | with: 39 | key: nix-${{ hashFiles('precheck/default.nix') }} 40 | nix_file: 'precheck/default.nix' 41 | 42 | - name: Build Nix packages 43 | working-directory: precheck 44 | run: nix-build 45 | 46 | - name: Run tests 47 | working-directory: precheck 48 | run: nix-shell --run "pytest" 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *swp 2 | setuptokens.sh 3 | __pycache__ 4 | *.egg-info 5 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile = black 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/PyCQA/flake8 3 | rev: 7.1.1 4 | hooks: 5 | - id: flake8 6 | 7 | - repo: https://github.com/PyCQA/isort 8 | rev: 5.13.2 9 | hooks: 10 | - id: isort 11 | 12 | - repo: https://github.com/ambv/black 13 | rev: 24.10.0 14 | hooks: 15 | - id: black 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiny Tapeout tools 2 | 3 | Tools used mostly by the Tiny Tapeout GitHub Actions 4 | 5 | ## Setup 6 | 7 | ``` 8 | pip install -r requirements.txt 9 | pre-commit install 10 | ``` 11 | 12 | ## Documentation 13 | 14 | * checks that basic fields are present in the info.yaml 15 | * create a PDF data sheet for this one design 16 | * create SVG and PNG renders of the project's GDS 17 | 18 | ## Reports 19 | 20 | * routing & utilisation 21 | * yosys warnings 22 | * standard cell usage and summary 23 | 24 | ## Configuration 25 | 26 | * creates a little TCL shim that tells OpenLane where the source is and the name of the top module 27 | * makes sure the top module is not called 'top' 28 | * if you have OpenLane installed locally, then you can harden the design with --harden 29 | -------------------------------------------------------------------------------- /caravel_template/dump_pic.rb: -------------------------------------------------------------------------------- 1 | RBA::Application.instance.main_window.current_view.save_image("./pics/tinytapeout.png",2000,2000) 2 | -------------------------------------------------------------------------------- /categories.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories" : [ 3 | "AND", 4 | "OR", 5 | "NAND", 6 | "NOR", 7 | "Clock", 8 | "Flip Flops", 9 | "Multiplexer", 10 | "Latch", 11 | "Inverter", 12 | "Buffer", 13 | "Fill", 14 | "Tap", 15 | "Diode", 16 | "Combo Logic", 17 | "Misc" 18 | ], 19 | "map" : { 20 | "a2111o" : 13, 21 | "a2111oi" : 13, 22 | "a211o" : 13, 23 | "a211oi" : 13, 24 | "a21bo" : 13, 25 | "a21boi" : 0, 26 | "a21o" : 13, 27 | "a21oi" : 13, 28 | "a221o" : 13, 29 | "a221oi" : 13, 30 | "a222oi" : 13, 31 | "a22o" : 13, 32 | "a22oi" : 13, 33 | "a2bb2o" : 13, 34 | "a2bb2oi" : 13, 35 | "a311o" : 13, 36 | "a311oi" : 13, 37 | "a31o" : 13, 38 | "a31oi" : 13, 39 | "a32o" : 13, 40 | "a32oi" : 13, 41 | "a41o" : 13, 42 | "a41oi" : 13, 43 | "and2" : 0, 44 | "and2b" : 13, 45 | "and3" : 0, 46 | "and3b" : 13, 47 | "and4" : 0, 48 | "and4b" : 13, 49 | "and4bb" : 13, 50 | "buf" : 9, 51 | "bufbuf" : 9, 52 | "bufinv" : 9, 53 | "clkbuf" : 9, 54 | "clkdlybuf4s15" : 4, 55 | "clkdlybuf4s18" : 4, 56 | "clkdlybuf4s25" : 4, 57 | "clkdlybuf4s50" : 4, 58 | "clkinv" : 4, 59 | "clkinvlp" : 4, 60 | "conb" : 14, 61 | "decap" : 10, 62 | "dfbbn" : 5, 63 | "dfbbp" : 5, 64 | "dfrbp" : 5, 65 | "dfrtn" : 5, 66 | "dfrtp" : 5, 67 | "dfsbp" : 5, 68 | "dfstp" : 5, 69 | "dfxbp" : 5, 70 | "dfxtp" : 5, 71 | "diode" : 12, 72 | "dlclkp" : 4, 73 | "dlrbn" : 7, 74 | "dlrbp" : 7, 75 | "dlrtn" : 7, 76 | "dlrtp" : 7, 77 | "dlxbn" : 7, 78 | "dlxbp" : 7, 79 | "dlxtn" : 7, 80 | "dlxtp" : 7, 81 | "dlygate4sd1" : 14, 82 | "dlygate4sd2" : 14, 83 | "dlygate4sd3" : 14, 84 | "dlymetal6s2s" : 14, 85 | "dlymetal6s4s" : 14, 86 | "dlymetal6s6s" : 14, 87 | "ebufn" : 14, 88 | "edfxbp" : 5, 89 | "edfxtp" : 5, 90 | "einvn" : 14, 91 | "einvp" : 11, 92 | "fa" : 14, 93 | "fah" : 14, 94 | "fahcin" : 14, 95 | "fahcon" : 14, 96 | "fill" : 10, 97 | "ha" : 14, 98 | "inv" : 8, 99 | "lpflow_bleeder" : 14, 100 | "lpflow_clkbufkapwr" : 4, 101 | "lpflow_clkinvkapwr" : 4, 102 | "lpflow_decapkapwr" : 10, 103 | "lpflow_inputiso0n" : 14, 104 | "lpflow_inputiso0p" : 14, 105 | "lpflow_inputiso1n" : 14, 106 | "lpflow_inputiso1p" : 14, 107 | "lpflow_inputisolatch" : 14, 108 | "lpflow_isobufsrc" : 14, 109 | "lpflow_isobufsrckapwr" : 14, 110 | "lpflow_lsbuf_lh_hl_isowell_tap" : 9, 111 | "lpflow_lsbuf_lh_isowell" : 9, 112 | "lpflow_lsbuf_lh_isowell_tap" : 9, 113 | "macro_sparecell" : 14, 114 | "maj3" : 14, 115 | "mux2" : 6, 116 | "mux2i" : 6, 117 | "mux4" : 6, 118 | "nand2" : 2, 119 | "nand2b" : 2, 120 | "nand3" : 2, 121 | "nand3b" : 13, 122 | "nand4" : 2, 123 | "nand4b" : 13, 124 | "nand4bb" : 13, 125 | "nor2" : 3, 126 | "nor2b" : 13, 127 | "nor3" : 3, 128 | "nor3b" : 13, 129 | "nor4" : 3, 130 | "nor4b" : 13, 131 | "nor4bb" : 13, 132 | "o2111a" : 13, 133 | "o2111ai" : 13, 134 | "o211a" : 13, 135 | "o211ai" : 13, 136 | "o21a" : 13, 137 | "o21ai" : 13, 138 | "o21ba" : 13, 139 | "o21bai" : 13, 140 | "o221a" : 13, 141 | "o221ai" : 13, 142 | "o22a" : 13, 143 | "o22ai" : 13, 144 | "o2bb2a" : 13, 145 | "o2bb2ai" : 13, 146 | "o311a" : 13, 147 | "o311ai" : 13, 148 | "o31a" : 13, 149 | "o31ai" : 13, 150 | "o32a" : 13, 151 | "o32ai" : 13, 152 | "o41a" : 13, 153 | "o41ai" : 13, 154 | "or2" : 1, 155 | "or2b" : 13, 156 | "or3" : 1, 157 | "or3b" : 13, 158 | "or4" : 1, 159 | "or4b" : 13, 160 | "or4bb" : 13, 161 | "probe_p" : 14, 162 | "probec_p" : 14, 163 | "sdfbbn" : 5, 164 | "sdfbbp" : 5, 165 | "sdfrbp" : 5, 166 | "sdfrtn" : 5, 167 | "sdfrtp" : 5, 168 | "sdfsbp" : 5, 169 | "sdfstp" : 5, 170 | "sdfxbp" : 5, 171 | "sdfxtp" : 5, 172 | "sdlclkp" : 4, 173 | "sedfxbp" : 5, 174 | "sedfxtp" : 5, 175 | "tap" : 11, 176 | "tapvgnd" : 11, 177 | "tapvgnd2" : 11, 178 | "tapvpwrvgnd" : 11, 179 | "xnor2" : 3, 180 | "xnor3" : 3, 181 | "xor2" : 1, 182 | "xor3" : 1 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /cells.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from typing import Dict, List, Optional, TypedDict 4 | 5 | 6 | class Port(TypedDict): 7 | kind: str 8 | name: str 9 | direction: str 10 | description: str 11 | 12 | 13 | class Sky130Cell(TypedDict): 14 | description: str 15 | file_prefix: str 16 | library: str 17 | name: str 18 | parameters: List[str] 19 | ports: List[Port] 20 | type: str 21 | verilog_name: str 22 | equation: Optional[str] 23 | 24 | 25 | class IHPCell(TypedDict): 26 | description: str 27 | doc_name: str 28 | doc_ref: int 29 | 30 | 31 | Sky130Cells = Dict[str, Sky130Cell] 32 | IHPCells = Dict[str, IHPCell] 33 | 34 | 35 | def load_sky130_cells() -> Sky130Cells: 36 | script_dir = os.path.dirname(os.path.realpath(__file__)) 37 | with open(os.path.join(script_dir, "cells.json")) as fh: 38 | return json.load(fh) 39 | 40 | 41 | def load_ihp_cells() -> IHPCells: 42 | script_dir = os.path.dirname(os.path.realpath(__file__)) 43 | with open(os.path.join(script_dir, "ihp/cells.json")) as fh: 44 | return json.load(fh) 45 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | 4 | class Config(TypedDict): 5 | """TypedDict for Tiny Tapeout's config.yaml file.""" 6 | 7 | id: str 8 | name: str 9 | project_dir: str 10 | end_date: str 11 | top_level_macro: str 12 | powered_netlists: bool 13 | no_power_gating: bool 14 | -------------------------------------------------------------------------------- /config_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import os 4 | import subprocess 5 | from collections.abc import Iterable 6 | 7 | import yaml 8 | 9 | 10 | class ConfigFileError(Exception): 11 | pass 12 | 13 | 14 | def read_json_config(file: str): 15 | config = json.load(open(file)) 16 | config.pop("//", None) 17 | return config 18 | 19 | 20 | def read_yaml_config(file: str): 21 | return yaml.safe_load(open(file)) 22 | 23 | 24 | def read_mk_config(file: str, design_dir: str | None = None): 25 | logging.warning( 26 | "Reading Makefile configuration files is an experimental feature and could be removed" 27 | ) 28 | env_pre = {} if design_dir is None else {"DESIGN_HOME": design_dir} 29 | mk_cg = "all:\n\t@env" 30 | mk_tg = f"-include {file}\n{mk_cg}" 31 | env_cg = subprocess.run( 32 | "make -f -", 33 | shell=True, 34 | env=env_pre, 35 | input=mk_cg, 36 | capture_output=True, 37 | text=True, 38 | ).stdout.strip() 39 | env_tg = subprocess.run( 40 | "make -f -", 41 | shell=True, 42 | env=env_pre, 43 | input=mk_tg, 44 | capture_output=True, 45 | text=True, 46 | ).stdout.strip() 47 | env_diff = set(env_tg.split("\n")) - set(env_cg.split("\n")) 48 | config = dict(i.split("=", 1) for i in env_diff) 49 | return config 50 | 51 | 52 | def read_config(basename: str, formats: Iterable[str], design_dir: str | None = None): 53 | for fmt in formats: 54 | file = f"{basename}.{fmt}" 55 | if os.path.exists(file): 56 | if fmt == "json": 57 | return read_json_config(file) 58 | elif fmt == "yaml": 59 | return read_yaml_config(file) 60 | elif fmt == "mk": 61 | return read_mk_config(file) 62 | else: 63 | raise ConfigFileError(f"Unexpected configuration file format: {fmt}") 64 | raise ConfigFileError( 65 | f"Could not file configuration file {basename}.{{{'|'.join(formats)}}}" 66 | ) 67 | 68 | 69 | def write_json_config(config: dict, file: str): 70 | with open(file, "w") as f: 71 | json.dump(config, f, indent=2) 72 | 73 | 74 | def write_yaml_config(config: dict, file: str): 75 | with open(file, "w") as f: 76 | yaml.safe_dump(config, f, default_flow_style=False, sort_keys=False) 77 | 78 | 79 | def write_mk_config(config: dict, file: str): 80 | with open(file, "w") as f: 81 | for key, value in config.items(): 82 | if type(value) in (list, tuple): 83 | value = " ".join(value) 84 | if type(value) is str: 85 | value = value.replace("dir::", "$(DESIGN_HOME)/") 86 | print(f"export {key} = {value}", file=f) 87 | 88 | 89 | def write_config(config: dict, basename: str, formats: Iterable[str]): 90 | for fmt in formats: 91 | file = f"{basename}.{fmt}" 92 | if fmt == "json": 93 | write_json_config(config, file) 94 | elif fmt == "yaml": 95 | write_yaml_config(config, file) 96 | elif fmt == "mk": 97 | write_mk_config(config, file) 98 | else: 99 | raise ConfigFileError("Unexpected configuration file format: {fmt}") 100 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import collections 4 | import datetime 5 | import json 6 | import logging 7 | import os 8 | import sys 9 | 10 | # pipe handling 11 | from signal import SIG_DFL, SIGPIPE, signal 12 | from typing import Dict, List, TypedDict 13 | 14 | import yaml 15 | 16 | from config import Config 17 | from documentation import Docs 18 | from logo import LogoGenerator 19 | from project import Project 20 | from rom import ROMFile 21 | from shuttle import ShuttleConfig 22 | 23 | signal(SIGPIPE, SIG_DFL) 24 | 25 | 26 | class Projects: 27 | def __init__(self, config: Config, args): 28 | self.args = args 29 | self.config = config 30 | self.project_dir = config["project_dir"] 31 | 32 | if not os.path.exists(self.project_dir): 33 | os.makedirs(self.project_dir) 34 | 35 | only_projects = os.getenv("TT_ONLY_PROJECTS") 36 | only_projects_list = only_projects.split(",") if only_projects else None 37 | 38 | self.projects: List[Project] = [] 39 | project_list = [ 40 | entry 41 | for entry in os.listdir(self.project_dir) 42 | if os.path.isdir(os.path.join(self.project_dir, entry)) 43 | ] 44 | if args.test: 45 | project_list = ["tt_um_chip_rom", "tt_um_factory_test"] 46 | elif args.sta_projects: 47 | project_list = ["tt_um_loopback"] 48 | 49 | for index, project_id in enumerate(project_list): 50 | project_dir = os.path.join(self.project_dir, project_id) 51 | 52 | commit_id_file = os.path.join(project_dir, "commit_id.json") 53 | if not os.path.exists(commit_id_file): 54 | logging.warning(f"no commit_id.json in {project_dir}, skippinggi") 55 | continue 56 | 57 | commit_id_data = json.load(open(commit_id_file)) 58 | if commit_id_data.get("skip", False): 59 | logging.warning(f"skipping {project_dir} (skip flag set)") 60 | continue 61 | 62 | if only_projects_list and project_id not in only_projects_list: 63 | continue 64 | 65 | project = Project( 66 | index, 67 | commit_id_data["repo"], 68 | project_dir, 69 | args, 70 | is_user_project=False, 71 | ) 72 | project.commit_id = commit_id_data["commit"] 73 | project.sort_id = commit_id_data["sort_id"] 74 | 75 | # projects should now be installed, so load all the data from the yaml files 76 | # fill projects will load from the fill project's directory 77 | logging.debug("post clone setup") 78 | project.post_clone_setup() 79 | logging.debug(project) 80 | 81 | if args.harden: 82 | project.create_user_config() 83 | project.golden_harden() 84 | 85 | if args.update_shuttle: 86 | project.check_ports(bool(config.get("powered_netlists", True))) 87 | project.check_num_cells() 88 | 89 | self.projects.append(project) 90 | 91 | self.projects.sort(key=lambda x: x.sort_id) 92 | 93 | all_macro_instances = [project.get_macro_name() for project in self.projects] 94 | self.assert_unique(all_macro_instances) 95 | 96 | all_gds_files = [project.get_macro_gds_filename() for project in self.projects] 97 | self.assert_unique(all_gds_files) 98 | 99 | logging.info(f"loaded {len(self.projects)} projects") 100 | 101 | def assert_unique(self, check: List[str]): 102 | duplicates = [ 103 | item for item, count in collections.Counter(check).items() if count > 1 104 | ] 105 | if duplicates: 106 | logging.error("duplicate projects: {}".format(duplicates)) 107 | exit(1) 108 | 109 | def build_metrics(self): 110 | total_seconds = 0.0 111 | total_wire_length = 0 112 | total_wires_count = 0 113 | total_physical_cells = 0 114 | max_cells = 0 115 | min_cells = 1000 116 | max_cell_project = None 117 | max_util = 0.0 118 | min_util = 100.0 119 | max_util_project = None 120 | languages: Dict[str, int] = {} 121 | 122 | for project in self.projects: 123 | try: 124 | dt = datetime.datetime.strptime( 125 | project.metrics["total_runtime"][:-3], "%Hh%Mm%Ss" 126 | ) 127 | except KeyError: 128 | continue 129 | 130 | if project.is_chip_rom(): 131 | continue 132 | 133 | delt = datetime.timedelta( 134 | hours=dt.hour, minutes=dt.minute, seconds=dt.second 135 | ) 136 | total_seconds += delt.total_seconds() 137 | 138 | cell_count = project.get_cell_counts_from_gl() 139 | script_dir = os.path.dirname(os.path.realpath(__file__)) 140 | with open(os.path.join(script_dir, "categories.json")) as fh: 141 | categories = json.load(fh) 142 | CategoryInfo = TypedDict( 143 | "CategoryInfo", {"count": int, "examples": List[str]} 144 | ) 145 | by_category: Dict[str, CategoryInfo] = {} 146 | total = 0 147 | for cell_name in cell_count: 148 | cat_index = categories["map"][cell_name] 149 | cat_name = categories["categories"][cat_index] 150 | if cat_name in by_category: 151 | by_category[cat_name]["count"] += cell_count[cell_name] 152 | by_category[cat_name]["examples"].append(cell_name) 153 | else: 154 | by_category[cat_name] = { 155 | "count": cell_count[cell_name], 156 | "examples": [cell_name], 157 | } 158 | 159 | if cat_name not in ["Fill", "Tap", "Buffer", "Misc"]: 160 | total += cell_count[cell_name] 161 | 162 | if total < 10: 163 | del by_category["Fill"] 164 | del by_category["Tap"] 165 | if "Buffer" in by_category: 166 | del by_category["Buffer"] 167 | print(project.get_macro_name(), total, by_category) 168 | 169 | total_wire_length += int(project.metrics["wire_length"]) 170 | total_wires_count += int(project.metrics["wires_count"]) 171 | util = float(project.metrics["OpenDP_Util"]) 172 | num_cells = project.get_cell_count_from_synth() 173 | total_physical_cells += num_cells 174 | 175 | lang = project.info.language 176 | if lang in languages: 177 | languages[lang] += 1 178 | else: 179 | languages[lang] = 1 180 | 181 | if num_cells > max_cells: 182 | max_cells = num_cells 183 | max_cell_project = project 184 | if num_cells < min_cells: 185 | min_cells = num_cells 186 | 187 | if util > max_util: 188 | max_util = util 189 | max_util_project = project 190 | if util < min_util: 191 | min_util = util 192 | 193 | logging.info(f"build time for all projects {total_seconds / 3600} hrs") 194 | logging.info(f"total wire length {total_wire_length} um") 195 | logging.info(f"total cells {total_physical_cells}") 196 | logging.info(f"max cells {max_cells} for project {max_cell_project}") 197 | logging.info(f"min cells {min_cells}") 198 | logging.info(f"max util {max_util} for project {max_util_project}") 199 | logging.info(f"min util {min_util}") 200 | logging.info(f"languages {languages}") 201 | 202 | 203 | if __name__ == "__main__": 204 | parser = argparse.ArgumentParser(description="Tiny Tapeout configuration and docs") 205 | 206 | with open("config.yaml") as fh: 207 | config = yaml.safe_load(fh) 208 | 209 | parser.add_argument( 210 | "--list", help="list projects", action="store_const", const=True 211 | ) 212 | parser.add_argument( 213 | "--single", help="do action on single project", type=int, default=-1 214 | ) 215 | parser.add_argument( 216 | "--update-shuttle", 217 | help="configure shuttle for build", 218 | action="store_const", 219 | const=True, 220 | ) 221 | parser.add_argument( 222 | "--copy-macros", 223 | help="copy macros for building the tt_top project", 224 | action="store_const", 225 | const=True, 226 | ) 227 | parser.add_argument( 228 | "--copy-final-results", 229 | help="copy final project files to gds/lef directories", 230 | action="store_const", 231 | const=True, 232 | ) 233 | parser.add_argument( 234 | "--create-efabless-submission", 235 | help="create efabless submission directory", 236 | action="store_const", 237 | const=True, 238 | ) 239 | parser.add_argument( 240 | "--create-ihp-submission", 241 | help="create ihp submission directory", 242 | action="store_const", 243 | const=True, 244 | ) 245 | parser.add_argument( 246 | "--harden", help="harden project", action="store_const", const=True 247 | ) 248 | parser.add_argument( 249 | "--test", help="use test projects", action="store_const", const=True 250 | ) 251 | parser.add_argument( 252 | "--sta-projects", help="use sta projects", action="store_const", const=True 253 | ) 254 | parser.add_argument( 255 | "--debug", 256 | help="debug logging", 257 | action="store_const", 258 | dest="loglevel", 259 | const=logging.DEBUG, 260 | default=logging.INFO, 261 | ) 262 | parser.add_argument( 263 | "--log-email", 264 | help="print persons email in messages", 265 | action="store_const", 266 | const=True, 267 | ) 268 | parser.add_argument( 269 | "--update-image", help="update the image", action="store_const", const=True 270 | ) 271 | parser.add_argument( 272 | "--dump-json", help="dump json of all project data to given file" 273 | ) 274 | parser.add_argument( 275 | "--dump-markdown", help="dump markdown of all project data to given file" 276 | ) 277 | parser.add_argument("--dump-pdf", help="create pdf from the markdown") 278 | parser.add_argument( 279 | "--metrics", help="print some project metrics", action="store_const", const=True 280 | ) 281 | 282 | args = parser.parse_args() 283 | 284 | # setup log 285 | log_format = logging.Formatter("%(asctime)s - %(levelname)-8s - %(message)s") 286 | # configure the client logging 287 | log = logging.getLogger("") 288 | # has to be set to debug as is the root logger 289 | log.setLevel(args.loglevel) 290 | 291 | # create console handler and set level to info 292 | ch = logging.StreamHandler(sys.stdout) 293 | # create formatter for console 294 | ch.setFormatter(log_format) 295 | log.addHandler(ch) 296 | 297 | projects = Projects(config, args) 298 | 299 | if args.test: 300 | modules_yaml_name = "modules.test.yaml" 301 | elif args.sta_projects: 302 | modules_yaml_name = "modules.sta.yaml" 303 | else: 304 | modules_yaml_name = "modules.yaml" 305 | 306 | docs = Docs(config, projects.projects) 307 | shuttle = ShuttleConfig(config, projects.projects, modules_yaml_name) 308 | rom = ROMFile(config) 309 | logo = LogoGenerator("tt", config) 310 | 311 | if args.list: 312 | shuttle.list() 313 | 314 | if args.metrics: 315 | projects.build_metrics() 316 | 317 | if args.update_shuttle: 318 | shuttle.configure_mux() 319 | rom.write_rom() 320 | logo.gen_logo("bottom", "tt/logo/tt_logo_bottom.gds") 321 | if not args.test: 322 | docs.build_index() 323 | 324 | if args.copy_macros: 325 | shuttle.copy_macros() 326 | 327 | if args.copy_final_results: 328 | shuttle.copy_final_results() 329 | 330 | if args.create_efabless_submission: 331 | shuttle.create_foundry_submission("efabless", True) 332 | 333 | if args.create_ihp_submission: 334 | shuttle.create_foundry_submission("ihp", False) 335 | 336 | if args.update_image: 337 | docs.update_image() 338 | 339 | if args.dump_markdown: 340 | shuttle.configure_mux() 341 | docs.write_datasheet(args.dump_markdown, args.dump_pdf) 342 | -------------------------------------------------------------------------------- /def/analog/magic_init_project.tcl: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright (c) 2024 Tiny Tapeout LTD 3 | # Author: Uri Shaked 4 | # Description: This script initializes a new Magic project for an analog design on Tiny Tapeout. 5 | 6 | # Important: before running this script, download the the .def file from 7 | # https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/tt08/def/analog/tt_analog_1x2.def 8 | 9 | # Change the settings below to match your design: 10 | # ------------------------------------------------ 11 | set TOP_LEVEL_CELL tt_um_analog_example 12 | set TEMPLATE_FILE tt_analog_1x2.def 13 | set POWER_STRIPE_WIDTH 2um ;# The minimum width is 1.2um 14 | 15 | # Power stripes: NET name, x position. You can add additional power stripes for each net, as needed. 16 | set POWER_STRIPES { 17 | VDPWR 1um 18 | VGND 4um 19 | } 20 | # If you use the 3v3 template, uncomment the line below: 21 | #lappend POWER_STRIPES VAPWR 7um 22 | 23 | # Read in the pin positions 24 | # ------------------------- 25 | def read $TEMPLATE_FILE 26 | cellname rename tt_um_template $TOP_LEVEL_CELL 27 | 28 | # Draw the power stripes 29 | # -------------------------------- 30 | proc draw_power_stripe {name x} { 31 | global POWER_STRIPE_WIDTH 32 | box $x 5um $x 220.76um 33 | box width $POWER_STRIPE_WIDTH 34 | paint met4 35 | label $name FreeSans 0.25u -met4 36 | port make 37 | port use [expr {$name eq "VGND" ? "ground" : "power"}] 38 | port class bidirectional 39 | port connections n s e w 40 | } 41 | 42 | # You can extra power stripes, as you need. 43 | foreach {name x} $POWER_STRIPES { 44 | puts "Drawing power stripe $name at $x" 45 | draw_power_stripe $name $x 46 | } 47 | 48 | # Save the layout and export GDS/LEF 49 | # ---------------------------------- 50 | save ${TOP_LEVEL_CELL}.mag 51 | file mkdir gds 52 | gds write gds/${TOP_LEVEL_CELL}.gds 53 | file mkdir lef 54 | lef write lef/${TOP_LEVEL_CELL}.lef -hide -pinonly 55 | -------------------------------------------------------------------------------- /def/tt_block_1x1_pg.def: -------------------------------------------------------------------------------- 1 | VERSION 5.8 ; 2 | DIVIDERCHAR "/" ; 3 | BUSBITCHARS "[]" ; 4 | DESIGN tt_um_template ; 5 | UNITS DISTANCE MICRONS 1000 ; 6 | DIEAREA ( 0 0 ) ( 161000 111520 ) ; 7 | ROW ROW_0 unithd 2760 2720 N DO 338 BY 1 STEP 460 0 ; 8 | ROW ROW_1 unithd 2760 5440 FS DO 338 BY 1 STEP 460 0 ; 9 | ROW ROW_2 unithd 2760 8160 N DO 338 BY 1 STEP 460 0 ; 10 | ROW ROW_3 unithd 2760 10880 FS DO 338 BY 1 STEP 460 0 ; 11 | ROW ROW_4 unithd 2760 13600 N DO 338 BY 1 STEP 460 0 ; 12 | ROW ROW_5 unithd 2760 16320 FS DO 338 BY 1 STEP 460 0 ; 13 | ROW ROW_6 unithd 2760 19040 N DO 338 BY 1 STEP 460 0 ; 14 | ROW ROW_7 unithd 2760 21760 FS DO 338 BY 1 STEP 460 0 ; 15 | ROW ROW_8 unithd 2760 24480 N DO 338 BY 1 STEP 460 0 ; 16 | ROW ROW_9 unithd 2760 27200 FS DO 338 BY 1 STEP 460 0 ; 17 | ROW ROW_10 unithd 2760 29920 N DO 338 BY 1 STEP 460 0 ; 18 | ROW ROW_11 unithd 2760 32640 FS DO 338 BY 1 STEP 460 0 ; 19 | ROW ROW_12 unithd 2760 35360 N DO 338 BY 1 STEP 460 0 ; 20 | ROW ROW_13 unithd 2760 38080 FS DO 338 BY 1 STEP 460 0 ; 21 | ROW ROW_14 unithd 2760 40800 N DO 338 BY 1 STEP 460 0 ; 22 | ROW ROW_15 unithd 2760 43520 FS DO 338 BY 1 STEP 460 0 ; 23 | ROW ROW_16 unithd 2760 46240 N DO 338 BY 1 STEP 460 0 ; 24 | ROW ROW_17 unithd 2760 48960 FS DO 338 BY 1 STEP 460 0 ; 25 | ROW ROW_18 unithd 2760 51680 N DO 338 BY 1 STEP 460 0 ; 26 | ROW ROW_19 unithd 2760 54400 FS DO 338 BY 1 STEP 460 0 ; 27 | ROW ROW_20 unithd 2760 57120 N DO 338 BY 1 STEP 460 0 ; 28 | ROW ROW_21 unithd 2760 59840 FS DO 338 BY 1 STEP 460 0 ; 29 | ROW ROW_22 unithd 2760 62560 N DO 338 BY 1 STEP 460 0 ; 30 | ROW ROW_23 unithd 2760 65280 FS DO 338 BY 1 STEP 460 0 ; 31 | ROW ROW_24 unithd 2760 68000 N DO 338 BY 1 STEP 460 0 ; 32 | ROW ROW_25 unithd 2760 70720 FS DO 338 BY 1 STEP 460 0 ; 33 | ROW ROW_26 unithd 2760 73440 N DO 338 BY 1 STEP 460 0 ; 34 | ROW ROW_27 unithd 2760 76160 FS DO 338 BY 1 STEP 460 0 ; 35 | ROW ROW_28 unithd 2760 78880 N DO 338 BY 1 STEP 460 0 ; 36 | ROW ROW_29 unithd 2760 81600 FS DO 338 BY 1 STEP 460 0 ; 37 | ROW ROW_30 unithd 2760 84320 N DO 338 BY 1 STEP 460 0 ; 38 | ROW ROW_31 unithd 2760 87040 FS DO 338 BY 1 STEP 460 0 ; 39 | ROW ROW_32 unithd 2760 89760 N DO 338 BY 1 STEP 460 0 ; 40 | ROW ROW_33 unithd 2760 92480 FS DO 338 BY 1 STEP 460 0 ; 41 | ROW ROW_34 unithd 2760 95200 N DO 338 BY 1 STEP 460 0 ; 42 | ROW ROW_35 unithd 2760 97920 FS DO 338 BY 1 STEP 460 0 ; 43 | ROW ROW_36 unithd 2760 100640 N DO 338 BY 1 STEP 460 0 ; 44 | ROW ROW_37 unithd 2760 103360 FS DO 338 BY 1 STEP 460 0 ; 45 | ROW ROW_38 unithd 2760 106080 N DO 338 BY 1 STEP 460 0 ; 46 | TRACKS X 230 DO 350 STEP 460 LAYER li1 ; 47 | TRACKS Y 170 DO 328 STEP 340 LAYER li1 ; 48 | TRACKS X 170 DO 473 STEP 340 LAYER met1 ; 49 | TRACKS Y 170 DO 328 STEP 340 LAYER met1 ; 50 | TRACKS X 230 DO 350 STEP 460 LAYER met2 ; 51 | TRACKS Y 230 DO 242 STEP 460 LAYER met2 ; 52 | TRACKS X 340 DO 237 STEP 680 LAYER met3 ; 53 | TRACKS Y 340 DO 164 STEP 680 LAYER met3 ; 54 | TRACKS X 460 DO 175 STEP 920 LAYER met4 ; 55 | TRACKS Y 460 DO 121 STEP 920 LAYER met4 ; 56 | TRACKS X 1700 DO 47 STEP 3400 LAYER met5 ; 57 | TRACKS Y 1700 DO 33 STEP 3400 LAYER met5 ; 58 | COMPONENTS 0 ; 59 | END COMPONENTS 60 | PINS 43 ; 61 | - clk + NET clk + DIRECTION INPUT + USE SIGNAL 62 | + PORT 63 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 64 | + PLACED ( 143980 111020 ) N ; 65 | - ena + NET ena + DIRECTION INPUT + USE SIGNAL 66 | + PORT 67 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 68 | + PLACED ( 146740 111020 ) N ; 69 | - rst_n + NET rst_n + DIRECTION INPUT + USE SIGNAL 70 | + PORT 71 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 72 | + PLACED ( 141220 111020 ) N ; 73 | - ui_in[0] + NET ui_in[0] + DIRECTION INPUT + USE SIGNAL 74 | + PORT 75 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 76 | + PLACED ( 138460 111020 ) N ; 77 | - ui_in[1] + NET ui_in[1] + DIRECTION INPUT + USE SIGNAL 78 | + PORT 79 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 80 | + PLACED ( 135700 111020 ) N ; 81 | - ui_in[2] + NET ui_in[2] + DIRECTION INPUT + USE SIGNAL 82 | + PORT 83 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 84 | + PLACED ( 132940 111020 ) N ; 85 | - ui_in[3] + NET ui_in[3] + DIRECTION INPUT + USE SIGNAL 86 | + PORT 87 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 88 | + PLACED ( 130180 111020 ) N ; 89 | - ui_in[4] + NET ui_in[4] + DIRECTION INPUT + USE SIGNAL 90 | + PORT 91 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 92 | + PLACED ( 127420 111020 ) N ; 93 | - ui_in[5] + NET ui_in[5] + DIRECTION INPUT + USE SIGNAL 94 | + PORT 95 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 96 | + PLACED ( 124660 111020 ) N ; 97 | - ui_in[6] + NET ui_in[6] + DIRECTION INPUT + USE SIGNAL 98 | + PORT 99 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 100 | + PLACED ( 121900 111020 ) N ; 101 | - ui_in[7] + NET ui_in[7] + DIRECTION INPUT + USE SIGNAL 102 | + PORT 103 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 104 | + PLACED ( 119140 111020 ) N ; 105 | - uio_in[0] + NET uio_in[0] + DIRECTION INPUT + USE SIGNAL 106 | + PORT 107 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 108 | + PLACED ( 116380 111020 ) N ; 109 | - uio_in[1] + NET uio_in[1] + DIRECTION INPUT + USE SIGNAL 110 | + PORT 111 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 112 | + PLACED ( 113620 111020 ) N ; 113 | - uio_in[2] + NET uio_in[2] + DIRECTION INPUT + USE SIGNAL 114 | + PORT 115 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 116 | + PLACED ( 110860 111020 ) N ; 117 | - uio_in[3] + NET uio_in[3] + DIRECTION INPUT + USE SIGNAL 118 | + PORT 119 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 120 | + PLACED ( 108100 111020 ) N ; 121 | - uio_in[4] + NET uio_in[4] + DIRECTION INPUT + USE SIGNAL 122 | + PORT 123 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 124 | + PLACED ( 105340 111020 ) N ; 125 | - uio_in[5] + NET uio_in[5] + DIRECTION INPUT + USE SIGNAL 126 | + PORT 127 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 128 | + PLACED ( 102580 111020 ) N ; 129 | - uio_in[6] + NET uio_in[6] + DIRECTION INPUT + USE SIGNAL 130 | + PORT 131 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 132 | + PLACED ( 99820 111020 ) N ; 133 | - uio_in[7] + NET uio_in[7] + DIRECTION INPUT + USE SIGNAL 134 | + PORT 135 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 136 | + PLACED ( 97060 111020 ) N ; 137 | - uio_oe[0] + NET uio_oe[0] + DIRECTION OUTPUT + USE SIGNAL 138 | + PORT 139 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 140 | + PLACED ( 50140 111020 ) N ; 141 | - uio_oe[1] + NET uio_oe[1] + DIRECTION OUTPUT + USE SIGNAL 142 | + PORT 143 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 144 | + PLACED ( 47380 111020 ) N ; 145 | - uio_oe[2] + NET uio_oe[2] + DIRECTION OUTPUT + USE SIGNAL 146 | + PORT 147 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 148 | + PLACED ( 44620 111020 ) N ; 149 | - uio_oe[3] + NET uio_oe[3] + DIRECTION OUTPUT + USE SIGNAL 150 | + PORT 151 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 152 | + PLACED ( 41860 111020 ) N ; 153 | - uio_oe[4] + NET uio_oe[4] + DIRECTION OUTPUT + USE SIGNAL 154 | + PORT 155 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 156 | + PLACED ( 39100 111020 ) N ; 157 | - uio_oe[5] + NET uio_oe[5] + DIRECTION OUTPUT + USE SIGNAL 158 | + PORT 159 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 160 | + PLACED ( 36340 111020 ) N ; 161 | - uio_oe[6] + NET uio_oe[6] + DIRECTION OUTPUT + USE SIGNAL 162 | + PORT 163 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 164 | + PLACED ( 33580 111020 ) N ; 165 | - uio_oe[7] + NET uio_oe[7] + DIRECTION OUTPUT + USE SIGNAL 166 | + PORT 167 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 168 | + PLACED ( 30820 111020 ) N ; 169 | - uio_out[0] + NET uio_out[0] + DIRECTION OUTPUT + USE SIGNAL 170 | + PORT 171 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 172 | + PLACED ( 72220 111020 ) N ; 173 | - uio_out[1] + NET uio_out[1] + DIRECTION OUTPUT + USE SIGNAL 174 | + PORT 175 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 176 | + PLACED ( 69460 111020 ) N ; 177 | - uio_out[2] + NET uio_out[2] + DIRECTION OUTPUT + USE SIGNAL 178 | + PORT 179 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 180 | + PLACED ( 66700 111020 ) N ; 181 | - uio_out[3] + NET uio_out[3] + DIRECTION OUTPUT + USE SIGNAL 182 | + PORT 183 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 184 | + PLACED ( 63940 111020 ) N ; 185 | - uio_out[4] + NET uio_out[4] + DIRECTION OUTPUT + USE SIGNAL 186 | + PORT 187 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 188 | + PLACED ( 61180 111020 ) N ; 189 | - uio_out[5] + NET uio_out[5] + DIRECTION OUTPUT + USE SIGNAL 190 | + PORT 191 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 192 | + PLACED ( 58420 111020 ) N ; 193 | - uio_out[6] + NET uio_out[6] + DIRECTION OUTPUT + USE SIGNAL 194 | + PORT 195 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 196 | + PLACED ( 55660 111020 ) N ; 197 | - uio_out[7] + NET uio_out[7] + DIRECTION OUTPUT + USE SIGNAL 198 | + PORT 199 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 200 | + PLACED ( 52900 111020 ) N ; 201 | - uo_out[0] + NET uo_out[0] + DIRECTION OUTPUT + USE SIGNAL 202 | + PORT 203 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 204 | + PLACED ( 94300 111020 ) N ; 205 | - uo_out[1] + NET uo_out[1] + DIRECTION OUTPUT + USE SIGNAL 206 | + PORT 207 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 208 | + PLACED ( 91540 111020 ) N ; 209 | - uo_out[2] + NET uo_out[2] + DIRECTION OUTPUT + USE SIGNAL 210 | + PORT 211 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 212 | + PLACED ( 88780 111020 ) N ; 213 | - uo_out[3] + NET uo_out[3] + DIRECTION OUTPUT + USE SIGNAL 214 | + PORT 215 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 216 | + PLACED ( 86020 111020 ) N ; 217 | - uo_out[4] + NET uo_out[4] + DIRECTION OUTPUT + USE SIGNAL 218 | + PORT 219 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 220 | + PLACED ( 83260 111020 ) N ; 221 | - uo_out[5] + NET uo_out[5] + DIRECTION OUTPUT + USE SIGNAL 222 | + PORT 223 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 224 | + PLACED ( 80500 111020 ) N ; 225 | - uo_out[6] + NET uo_out[6] + DIRECTION OUTPUT + USE SIGNAL 226 | + PORT 227 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 228 | + PLACED ( 77740 111020 ) N ; 229 | - uo_out[7] + NET uo_out[7] + DIRECTION OUTPUT + USE SIGNAL 230 | + PORT 231 | + LAYER met4 ( -150 -500 ) ( 150 500 ) 232 | + PLACED ( 74980 111020 ) N ; 233 | END PINS 234 | SPECIALNETS 2 ; 235 | - VGND + USE GROUND ; 236 | - VPWR + USE POWER ; 237 | END SPECIALNETS 238 | NETS 43 ; 239 | - clk ( PIN clk ) + USE SIGNAL ; 240 | - ena ( PIN ena ) + USE SIGNAL ; 241 | - rst_n ( PIN rst_n ) + USE SIGNAL ; 242 | - ui_in[0] ( PIN ui_in[0] ) + USE SIGNAL ; 243 | - ui_in[1] ( PIN ui_in[1] ) + USE SIGNAL ; 244 | - ui_in[2] ( PIN ui_in[2] ) + USE SIGNAL ; 245 | - ui_in[3] ( PIN ui_in[3] ) + USE SIGNAL ; 246 | - ui_in[4] ( PIN ui_in[4] ) + USE SIGNAL ; 247 | - ui_in[5] ( PIN ui_in[5] ) + USE SIGNAL ; 248 | - ui_in[6] ( PIN ui_in[6] ) + USE SIGNAL ; 249 | - ui_in[7] ( PIN ui_in[7] ) + USE SIGNAL ; 250 | - uio_in[0] ( PIN uio_in[0] ) + USE SIGNAL ; 251 | - uio_in[1] ( PIN uio_in[1] ) + USE SIGNAL ; 252 | - uio_in[2] ( PIN uio_in[2] ) + USE SIGNAL ; 253 | - uio_in[3] ( PIN uio_in[3] ) + USE SIGNAL ; 254 | - uio_in[4] ( PIN uio_in[4] ) + USE SIGNAL ; 255 | - uio_in[5] ( PIN uio_in[5] ) + USE SIGNAL ; 256 | - uio_in[6] ( PIN uio_in[6] ) + USE SIGNAL ; 257 | - uio_in[7] ( PIN uio_in[7] ) + USE SIGNAL ; 258 | - uio_oe[0] ( PIN uio_oe[0] ) + USE SIGNAL ; 259 | - uio_oe[1] ( PIN uio_oe[1] ) + USE SIGNAL ; 260 | - uio_oe[2] ( PIN uio_oe[2] ) + USE SIGNAL ; 261 | - uio_oe[3] ( PIN uio_oe[3] ) + USE SIGNAL ; 262 | - uio_oe[4] ( PIN uio_oe[4] ) + USE SIGNAL ; 263 | - uio_oe[5] ( PIN uio_oe[5] ) + USE SIGNAL ; 264 | - uio_oe[6] ( PIN uio_oe[6] ) + USE SIGNAL ; 265 | - uio_oe[7] ( PIN uio_oe[7] ) + USE SIGNAL ; 266 | - uio_out[0] ( PIN uio_out[0] ) + USE SIGNAL ; 267 | - uio_out[1] ( PIN uio_out[1] ) + USE SIGNAL ; 268 | - uio_out[2] ( PIN uio_out[2] ) + USE SIGNAL ; 269 | - uio_out[3] ( PIN uio_out[3] ) + USE SIGNAL ; 270 | - uio_out[4] ( PIN uio_out[4] ) + USE SIGNAL ; 271 | - uio_out[5] ( PIN uio_out[5] ) + USE SIGNAL ; 272 | - uio_out[6] ( PIN uio_out[6] ) + USE SIGNAL ; 273 | - uio_out[7] ( PIN uio_out[7] ) + USE SIGNAL ; 274 | - uo_out[0] ( PIN uo_out[0] ) + USE SIGNAL ; 275 | - uo_out[1] ( PIN uo_out[1] ) + USE SIGNAL ; 276 | - uo_out[2] ( PIN uo_out[2] ) + USE SIGNAL ; 277 | - uo_out[3] ( PIN uo_out[3] ) + USE SIGNAL ; 278 | - uo_out[4] ( PIN uo_out[4] ) + USE SIGNAL ; 279 | - uo_out[5] ( PIN uo_out[5] ) + USE SIGNAL ; 280 | - uo_out[6] ( PIN uo_out[6] ) + USE SIGNAL ; 281 | - uo_out[7] ( PIN uo_out[7] ) + USE SIGNAL ; 282 | END NETS 283 | END DESIGN 284 | -------------------------------------------------------------------------------- /discord_bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import discord 7 | import yaml 8 | 9 | TOKEN = os.environ["DISCORD_TOKEN"] 10 | TARG = os.environ["TARGET_SERVER"] 11 | ROLE_ID = os.environ["ROLE_ID"] 12 | 13 | intents = discord.Intents.default() 14 | intents.members = True 15 | 16 | client = discord.Client(intents=intents) 17 | exit_code = 0 18 | 19 | 20 | @client.event 21 | async def on_ready(): 22 | global exit_code 23 | try: 24 | print(f"{client.user} has connected to Discord!") 25 | server = None 26 | for g in client.guilds: 27 | if g.name == TARG: 28 | server = g 29 | break 30 | if server is None: 31 | print(f"Error: Bot is not in a server named {TARG}") 32 | exit_code = 1 33 | await client.close() 34 | 35 | role = server.get_role(int(ROLE_ID)) 36 | if not role: 37 | print("Error: requested role does not exist on server!") 38 | exit_code = 1 39 | await client.close() 40 | 41 | participants = [] 42 | for project in os.listdir("./projects"): 43 | info_file = os.path.join("projects", project, "info.yaml") 44 | if not os.path.exists(info_file): 45 | print(f"info.yaml does not exist for project {project}. Skipping.") 46 | else: 47 | try: 48 | stream = open(info_file, "r") 49 | data = yaml.load(stream, Loader=yaml.FullLoader) 50 | stream.close() 51 | if "discord" in data["documentation"]: 52 | dname = data["documentation"]["discord"] 53 | if len(dname): 54 | if "#" in dname: 55 | dname = dname.split("#")[0] 56 | # print(f'adding {dname}') 57 | participants.append(dname) 58 | else: 59 | print( 60 | f"Project {project} has no discord username listed. Skipping." 61 | ) 62 | except Exception as ex: 63 | print(f"Error parsing info.yaml for project {project}. Skipping.") 64 | print(f"Error was: {ex}") 65 | 66 | members = [] 67 | for p in participants: 68 | for m in server.members: 69 | if m.name == p: 70 | members.append(m) 71 | break 72 | if m.global_name == p: 73 | members.append(m) 74 | break 75 | else: 76 | print(f"{p} not found in members") 77 | 78 | new_role_members = [] 79 | for m in members: 80 | if len([d for d in m.roles if d.id == role.id]) == 0: 81 | print(f"Giving the role to {m.name}") 82 | new_role_members.append(m.name) 83 | await m.add_roles( 84 | role, reason="Automatic role assignment by GitHub actions pipeline." 85 | ) 86 | else: 87 | print(f"{m.name} already has role") 88 | 89 | print("All done!") 90 | print("gave new role to:") 91 | print("@" + " @".join(new_role_members)) 92 | await client.close() 93 | except Exception as ex: 94 | print("Exception was thrown!") 95 | print(ex) 96 | exit_code = 1 97 | await client.close() 98 | 99 | 100 | client.run(TOKEN) 101 | sys.exit(exit_code) 102 | -------------------------------------------------------------------------------- /docs/CREDITS.md: -------------------------------------------------------------------------------- 1 | # Funding 2 | 3 | IHP PDK support for Tiny Tapeout was funded by The SwissChips Initiative. 4 | 5 | The manufacturing of Tiny Tapeout IHP 0p2 silicon was funded by the German BMBF project FMD-QNC (16ME0831). 6 | 7 | # Team 8 | 9 | Tiny Tapeout would not be possible without a lot of people helping. We would especially like to thank: 10 | 11 | * Uri Shaked for [wokwi](https://wokwi.com/) development and lots more 12 | * [Patrick Deegan](https://psychogenic.com/) for PCBs, software, documentation and lots more 13 | * [Sylvain Munaut](https://twitter.com/tnt) for help with scan chain improvements 14 | * [Mike Thompson](https://www.linkedin.com/in/michael-thompson-0a581a/) and [Mitch Bailey](https://www.linkedin.com/in/mitch-bailey-8ba0b45/) for verification expertise 15 | * [Tim Edwards](https://www.linkedin.com/in/tim-edwards-4376a18/) and [Harald Pretl](https://www.linkedin.com/in/harald-pretl-4911ba10/) for ASIC expertise 16 | * [Jix](https://twitter.com/jix_) for formal verification support 17 | * [Proppy](https://twitter.com/proppy) for help with GitHub actions 18 | * [Maximo Balestrini](https://twitter.com/maxiborga) for all the amazing renders and the interactive GDS viewer 19 | * James Rosenthal for coming up with digital design examples 20 | * All the people who took part in [Tiny Tapeout 01](/runs/tt01) and volunteered time to improve docs and test the flow 21 | * The team at [YosysHQ](https://www.yosyshq.com/) and all the other open source EDA tool makers 22 | * Jeff and the [Efabless Team](https://efabless.com/) for running the shuttles and providing OpenLane and sponsorship 23 | * [Tim Ansell and Google](https://www.youtube.com/watch?v=EczW2IWdnOM) for supporting the open source silicon movement 24 | * [Zero to ASIC course](https://zerotoasiccourse.com/) community for all your support 25 | * Jeremy Birch for help with STA 26 | -------------------------------------------------------------------------------- /docs/PINOUT.md: -------------------------------------------------------------------------------- 1 | # Pinout 2 | 3 | The chip is packaged in a 64-pin QFN package. The pinout is shown below. 4 | 5 | ![Pinout](pics/tt-qfn-64.svg) 6 | 7 | Note: you will receive the chip mounted on a [breakout board](https://github.com/TinyTapeout/caravel-breakout-pcb/tree/main/breakout-qfn). The pinout is provided for advanced users, as most users will not need to solder the chip directly. 8 | -------------------------------------------------------------------------------- /docs/doc_header.md.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | title: | 3 | ![Tiny Tapeout Logo](pics/ttlogo.png){width=15cm} 4 | {{name}} Datasheet 5 | subtitle: Project Repository [{{repo}}]({{repo}}) 6 | documentclass: scrartcl 7 | date: | 8 | ```{=latex} 9 | \today{} 10 | ``` 11 | geometry: "left=2cm,right=2cm,top=2cm,bottom=2.5cm" 12 | fontsize: 14pt 13 | mainfont: Latin Modern Sans 14 | header-includes: 15 | - | 16 | ```{=latex} 17 | \usepackage{hyperref} 18 | \hypersetup{colorlinks=true, 19 | urlcolor=blue, 20 | linkcolor=[rgb]{0,0,0.5}, 21 | allbordercolors={0 0 0}, 22 | pdfborderstyle={/S/U/W 1} } 23 | ``` 24 | --- 25 | 26 | ```{=latex} 27 | \pagebreak 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/doc_template.md.mustache: -------------------------------------------------------------------------------- 1 | ## {{title}} [{{mux_address}}] 2 | 3 | * Author: {{author}} 4 | * Description: {{&description}} 5 | * [GitHub repository]({{git_url}}) 6 | * {{project_type}} project 7 | * Mux address: {{mux_address}} 8 | * [Extra docs]({{doc_link}}) 9 | * Clock: {{clock_hz}} Hz 10 | 11 | {{&user_docs}} 12 | 13 | ### Pinout 14 | 15 | | # | Input | Output | Bidirectional | 16 | | ------------- | -------- | -------- | --------------- | 17 | {{#pins}} 18 | | {{pin_index}} | {{&ui}} | {{&uo}} | {{&uio}} | 19 | {{/pins}} 20 | 21 | {{#is_analog}} 22 | ### Analog pins 23 | 24 | | `ua`# | `analog`# | Description | 25 | | ------------ | ---------------- | ------------------- | 26 | {{#analog_pins}} 27 | | {{ua_index}} | {{analog_index}} | {{&desc}} | 28 | {{/analog_pins}} 29 | {{/is_analog}} 30 | -------------------------------------------------------------------------------- /docs/pics/efabless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/docs/pics/efabless.png -------------------------------------------------------------------------------- /docs/pics/ttlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/docs/pics/ttlogo.png -------------------------------------------------------------------------------- /docs/project_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | documentclass: scrartcl 3 | geometry: "left=2cm,right=2cm,top=2cm,bottom=3cm" 4 | fontsize: 14pt 5 | mainfont: Latin Modern Sans 6 | header-includes: 7 | - | 8 | ```{=latex} 9 | \usepackage{hyperref} 10 | \hypersetup{colorlinks=false, 11 | allbordercolors={0 0 0}, 12 | pdfborderstyle={/S/U/W 1}} 13 | ``` 14 | --- 15 | 16 | -------------------------------------------------------------------------------- /docs/project_preview.md.mustache: -------------------------------------------------------------------------------- 1 | # {{title}} 2 | 3 | * Author: {{author}} 4 | * Description: {{&description}} 5 | * Language: {{language}} 6 | 7 | {{&info}} 8 | 9 | ## Pinout 10 | 11 | | # | Input | Output | Bidirectional | 12 | |---------------|----------|----------|----------------| 13 | {{#pins}} 14 | | {{pin_index}} | {{&ui}} | {{&uo}} | {{&uio}} | 15 | {{/pins}} 16 | 17 | {{#is_analog}} 18 | ## Analog pins 19 | 20 | | `ua`# | `analog`# | Description | 21 | |--------------|------------------|--------------------| 22 | {{#analog_pins}} 23 | | {{ua_index}} | {{analog_index}} | {{&desc}} | 24 | {{/analog_pins}} 25 | {{/is_analog}} 26 | -------------------------------------------------------------------------------- /docs/shuttle_index_header.md.mustache: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | This repository contains the GDS file for the [{{name}}](https://tinytapeout.com) project. It was generated from {{git_repo}}@{{git_commit}}. 4 | 5 | ## Project Index 6 | 7 | -------------------------------------------------------------------------------- /documentation.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import subprocess 4 | from typing import List, Optional 5 | 6 | import chevron 7 | import frontmatter # type: ignore 8 | import git # type: ignore 9 | 10 | from config import Config 11 | from git_utils import get_first_remote 12 | from markdown_utils import rewrite_image_paths 13 | from project import Project 14 | 15 | 16 | class Docs: 17 | def __init__(self, config: Config, projects: List[Project]): 18 | self.config = config 19 | self.projects = projects 20 | self.script_dir = os.path.dirname(os.path.realpath(__file__)) 21 | 22 | # stuff related to docs 23 | def build_index(self, filename: str = "shuttle_index.md"): 24 | logging.info(f"building {filename}") 25 | repo = git.Repo(".") 26 | readme = self.load_doc_template("shuttle_index_header.md.mustache") 27 | with open(filename, "w") as fh: 28 | fh.write( 29 | chevron.render( 30 | readme, 31 | { 32 | "name": self.config["name"], 33 | "git_repo": get_first_remote(repo), 34 | "git_commit": repo.head.commit.hexsha, 35 | }, 36 | ) 37 | ) 38 | fh.write("| Address | Author | Title | Type | Git Repo |\n") 39 | fh.write("| ------- | ------ | ------| -----| ---------|\n") 40 | self.projects.sort(key=lambda x: x.mux_address) 41 | for project in self.projects: 42 | fh.write(project.get_index_row()) 43 | 44 | def update_image(self): 45 | ruby = os.path.join(self.script_dir, "caravel_template", "dump_pic.rb") 46 | klayoutrc = os.path.join(self.script_dir, "caravel_template", "klayoutrc") 47 | lyp = os.path.join(self.script_dir, "caravel_template", "caravel.lyp") 48 | cmd = f"klayout -l {lyp} gds/user_project_wrapper.gds* -r {ruby} -c {klayoutrc}" 49 | logging.info(cmd) 50 | os.system(cmd) 51 | 52 | def load_doc_template(self, name: str) -> str: 53 | root = os.path.join(self.script_dir, "docs") 54 | doc_path = os.path.join(root, name) 55 | image_root = os.path.relpath(os.path.dirname(doc_path), ".") 56 | doc = frontmatter.load(doc_path) 57 | doc.content = rewrite_image_paths(doc.content, image_root) 58 | if "title" in doc: 59 | doc["title"] = rewrite_image_paths(doc["title"], image_root) 60 | if len(doc.keys()) > 0: 61 | return frontmatter.dumps(doc) + "\n" 62 | else: 63 | return doc.content + "\n" 64 | 65 | def write_datasheet(self, markdown_file: str, pdf_file: Optional[str] = None): 66 | doc_header = self.load_doc_template("doc_header.md.mustache") 67 | doc_chip_map = self.load_doc_template("../../docs/chip_map.md") 68 | doc_template = self.load_doc_template("doc_template.md.mustache") 69 | doc_pinout = self.load_doc_template("PINOUT.md") 70 | doc_info = self.load_doc_template("../../tt-multiplexer/docs/INFO.md") 71 | doc_credits = self.load_doc_template("CREDITS.md") 72 | 73 | with open(markdown_file, "w") as fh: 74 | repo = git.Repo(".") 75 | fh.write( 76 | chevron.render( 77 | doc_header, 78 | { 79 | "name": self.config["name"], 80 | "repo": get_first_remote(repo), 81 | }, 82 | ) 83 | ) 84 | fh.write(doc_chip_map) 85 | fh.write("# Projects\n") 86 | 87 | self.projects.sort(key=lambda x: x.mux_address) 88 | 89 | for project in self.projects: 90 | yaml_data = project.get_project_docs_dict() 91 | analog_pins = project.info.analog_pins 92 | yaml_data.update( 93 | { 94 | "user_docs": rewrite_image_paths( 95 | yaml_data["user_docs"], 96 | f"projects/{project.get_macro_name()}/docs", 97 | ), 98 | "mux_address": project.mux_address, 99 | "pins": [ 100 | { 101 | "pin_index": str(i), 102 | "ui": project.info.pinout.ui[i], 103 | "uo": project.info.pinout.uo[i], 104 | "uio": project.info.pinout.uio[i], 105 | } 106 | for i in range(8) 107 | ], 108 | "analog_pins": [ 109 | { 110 | "ua_index": str(i), 111 | "analog_index": str(project.analog_pins[i]), 112 | "desc": desc, 113 | } 114 | for i, desc in enumerate( 115 | project.info.pinout.ua[:analog_pins] 116 | ) 117 | ], 118 | "is_analog": analog_pins > 0, 119 | } 120 | ) 121 | 122 | logging.info(f"building datasheet for {project}") 123 | 124 | # ensure that optional fields are set 125 | for key in [ 126 | "author", 127 | "description", 128 | "clock_hz", 129 | "git_url", 130 | "doc_link", 131 | ]: 132 | if key not in yaml_data: 133 | yaml_data[key] = "" 134 | 135 | # now build the doc & print it 136 | try: 137 | doc = chevron.render(doc_template, yaml_data) 138 | fh.write(doc) 139 | fh.write("\n```{=latex}\n\\clearpage\n```\n") 140 | except IndexError: 141 | logging.warning("missing pins in info.yaml, skipping") 142 | 143 | # ending 144 | fh.write(doc_pinout) 145 | fh.write("\n```{=latex}\n\\clearpage\n```\n") 146 | fh.write(doc_info) 147 | fh.write("\n```{=latex}\n\\clearpage\n```\n") 148 | fh.write(doc_credits) 149 | 150 | logging.info(f"wrote markdown to {markdown_file}") 151 | 152 | if pdf_file is not None: 153 | pdf_cmd = f"pandoc --toc --toc-depth 2 --pdf-engine=xelatex -i {markdown_file} -o {pdf_file} --from gfm+raw_attribute+smart+attributes" 154 | logging.info(pdf_cmd) 155 | p = subprocess.run(pdf_cmd, shell=True) 156 | if p.returncode != 0: 157 | logging.error("pdf generation failed") 158 | raise RuntimeError(f"pdf generation failed with code {p.returncode}") 159 | -------------------------------------------------------------------------------- /fpga/tt_fpga_top.pcf: -------------------------------------------------------------------------------- 1 | # 2 | # Pinmap for the TT ASIC SIMULATOR version 1.0 3 | # 4 | 5 | 6 | # Clock 7 | set_io -nowarn clk_fpga 37 # IOT_45a_G1 8 | 9 | 10 | # SPI 11 | set_io -nowarn spi_mosi 14 12 | set_io -nowarn spi_miso 17 13 | set_io -nowarn spi_clk 15 14 | set_io -nowarn spi_cs_n 16 15 | 16 | # USB 17 | set_io -nowarn usb_dp 32 # IOT_43A 18 | set_io -nowarn usb_dn 31 # IOT_42B 19 | set_io -nowarn usb_pu 34 # IOT_44B 20 | 21 | 22 | # Button 23 | set_io -nowarn -pullup yes btn 13 # IOB_24A / boot button 24 | 25 | 26 | # Leds 27 | set_io -nowarn rgb[0] 39 28 | set_io -nowarn rgb[1] 40 29 | set_io -nowarn rgb[2] 41 30 | 31 | 32 | # "project clock" 33 | set_io -nowarn clk 35 # IOT_46B_G0 34 | set_io -nowarn rst_n 38 # IOT_50B 35 | set_io -nowarn gpio_led 36 # IOT_48B 36 | 37 | 38 | # I/O 39 | # pmod buttons 40 | set_io -nowarn ui_in[0] 2 41 | set_io -nowarn ui_in[1] 3 42 | set_io -nowarn ui_in[2] 4 43 | set_io -nowarn ui_in[3] 6 44 | set_io -nowarn ui_in[4] 9 45 | set_io -nowarn ui_in[5] 10 46 | set_io -nowarn ui_in[6] 11 47 | set_io -nowarn ui_in[7] 12 48 | 49 | 50 | set_io -nowarn uo_out[0] 18 51 | set_io -nowarn uo_out[1] 20 52 | set_io -nowarn uo_out[2] 19 53 | set_io -nowarn uo_out[3] 21 54 | set_io -nowarn uo_out[4] 23 55 | set_io -nowarn uo_out[5] 26 56 | set_io -nowarn uo_out[6] 25 57 | set_io -nowarn uo_out[7] 28 58 | 59 | 60 | 61 | set_io -nowarn uio[0] 27 62 | set_io -nowarn uio[1] 42 63 | set_io -nowarn uio[2] 44 64 | set_io -nowarn uio[3] 43 65 | set_io -nowarn uio[4] 46 66 | set_io -nowarn uio[5] 45 67 | set_io -nowarn uio[6] 48 68 | set_io -nowarn uio[7] 47 69 | -------------------------------------------------------------------------------- /fpga/tt_fpga_top.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache 2.0 2 | // Copyright (C) 2024, Tiny Tapeout LTD 3 | 4 | `default_nettype none 5 | 6 | module tt_fpga_top ( 7 | input wire [7:0] ui_in, 8 | output wire [7:0] uo_out, 9 | inout wire [7:0] uio, 10 | input wire clk, 11 | input wire rst_n 12 | ); 13 | wire [7:0] uio_in; 14 | wire [7:0] uio_out; 15 | wire [7:0] uio_oe; 16 | 17 | SB_IO #( 18 | .PIN_TYPE(6'b1010_01) 19 | ) uio_pin[7:0] ( 20 | .PACKAGE_PIN(uio), 21 | .OUTPUT_ENABLE(uio_oe), 22 | .D_OUT_0(uio_out), 23 | .D_IN_0(uio_in), 24 | ); 25 | 26 | __tt_um_placeholder user_project ( 27 | .ui_in(ui_in), 28 | .uo_out(uo_out), 29 | .uio_in(uio_in), 30 | .uio_out(uio_out), 31 | .uio_oe(uio_oe), 32 | .ena(1'b1), 33 | .clk(clk), 34 | .rst_n(rst_n) 35 | ); 36 | 37 | endmodule 38 | -------------------------------------------------------------------------------- /gds_compare.py: -------------------------------------------------------------------------------- 1 | # Usage: 2 | # klayout -b -r gds_compare.py -rd gds1=file1.gds -rd gds2=file2.gds 3 | 4 | import sys 5 | 6 | import pya 7 | 8 | 9 | def compare_gds(file1, file2): 10 | diff = pya.LayoutDiff() 11 | 12 | # Load the layouts 13 | layout1 = pya.Layout() 14 | layout1.read(file1) 15 | 16 | layout2 = pya.Layout() 17 | layout2.read(file2) 18 | 19 | # Check if the layouts are identical 20 | return diff.compare(layout1, layout2) 21 | 22 | 23 | if __name__ == "__main__": 24 | if compare_gds(gds1, gds2): # noqa: F821 25 | sys.exit(0) 26 | else: 27 | sys.exit(-1) 28 | -------------------------------------------------------------------------------- /git_utils.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import errno 3 | import logging 4 | import os 5 | import sys 6 | import typing 7 | from urllib.parse import urlparse 8 | 9 | import requests 10 | from git.repo import Repo 11 | 12 | 13 | def fetch_file(url: str, filename: str): 14 | logging.info("trying to download {}".format(url)) 15 | r = requests.get(url) 16 | if r.status_code != 200: 17 | logging.warning("couldn't download {}".format(url)) 18 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), filename) 19 | 20 | with open(filename, "wb") as fh: 21 | logging.info("written to {}".format(filename)) 22 | fh.write(r.content) 23 | 24 | 25 | def check_status(r: requests.Response): 26 | if r.status_code == 401: 27 | logging.error( 28 | "unauthorised, check INFO.md for information about GitHub API keys" 29 | ) 30 | exit(1) 31 | 32 | 33 | def headers_try_to_add_authorization_from_environment( 34 | headers: typing.Dict[str, str] 35 | ) -> bool: 36 | gh_token = os.getenv("GH_TOKEN", "") # override like gh CLI 37 | if not gh_token: 38 | gh_token = os.getenv("GITHUB_TOKEN", "") # GHA inherited 39 | 40 | if len(gh_token) > 0: 41 | # As per https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api 42 | headers["authorization"] = "Bearer " + gh_token 43 | return True 44 | 45 | # Use a token instead which is designed to limit exposure of passwords 46 | # I can't find any GH docs explaining use cases for Basic auth and confirming a token 47 | # can be used instead of PASSWORD in the password field of authorization header. 48 | gh_username = os.getenv("GH_USERNAME", "") # override like gh CLI 49 | if not gh_username: 50 | gh_username = os.getenv("GITHUB_ACTOR", "") # GHA inherited 51 | 52 | gh_password = os.getenv("GH_PASSWORD", "") 53 | 54 | if len(gh_username) > 0 and len(gh_password) > 0: 55 | auth_string = gh_username + ":" + gh_password 56 | encoded = base64.b64encode(auth_string.encode("ascii")) 57 | headers["authorization"] = "Basic " + encoded.decode("ascii") 58 | return True 59 | 60 | print( 61 | "WARNING: No github token found from environment, trying public API requests without, see docs/INFO.md#instructions-to-build-gds", 62 | file=sys.stderr, 63 | ) 64 | return False 65 | 66 | 67 | def get_most_recent_action_page( 68 | commits: typing.List[typing.Dict[str, str]], 69 | runs: typing.List[typing.Dict[str, str]], 70 | ) -> typing.Optional[str]: 71 | release_sha_to_page_url = { 72 | run["head_sha"]: run["html_url"] for run in runs if run["name"] == "gds" 73 | } 74 | for commit in commits: 75 | if commit["sha"] in release_sha_to_page_url: 76 | return release_sha_to_page_url[commit["sha"]] 77 | return None 78 | 79 | 80 | def split_git_url(url: str): 81 | res = urlparse(url) 82 | try: 83 | _, user_name, repo = res.path.split("/") 84 | except ValueError: 85 | logging.error(f"couldn't split repo from {url}") 86 | exit(1) 87 | repo = repo.replace(".git", "") 88 | return user_name, repo 89 | 90 | 91 | def get_latest_action_url(url: str): 92 | logging.debug(url) 93 | user_name, repo = split_git_url(url) 94 | 95 | headers = { 96 | "Accept": "application/vnd.github+json", 97 | } 98 | # authenticate for rate limiting 99 | headers_try_to_add_authorization_from_environment(headers) 100 | 101 | # first fetch the git commit history 102 | api_url = f"https://api.github.com/repos/{user_name}/{repo}/commits" 103 | r = requests.get(api_url, headers=headers) 104 | check_status(r) 105 | requests_remaining = int(r.headers["X-RateLimit-Remaining"]) 106 | if requests_remaining == 0: 107 | logging.error("no API requests remaining") 108 | exit(1) 109 | 110 | commits = r.json() 111 | 112 | # get runs 113 | api_url = f"https://api.github.com/repos/{user_name}/{repo}/actions/runs" 114 | r = requests.get(api_url, headers=headers, params={"per_page": 100}) 115 | check_status(r) 116 | runs = r.json() 117 | page_url = get_most_recent_action_page(commits, runs["workflow_runs"]) 118 | 119 | return page_url 120 | 121 | 122 | def get_first_remote(repo: Repo) -> str: 123 | return list(repo.remotes[0].urls)[0] 124 | -------------------------------------------------------------------------------- /ihp/categories.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories" : [ 3 | "AND", 4 | "OR", 5 | "NAND", 6 | "NOR", 7 | "Clock", 8 | "Flip Flops", 9 | "Multiplexer", 10 | "Latch", 11 | "Inverter", 12 | "Buffer", 13 | "Fill", 14 | "Tap", 15 | "Diode", 16 | "Combo Logic", 17 | "Misc" 18 | ], 19 | "map" : { 20 | "a21o" : 13, 21 | "a21oi" : 13, 22 | "a22oi" : 13, 23 | "a221oi" : 13, 24 | "and2" : 0, 25 | "and3" : 0, 26 | "and4" : 0, 27 | "antennanp" : 12, 28 | "buf" : 9, 29 | "decap" : 10, 30 | "dfrbp" : 5, 31 | "dlhq" : 7, 32 | "dlhr" : 7, 33 | "dlhrq" : 7, 34 | "dllr" : 7, 35 | "dllrq" : 7, 36 | "dlygate4sd1" : 14, 37 | "dlygate4sd2" : 14, 38 | "dlygate4sd3" : 14, 39 | "ebufn" : 14, 40 | "einvn" : 14, 41 | "fill" : 10, 42 | "inv" : 8, 43 | "lgcp" : 4, 44 | "mux2" : 6, 45 | "mux4" : 6, 46 | "nand2" : 2, 47 | "nand2b" : 2, 48 | "nand3" : 2, 49 | "nand3b" : 2, 50 | "nand4" : 2, 51 | "nor2" : 3, 52 | "nor2b" : 3, 53 | "nor3" : 3, 54 | "nor4" : 3, 55 | "o21ai" : 13, 56 | "or2" : 1, 57 | "or3" : 1, 58 | "or4" : 1, 59 | "sdfbbp" : 5, 60 | "sighold" : 14, 61 | "slgcp" : 4, 62 | "tiehi" : 14, 63 | "tielo" : 14, 64 | "xnor2" : 3, 65 | "xor2" : 1 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ihp/cells.json: -------------------------------------------------------------------------------- 1 | { 2 | "a21o" : { 3 | "description" : "2-input AND into first input of 2-input OR.", 4 | "doc_name": "AO21x", 5 | "doc_ref" : 7 6 | }, 7 | "a21oi" : { 8 | "description" : "2-input AND into first input of 2-input NOR.", 9 | "doc_name": "A21OIx", 10 | "doc_ref" : 1 11 | }, 12 | "a221oi" : { 13 | "description" : "2-input AND into first two inputs of 3-input NOR.", 14 | "doc_name": "A221OI", 15 | "doc_ref" : 2 16 | }, 17 | "a22oi" : { 18 | "description" : "2-input AND into both inputs of 2-input NOR.", 19 | "doc_name": "A22OI", 20 | "doc_ref" : 3 21 | }, 22 | "and2" : { 23 | "description" : "2-input AND.", 24 | "doc_name": "AND2x", 25 | "doc_ref" : 4 26 | }, 27 | "and3" : { 28 | "description" : "3-input AND.", 29 | "doc_name": "AND3x", 30 | "doc_ref" : 5 31 | }, 32 | "and4" : { 33 | "description" : "4-input AND.", 34 | "doc_name": "AND4x", 35 | "doc_ref" : 6 36 | }, 37 | "antennanp" : { 38 | "description" : "Antenna tie-down diode.", 39 | "doc_name": "NP_ANT", 40 | "doc_ref" : 38 41 | }, 42 | "buf" : { 43 | "description" : "Buffer.", 44 | "doc_name": "BUx", 45 | "doc_ref" : 9 46 | }, 47 | "decap" : { 48 | "description" : "Decoupling capacitance filler.", 49 | "doc_name": "DECAPx", 50 | "doc_ref" : 10 51 | }, 52 | "dfrbp" : { 53 | "description" : "Delay flop, inverted reset, complementary outputs.", 54 | "doc_name": "DFFRRx", 55 | "doc_ref" : 11 56 | }, 57 | "dlhq" : { 58 | "description" : "Delay latch, non-inverted enable, single output.", 59 | "doc_name": "DLHQ", 60 | "doc_ref" : 12 61 | }, 62 | "dlhr" : { 63 | "description" : "Delay latch, inverted reset, non-inverted enable, complementary outputs.", 64 | "doc_name": "DLHR", 65 | "doc_ref" : 14 66 | }, 67 | "dlhrq" : { 68 | "description" : "Delay latch, inverted reset, non-inverted enable, single output.", 69 | "doc_name": "DLHRQ", 70 | "doc_ref" : 13 71 | }, 72 | "dllr" : { 73 | "description" : "Delay latch, inverted reset, inverted enable, complementary outputs.", 74 | "doc_name": "DLLR", 75 | "doc_ref" : 16 76 | }, 77 | "dllrq" : { 78 | "description" : "Delay latch, inverted reset, inverted enable, single output.", 79 | "doc_name": "DLLRQ", 80 | "doc_ref" : 15 81 | }, 82 | "dlygate4sd1" : { 83 | "description" : "Delay Buffer 4-stage ... length inner stage gates.", 84 | "doc_name": "DLY1", 85 | "doc_ref" : 17 86 | }, 87 | "dlygate4sd2" : { 88 | "description" : "Delay Buffer 4-stage ... length inner stage gates.", 89 | "doc_name": "DLY2", 90 | "doc_ref" : 18 91 | }, 92 | "dlygate4sd3" : { 93 | "description" : "Delay Buffer 4-stage ... length inner stage gates.", 94 | "doc_name": "DLY4", 95 | "doc_ref" : 19 96 | }, 97 | "ebufn" : { 98 | "description" : "Tri-state buffer, negative enable.", 99 | "doc_name": "BTLx", 100 | "doc_ref" : 8 101 | }, 102 | "einvn" : { 103 | "description" : "Tri-state inverter, negative enable.", 104 | "doc_name": "EINVINx, ITL", 105 | "doc_ref" : 20 106 | }, 107 | "fill" : { 108 | "description" : "Fill cell.", 109 | "doc_name": "FILLx", 110 | "doc_ref" : 21 111 | }, 112 | "inv" : { 113 | "description" : "Inverter.", 114 | "doc_name": "INx", 115 | "doc_ref" : 23 116 | }, 117 | "lgcp" : { 118 | "description" : "Clock gate.", 119 | "doc_name": "GCLK", 120 | "doc_ref" : 22 121 | }, 122 | "mux2" : { 123 | "description" : "2-input multiplexer.", 124 | "doc_name": "MUX2x", 125 | "doc_ref" : 26 126 | }, 127 | "mux4" : { 128 | "description" : "4-input multiplexer.", 129 | "doc_name": "MUX4", 130 | "doc_ref" : 27 131 | }, 132 | "nand2" : { 133 | "description" : "2-input NAND.", 134 | "doc_name": "NAND2x", 135 | "doc_ref" : 30 136 | }, 137 | "nand2b" : { 138 | "description" : "2-input NAND, first input inverted.", 139 | "doc_name": "NAND2B1, NAND2B2", 140 | "doc_ref" : 28 141 | }, 142 | "nand3" : { 143 | "description" : "3-input NAND.", 144 | "doc_name": "NAND3", 145 | "doc_ref" : 32 146 | }, 147 | "nand3b" : { 148 | "description" : "3-input NAND, first input inverted.", 149 | "doc_name": "NAND3B1", 150 | "doc_ref" : 31 151 | }, 152 | "nand4" : { 153 | "description" : "4-input NAND.", 154 | "doc_name": "NAND4", 155 | "doc_ref" : 33 156 | }, 157 | "nor2" : { 158 | "description" : "2-input NOR.", 159 | "doc_name": "NOR2x", 160 | "doc_ref" : 35 161 | }, 162 | "nor2b" : { 163 | "description" : "2-input NOR, first input inverted.", 164 | "doc_name": "NOR2Bx", 165 | "doc_ref" : 34 166 | }, 167 | "nor3" : { 168 | "description" : "3-input NOR.", 169 | "doc_name": "NOR3x", 170 | "doc_ref" : 36 171 | }, 172 | "nor4" : { 173 | "description" : "4-input NOR.", 174 | "doc_name": "NOR4x", 175 | "doc_ref" : 37 176 | }, 177 | "o21ai" : { 178 | "description" : "2-input OR into first input of 2-input NAND.", 179 | "doc_name": "O21AI", 180 | "doc_ref" : 39 181 | }, 182 | "or2" : { 183 | "description" : "2-input OR.", 184 | "doc_name": "OR2x", 185 | "doc_ref" : 40 186 | }, 187 | "or3" : { 188 | "description" : "3-input OR.", 189 | "doc_name": "OR3x", 190 | "doc_ref" : 41 191 | }, 192 | "or4" : { 193 | "description" : "4-input OR.", 194 | "doc_name": "OR4x", 195 | "doc_ref" : 42 196 | }, 197 | "sdfbbp" : { 198 | "description" : "Scan delay flop, inverted set, inverted reset, non-inverted clock, complementary outputs.", 199 | "doc_name": "SDFRRS", 200 | "doc_ref" : 43 201 | }, 202 | "sighold" : { 203 | "description" : "Cross-coupled inverters.", 204 | "doc_name": "KEEPSTATE", 205 | "doc_ref" : 25 206 | }, 207 | "slgcp" : { 208 | "description" : "Scan gated clock.", 209 | "doc_name": "SGCLK", 210 | "doc_ref" : 44 211 | }, 212 | "tiehi" : { 213 | "description" : "Constant high output.", 214 | "doc_name": "TIE1", 215 | "doc_ref" : 46 216 | }, 217 | "tielo" : { 218 | "description" : "Constant low output.", 219 | "doc_name": "TIE0", 220 | "doc_ref" : 45 221 | }, 222 | "xnor2" : { 223 | "description" : "2-input exclusive NOR.", 224 | "doc_name": "XNOR2_1", 225 | "doc_ref" : 47 226 | }, 227 | "xor2" : { 228 | "description" : "2-input exclusive OR.", 229 | "doc_name": "XOR2_1", 230 | "doc_ref" : 48 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /ihp/def/tt_block_1x1_pgvdd.def: -------------------------------------------------------------------------------- 1 | VERSION 5.8 ; 2 | DIVIDERCHAR "/" ; 3 | BUSBITCHARS "[]" ; 4 | DESIGN tt_um_template ; 5 | UNITS DISTANCE MICRONS 1000 ; 6 | DIEAREA ( 0 0 ) ( 202080 154980 ) ; 7 | ROW ROW_0 CoreSite 2880 3780 N DO 409 BY 1 STEP 480 0 ; 8 | ROW ROW_1 CoreSite 2880 7560 FS DO 409 BY 1 STEP 480 0 ; 9 | ROW ROW_2 CoreSite 2880 11340 N DO 409 BY 1 STEP 480 0 ; 10 | ROW ROW_3 CoreSite 2880 15120 FS DO 409 BY 1 STEP 480 0 ; 11 | ROW ROW_4 CoreSite 2880 18900 N DO 409 BY 1 STEP 480 0 ; 12 | ROW ROW_5 CoreSite 2880 22680 FS DO 409 BY 1 STEP 480 0 ; 13 | ROW ROW_6 CoreSite 2880 26460 N DO 409 BY 1 STEP 480 0 ; 14 | ROW ROW_7 CoreSite 2880 30240 FS DO 409 BY 1 STEP 480 0 ; 15 | ROW ROW_8 CoreSite 2880 34020 N DO 409 BY 1 STEP 480 0 ; 16 | ROW ROW_9 CoreSite 2880 37800 FS DO 409 BY 1 STEP 480 0 ; 17 | ROW ROW_10 CoreSite 2880 41580 N DO 409 BY 1 STEP 480 0 ; 18 | ROW ROW_11 CoreSite 2880 45360 FS DO 409 BY 1 STEP 480 0 ; 19 | ROW ROW_12 CoreSite 2880 49140 N DO 409 BY 1 STEP 480 0 ; 20 | ROW ROW_13 CoreSite 2880 52920 FS DO 409 BY 1 STEP 480 0 ; 21 | ROW ROW_14 CoreSite 2880 56700 N DO 409 BY 1 STEP 480 0 ; 22 | ROW ROW_15 CoreSite 2880 60480 FS DO 409 BY 1 STEP 480 0 ; 23 | ROW ROW_16 CoreSite 2880 64260 N DO 409 BY 1 STEP 480 0 ; 24 | ROW ROW_17 CoreSite 2880 68040 FS DO 409 BY 1 STEP 480 0 ; 25 | ROW ROW_18 CoreSite 2880 71820 N DO 409 BY 1 STEP 480 0 ; 26 | ROW ROW_19 CoreSite 2880 75600 FS DO 409 BY 1 STEP 480 0 ; 27 | ROW ROW_20 CoreSite 2880 79380 N DO 409 BY 1 STEP 480 0 ; 28 | ROW ROW_21 CoreSite 2880 83160 FS DO 409 BY 1 STEP 480 0 ; 29 | ROW ROW_22 CoreSite 2880 86940 N DO 409 BY 1 STEP 480 0 ; 30 | ROW ROW_23 CoreSite 2880 90720 FS DO 409 BY 1 STEP 480 0 ; 31 | ROW ROW_24 CoreSite 2880 94500 N DO 409 BY 1 STEP 480 0 ; 32 | ROW ROW_25 CoreSite 2880 98280 FS DO 409 BY 1 STEP 480 0 ; 33 | ROW ROW_26 CoreSite 2880 102060 N DO 409 BY 1 STEP 480 0 ; 34 | ROW ROW_27 CoreSite 2880 105840 FS DO 409 BY 1 STEP 480 0 ; 35 | ROW ROW_28 CoreSite 2880 109620 N DO 409 BY 1 STEP 480 0 ; 36 | ROW ROW_29 CoreSite 2880 113400 FS DO 409 BY 1 STEP 480 0 ; 37 | ROW ROW_30 CoreSite 2880 117180 N DO 409 BY 1 STEP 480 0 ; 38 | ROW ROW_31 CoreSite 2880 120960 FS DO 409 BY 1 STEP 480 0 ; 39 | ROW ROW_32 CoreSite 2880 124740 N DO 409 BY 1 STEP 480 0 ; 40 | ROW ROW_33 CoreSite 2880 128520 FS DO 409 BY 1 STEP 480 0 ; 41 | ROW ROW_34 CoreSite 2880 132300 N DO 409 BY 1 STEP 480 0 ; 42 | ROW ROW_35 CoreSite 2880 136080 FS DO 409 BY 1 STEP 480 0 ; 43 | ROW ROW_36 CoreSite 2880 139860 N DO 409 BY 1 STEP 480 0 ; 44 | ROW ROW_37 CoreSite 2880 143640 FS DO 409 BY 1 STEP 480 0 ; 45 | ROW ROW_38 CoreSite 2880 147420 N DO 409 BY 1 STEP 480 0 ; 46 | TRACKS X 480 DO 420 STEP 480 LAYER Metal1 ; 47 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal1 ; 48 | TRACKS X 420 DO 480 STEP 420 LAYER Metal2 ; 49 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal2 ; 50 | TRACKS X 480 DO 420 STEP 480 LAYER Metal3 ; 51 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal3 ; 52 | TRACKS X 420 DO 480 STEP 420 LAYER Metal4 ; 53 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal4 ; 54 | TRACKS X 480 DO 420 STEP 480 LAYER Metal5 ; 55 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal5 ; 56 | TRACKS X 1460 DO 88 STEP 2280 LAYER TopMetal1 ; 57 | TRACKS Y 1460 DO 67 STEP 2280 LAYER TopMetal1 ; 58 | TRACKS X 2000 DO 50 STEP 4000 LAYER TopMetal2 ; 59 | TRACKS Y 2000 DO 38 STEP 4000 LAYER TopMetal2 ; 60 | COMPONENTS 0 ; 61 | END COMPONENTS 62 | PINS 43 ; 63 | - clk + NET clk + DIRECTION INPUT + USE SIGNAL 64 | + PORT 65 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 66 | + PLACED ( 187200 154480 ) N ; 67 | - ena + NET ena + DIRECTION INPUT + USE SIGNAL 68 | + PORT 69 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 70 | + PLACED ( 191040 154480 ) N ; 71 | - rst_n + NET rst_n + DIRECTION INPUT + USE SIGNAL 72 | + PORT 73 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 74 | + PLACED ( 183360 154480 ) N ; 75 | - ui_in[0] + NET ui_in[0] + DIRECTION INPUT + USE SIGNAL 76 | + PORT 77 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 78 | + PLACED ( 179520 154480 ) N ; 79 | - ui_in[1] + NET ui_in[1] + DIRECTION INPUT + USE SIGNAL 80 | + PORT 81 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 82 | + PLACED ( 175680 154480 ) N ; 83 | - ui_in[2] + NET ui_in[2] + DIRECTION INPUT + USE SIGNAL 84 | + PORT 85 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 86 | + PLACED ( 171840 154480 ) N ; 87 | - ui_in[3] + NET ui_in[3] + DIRECTION INPUT + USE SIGNAL 88 | + PORT 89 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 90 | + PLACED ( 168000 154480 ) N ; 91 | - ui_in[4] + NET ui_in[4] + DIRECTION INPUT + USE SIGNAL 92 | + PORT 93 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 94 | + PLACED ( 164160 154480 ) N ; 95 | - ui_in[5] + NET ui_in[5] + DIRECTION INPUT + USE SIGNAL 96 | + PORT 97 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 98 | + PLACED ( 160320 154480 ) N ; 99 | - ui_in[6] + NET ui_in[6] + DIRECTION INPUT + USE SIGNAL 100 | + PORT 101 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 102 | + PLACED ( 156480 154480 ) N ; 103 | - ui_in[7] + NET ui_in[7] + DIRECTION INPUT + USE SIGNAL 104 | + PORT 105 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 106 | + PLACED ( 152640 154480 ) N ; 107 | - uio_in[0] + NET uio_in[0] + DIRECTION INPUT + USE SIGNAL 108 | + PORT 109 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 110 | + PLACED ( 148800 154480 ) N ; 111 | - uio_in[1] + NET uio_in[1] + DIRECTION INPUT + USE SIGNAL 112 | + PORT 113 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 114 | + PLACED ( 144960 154480 ) N ; 115 | - uio_in[2] + NET uio_in[2] + DIRECTION INPUT + USE SIGNAL 116 | + PORT 117 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 118 | + PLACED ( 141120 154480 ) N ; 119 | - uio_in[3] + NET uio_in[3] + DIRECTION INPUT + USE SIGNAL 120 | + PORT 121 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 122 | + PLACED ( 137280 154480 ) N ; 123 | - uio_in[4] + NET uio_in[4] + DIRECTION INPUT + USE SIGNAL 124 | + PORT 125 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 126 | + PLACED ( 133440 154480 ) N ; 127 | - uio_in[5] + NET uio_in[5] + DIRECTION INPUT + USE SIGNAL 128 | + PORT 129 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 130 | + PLACED ( 129600 154480 ) N ; 131 | - uio_in[6] + NET uio_in[6] + DIRECTION INPUT + USE SIGNAL 132 | + PORT 133 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 134 | + PLACED ( 125760 154480 ) N ; 135 | - uio_in[7] + NET uio_in[7] + DIRECTION INPUT + USE SIGNAL 136 | + PORT 137 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 138 | + PLACED ( 121920 154480 ) N ; 139 | - uio_oe[0] + NET uio_oe[0] + DIRECTION OUTPUT + USE SIGNAL 140 | + PORT 141 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 142 | + PLACED ( 56640 154480 ) N ; 143 | - uio_oe[1] + NET uio_oe[1] + DIRECTION OUTPUT + USE SIGNAL 144 | + PORT 145 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 146 | + PLACED ( 52800 154480 ) N ; 147 | - uio_oe[2] + NET uio_oe[2] + DIRECTION OUTPUT + USE SIGNAL 148 | + PORT 149 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 150 | + PLACED ( 48960 154480 ) N ; 151 | - uio_oe[3] + NET uio_oe[3] + DIRECTION OUTPUT + USE SIGNAL 152 | + PORT 153 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 154 | + PLACED ( 45120 154480 ) N ; 155 | - uio_oe[4] + NET uio_oe[4] + DIRECTION OUTPUT + USE SIGNAL 156 | + PORT 157 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 158 | + PLACED ( 41280 154480 ) N ; 159 | - uio_oe[5] + NET uio_oe[5] + DIRECTION OUTPUT + USE SIGNAL 160 | + PORT 161 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 162 | + PLACED ( 37440 154480 ) N ; 163 | - uio_oe[6] + NET uio_oe[6] + DIRECTION OUTPUT + USE SIGNAL 164 | + PORT 165 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 166 | + PLACED ( 33600 154480 ) N ; 167 | - uio_oe[7] + NET uio_oe[7] + DIRECTION OUTPUT + USE SIGNAL 168 | + PORT 169 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 170 | + PLACED ( 29760 154480 ) N ; 171 | - uio_out[0] + NET uio_out[0] + DIRECTION OUTPUT + USE SIGNAL 172 | + PORT 173 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 174 | + PLACED ( 87360 154480 ) N ; 175 | - uio_out[1] + NET uio_out[1] + DIRECTION OUTPUT + USE SIGNAL 176 | + PORT 177 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 178 | + PLACED ( 83520 154480 ) N ; 179 | - uio_out[2] + NET uio_out[2] + DIRECTION OUTPUT + USE SIGNAL 180 | + PORT 181 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 182 | + PLACED ( 79680 154480 ) N ; 183 | - uio_out[3] + NET uio_out[3] + DIRECTION OUTPUT + USE SIGNAL 184 | + PORT 185 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 186 | + PLACED ( 75840 154480 ) N ; 187 | - uio_out[4] + NET uio_out[4] + DIRECTION OUTPUT + USE SIGNAL 188 | + PORT 189 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 190 | + PLACED ( 72000 154480 ) N ; 191 | - uio_out[5] + NET uio_out[5] + DIRECTION OUTPUT + USE SIGNAL 192 | + PORT 193 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 194 | + PLACED ( 68160 154480 ) N ; 195 | - uio_out[6] + NET uio_out[6] + DIRECTION OUTPUT + USE SIGNAL 196 | + PORT 197 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 198 | + PLACED ( 64320 154480 ) N ; 199 | - uio_out[7] + NET uio_out[7] + DIRECTION OUTPUT + USE SIGNAL 200 | + PORT 201 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 202 | + PLACED ( 60480 154480 ) N ; 203 | - uo_out[0] + NET uo_out[0] + DIRECTION OUTPUT + USE SIGNAL 204 | + PORT 205 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 206 | + PLACED ( 118080 154480 ) N ; 207 | - uo_out[1] + NET uo_out[1] + DIRECTION OUTPUT + USE SIGNAL 208 | + PORT 209 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 210 | + PLACED ( 114240 154480 ) N ; 211 | - uo_out[2] + NET uo_out[2] + DIRECTION OUTPUT + USE SIGNAL 212 | + PORT 213 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 214 | + PLACED ( 110400 154480 ) N ; 215 | - uo_out[3] + NET uo_out[3] + DIRECTION OUTPUT + USE SIGNAL 216 | + PORT 217 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 218 | + PLACED ( 106560 154480 ) N ; 219 | - uo_out[4] + NET uo_out[4] + DIRECTION OUTPUT + USE SIGNAL 220 | + PORT 221 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 222 | + PLACED ( 102720 154480 ) N ; 223 | - uo_out[5] + NET uo_out[5] + DIRECTION OUTPUT + USE SIGNAL 224 | + PORT 225 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 226 | + PLACED ( 98880 154480 ) N ; 227 | - uo_out[6] + NET uo_out[6] + DIRECTION OUTPUT + USE SIGNAL 228 | + PORT 229 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 230 | + PLACED ( 95040 154480 ) N ; 231 | - uo_out[7] + NET uo_out[7] + DIRECTION OUTPUT + USE SIGNAL 232 | + PORT 233 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 234 | + PLACED ( 91200 154480 ) N ; 235 | END PINS 236 | SPECIALNETS 2 ; 237 | - VGND + USE GROUND ; 238 | - VPWR + USE POWER ; 239 | END SPECIALNETS 240 | NETS 43 ; 241 | - clk ( PIN clk ) + USE SIGNAL ; 242 | - ena ( PIN ena ) + USE SIGNAL ; 243 | - rst_n ( PIN rst_n ) + USE SIGNAL ; 244 | - ui_in[0] ( PIN ui_in[0] ) + USE SIGNAL ; 245 | - ui_in[1] ( PIN ui_in[1] ) + USE SIGNAL ; 246 | - ui_in[2] ( PIN ui_in[2] ) + USE SIGNAL ; 247 | - ui_in[3] ( PIN ui_in[3] ) + USE SIGNAL ; 248 | - ui_in[4] ( PIN ui_in[4] ) + USE SIGNAL ; 249 | - ui_in[5] ( PIN ui_in[5] ) + USE SIGNAL ; 250 | - ui_in[6] ( PIN ui_in[6] ) + USE SIGNAL ; 251 | - ui_in[7] ( PIN ui_in[7] ) + USE SIGNAL ; 252 | - uio_in[0] ( PIN uio_in[0] ) + USE SIGNAL ; 253 | - uio_in[1] ( PIN uio_in[1] ) + USE SIGNAL ; 254 | - uio_in[2] ( PIN uio_in[2] ) + USE SIGNAL ; 255 | - uio_in[3] ( PIN uio_in[3] ) + USE SIGNAL ; 256 | - uio_in[4] ( PIN uio_in[4] ) + USE SIGNAL ; 257 | - uio_in[5] ( PIN uio_in[5] ) + USE SIGNAL ; 258 | - uio_in[6] ( PIN uio_in[6] ) + USE SIGNAL ; 259 | - uio_in[7] ( PIN uio_in[7] ) + USE SIGNAL ; 260 | - uio_oe[0] ( PIN uio_oe[0] ) + USE SIGNAL ; 261 | - uio_oe[1] ( PIN uio_oe[1] ) + USE SIGNAL ; 262 | - uio_oe[2] ( PIN uio_oe[2] ) + USE SIGNAL ; 263 | - uio_oe[3] ( PIN uio_oe[3] ) + USE SIGNAL ; 264 | - uio_oe[4] ( PIN uio_oe[4] ) + USE SIGNAL ; 265 | - uio_oe[5] ( PIN uio_oe[5] ) + USE SIGNAL ; 266 | - uio_oe[6] ( PIN uio_oe[6] ) + USE SIGNAL ; 267 | - uio_oe[7] ( PIN uio_oe[7] ) + USE SIGNAL ; 268 | - uio_out[0] ( PIN uio_out[0] ) + USE SIGNAL ; 269 | - uio_out[1] ( PIN uio_out[1] ) + USE SIGNAL ; 270 | - uio_out[2] ( PIN uio_out[2] ) + USE SIGNAL ; 271 | - uio_out[3] ( PIN uio_out[3] ) + USE SIGNAL ; 272 | - uio_out[4] ( PIN uio_out[4] ) + USE SIGNAL ; 273 | - uio_out[5] ( PIN uio_out[5] ) + USE SIGNAL ; 274 | - uio_out[6] ( PIN uio_out[6] ) + USE SIGNAL ; 275 | - uio_out[7] ( PIN uio_out[7] ) + USE SIGNAL ; 276 | - uo_out[0] ( PIN uo_out[0] ) + USE SIGNAL ; 277 | - uo_out[1] ( PIN uo_out[1] ) + USE SIGNAL ; 278 | - uo_out[2] ( PIN uo_out[2] ) + USE SIGNAL ; 279 | - uo_out[3] ( PIN uo_out[3] ) + USE SIGNAL ; 280 | - uo_out[4] ( PIN uo_out[4] ) + USE SIGNAL ; 281 | - uo_out[5] ( PIN uo_out[5] ) + USE SIGNAL ; 282 | - uo_out[6] ( PIN uo_out[6] ) + USE SIGNAL ; 283 | - uo_out[7] ( PIN uo_out[7] ) + USE SIGNAL ; 284 | END NETS 285 | END DESIGN 286 | -------------------------------------------------------------------------------- /ihp/def/tt_block_2x1_pgvdd.def: -------------------------------------------------------------------------------- 1 | VERSION 5.8 ; 2 | DIVIDERCHAR "/" ; 3 | BUSBITCHARS "[]" ; 4 | DESIGN tt_um_template ; 5 | UNITS DISTANCE MICRONS 1000 ; 6 | DIEAREA ( 0 0 ) ( 419520 154980 ) ; 7 | ROW ROW_0 CoreSite 2880 3780 N DO 862 BY 1 STEP 480 0 ; 8 | ROW ROW_1 CoreSite 2880 7560 FS DO 862 BY 1 STEP 480 0 ; 9 | ROW ROW_2 CoreSite 2880 11340 N DO 862 BY 1 STEP 480 0 ; 10 | ROW ROW_3 CoreSite 2880 15120 FS DO 862 BY 1 STEP 480 0 ; 11 | ROW ROW_4 CoreSite 2880 18900 N DO 862 BY 1 STEP 480 0 ; 12 | ROW ROW_5 CoreSite 2880 22680 FS DO 862 BY 1 STEP 480 0 ; 13 | ROW ROW_6 CoreSite 2880 26460 N DO 862 BY 1 STEP 480 0 ; 14 | ROW ROW_7 CoreSite 2880 30240 FS DO 862 BY 1 STEP 480 0 ; 15 | ROW ROW_8 CoreSite 2880 34020 N DO 862 BY 1 STEP 480 0 ; 16 | ROW ROW_9 CoreSite 2880 37800 FS DO 862 BY 1 STEP 480 0 ; 17 | ROW ROW_10 CoreSite 2880 41580 N DO 862 BY 1 STEP 480 0 ; 18 | ROW ROW_11 CoreSite 2880 45360 FS DO 862 BY 1 STEP 480 0 ; 19 | ROW ROW_12 CoreSite 2880 49140 N DO 862 BY 1 STEP 480 0 ; 20 | ROW ROW_13 CoreSite 2880 52920 FS DO 862 BY 1 STEP 480 0 ; 21 | ROW ROW_14 CoreSite 2880 56700 N DO 862 BY 1 STEP 480 0 ; 22 | ROW ROW_15 CoreSite 2880 60480 FS DO 862 BY 1 STEP 480 0 ; 23 | ROW ROW_16 CoreSite 2880 64260 N DO 862 BY 1 STEP 480 0 ; 24 | ROW ROW_17 CoreSite 2880 68040 FS DO 862 BY 1 STEP 480 0 ; 25 | ROW ROW_18 CoreSite 2880 71820 N DO 862 BY 1 STEP 480 0 ; 26 | ROW ROW_19 CoreSite 2880 75600 FS DO 862 BY 1 STEP 480 0 ; 27 | ROW ROW_20 CoreSite 2880 79380 N DO 862 BY 1 STEP 480 0 ; 28 | ROW ROW_21 CoreSite 2880 83160 FS DO 862 BY 1 STEP 480 0 ; 29 | ROW ROW_22 CoreSite 2880 86940 N DO 862 BY 1 STEP 480 0 ; 30 | ROW ROW_23 CoreSite 2880 90720 FS DO 862 BY 1 STEP 480 0 ; 31 | ROW ROW_24 CoreSite 2880 94500 N DO 862 BY 1 STEP 480 0 ; 32 | ROW ROW_25 CoreSite 2880 98280 FS DO 862 BY 1 STEP 480 0 ; 33 | ROW ROW_26 CoreSite 2880 102060 N DO 862 BY 1 STEP 480 0 ; 34 | ROW ROW_27 CoreSite 2880 105840 FS DO 862 BY 1 STEP 480 0 ; 35 | ROW ROW_28 CoreSite 2880 109620 N DO 862 BY 1 STEP 480 0 ; 36 | ROW ROW_29 CoreSite 2880 113400 FS DO 862 BY 1 STEP 480 0 ; 37 | ROW ROW_30 CoreSite 2880 117180 N DO 862 BY 1 STEP 480 0 ; 38 | ROW ROW_31 CoreSite 2880 120960 FS DO 862 BY 1 STEP 480 0 ; 39 | ROW ROW_32 CoreSite 2880 124740 N DO 862 BY 1 STEP 480 0 ; 40 | ROW ROW_33 CoreSite 2880 128520 FS DO 862 BY 1 STEP 480 0 ; 41 | ROW ROW_34 CoreSite 2880 132300 N DO 862 BY 1 STEP 480 0 ; 42 | ROW ROW_35 CoreSite 2880 136080 FS DO 862 BY 1 STEP 480 0 ; 43 | ROW ROW_36 CoreSite 2880 139860 N DO 862 BY 1 STEP 480 0 ; 44 | ROW ROW_37 CoreSite 2880 143640 FS DO 862 BY 1 STEP 480 0 ; 45 | ROW ROW_38 CoreSite 2880 147420 N DO 862 BY 1 STEP 480 0 ; 46 | TRACKS X 480 DO 873 STEP 480 LAYER Metal1 ; 47 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal1 ; 48 | TRACKS X 420 DO 998 STEP 420 LAYER Metal2 ; 49 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal2 ; 50 | TRACKS X 480 DO 873 STEP 480 LAYER Metal3 ; 51 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal3 ; 52 | TRACKS X 420 DO 998 STEP 420 LAYER Metal4 ; 53 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal4 ; 54 | TRACKS X 480 DO 873 STEP 480 LAYER Metal5 ; 55 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal5 ; 56 | TRACKS X 1460 DO 184 STEP 2280 LAYER TopMetal1 ; 57 | TRACKS Y 1460 DO 67 STEP 2280 LAYER TopMetal1 ; 58 | TRACKS X 2000 DO 105 STEP 4000 LAYER TopMetal2 ; 59 | TRACKS Y 2000 DO 38 STEP 4000 LAYER TopMetal2 ; 60 | COMPONENTS 0 ; 61 | END COMPONENTS 62 | PINS 43 ; 63 | - clk + NET clk + DIRECTION INPUT + USE SIGNAL 64 | + PORT 65 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 66 | + PLACED ( 187200 154480 ) N ; 67 | - ena + NET ena + DIRECTION INPUT + USE SIGNAL 68 | + PORT 69 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 70 | + PLACED ( 191040 154480 ) N ; 71 | - rst_n + NET rst_n + DIRECTION INPUT + USE SIGNAL 72 | + PORT 73 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 74 | + PLACED ( 183360 154480 ) N ; 75 | - ui_in[0] + NET ui_in[0] + DIRECTION INPUT + USE SIGNAL 76 | + PORT 77 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 78 | + PLACED ( 179520 154480 ) N ; 79 | - ui_in[1] + NET ui_in[1] + DIRECTION INPUT + USE SIGNAL 80 | + PORT 81 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 82 | + PLACED ( 175680 154480 ) N ; 83 | - ui_in[2] + NET ui_in[2] + DIRECTION INPUT + USE SIGNAL 84 | + PORT 85 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 86 | + PLACED ( 171840 154480 ) N ; 87 | - ui_in[3] + NET ui_in[3] + DIRECTION INPUT + USE SIGNAL 88 | + PORT 89 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 90 | + PLACED ( 168000 154480 ) N ; 91 | - ui_in[4] + NET ui_in[4] + DIRECTION INPUT + USE SIGNAL 92 | + PORT 93 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 94 | + PLACED ( 164160 154480 ) N ; 95 | - ui_in[5] + NET ui_in[5] + DIRECTION INPUT + USE SIGNAL 96 | + PORT 97 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 98 | + PLACED ( 160320 154480 ) N ; 99 | - ui_in[6] + NET ui_in[6] + DIRECTION INPUT + USE SIGNAL 100 | + PORT 101 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 102 | + PLACED ( 156480 154480 ) N ; 103 | - ui_in[7] + NET ui_in[7] + DIRECTION INPUT + USE SIGNAL 104 | + PORT 105 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 106 | + PLACED ( 152640 154480 ) N ; 107 | - uio_in[0] + NET uio_in[0] + DIRECTION INPUT + USE SIGNAL 108 | + PORT 109 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 110 | + PLACED ( 148800 154480 ) N ; 111 | - uio_in[1] + NET uio_in[1] + DIRECTION INPUT + USE SIGNAL 112 | + PORT 113 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 114 | + PLACED ( 144960 154480 ) N ; 115 | - uio_in[2] + NET uio_in[2] + DIRECTION INPUT + USE SIGNAL 116 | + PORT 117 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 118 | + PLACED ( 141120 154480 ) N ; 119 | - uio_in[3] + NET uio_in[3] + DIRECTION INPUT + USE SIGNAL 120 | + PORT 121 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 122 | + PLACED ( 137280 154480 ) N ; 123 | - uio_in[4] + NET uio_in[4] + DIRECTION INPUT + USE SIGNAL 124 | + PORT 125 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 126 | + PLACED ( 133440 154480 ) N ; 127 | - uio_in[5] + NET uio_in[5] + DIRECTION INPUT + USE SIGNAL 128 | + PORT 129 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 130 | + PLACED ( 129600 154480 ) N ; 131 | - uio_in[6] + NET uio_in[6] + DIRECTION INPUT + USE SIGNAL 132 | + PORT 133 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 134 | + PLACED ( 125760 154480 ) N ; 135 | - uio_in[7] + NET uio_in[7] + DIRECTION INPUT + USE SIGNAL 136 | + PORT 137 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 138 | + PLACED ( 121920 154480 ) N ; 139 | - uio_oe[0] + NET uio_oe[0] + DIRECTION OUTPUT + USE SIGNAL 140 | + PORT 141 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 142 | + PLACED ( 56640 154480 ) N ; 143 | - uio_oe[1] + NET uio_oe[1] + DIRECTION OUTPUT + USE SIGNAL 144 | + PORT 145 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 146 | + PLACED ( 52800 154480 ) N ; 147 | - uio_oe[2] + NET uio_oe[2] + DIRECTION OUTPUT + USE SIGNAL 148 | + PORT 149 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 150 | + PLACED ( 48960 154480 ) N ; 151 | - uio_oe[3] + NET uio_oe[3] + DIRECTION OUTPUT + USE SIGNAL 152 | + PORT 153 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 154 | + PLACED ( 45120 154480 ) N ; 155 | - uio_oe[4] + NET uio_oe[4] + DIRECTION OUTPUT + USE SIGNAL 156 | + PORT 157 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 158 | + PLACED ( 41280 154480 ) N ; 159 | - uio_oe[5] + NET uio_oe[5] + DIRECTION OUTPUT + USE SIGNAL 160 | + PORT 161 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 162 | + PLACED ( 37440 154480 ) N ; 163 | - uio_oe[6] + NET uio_oe[6] + DIRECTION OUTPUT + USE SIGNAL 164 | + PORT 165 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 166 | + PLACED ( 33600 154480 ) N ; 167 | - uio_oe[7] + NET uio_oe[7] + DIRECTION OUTPUT + USE SIGNAL 168 | + PORT 169 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 170 | + PLACED ( 29760 154480 ) N ; 171 | - uio_out[0] + NET uio_out[0] + DIRECTION OUTPUT + USE SIGNAL 172 | + PORT 173 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 174 | + PLACED ( 87360 154480 ) N ; 175 | - uio_out[1] + NET uio_out[1] + DIRECTION OUTPUT + USE SIGNAL 176 | + PORT 177 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 178 | + PLACED ( 83520 154480 ) N ; 179 | - uio_out[2] + NET uio_out[2] + DIRECTION OUTPUT + USE SIGNAL 180 | + PORT 181 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 182 | + PLACED ( 79680 154480 ) N ; 183 | - uio_out[3] + NET uio_out[3] + DIRECTION OUTPUT + USE SIGNAL 184 | + PORT 185 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 186 | + PLACED ( 75840 154480 ) N ; 187 | - uio_out[4] + NET uio_out[4] + DIRECTION OUTPUT + USE SIGNAL 188 | + PORT 189 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 190 | + PLACED ( 72000 154480 ) N ; 191 | - uio_out[5] + NET uio_out[5] + DIRECTION OUTPUT + USE SIGNAL 192 | + PORT 193 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 194 | + PLACED ( 68160 154480 ) N ; 195 | - uio_out[6] + NET uio_out[6] + DIRECTION OUTPUT + USE SIGNAL 196 | + PORT 197 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 198 | + PLACED ( 64320 154480 ) N ; 199 | - uio_out[7] + NET uio_out[7] + DIRECTION OUTPUT + USE SIGNAL 200 | + PORT 201 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 202 | + PLACED ( 60480 154480 ) N ; 203 | - uo_out[0] + NET uo_out[0] + DIRECTION OUTPUT + USE SIGNAL 204 | + PORT 205 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 206 | + PLACED ( 118080 154480 ) N ; 207 | - uo_out[1] + NET uo_out[1] + DIRECTION OUTPUT + USE SIGNAL 208 | + PORT 209 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 210 | + PLACED ( 114240 154480 ) N ; 211 | - uo_out[2] + NET uo_out[2] + DIRECTION OUTPUT + USE SIGNAL 212 | + PORT 213 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 214 | + PLACED ( 110400 154480 ) N ; 215 | - uo_out[3] + NET uo_out[3] + DIRECTION OUTPUT + USE SIGNAL 216 | + PORT 217 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 218 | + PLACED ( 106560 154480 ) N ; 219 | - uo_out[4] + NET uo_out[4] + DIRECTION OUTPUT + USE SIGNAL 220 | + PORT 221 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 222 | + PLACED ( 102720 154480 ) N ; 223 | - uo_out[5] + NET uo_out[5] + DIRECTION OUTPUT + USE SIGNAL 224 | + PORT 225 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 226 | + PLACED ( 98880 154480 ) N ; 227 | - uo_out[6] + NET uo_out[6] + DIRECTION OUTPUT + USE SIGNAL 228 | + PORT 229 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 230 | + PLACED ( 95040 154480 ) N ; 231 | - uo_out[7] + NET uo_out[7] + DIRECTION OUTPUT + USE SIGNAL 232 | + PORT 233 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 234 | + PLACED ( 91200 154480 ) N ; 235 | END PINS 236 | SPECIALNETS 2 ; 237 | - VGND + USE GROUND ; 238 | - VPWR + USE POWER ; 239 | END SPECIALNETS 240 | NETS 43 ; 241 | - clk ( PIN clk ) + USE SIGNAL ; 242 | - ena ( PIN ena ) + USE SIGNAL ; 243 | - rst_n ( PIN rst_n ) + USE SIGNAL ; 244 | - ui_in[0] ( PIN ui_in[0] ) + USE SIGNAL ; 245 | - ui_in[1] ( PIN ui_in[1] ) + USE SIGNAL ; 246 | - ui_in[2] ( PIN ui_in[2] ) + USE SIGNAL ; 247 | - ui_in[3] ( PIN ui_in[3] ) + USE SIGNAL ; 248 | - ui_in[4] ( PIN ui_in[4] ) + USE SIGNAL ; 249 | - ui_in[5] ( PIN ui_in[5] ) + USE SIGNAL ; 250 | - ui_in[6] ( PIN ui_in[6] ) + USE SIGNAL ; 251 | - ui_in[7] ( PIN ui_in[7] ) + USE SIGNAL ; 252 | - uio_in[0] ( PIN uio_in[0] ) + USE SIGNAL ; 253 | - uio_in[1] ( PIN uio_in[1] ) + USE SIGNAL ; 254 | - uio_in[2] ( PIN uio_in[2] ) + USE SIGNAL ; 255 | - uio_in[3] ( PIN uio_in[3] ) + USE SIGNAL ; 256 | - uio_in[4] ( PIN uio_in[4] ) + USE SIGNAL ; 257 | - uio_in[5] ( PIN uio_in[5] ) + USE SIGNAL ; 258 | - uio_in[6] ( PIN uio_in[6] ) + USE SIGNAL ; 259 | - uio_in[7] ( PIN uio_in[7] ) + USE SIGNAL ; 260 | - uio_oe[0] ( PIN uio_oe[0] ) + USE SIGNAL ; 261 | - uio_oe[1] ( PIN uio_oe[1] ) + USE SIGNAL ; 262 | - uio_oe[2] ( PIN uio_oe[2] ) + USE SIGNAL ; 263 | - uio_oe[3] ( PIN uio_oe[3] ) + USE SIGNAL ; 264 | - uio_oe[4] ( PIN uio_oe[4] ) + USE SIGNAL ; 265 | - uio_oe[5] ( PIN uio_oe[5] ) + USE SIGNAL ; 266 | - uio_oe[6] ( PIN uio_oe[6] ) + USE SIGNAL ; 267 | - uio_oe[7] ( PIN uio_oe[7] ) + USE SIGNAL ; 268 | - uio_out[0] ( PIN uio_out[0] ) + USE SIGNAL ; 269 | - uio_out[1] ( PIN uio_out[1] ) + USE SIGNAL ; 270 | - uio_out[2] ( PIN uio_out[2] ) + USE SIGNAL ; 271 | - uio_out[3] ( PIN uio_out[3] ) + USE SIGNAL ; 272 | - uio_out[4] ( PIN uio_out[4] ) + USE SIGNAL ; 273 | - uio_out[5] ( PIN uio_out[5] ) + USE SIGNAL ; 274 | - uio_out[6] ( PIN uio_out[6] ) + USE SIGNAL ; 275 | - uio_out[7] ( PIN uio_out[7] ) + USE SIGNAL ; 276 | - uo_out[0] ( PIN uo_out[0] ) + USE SIGNAL ; 277 | - uo_out[1] ( PIN uo_out[1] ) + USE SIGNAL ; 278 | - uo_out[2] ( PIN uo_out[2] ) + USE SIGNAL ; 279 | - uo_out[3] ( PIN uo_out[3] ) + USE SIGNAL ; 280 | - uo_out[4] ( PIN uo_out[4] ) + USE SIGNAL ; 281 | - uo_out[5] ( PIN uo_out[5] ) + USE SIGNAL ; 282 | - uo_out[6] ( PIN uo_out[6] ) + USE SIGNAL ; 283 | - uo_out[7] ( PIN uo_out[7] ) + USE SIGNAL ; 284 | END NETS 285 | END DESIGN 286 | -------------------------------------------------------------------------------- /ihp/def/tt_block_3x1_pgvdd.def: -------------------------------------------------------------------------------- 1 | VERSION 5.8 ; 2 | DIVIDERCHAR "/" ; 3 | BUSBITCHARS "[]" ; 4 | DESIGN tt_um_template ; 5 | UNITS DISTANCE MICRONS 1000 ; 6 | DIEAREA ( 0 0 ) ( 636960 154980 ) ; 7 | ROW ROW_0 CoreSite 2880 3780 N DO 1315 BY 1 STEP 480 0 ; 8 | ROW ROW_1 CoreSite 2880 7560 FS DO 1315 BY 1 STEP 480 0 ; 9 | ROW ROW_2 CoreSite 2880 11340 N DO 1315 BY 1 STEP 480 0 ; 10 | ROW ROW_3 CoreSite 2880 15120 FS DO 1315 BY 1 STEP 480 0 ; 11 | ROW ROW_4 CoreSite 2880 18900 N DO 1315 BY 1 STEP 480 0 ; 12 | ROW ROW_5 CoreSite 2880 22680 FS DO 1315 BY 1 STEP 480 0 ; 13 | ROW ROW_6 CoreSite 2880 26460 N DO 1315 BY 1 STEP 480 0 ; 14 | ROW ROW_7 CoreSite 2880 30240 FS DO 1315 BY 1 STEP 480 0 ; 15 | ROW ROW_8 CoreSite 2880 34020 N DO 1315 BY 1 STEP 480 0 ; 16 | ROW ROW_9 CoreSite 2880 37800 FS DO 1315 BY 1 STEP 480 0 ; 17 | ROW ROW_10 CoreSite 2880 41580 N DO 1315 BY 1 STEP 480 0 ; 18 | ROW ROW_11 CoreSite 2880 45360 FS DO 1315 BY 1 STEP 480 0 ; 19 | ROW ROW_12 CoreSite 2880 49140 N DO 1315 BY 1 STEP 480 0 ; 20 | ROW ROW_13 CoreSite 2880 52920 FS DO 1315 BY 1 STEP 480 0 ; 21 | ROW ROW_14 CoreSite 2880 56700 N DO 1315 BY 1 STEP 480 0 ; 22 | ROW ROW_15 CoreSite 2880 60480 FS DO 1315 BY 1 STEP 480 0 ; 23 | ROW ROW_16 CoreSite 2880 64260 N DO 1315 BY 1 STEP 480 0 ; 24 | ROW ROW_17 CoreSite 2880 68040 FS DO 1315 BY 1 STEP 480 0 ; 25 | ROW ROW_18 CoreSite 2880 71820 N DO 1315 BY 1 STEP 480 0 ; 26 | ROW ROW_19 CoreSite 2880 75600 FS DO 1315 BY 1 STEP 480 0 ; 27 | ROW ROW_20 CoreSite 2880 79380 N DO 1315 BY 1 STEP 480 0 ; 28 | ROW ROW_21 CoreSite 2880 83160 FS DO 1315 BY 1 STEP 480 0 ; 29 | ROW ROW_22 CoreSite 2880 86940 N DO 1315 BY 1 STEP 480 0 ; 30 | ROW ROW_23 CoreSite 2880 90720 FS DO 1315 BY 1 STEP 480 0 ; 31 | ROW ROW_24 CoreSite 2880 94500 N DO 1315 BY 1 STEP 480 0 ; 32 | ROW ROW_25 CoreSite 2880 98280 FS DO 1315 BY 1 STEP 480 0 ; 33 | ROW ROW_26 CoreSite 2880 102060 N DO 1315 BY 1 STEP 480 0 ; 34 | ROW ROW_27 CoreSite 2880 105840 FS DO 1315 BY 1 STEP 480 0 ; 35 | ROW ROW_28 CoreSite 2880 109620 N DO 1315 BY 1 STEP 480 0 ; 36 | ROW ROW_29 CoreSite 2880 113400 FS DO 1315 BY 1 STEP 480 0 ; 37 | ROW ROW_30 CoreSite 2880 117180 N DO 1315 BY 1 STEP 480 0 ; 38 | ROW ROW_31 CoreSite 2880 120960 FS DO 1315 BY 1 STEP 480 0 ; 39 | ROW ROW_32 CoreSite 2880 124740 N DO 1315 BY 1 STEP 480 0 ; 40 | ROW ROW_33 CoreSite 2880 128520 FS DO 1315 BY 1 STEP 480 0 ; 41 | ROW ROW_34 CoreSite 2880 132300 N DO 1315 BY 1 STEP 480 0 ; 42 | ROW ROW_35 CoreSite 2880 136080 FS DO 1315 BY 1 STEP 480 0 ; 43 | ROW ROW_36 CoreSite 2880 139860 N DO 1315 BY 1 STEP 480 0 ; 44 | ROW ROW_37 CoreSite 2880 143640 FS DO 1315 BY 1 STEP 480 0 ; 45 | ROW ROW_38 CoreSite 2880 147420 N DO 1315 BY 1 STEP 480 0 ; 46 | TRACKS X 480 DO 1326 STEP 480 LAYER Metal1 ; 47 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal1 ; 48 | TRACKS X 420 DO 1516 STEP 420 LAYER Metal2 ; 49 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal2 ; 50 | TRACKS X 480 DO 1326 STEP 480 LAYER Metal3 ; 51 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal3 ; 52 | TRACKS X 420 DO 1516 STEP 420 LAYER Metal4 ; 53 | TRACKS Y 420 DO 368 STEP 420 LAYER Metal4 ; 54 | TRACKS X 480 DO 1326 STEP 480 LAYER Metal5 ; 55 | TRACKS Y 480 DO 322 STEP 480 LAYER Metal5 ; 56 | TRACKS X 1460 DO 279 STEP 2280 LAYER TopMetal1 ; 57 | TRACKS Y 1460 DO 67 STEP 2280 LAYER TopMetal1 ; 58 | TRACKS X 2000 DO 159 STEP 4000 LAYER TopMetal2 ; 59 | TRACKS Y 2000 DO 38 STEP 4000 LAYER TopMetal2 ; 60 | COMPONENTS 0 ; 61 | END COMPONENTS 62 | PINS 43 ; 63 | - clk + NET clk + DIRECTION INPUT + USE SIGNAL 64 | + PORT 65 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 66 | + PLACED ( 187200 154480 ) N ; 67 | - ena + NET ena + DIRECTION INPUT + USE SIGNAL 68 | + PORT 69 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 70 | + PLACED ( 191040 154480 ) N ; 71 | - rst_n + NET rst_n + DIRECTION INPUT + USE SIGNAL 72 | + PORT 73 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 74 | + PLACED ( 183360 154480 ) N ; 75 | - ui_in[0] + NET ui_in[0] + DIRECTION INPUT + USE SIGNAL 76 | + PORT 77 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 78 | + PLACED ( 179520 154480 ) N ; 79 | - ui_in[1] + NET ui_in[1] + DIRECTION INPUT + USE SIGNAL 80 | + PORT 81 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 82 | + PLACED ( 175680 154480 ) N ; 83 | - ui_in[2] + NET ui_in[2] + DIRECTION INPUT + USE SIGNAL 84 | + PORT 85 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 86 | + PLACED ( 171840 154480 ) N ; 87 | - ui_in[3] + NET ui_in[3] + DIRECTION INPUT + USE SIGNAL 88 | + PORT 89 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 90 | + PLACED ( 168000 154480 ) N ; 91 | - ui_in[4] + NET ui_in[4] + DIRECTION INPUT + USE SIGNAL 92 | + PORT 93 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 94 | + PLACED ( 164160 154480 ) N ; 95 | - ui_in[5] + NET ui_in[5] + DIRECTION INPUT + USE SIGNAL 96 | + PORT 97 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 98 | + PLACED ( 160320 154480 ) N ; 99 | - ui_in[6] + NET ui_in[6] + DIRECTION INPUT + USE SIGNAL 100 | + PORT 101 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 102 | + PLACED ( 156480 154480 ) N ; 103 | - ui_in[7] + NET ui_in[7] + DIRECTION INPUT + USE SIGNAL 104 | + PORT 105 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 106 | + PLACED ( 152640 154480 ) N ; 107 | - uio_in[0] + NET uio_in[0] + DIRECTION INPUT + USE SIGNAL 108 | + PORT 109 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 110 | + PLACED ( 148800 154480 ) N ; 111 | - uio_in[1] + NET uio_in[1] + DIRECTION INPUT + USE SIGNAL 112 | + PORT 113 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 114 | + PLACED ( 144960 154480 ) N ; 115 | - uio_in[2] + NET uio_in[2] + DIRECTION INPUT + USE SIGNAL 116 | + PORT 117 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 118 | + PLACED ( 141120 154480 ) N ; 119 | - uio_in[3] + NET uio_in[3] + DIRECTION INPUT + USE SIGNAL 120 | + PORT 121 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 122 | + PLACED ( 137280 154480 ) N ; 123 | - uio_in[4] + NET uio_in[4] + DIRECTION INPUT + USE SIGNAL 124 | + PORT 125 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 126 | + PLACED ( 133440 154480 ) N ; 127 | - uio_in[5] + NET uio_in[5] + DIRECTION INPUT + USE SIGNAL 128 | + PORT 129 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 130 | + PLACED ( 129600 154480 ) N ; 131 | - uio_in[6] + NET uio_in[6] + DIRECTION INPUT + USE SIGNAL 132 | + PORT 133 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 134 | + PLACED ( 125760 154480 ) N ; 135 | - uio_in[7] + NET uio_in[7] + DIRECTION INPUT + USE SIGNAL 136 | + PORT 137 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 138 | + PLACED ( 121920 154480 ) N ; 139 | - uio_oe[0] + NET uio_oe[0] + DIRECTION OUTPUT + USE SIGNAL 140 | + PORT 141 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 142 | + PLACED ( 56640 154480 ) N ; 143 | - uio_oe[1] + NET uio_oe[1] + DIRECTION OUTPUT + USE SIGNAL 144 | + PORT 145 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 146 | + PLACED ( 52800 154480 ) N ; 147 | - uio_oe[2] + NET uio_oe[2] + DIRECTION OUTPUT + USE SIGNAL 148 | + PORT 149 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 150 | + PLACED ( 48960 154480 ) N ; 151 | - uio_oe[3] + NET uio_oe[3] + DIRECTION OUTPUT + USE SIGNAL 152 | + PORT 153 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 154 | + PLACED ( 45120 154480 ) N ; 155 | - uio_oe[4] + NET uio_oe[4] + DIRECTION OUTPUT + USE SIGNAL 156 | + PORT 157 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 158 | + PLACED ( 41280 154480 ) N ; 159 | - uio_oe[5] + NET uio_oe[5] + DIRECTION OUTPUT + USE SIGNAL 160 | + PORT 161 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 162 | + PLACED ( 37440 154480 ) N ; 163 | - uio_oe[6] + NET uio_oe[6] + DIRECTION OUTPUT + USE SIGNAL 164 | + PORT 165 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 166 | + PLACED ( 33600 154480 ) N ; 167 | - uio_oe[7] + NET uio_oe[7] + DIRECTION OUTPUT + USE SIGNAL 168 | + PORT 169 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 170 | + PLACED ( 29760 154480 ) N ; 171 | - uio_out[0] + NET uio_out[0] + DIRECTION OUTPUT + USE SIGNAL 172 | + PORT 173 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 174 | + PLACED ( 87360 154480 ) N ; 175 | - uio_out[1] + NET uio_out[1] + DIRECTION OUTPUT + USE SIGNAL 176 | + PORT 177 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 178 | + PLACED ( 83520 154480 ) N ; 179 | - uio_out[2] + NET uio_out[2] + DIRECTION OUTPUT + USE SIGNAL 180 | + PORT 181 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 182 | + PLACED ( 79680 154480 ) N ; 183 | - uio_out[3] + NET uio_out[3] + DIRECTION OUTPUT + USE SIGNAL 184 | + PORT 185 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 186 | + PLACED ( 75840 154480 ) N ; 187 | - uio_out[4] + NET uio_out[4] + DIRECTION OUTPUT + USE SIGNAL 188 | + PORT 189 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 190 | + PLACED ( 72000 154480 ) N ; 191 | - uio_out[5] + NET uio_out[5] + DIRECTION OUTPUT + USE SIGNAL 192 | + PORT 193 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 194 | + PLACED ( 68160 154480 ) N ; 195 | - uio_out[6] + NET uio_out[6] + DIRECTION OUTPUT + USE SIGNAL 196 | + PORT 197 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 198 | + PLACED ( 64320 154480 ) N ; 199 | - uio_out[7] + NET uio_out[7] + DIRECTION OUTPUT + USE SIGNAL 200 | + PORT 201 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 202 | + PLACED ( 60480 154480 ) N ; 203 | - uo_out[0] + NET uo_out[0] + DIRECTION OUTPUT + USE SIGNAL 204 | + PORT 205 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 206 | + PLACED ( 118080 154480 ) N ; 207 | - uo_out[1] + NET uo_out[1] + DIRECTION OUTPUT + USE SIGNAL 208 | + PORT 209 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 210 | + PLACED ( 114240 154480 ) N ; 211 | - uo_out[2] + NET uo_out[2] + DIRECTION OUTPUT + USE SIGNAL 212 | + PORT 213 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 214 | + PLACED ( 110400 154480 ) N ; 215 | - uo_out[3] + NET uo_out[3] + DIRECTION OUTPUT + USE SIGNAL 216 | + PORT 217 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 218 | + PLACED ( 106560 154480 ) N ; 219 | - uo_out[4] + NET uo_out[4] + DIRECTION OUTPUT + USE SIGNAL 220 | + PORT 221 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 222 | + PLACED ( 102720 154480 ) N ; 223 | - uo_out[5] + NET uo_out[5] + DIRECTION OUTPUT + USE SIGNAL 224 | + PORT 225 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 226 | + PLACED ( 98880 154480 ) N ; 227 | - uo_out[6] + NET uo_out[6] + DIRECTION OUTPUT + USE SIGNAL 228 | + PORT 229 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 230 | + PLACED ( 95040 154480 ) N ; 231 | - uo_out[7] + NET uo_out[7] + DIRECTION OUTPUT + USE SIGNAL 232 | + PORT 233 | + LAYER Metal5 ( -150 -500 ) ( 150 500 ) 234 | + PLACED ( 91200 154480 ) N ; 235 | END PINS 236 | SPECIALNETS 2 ; 237 | - VGND + USE GROUND ; 238 | - VPWR + USE POWER ; 239 | END SPECIALNETS 240 | NETS 43 ; 241 | - clk ( PIN clk ) + USE SIGNAL ; 242 | - ena ( PIN ena ) + USE SIGNAL ; 243 | - rst_n ( PIN rst_n ) + USE SIGNAL ; 244 | - ui_in[0] ( PIN ui_in[0] ) + USE SIGNAL ; 245 | - ui_in[1] ( PIN ui_in[1] ) + USE SIGNAL ; 246 | - ui_in[2] ( PIN ui_in[2] ) + USE SIGNAL ; 247 | - ui_in[3] ( PIN ui_in[3] ) + USE SIGNAL ; 248 | - ui_in[4] ( PIN ui_in[4] ) + USE SIGNAL ; 249 | - ui_in[5] ( PIN ui_in[5] ) + USE SIGNAL ; 250 | - ui_in[6] ( PIN ui_in[6] ) + USE SIGNAL ; 251 | - ui_in[7] ( PIN ui_in[7] ) + USE SIGNAL ; 252 | - uio_in[0] ( PIN uio_in[0] ) + USE SIGNAL ; 253 | - uio_in[1] ( PIN uio_in[1] ) + USE SIGNAL ; 254 | - uio_in[2] ( PIN uio_in[2] ) + USE SIGNAL ; 255 | - uio_in[3] ( PIN uio_in[3] ) + USE SIGNAL ; 256 | - uio_in[4] ( PIN uio_in[4] ) + USE SIGNAL ; 257 | - uio_in[5] ( PIN uio_in[5] ) + USE SIGNAL ; 258 | - uio_in[6] ( PIN uio_in[6] ) + USE SIGNAL ; 259 | - uio_in[7] ( PIN uio_in[7] ) + USE SIGNAL ; 260 | - uio_oe[0] ( PIN uio_oe[0] ) + USE SIGNAL ; 261 | - uio_oe[1] ( PIN uio_oe[1] ) + USE SIGNAL ; 262 | - uio_oe[2] ( PIN uio_oe[2] ) + USE SIGNAL ; 263 | - uio_oe[3] ( PIN uio_oe[3] ) + USE SIGNAL ; 264 | - uio_oe[4] ( PIN uio_oe[4] ) + USE SIGNAL ; 265 | - uio_oe[5] ( PIN uio_oe[5] ) + USE SIGNAL ; 266 | - uio_oe[6] ( PIN uio_oe[6] ) + USE SIGNAL ; 267 | - uio_oe[7] ( PIN uio_oe[7] ) + USE SIGNAL ; 268 | - uio_out[0] ( PIN uio_out[0] ) + USE SIGNAL ; 269 | - uio_out[1] ( PIN uio_out[1] ) + USE SIGNAL ; 270 | - uio_out[2] ( PIN uio_out[2] ) + USE SIGNAL ; 271 | - uio_out[3] ( PIN uio_out[3] ) + USE SIGNAL ; 272 | - uio_out[4] ( PIN uio_out[4] ) + USE SIGNAL ; 273 | - uio_out[5] ( PIN uio_out[5] ) + USE SIGNAL ; 274 | - uio_out[6] ( PIN uio_out[6] ) + USE SIGNAL ; 275 | - uio_out[7] ( PIN uio_out[7] ) + USE SIGNAL ; 276 | - uo_out[0] ( PIN uo_out[0] ) + USE SIGNAL ; 277 | - uo_out[1] ( PIN uo_out[1] ) + USE SIGNAL ; 278 | - uo_out[2] ( PIN uo_out[2] ) + USE SIGNAL ; 279 | - uo_out[3] ( PIN uo_out[3] ) + USE SIGNAL ; 280 | - uo_out[4] ( PIN uo_out[4] ) + USE SIGNAL ; 281 | - uo_out[5] ( PIN uo_out[5] ) + USE SIGNAL ; 282 | - uo_out[6] ( PIN uo_out[6] ) + USE SIGNAL ; 283 | - uo_out[7] ( PIN uo_out[7] ) + USE SIGNAL ; 284 | END NETS 285 | END DESIGN 286 | -------------------------------------------------------------------------------- /ihp/tile_sizes.yaml: -------------------------------------------------------------------------------- 1 | 1x1: "0 0 202.08 154.98" 2 | 1x2: "0 0 202.08 313.74" 3 | 2x1: "0 0 419.52 154.98" 4 | 2x2: "0 0 419.52 313.74" 5 | 3x1: "0 0 636.96 154.98" 6 | 3x2: "0 0 636.96 313.74" 7 | 4x1: "0 0 854.40 154.98" 8 | 4x2: "0 0 854.40 313.74" 9 | 6x1: "0 0 1289.28 154.98" 10 | 6x2: "0 0 1289.28 313.74" 11 | 8x1: "0 0 1724.16 154.98" 12 | 8x2: "0 0 1724.16 313.74" 13 | -------------------------------------------------------------------------------- /logo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | import gdstk 6 | from git.repo import Repo 7 | from PIL import Image, ImageDraw, ImageFont 8 | 9 | from config import Config 10 | 11 | 12 | class LogoGenerator: 13 | def __init__(self, tt_dir, config: Config | None = None): 14 | self.tt_dir = tt_dir 15 | self.config = config 16 | 17 | def gen_logo(self, variant: str, gds_file: str, shuttle=None, commit=None): 18 | 19 | assert variant in ("top", "bottom") 20 | if variant == "top": 21 | # use included bitmap 22 | img = Image.open(f"{self.tt_dir}/logo/tt_logo.png").convert("L") 23 | 24 | elif variant == "bottom": 25 | # generate bitmap from shuttle ID & commit hash 26 | if shuttle is None: 27 | assert self.config is not None 28 | shuttle = self.config["id"] 29 | if commit is None: 30 | commit = Repo(".").commit().hexsha 31 | 32 | img = Image.new("L", (200, 200), (0,)) 33 | draw = ImageDraw.Draw(img) 34 | 35 | font_file = f"{self.tt_dir}/logo/font/UbuntuSansMono.ttf" 36 | 37 | def font(size): 38 | f = ImageFont.truetype(font_file, size) 39 | f.set_variation_by_axes([700]) 40 | return f 41 | 42 | draw.text((2, -15), shuttle, fill=255, font=font(88)) 43 | draw.text((2, 68), commit[:8], fill=255, font=font(44)) 44 | draw.text((2, 107), commit[8:18], fill=255, font=font(35)) 45 | draw.text((1, 137), commit[18:29], fill=255, font=font(32)) 46 | draw.text((1, 165), commit[29:], fill=255, font=font(32)) 47 | 48 | PRBOUNDARY_LAYER = 235 49 | PRBOUNDARY_DATATYPE = 4 50 | MET4_LAYER = 71 51 | DRAWING_DATATYPE = 20 52 | PIXEL_SIZE = 0.5 # um 53 | 54 | lib = gdstk.Library() 55 | cell = lib.new_cell(f"tt_logo_{variant}") 56 | boundary = gdstk.rectangle( 57 | (0, 0), 58 | (img.width * PIXEL_SIZE, img.height * PIXEL_SIZE), 59 | layer=PRBOUNDARY_LAYER, 60 | datatype=PRBOUNDARY_DATATYPE, 61 | ) 62 | cell.add(boundary) 63 | 64 | for y in range(img.height): 65 | for x in range(img.width): 66 | color: int = img.getpixel((x, y)) # type: ignore 67 | if color >= 128: 68 | flipped_y = img.height - y - 1 # flip vertically 69 | rect = gdstk.rectangle( 70 | (x * PIXEL_SIZE, flipped_y * PIXEL_SIZE), 71 | ((x + 1) * PIXEL_SIZE, (flipped_y + 1) * PIXEL_SIZE), 72 | layer=MET4_LAYER, 73 | datatype=DRAWING_DATATYPE, 74 | ) 75 | cell.add(rect) 76 | 77 | lib.write_gds(gds_file) 78 | 79 | 80 | if __name__ == "__main__": 81 | 82 | try: 83 | if sys.argv[1] == "--top": 84 | LogoGenerator(".").gen_logo("top", "logo/tt_logo_top.gds") 85 | 86 | elif sys.argv[1] == "--bottom": 87 | shuttle = sys.argv[2] # e.g. "tt10" 88 | commit = sys.argv[3] # e.g. "0123456789abcdef0123456789abcdef01234567" 89 | LogoGenerator(".").gen_logo( 90 | "bottom", "logo/tt_logo_bottom.gds", shuttle, commit 91 | ) 92 | 93 | except IndexError: 94 | print( 95 | f"Usage:\n {sys.argv[0]} --top\n {sys.argv[0]} --bottom ", 96 | file=sys.stderr, 97 | ) 98 | -------------------------------------------------------------------------------- /logo/font/LICENSE: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /logo/font/UbuntuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/logo/font/UbuntuSansMono.ttf -------------------------------------------------------------------------------- /logo/tt_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/logo/tt_logo.png -------------------------------------------------------------------------------- /logo/tt_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 40 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /logo/tt_logo_bottom.lef: -------------------------------------------------------------------------------- 1 | VERSION 5.7 ; 2 | NOWIREEXTENSIONATPIN ON ; 3 | DIVIDERCHAR "/" ; 4 | BUSBITCHARS "[]" ; 5 | MACRO tt_logo_bottom 6 | CLASS BLOCK ; 7 | FOREIGN tt_logo_bottom ; 8 | ORIGIN 0.000 0.000 ; 9 | SIZE 100.000 BY 100.000 ; 10 | OBS 11 | LAYER met4 ; 12 | RECT 0.000 0.000 100.000 100.000 ; 13 | END 14 | END tt_logo_bottom 15 | END LIBRARY 16 | 17 | -------------------------------------------------------------------------------- /logo/tt_logo_bottom.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module tt_logo_bottom (); 4 | endmodule 5 | -------------------------------------------------------------------------------- /logo/tt_logo_top.gds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/logo/tt_logo_top.gds -------------------------------------------------------------------------------- /logo/tt_logo_top.lef: -------------------------------------------------------------------------------- 1 | VERSION 5.7 ; 2 | NOWIREEXTENSIONATPIN ON ; 3 | DIVIDERCHAR "/" ; 4 | BUSBITCHARS "[]" ; 5 | MACRO tt_logo_top 6 | CLASS BLOCK ; 7 | FOREIGN tt_logo_top ; 8 | ORIGIN 0.000 0.000 ; 9 | SIZE 100.000 BY 100.000 ; 10 | OBS 11 | LAYER met4 ; 12 | RECT 0.000 0.000 100.000 100.000 ; 13 | END 14 | END tt_logo_top 15 | END LIBRARY 16 | 17 | -------------------------------------------------------------------------------- /logo/tt_logo_top.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module tt_logo_top (); 4 | endmodule 5 | -------------------------------------------------------------------------------- /markdown_utils.py: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import shutil 4 | from typing import Any, Dict 5 | 6 | import mistune 7 | from mistune.renderers.markdown import MarkdownRenderer 8 | 9 | 10 | class HeadingsRenderer(MarkdownRenderer): 11 | def __init__(self, min_level: int): 12 | super().__init__() 13 | self.min_level = min_level 14 | self.initial_level = math.inf 15 | 16 | def heading(self, token: Dict[str, Any], state: Any): 17 | if self.initial_level == math.inf: 18 | self.initial_level = token["attrs"]["level"] 19 | token["attrs"]["level"] = self.min_level + max( 20 | token["attrs"]["level"] - self.initial_level, 0 21 | ) 22 | return super().heading(token, state) 23 | 24 | 25 | def limit_markdown_headings(source: str, min_level: int) -> str: 26 | markdown = mistune.create_markdown(renderer=HeadingsRenderer(min_level)) 27 | return markdown(source) 28 | 29 | 30 | def unescape_braces(text: str): 31 | return text.replace("%7B", "{").replace("%7D", "}") 32 | 33 | 34 | class ImagePathRewriterRenderer(MarkdownRenderer): 35 | def __init__(self, prefix: str): 36 | super().__init__() 37 | self.prefix = prefix 38 | 39 | def image(self, token, state): 40 | url = token["attrs"]["url"] 41 | if "%7B" in url: 42 | pass 43 | elif "://" not in url and not url.startswith("/"): 44 | url = os.path.join(self.prefix, url) 45 | elif ".." in url: 46 | url = "" 47 | elif url.startswith("/"): 48 | url = os.path.join(self.prefix, "../" + url[1:]) 49 | token["attrs"]["url"] = url 50 | return super().image(token, state) 51 | 52 | 53 | def rewrite_image_paths(source: str, prefix: str) -> str: 54 | markdown = mistune.create_markdown(renderer=ImagePathRewriterRenderer(prefix)) 55 | return unescape_braces(markdown(source)) 56 | 57 | 58 | class WebsiteImagePathRewriterRenderer(MarkdownRenderer): 59 | def __init__(self, source_dir: str, target_dir: str): 60 | super().__init__() 61 | self.source_dir = source_dir 62 | self.target_dir = target_dir 63 | 64 | def image(self, token, state): 65 | url = token["attrs"]["url"] 66 | if "%7B" in url: 67 | pass 68 | elif ".." in url: 69 | url = "" 70 | elif "://" not in url and not url.startswith("/"): 71 | filename = os.path.basename(url) 72 | if url.startswith("/"): 73 | url = "../" + url[1:] 74 | shutil.copyfile( 75 | os.path.join(self.source_dir, url), 76 | os.path.join(self.target_dir, filename), 77 | ) 78 | url = f"images/{filename}" 79 | token["attrs"]["url"] = url 80 | return super().image(token, state) 81 | -------------------------------------------------------------------------------- /precheck/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .pytest_cache 3 | -------------------------------------------------------------------------------- /precheck/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/8f7a6554c058bfdae00f4fb36f5cd52838f410e7.tar.gz") 2 | { } 3 | , 4 | }: 5 | 6 | pkgs.mkShell { 7 | buildInputs = [ 8 | pkgs.klayout 9 | pkgs.magic-vlsi 10 | ]; 11 | } 12 | -------------------------------------------------------------------------------- /precheck/klayout_tools.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | from typing import Dict 3 | 4 | 5 | class LayerInfo: 6 | def __init__(self, name: str, source: str, layer: int, data_type: int): 7 | self.name = name 8 | self.source = source 9 | self.layer = layer 10 | self.data_type = data_type 11 | 12 | def __repr__(self): 13 | return f"LayerInfo(name={self.name}, source={self.source}, layer={self.layer}, data_type={self.data_type})" 14 | 15 | 16 | def parse_lyp_layers(lyp_file: str, only_valid: bool = True): 17 | with open(lyp_file) as f: 18 | xml_content = f.read() 19 | root = ET.fromstring(xml_content) 20 | 21 | layers_dict: Dict[str, LayerInfo] = {} 22 | 23 | for properties in root.findall("properties"): 24 | name = properties.find("name") 25 | source = properties.find("source") 26 | valid = properties.find("valid") 27 | 28 | if only_valid and valid is not None and valid.text == "false": 29 | continue 30 | 31 | if ( 32 | name is not None 33 | and name.text is not None 34 | and source is not None 35 | and source.text is not None 36 | ): 37 | name_key = name.text.split("-")[0].strip() 38 | layer, data_type = source.text.split("@")[0].split("/") 39 | # Add the 'source' text as the value in the dictionary 40 | layers_dict[name_key] = LayerInfo( 41 | name=name.text, 42 | source=source.text, 43 | layer=int(layer), 44 | data_type=int(data_type), 45 | ) 46 | 47 | return layers_dict 48 | -------------------------------------------------------------------------------- /precheck/magic_drc.tcl: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Efabless Corporation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # SPDX-License-Identifier: Apache-2.0 15 | 16 | 17 | set GDS_UT_PATH [lindex $argv 6] 18 | set DESIGN_NAME [lindex $argv 7] 19 | set PDK_PATH [lindex $argv 8] 20 | set DRC_REPORT [lindex $argv 9] 21 | set DRC_MAG [lindex $argv 10] 22 | 23 | gds read $GDS_UT_PATH 24 | 25 | set fout [open $DRC_REPORT w] 26 | set oscale [cif scale out] 27 | set cell_name $DESIGN_NAME 28 | magic::suspendall 29 | puts stdout "\[INFO\]: Loading $cell_name\n" 30 | flush stdout 31 | load $cell_name 32 | select top cell 33 | expand 34 | drc euclidean on 35 | drc style drc(full) 36 | drc check 37 | set drc_result [drc listall why] 38 | 39 | 40 | set count 0 41 | puts $fout "$cell_name" 42 | puts $fout "----------------------------------------" 43 | foreach {errtype coordlist} $drc_result { 44 | puts $fout $errtype 45 | puts $fout "----------------------------------------" 46 | foreach coord $coordlist { 47 | set bllx [expr {$oscale * [lindex $coord 0]}] 48 | set blly [expr {$oscale * [lindex $coord 1]}] 49 | set burx [expr {$oscale * [lindex $coord 2]}] 50 | set bury [expr {$oscale * [lindex $coord 3]}] 51 | set coords [format " %.3f %.3f %.3f %.3f" $bllx $blly $burx $bury] 52 | puts $fout "$coords" 53 | set count [expr {$count + 1} ] 54 | } 55 | puts $fout "----------------------------------------" 56 | } 57 | 58 | puts $fout "\[INFO\]: COUNT: $count" 59 | puts $fout "\[INFO\]: Should be divided by 3 or 4" 60 | 61 | puts $fout "" 62 | close $fout 63 | 64 | puts stdout "\[INFO\]: DRC Checking DONE ($DRC_REPORT)" 65 | flush stdout 66 | 67 | if {$count > 0} { 68 | puts stderr "\[ERROR\]: $count DRC errors found" 69 | puts stdout "\[INFO\]: Saving mag view with DRC errors($DRC_MAG)" 70 | # WARNING: changes the name of the cell; keep as last step 71 | save $DRC_MAG 72 | puts stdout "\[INFO\]: Saved" 73 | flush stdout 74 | exit -1 75 | } else { 76 | exit 0 77 | } 78 | -------------------------------------------------------------------------------- /precheck/precheck_failure.py: -------------------------------------------------------------------------------- 1 | class PrecheckFailure(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /precheck/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tt-precheck" 3 | requires-python = ">=3.11" 4 | dynamic = ["version"] 5 | dependencies = [ 6 | "brotli", 7 | "gdstk", 8 | "klayout>=0.28.17.post1,<0.29.0", 9 | "numpy<2", 10 | "pytest", 11 | "PyYAML", 12 | ] 13 | 14 | [tool.setuptools] 15 | packages = [] 16 | -------------------------------------------------------------------------------- /precheck/reports/.gitignore: -------------------------------------------------------------------------------- 1 | drc_beol.xml 2 | drc_feol.xml 3 | drc_offgrid.xml 4 | drc_pin_label_purposes_overlapping_drawing.xml 5 | drc_zero_area.xml 6 | drc_nwell_urpm.xml 7 | drc_sg13g2.xml 8 | magic_drc.mag 9 | magic_drc.txt 10 | results.md 11 | results.xml 12 | -------------------------------------------------------------------------------- /precheck/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.13 3 | # by the following command: 4 | # 5 | # pip-compile 6 | # 7 | brotli==1.1.0 8 | # via tt-precheck (pyproject.toml) 9 | gdstk==0.9.52 10 | # via tt-precheck (pyproject.toml) 11 | iniconfig==2.0.0 12 | # via pytest 13 | klayout==0.28.17.post1 14 | # via tt-precheck (pyproject.toml) 15 | numpy==1.26.4 16 | # via 17 | # gdstk 18 | # tt-precheck (pyproject.toml) 19 | packaging==24.1 20 | # via pytest 21 | pluggy==1.5.0 22 | # via pytest 23 | pytest==8.2.2 24 | # via tt-precheck (pyproject.toml) 25 | pyyaml==6.0.1 26 | # via tt-precheck (pyproject.toml) 27 | -------------------------------------------------------------------------------- /precheck/tech-files/README.txt: -------------------------------------------------------------------------------- 1 | sky130A_mr.drc from https://github.com/efabless/mpw_precheck revision 3467a4c8252aa884cddc7d3f73370df9744dd65e 2 | pin_label_purposes_overlapping_drawing.rb.drc from https://github.com/efabless/mpw_precheck revision f6b9c3d3f00694f96dce8444149449b4719180f0 3 | zeroarea.rb.drc from https://github.com/efabless/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2 4 | nwell_urpm.drc from https://open-source-silicon.slack.com/archives/C05FW1VMY5A/p1729112262642699?thread_ts=1728921677.220509&cid=C05FW1VMY5A 5 | 6 | sg13g2_mr.lydrc: 7 | from https://github.com/IHP-GmbH/TO_Apr2025 revision 772a369f3ceabc2c6e1eba57d91ae755682c8323 8 | with the following minimum density rules disabled: 9 | - M1.j, M2.j, M3.j, M4.j, M5.j 10 | - M1Fil.h, M2Fil.h, M3Fil.h, M4Fil.h, M5Fil.h 11 | -------------------------------------------------------------------------------- /precheck/tech-files/nwell_urpm.drc: -------------------------------------------------------------------------------- 1 | source($input, $top_cell) 2 | report("SKY130 DRC runset", $report) 3 | deep 4 | threads($thr) 5 | verbose(true) 6 | 7 | nwell = polygons(64, 20) 8 | urpm = polygons(79, 20) 9 | 10 | nwell.separation(urpm,0.7, euclidian).output( 11 | "nwell space to urpm", 12 | "nwell space to urpm: min. nwell spacing to urpm 0.7um" 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /precheck/tech-files/pin_label_purposes_overlapping_drawing.rb.drc: -------------------------------------------------------------------------------- 1 | # usage: 2 | # klayout -r pin_label_purposes_overlapping_drawing <-rd threads=THREADS_NUM> <-rd tile=TILE_SIZE> <-rd flat_mode=true> <-rd check_pwell=true> 3 | # 4 | # Flag pin.not(drawing) for all known pin/label layers in sky130A. 5 | # WARNING: if markerFile is RELATIVE-PATH it is written in SAME-DIR as input-GDS. 6 | # 7 | # pwell & pwelliso not checked by default. Believe labelling pwell without drawn pwell is legal. 8 | # 9 | # TODO?: are results correct if deep mode, for pin & drawing in different hier. levels? 10 | # 11 | # Exit status (does not work in klayout 0.23.11; does in 0.24 and later): 12 | # 1 : I/O error or other internal error (uncaught exceptions). 13 | # 2...127 : means 1... rules did flag error(s). If over 126 rules had errors, status is 127 max. 14 | # That is this # is the how many rule-types had non-zero errors, NOT total errors count. 15 | # 0 : no rules flagged errors. 16 | # If process dies thru signal, exit status is 128+SIGNUM, so that range is reserved. 17 | # i.e. if kernel oom-killer sends kill -9: status=137. 18 | # 19 | # Runs klayout (in batch) to do partial/crude layer grid checks; output to a MarkerDB (*.lyrdb) 20 | # Crude because no partitioning is done, to enforce unique grid requirements by areaid. 21 | # 22 | # Script starts as regular ruby, then exec's via klayout passing self to it. 23 | # (klayout requirement is this script-name *must* end in .drc). 24 | # 25 | # Known reasons why mult. klayout-versions produce non-identical output: 26 | # 1. In some earlier versions (than 0.27), markerReport may state "wrong" generator: 27 | # :/built-in-macros/drc.lym 28 | # the "better" generator would look like: 29 | # drc: script='/gdsSky130Apin1.drc' 30 | # 2. When errors are flagged, the ordering of errors may differ between klayout versions. 31 | # 32 | 33 | # include RBA 34 | begin 35 | 36 | if $flat_mode == "true" 37 | flat # flat, with or without tiling 38 | else 39 | deep 40 | end 41 | 42 | if $check_pwell == "true" 43 | $check_pwell = true 44 | else 45 | $check_pwell = false 46 | end 47 | 48 | if !$threads || $threads.to_i == 0 49 | $threads=`nproc` 50 | end 51 | 52 | if $tile 53 | if $tile.to_f > 0 54 | flat 55 | tiles($tile) 56 | puts "Tiling Enabled" 57 | # no border 58 | # tile_borders(0) 59 | no_borders 60 | end 61 | end 62 | 63 | threads($threads.to_i) 64 | title = "pin_label_purposes_overlapping_drawing.rb.drc, input=#{$input}, topcell=#{$top_cell_name}" 65 | puts "Running pin_label_purposes_overlapping_drawing.rb.drc on file=#{$input}, topcell=#{$top_cell_name}, output to #{$report}" 66 | puts " deep:#{is_deep?} tiled:#{is_tiled?} threads:#{$threads}" 67 | 68 | STDOUT.flush 69 | 70 | source($input, $top_cell_name) 71 | report(title, $report) 72 | 73 | # $ruleHeader = "Checks with errors:" 74 | $ruleHeader = "--- #err|description, table for cell: %s" % [$top_cell_name] 75 | $didHeader = false 76 | $errs = 0 77 | $totals = 0 78 | $rules = 0 79 | 80 | # 81 | # Direct rule checks like: 82 | # m2.width(1.5).output("m2 width < 1.5") 83 | # write to report() but don't give opportunity to count/print which rules did flag. 84 | # Instead: 85 | # rule(m2.width(1.5), "m2 width < 1.5") 86 | # 87 | # Wish to use direct-form, and just tabulate/post-process report after the fact. 88 | # This form (empty or not) still does not nicely report/summarize error-count per-rule. 89 | 90 | # Return value not meaningful if the is_empty? fails (in 0.26.9 or earlier). 91 | # 92 | def rule(marker, msg) 93 | $rules = $rules + 1 94 | empty = false 95 | size = -1 96 | emptyFails = false 97 | 98 | # test if marker is empty. 99 | # marker.is_empty? : works in 0.27, not 0.26 and earlier. 100 | # marker.size : does not work in any so far. 101 | # marker.bbox appears universal, works in klayout versions 0.23.11 ... 0.27. 102 | # In case it fails, catch exception and revert to less information in our stdout. 103 | begin 104 | size = marker.data.size 105 | empty = (size == 0) 106 | # works all versions: size = marker.data.size 107 | # works all versions: empty = marker.bbox.to_s == "()" 108 | # fails pre 0.27: empty = marker.is_empty? 109 | rescue StandardError => e 110 | # when can't determine which rules flagged errors, signal not to summarize. 111 | emptyFails = true 112 | empty = false 113 | size = -1 114 | if $errs >= 0 115 | $errs = -8 116 | end 117 | if ($errs & 1) == 0 118 | puts "turned off marker empty detect..." 119 | end 120 | $errs = $errs | 1 121 | end 122 | if ! empty 123 | marker.output(msg) 124 | if ! emptyFails 125 | if $errs == 0 && ! $didHeader 126 | puts $ruleHeader 127 | $didHeader = true 128 | end 129 | $errs = $errs + 1 130 | $totals = $totals + size 131 | puts "%8d %s" % [size, msg] 132 | return 1 133 | end 134 | end 135 | return 0 136 | end 137 | 138 | # call like: 139 | # 140 | # pinCheck( "met1", 68,20, 68,16, 68, 5 ) 141 | # 142 | # where 68,20 is met1/drawing, and purposes 16 & 5 are pin,label 143 | # 144 | # It is an error if called: 145 | # ... with less than 5 args, i.e. MUST have at least one non-drawing layer-purpose-pair. 146 | # e.g.: pinCheck( "met1", 68,20 ) 147 | # ... with an odd number of arguments after the first three. 148 | # e.g.: pinCheck( "met1", 68,20, 68 ) 149 | # e.g.: pinCheck( "met1", 68,20, 68,16, 68 ) 150 | # 151 | # Variations: 152 | # pinCheck: do the regular AndNot pin check 153 | # pinSkip: do not do the check, but still report on which lpps have data vs empty. 154 | def pinCheck(name, layn, typen, *nonMaskLpps) 155 | pinCheckIf(true, name, layn, typen, *nonMaskLpps) 156 | end 157 | def pinSkip(name, layn, typen, *nonMaskLpps) 158 | pinCheckIf(false, name, layn, typen, *nonMaskLpps) 159 | end 160 | def pinCheckIf(checkp, name, layn, typen, *nonMaskLpps) 161 | if nonMaskLpps.length == 0 || nonMaskLpps.length % 2 != 0 162 | STDERR.puts "pinCheck called with empty or odd-number of args for non-drawing layer-purpose-pairs." 163 | exit 1 164 | end 165 | 166 | lyfmt = "%22s" 167 | lyfmt2 = "%12s" 168 | 169 | ly = polygons(layn, typen) # get main drawing 170 | drpair = "#{layn}/#{typen}" 171 | # isEmpty(ly, name) 172 | lye = (ly.is_empty?) ? "EMP" : "dat" 173 | sumry = [ lyfmt % "#{name}:#{drpair}/#{lye}" ] 174 | 175 | nonMaskLpps.each_slice(2) {|lay, typ| 176 | l2 = polygons(lay, typ) 177 | l2e = (l2.is_empty?) ? "EMP" : "dat" 178 | sumry += [ lyfmt2 % "#{lay}/#{typ}/#{l2e}" ] 179 | 180 | msg = "#{lay}/#{typ}: #{name}, pin/label not-over drawing:#{drpair}" 181 | if checkp 182 | rule( l2.not(ly), msg ) 183 | end 184 | } 185 | if checkp 186 | mode = " " 187 | else 188 | mode = "NO-Check" 189 | end 190 | # force header output (if not yet done) 191 | if ! $didHeader 192 | puts $ruleHeader 193 | $didHeader = true 194 | end 195 | puts ("%8s ---- " % mode) + sumry.join(" ") 196 | 197 | end 198 | 199 | # verbose input(), flag if its empty. description is a string. 200 | def inputVerb(layn, typen, desc) 201 | ly = input(layn, typen) 202 | isEmpty(ly, desc) 203 | return ly 204 | end 205 | 206 | def isEmpty(layer, desc) 207 | if layer.is_empty? 208 | puts "--EMPTY : #{desc}" 209 | else 210 | puts "data : #{desc}" 211 | end 212 | end 213 | 214 | # check all layer-purpose-pairs found in input-layout: 215 | # Report ALL that are pin-purpose. 216 | 217 | #? should these be checked against pwell/drawing: pwelliso_pin - 44/16 pwelliso_label - 44/5 218 | pinCheckIf($check_pwell, 219 | "pwell", 64,44, 122,16, 64,59, 44,16, 44,5) 220 | pinCheck( "nwell", 64,20, 64,16, 64,5) 221 | pinCheck( "diff", 65,20, 65,16, 65,6) 222 | pinCheck( "tap", 65,44, 65,48, 65,5) 223 | pinCheck( "poly", 66,20, 66,16, 66,5) 224 | pinCheck( "licon1", 66,44, 66,58) 225 | pinCheck( "li1", 67,20, 67,16, 67,5) 226 | pinCheck( "mcon", 67,44, 67,48) 227 | pinCheck( "met1", 68,20, 68,16, 68,5) 228 | pinCheck( "via", 68,44, 68,58) 229 | pinCheck( "met2", 69,20, 69,16, 69,5) 230 | pinCheck( "via2", 69,44, 69,58) 231 | pinCheck( "met3", 70,20, 70,16, 70,5) 232 | pinCheck( "via3", 70,44, 70,48) 233 | pinCheck( "met4", 71,20, 71,16, 71,5) 234 | pinCheck( "via4", 71,44, 71,48) 235 | pinCheck( "met5", 72,20, 72,16, 72,5) 236 | pinCheck( "pad", 76,20, 76,5, 76,16) 237 | pinCheck( "pnp", 82,44, 82,59) 238 | pinCheck( "npn", 82,20, 82,5) 239 | pinCheck( "rdl", 74,20, 74,16, 74,5) 240 | pinCheck( "inductor", 82,24, 82,25) 241 | 242 | end 243 | # How to tabulate as-if-flat "error-counts by error-message" from current report()? 244 | # In old versions, we can't seem to determine here if a check flagged errors ($errs == -1). 245 | if $errs >= 0 246 | puts "%8d total error(s) among %d error type(s), %d checks, cell: %s" % [$totals, $errs, $rules, $top_cell_name] 247 | # puts "#{$errs} of #{$rules} checks have errors" 248 | else 249 | puts "#{$rules} checks" 250 | $errs = 0 # so exit status == 0. 251 | end 252 | puts "Writing report..." 253 | 254 | # if we roll-over to 256, exit-status seen by shell is zero. 255 | # uncaught I/O errors will yield (built-in) exit status of 1. 256 | if $errs > 0 257 | $errs = $errs + 1 258 | end 259 | if $errs > 127 260 | $errs = 127 261 | end 262 | 263 | # experimental: report own peak process-stats. BUT: report-file isn't really written 264 | # until we exit (during exit). So these results are not 100% accurate. 265 | # VmHWM: max-resident-size, VmPeak: max virtual-size. 266 | # don't need: pid=Process.pid 267 | if File.readable?("/proc/self/status") 268 | puts File.foreach("/proc/self/status").grep(/^(VmPeak|VmHWM)/) 269 | end 270 | 271 | # does not work (to set exit-status) in 0.23.11. 272 | # Does work in 0.24.2, 0.27! 273 | exit $errs 274 | 275 | # For 0.23.11 we could set exit-status as below, but: 276 | # if we do: the report() does not get written! 277 | # 278 | # for exit! we'd lose buffered output unless we flush: 279 | # STDOUT.flush 280 | # STDERR.flush 281 | # Kernel.exit!($errs) 282 | # 283 | # emacs syntax-mode: 284 | # Local Variables: 285 | # mode:ruby 286 | # End: 287 | -------------------------------------------------------------------------------- /precheck/tech-files/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | curl -L -O https://github.com/efabless/mpw_precheck/raw/main/checks/tech-files/sky130A_mr.drc 4 | curl -L -O https://github.com/efabless/mpw_precheck/raw/main/checks/drc_checks/klayout/zeroarea.rb.drc 5 | curl -L -O https://github.com/efabless/mpw_precheck/raw/main/checks/drc_checks/klayout/pin_label_purposes_overlapping_drawing.rb.drc 6 | 7 | echo "Don't forget to update the revision numbers in README.txt" 8 | -------------------------------------------------------------------------------- /precheck/tech-files/zeroarea.rb.drc: -------------------------------------------------------------------------------- 1 | # based in part on: https://www.klayout.de/forum/discussion/173/use-klayout-in-batch-mode 2 | 3 | $errs = 0 4 | del = 0 5 | delZLp = true 6 | len0 = 0 7 | len0Del = 0 8 | 9 | ly = RBA::Layout.new 10 | source($input, $top_cell_name) 11 | ly.read($input) 12 | report("zero area check", $report) 13 | 14 | ly.layer_indices.each { |li| 15 | ly.each_cell { |cell| 16 | cell.shapes(li).each { |shape| 17 | # TODO: must we check that shape is one of: box, polygon, path? What happens to text? 18 | if shape.is_valid? && (!shape.is_null?) && 19 | (shape.is_box? || shape.is_path? || shape.is_polygon?) && 20 | shape.area == 0 21 | $errs += 1 22 | polygons().insert(shape.dpolygon).output("zero_area_shape") 23 | if $cleaned_output 24 | shape.delete 25 | del += 1 26 | end 27 | elsif delZLp && shape.is_valid? && (!shape.is_null?) && shape.is_path? 28 | # puts "path on layer:#{layer_info} area:#{shape.area}" 29 | lastpt = nil 30 | same = true 31 | # walk the points. First time we identify at least two unique-points, skip: not a zero-length path. 32 | shape.each_point { |pt| 33 | if lastpt && lastpt != pt 34 | same = false 35 | break 36 | end 37 | lastpt = pt 38 | } 39 | if same 40 | # puts "zero-length path on layer:#{layer_info} area:#{shape.area} path:#{shape.to_s}" 41 | len0 += 1 42 | $errs += 1 43 | if $cleaned_output 44 | shape.delete 45 | len0Del += 1 46 | end 47 | end 48 | end 49 | } 50 | } 51 | } 52 | 53 | if $cleaned_output 54 | puts "writing to #{$cleaned_output}" 55 | ly.write($cleaned_output) 56 | end 57 | 58 | puts "#{$errs} zero-area shapes" 59 | 60 | del += len0Del 61 | if delZLp 62 | puts "#{len0} zero-length paths, #{len0Del} zero-length paths deleted." 63 | end 64 | puts "#{$errs} total zero-area objects, #{del} total objects deleted." 65 | 66 | # if we roll-over to 256, exit-status seen by shell is zero. 67 | # uncaught I/O errors will yield (built-in) exit status of 1. 68 | if $errs > 0 69 | $errs = $errs + 1 70 | end 71 | if $errs > 127 72 | $errs = 127 73 | end 74 | # experimental: report own peak process-stats. BUT: output-file isn't really written 75 | # until we exit (during exit). So these results are not 100% accurate. 76 | # VmHWM: max-resident-size, VmPeak: max virtual-size. 77 | # don't need: pid=Process.pid 78 | if File.readable?("/proc/self/status") 79 | puts File.foreach("/proc/self/status").grep(/^(VmPeak|VmHWM)/) 80 | end 81 | # does not work (to set exit-status) in 0.23.11. Does work in 0.24.2, 0.27. 82 | exit $errs 83 | -------------------------------------------------------------------------------- /precheck/tech_data.py: -------------------------------------------------------------------------------- 1 | # fmt: off 2 | valid_layers_sky130 = { 3 | (10, 0), (11, 0), (11, 20), (11, 44), (17, 0), (18, 20), (20, 0), (21, 0), 4 | (22, 0), (22, 20), (22, 21), (22, 22), (22, 23), (22, 24), (23, 0), (23, 28), 5 | (25, 0), (25, 42), (25, 43), (25, 44), (26, 20), (26, 21), (26, 22), (27, 0), 6 | (28, 0), (28, 28), (29, 20), (29, 21), (29, 22), (30, 0), (31, 20), (31, 21), 7 | (31, 22), (32, 0), (33, 24), (33, 42), (33, 43), (33, 44), (34, 0), (34, 28), 8 | (35, 0), (36, 0), (36, 28), (37, 0), (38, 20), (38, 21), (38, 22), (39, 0), 9 | (40, 0), (41, 0), (41, 28), (43, 0), (44, 0), (44, 5), (44, 16), (44, 20), 10 | (44, 42), (44, 43), (45, 20), (45, 21), (45, 22), (46, 0), (48, 0), (49, 0), 11 | (50, 0), (51, 0), (51, 28), (53, 42), (53, 43), (53, 44), (56, 0), (56, 28), 12 | (58, 0), (59, 0), (59, 28), (61, 20), (62, 20), (62, 21), (62, 22), (62, 24), 13 | (64, 5), (64, 13), (64, 14), (64, 16), (64, 18), (64, 20), (64, 44), (64, 59), 14 | (65, 4), (65, 5), (65, 6), (65, 8), (65, 13), (65, 14), (65, 16), (65, 20), 15 | (65, 23), (65, 41), (65, 44), (65, 48), (65, 60), (66, 4), (66, 5), (66, 9), 16 | (66, 13), (66, 14), (66, 15), (66, 16), (66, 20), (66, 23), (66, 25), (66, 41), 17 | (66, 44), (66, 58), (66, 60), (66, 83), (67, 4), (67, 5), (67, 10), (67, 13), 18 | (67, 14), (67, 15), (67, 16), (67, 20), (67, 23), (67, 25), (67, 41), (67, 44), 19 | (67, 48), (67, 60), (68, 4), (68, 5), (68, 10), (68, 13), (68, 14), (68, 15), 20 | (68, 16), (68, 20), (68, 23), (68, 25), (68, 32), (68, 33), (68, 34), (68, 35), 21 | (68, 36), (68, 37), (68, 38), (68, 39), (68, 41), (68, 44), (68, 48), (68, 58), 22 | (68, 60), (68, 88), (68, 89), (68, 90), (68, 91), (68, 92), (68, 93), (69, 4), 23 | (69, 5), (69, 10), (69, 13), (69, 14), (69, 15), (69, 16), (69, 20), (69, 23), 24 | (69, 25), (69, 32), (69, 33), (69, 34), (69, 35), (69, 36), (69, 37), (69, 38), 25 | (69, 39), (69, 41), (69, 44), (69, 48), (69, 58), (69, 60), (69, 88), (69, 89), 26 | (69, 90), (69, 91), (69, 92), (69, 93), (70, 4), (70, 5), (70, 10), (70, 13), 27 | (70, 14), (70, 15), (70, 16), (70, 17), (70, 20), (70, 23), (70, 25), (70, 32), 28 | (70, 33), (70, 34), (70, 35), (70, 36), (70, 37), (70, 38), (70, 39), (70, 41), 29 | (70, 44), (70, 48), (70, 60), (70, 88), (70, 89), (70, 90), (70, 91), (70, 92), 30 | (70, 93), (71, 4), (71, 5), (71, 10), (71, 13), (71, 14), (71, 15), (71, 16), 31 | (71, 17), (71, 20), (71, 23), (71, 25), (71, 32), (71, 33), (71, 34), (71, 35), 32 | (71, 36), (71, 37), (71, 38), (71, 39), (71, 41), (71, 44), (71, 48), (71, 60), 33 | (71, 88), (71, 89), (71, 90), (71, 91), (71, 92), (71, 93), (72, 4), (72, 5), 34 | (72, 10), (72, 13), (72, 14), (72, 15), (72, 16), (72, 17), (72, 20), (72, 23), 35 | (72, 25), (72, 32), (72, 33), (72, 34), (72, 35), (72, 36), (72, 37), (72, 38), 36 | (72, 39), (72, 88), (72, 89), (72, 90), (72, 91), (72, 92), (72, 93), (74, 5), 37 | (74, 13), (74, 14), (74, 15), (74, 16), (74, 20), (74, 21), (74, 22), (74, 88), 38 | (74, 89), (74, 90), (74, 91), (74, 92), (74, 93), (75, 20), (76, 5), (76, 16), 39 | (76, 20), (76, 44), (77, 20), (78, 44), (79, 20), (80, 20), (81, 1), (81, 2), 40 | (81, 3), (81, 4), (81, 6), (81, 7), (81, 8), (81, 10), (81, 11), (81, 12), 41 | (81, 13), (81, 14), (81, 15), (81, 17), (81, 19), (81, 20), (81, 23), (81, 27), 42 | (81, 50), (81, 51), (81, 52), (81, 53), (81, 54), (81, 57), (81, 60), (81, 63), 43 | (81, 79), (81, 81), (81, 101), (81, 125), (82, 5), (82, 20), (82, 24), 44 | (82, 25), (82, 26), (82, 27), (82, 28), (82, 44), (82, 59), (82, 64), (83, 44), 45 | (84, 23), (84, 44), (85, 44), (86, 20), (87, 42), (87, 43), (87, 44), (88, 0), 46 | (88, 44), (89, 32), (89, 33), (89, 34), (89, 35), (89, 36), (89, 37), (89, 38), 47 | (89, 39), (89, 44), (90, 4), (90, 20), (91, 44), (92, 44), (93, 0), (93, 44), 48 | (94, 0), (94, 20), (95, 20), (96, 0), (96, 20), (96, 21), (96, 22), (96, 44), 49 | (97, 0), (97, 42), (97, 43), (97, 44), (98, 0), (98, 42), (98, 43), (98, 44), 50 | (99, 0), (100, 0), (101, 0), (101, 42), (101, 43), (101, 44), (104, 42), 51 | (104, 43), (104, 44), (105, 20), (105, 21), (105, 22), (105, 42), (105, 43), 52 | (105, 44), (105, 52), (106, 42), (106, 43), (106, 44), (107, 20), (107, 21), 53 | (107, 22), (107, 24), (108, 20), (108, 21), (108, 22), (109, 42), (109, 43), 54 | (109, 44), (110, 20), (110, 21), (110, 22), (112, 4), (112, 20), (112, 21), 55 | (112, 22), (112, 42), (112, 43), (115, 42), (115, 43), (115, 44), (117, 4), 56 | (117, 20), (117, 21), (117, 22), (122, 5), (122, 16), (124, 40), (125, 20), 57 | (125, 44), (127, 21), (127, 22), (201, 20), (235, 0), (235, 4), (235, 250), 58 | (235, 252), (236, 0) 59 | } 60 | # fmt: on 61 | 62 | valid_layers_ihp_sg13g2 = [ 63 | "Substrate.drawing", 64 | "Substrate.text", 65 | "Activ.drawing", 66 | "Activ.label", 67 | "Activ.pin", 68 | "GatPoly.drawing", 69 | "GatPoly.label", 70 | "GatPoly.pin", 71 | "PolyRes.drawing", 72 | "PolyRes.label", 73 | "PolyRes.pin", 74 | "NWell.drawing", 75 | "NWell.label", 76 | "NWell.pin", 77 | "nBuLay.drawing", 78 | "nBuLay.label", 79 | "nBuLay.pin", 80 | "nBuLay.block", 81 | "PWell.block", 82 | "pSD.drawing", 83 | "nSD.drawing", 84 | "nSD.block", 85 | "SalBlock.drawing", 86 | "ThickGateOx.drawing", 87 | "Cont.drawing", 88 | "Metal1.drawing", 89 | "Metal1.label", 90 | "Metal1.pin", 91 | "Metal1.text", 92 | "Metal1.res", 93 | "Via1.drawing", 94 | "Metal2.drawing", 95 | "Metal2.label", 96 | "Metal2.pin", 97 | "Metal2.text", 98 | "Metal2.res", 99 | "Via2.drawing", 100 | "Via2.net", 101 | "Via2.boundary", 102 | "Metal3.drawing", 103 | "Metal3.label", 104 | "Metal3.pin", 105 | "Metal3.text", 106 | "Metal3.res", 107 | "MIM.drawing", 108 | "Vmim.drawing", 109 | "Via3.drawing", 110 | "Metal4.drawing", 111 | "Metal4.label", 112 | "Metal4.pin", 113 | "Metal4.text", 114 | "Metal4.res", 115 | "Via4.drawing", 116 | "Metal5.drawing", 117 | "Metal5.label", 118 | "Metal5.pin", 119 | "Metal5.text", 120 | "Metal5.res", 121 | "TopVia1.drawing", 122 | "TopMetal1.drawing", 123 | "TopMetal1.label", 124 | "TopMetal1.pin", 125 | "TopMetal1.text", 126 | "TopMetal1.res", 127 | "IND.drawing", 128 | "IND.pin", 129 | "IND.boundary", 130 | "IND.text", 131 | "TEXT.drawing", 132 | "Recog.drawing", 133 | "Recog.pin", 134 | "Recog.esd", 135 | "Recog.diode", 136 | "Recog.tsv", 137 | "Recog.pdiode", 138 | "Recog.mom", 139 | "DigiBnd.drawing", 140 | "DigiBnd.drawing0", 141 | "RES.drawing", 142 | "RES.label", 143 | "SRAM.drawing", 144 | "SRAM.label", 145 | "SRAM.boundary", 146 | "DigiSub.drawing", 147 | "HeatRes.drawing", 148 | "NoRCX.drawing", 149 | "NoRCX.m2m3", 150 | "NoRCX.m2m4", 151 | "NoRCX.m2m5", 152 | "NoRCX.m2tm1", 153 | "NoRCX.m2tm2", 154 | "NoRCX.m3m4", 155 | "NoRCX.m3m5", 156 | "NoRCX.m3tm1", 157 | "NoRCX.m3tm2", 158 | "NoRCX.m4m5", 159 | "NoRCX.m4tm1", 160 | "NoRCX.m4tm2", 161 | "NoRCX.m5tm1", 162 | "NoRCX.m5tm2", 163 | "NoRCX.tm1tm2", 164 | "NoRCX.m1sub", 165 | "NoRCX.m2sub", 166 | "NoRCX.m3sub", 167 | "NoRCX.m4sub", 168 | "NoRCX.m5sub", 169 | "NoRCX.tm1sub", 170 | "Varicap.drawing", 171 | "EXTBlock.drawing", 172 | "prBoundary.drawing", 173 | "prBoundary.label", 174 | "prBoundary.boundary", 175 | "isoNWell.drawing", 176 | ] 177 | 178 | # layers used for the analog pin check 179 | layer_map_sky130 = { 180 | "met4": (71, 20), 181 | "via3": (70, 44), 182 | } 183 | 184 | 185 | def analog_pin_pos_sky130(pin_number: int, uses_3v3: bool): 186 | return 151.81 - 19.32 * pin_number - (15.64 if uses_3v3 else 0) 187 | 188 | 189 | valid_layers = {"sky130": valid_layers_sky130, "ihp-sg13g2": valid_layers_ihp_sg13g2} 190 | layer_map = {"sky130": layer_map_sky130} 191 | analog_pin_pos = {"sky130": analog_pin_pos_sky130} 192 | lyp_filename = {"sky130": "sky130A.lyp", "ihp-sg13g2": "sg13g2.lyp"} 193 | 194 | # TODO: read layer numbers from lyp file 195 | valid_lef_port_layers = { 196 | "sky130": { 197 | "met1.pin": (68, 16), 198 | "met2.pin": (69, 16), 199 | "met3.pin": (70, 16), 200 | "met4.pin": (71, 16), 201 | }, 202 | "ihp-sg13g2": { 203 | "Metal1.pin": (8, 2), 204 | "Metal2.pin": (10, 2), 205 | "Metal3.pin": (30, 2), 206 | "Metal4.pin": (50, 2), 207 | "Metal5.pin": (67, 2), 208 | }, 209 | } 210 | forbidden_layers = { 211 | "sky130": ["met5.drawing", "met5.pin", "met5.label"], 212 | "ihp-sg13g2": [ 213 | "TopMetal1.drawing", 214 | "TopMetal1.pin", 215 | "TopMetal1.label", 216 | "TopMetal2.drawing", 217 | "TopMetal2.pin", 218 | "TopMetal2.label", 219 | ], 220 | } 221 | power_pins_layer = { 222 | "sky130": "met4", 223 | "ihp-sg13g2": "Metal5", 224 | } 225 | 226 | tech_names = ["sky130", "ihp-sg13g2"] 227 | -------------------------------------------------------------------------------- /project_info.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List, Optional 2 | 3 | from tile_sizes import tile_sizes 4 | 5 | YAML_VERSION = 6 6 | 7 | 8 | class ProjectYamlError(Exception): 9 | pass 10 | 11 | 12 | class PinoutSection: 13 | def __init__(self, yaml_data: Dict[str, Any]): 14 | self.__nonEmptyPins = 0 15 | yaml_data = yaml_data.copy() 16 | self.ui = self._pins(yaml_data, "ui", 8) 17 | self.uo = self._pins(yaml_data, "uo", 8) 18 | self.uio = self._pins(yaml_data, "uio", 8) 19 | self.ua = self._pins(yaml_data, "ua", 6, True) 20 | if self.__nonEmptyPins == 0: 21 | raise ProjectYamlError("Please fill in the 'pinout' section") 22 | if len(yaml_data) > 0: 23 | raise ProjectYamlError( 24 | f"Invalid keys {list(yaml_data.keys())} in 'pinout' section. Please remove them." 25 | ) 26 | 27 | def _pins( 28 | self, yaml_data: Dict[str, Any], name: str, count: int, optional: bool = False 29 | ) -> List[str]: 30 | result: List[str] = [] 31 | for i in range(count): 32 | key = f"{name}[{i}]" 33 | pin = yaml_data.get(key) 34 | if pin is None: 35 | if optional and i == 0: 36 | break 37 | raise ProjectYamlError(f"Missing '{name}[{i}]' in 'pinout' section") 38 | if pin != "": 39 | self.__nonEmptyPins += 1 40 | result.append(pin) 41 | del yaml_data[key] 42 | return result 43 | 44 | 45 | class ProjectInfo: 46 | top_module: str 47 | source_files: List[str] 48 | 49 | def __init__(self, yaml_data: Dict[str, Any]): 50 | # Validate Version 51 | yaml_version = yaml_data.get("yaml_version") 52 | if yaml_version is None: 53 | raise ProjectYamlError("Missing 'yaml_version'") 54 | if yaml_version != YAML_VERSION: 55 | raise ProjectYamlError( 56 | f"Unsupported YAML version: {yaml_data['yaml_version']}, expected {YAML_VERSION}" 57 | ) 58 | 59 | # Read "project" Section 60 | project_section: Optional[Dict[str, Any]] = yaml_data.get("project") 61 | if project_section is None: 62 | raise ProjectYamlError("Missing 'project' section") 63 | 64 | title = project_section.get("title") 65 | if title is None: 66 | raise ProjectYamlError("Missing key 'title' in 'project' section") 67 | if title == "": 68 | raise ProjectYamlError("Project title cannot be empty") 69 | self.title: str = title 70 | 71 | author = project_section.get("author") 72 | if author is None: 73 | raise ProjectYamlError("Missing key 'author' in 'project' section") 74 | if author == "": 75 | raise ProjectYamlError("Project author cannot be empty") 76 | self.author: str = author 77 | 78 | description = project_section.get("description") 79 | if description is None: 80 | raise ProjectYamlError("Missing key 'description' in 'project' section") 81 | if description == "": 82 | raise ProjectYamlError("Project description cannot be empty") 83 | self.description: str = description 84 | 85 | tiles = project_section.get("tiles") 86 | if tiles is None: 87 | raise ProjectYamlError("Missing key 'tiles' in 'project' section") 88 | if tiles not in tile_sizes.keys(): 89 | raise ProjectYamlError( 90 | f"Invalid value for 'tiles' in 'project' section: {tiles}" 91 | ) 92 | self.tiles: str = tiles 93 | 94 | analog_pins = project_section.get("analog_pins", 0) 95 | if not isinstance(analog_pins, int): 96 | raise ProjectYamlError( 97 | "Invalid value for 'analog_pins' in 'project' section, must be an integer" 98 | ) 99 | if analog_pins < 0 or analog_pins > 6: 100 | raise ProjectYamlError( 101 | "Invalid value for 'analog_pins' in 'project' section, must be between 0 and 6" 102 | ) 103 | self.analog_pins: int = analog_pins 104 | self.is_analog: bool = self.analog_pins > 0 105 | 106 | self.uses_3v3: bool = project_section.get("uses_3v3", False) 107 | if self.uses_3v3 and not self.is_analog: 108 | raise ProjectYamlError( 109 | "Projects with 3v3 power need at least one analog pin" 110 | ) 111 | 112 | language = project_section.get("language") 113 | if language is None: 114 | raise ProjectYamlError("Missing key 'language' in 'project' section") 115 | if language == "": 116 | raise ProjectYamlError("Project language cannot be empty") 117 | self.language: str = language 118 | 119 | if language == "Wokwi": 120 | wokwi_id = project_section.get("wokwi_id") 121 | if wokwi_id is None: 122 | raise ProjectYamlError("Missing key 'wokwi_id' in 'project' section") 123 | if wokwi_id == "" or wokwi_id == "0": 124 | raise ProjectYamlError("Please provide a valid Wokwi project ID") 125 | self.wokwi_id: Optional[int] = wokwi_id 126 | self.top_module = f"tt_um_wokwi_{wokwi_id}" 127 | self.source_files = [f"tt_um_wokwi_{self.wokwi_id}.v", f"cells.v"] 128 | else: 129 | top_module = project_section.get("top_module") 130 | if top_module is None: 131 | raise ProjectYamlError("Missing key 'top_module' in 'project' section") 132 | if not top_module.startswith("tt_um_"): 133 | raise ProjectYamlError( 134 | "Top module must start with 'tt_um_' (e.g. tt_um_my_project)" 135 | ) 136 | self.top_module = top_module 137 | 138 | if "source_files" not in project_section: 139 | raise ProjectYamlError( 140 | "Missing key 'source_files' in 'project' section" 141 | ) 142 | if len(project_section["source_files"]) == 0: 143 | raise ProjectYamlError("No source files specified") 144 | self.source_files = project_section["source_files"] 145 | 146 | if "clock_hz" not in project_section: 147 | raise ProjectYamlError("Missing key 'clock_hz' in 'project' section") 148 | if not isinstance(project_section["clock_hz"], int): 149 | raise ProjectYamlError( 150 | "Invalid value for 'clock_hz' in 'project' section, must be an integer" 151 | ) 152 | self.clock_hz: int = project_section["clock_hz"] 153 | 154 | if "pinout" not in yaml_data: 155 | raise ProjectYamlError("Missing 'pinout' section") 156 | self.pinout = PinoutSection(yaml_data["pinout"]) 157 | 158 | self.discord: Optional[str] = project_section.get("discord") 159 | self.doc_link: Optional[str] = project_section.get("doc_link") 160 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tt-support-tools" 3 | requires-python = ">=3.11" 4 | dynamic = ["version"] 5 | dependencies = [ 6 | "brotli", 7 | "CairoSVG", 8 | "chevron", 9 | "cocotb", 10 | "gdstk", 11 | "GitPython", 12 | "klayout<0.30.0,>=0.29.0", 13 | "mistune", 14 | "numpy<2", 15 | "pre-commit", 16 | "pytest", 17 | "python-frontmatter", 18 | "PyYAML", 19 | "requests", 20 | "volare", 21 | "yowasp-yosys", 22 | ] 23 | 24 | [tool.setuptools] 25 | packages = [] 26 | -------------------------------------------------------------------------------- /reharden.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import csv 4 | import datetime 5 | import glob 6 | import json 7 | import logging 8 | import os 9 | import re 10 | import sys 11 | from typing import List 12 | 13 | import git 14 | 15 | ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) 16 | PROJECTS_DIR = os.path.join(ROOT, "projects") 17 | REHARDEN_DIR = os.path.join(ROOT, "reharden") 18 | 19 | 20 | def load_metrics(path): 21 | try: 22 | with open(os.path.join(path, "runs/wokwi/reports/metrics.csv")) as fh: 23 | metrics = next(csv.DictReader(fh)) 24 | return metrics 25 | except FileNotFoundError: 26 | logging.warning(f"no metrics found for {path}") 27 | return None 28 | 29 | 30 | # get cell count from synth report 31 | def get_cell_count_from_synth(path): 32 | num_cells = 0 33 | try: 34 | yosys_report = ( 35 | f"{path}/runs/wokwi/reports/synthesis/1-synthesis.AREA_0.stat.rpt" 36 | ) 37 | with open(yosys_report) as fh: 38 | for line in fh.readlines(): 39 | m = re.search(r"Number of cells:\s+(\d+)", line) 40 | if m is not None: 41 | num_cells = int(m.group(1)) 42 | 43 | except IndexError: 44 | logging.warning(f"couldn't open yosys cell report for cell checking {path}") 45 | return 0 46 | except FileNotFoundError: 47 | logging.warning(f"no cell count found for {path}") 48 | return 0 49 | 50 | return num_cells 51 | 52 | 53 | def get_cell_counts_from_gl(path): 54 | cell_count = {} 55 | total = 0 56 | 57 | try: 58 | gl_path = glob.glob( 59 | os.path.join(path, "runs/wokwi/results/final/verilog/gl/*.nl.v") 60 | )[0] 61 | except IndexError: 62 | logging.warning("no gl cell count found") 63 | return 0 64 | with open(gl_path) as fh: 65 | for line in fh.readlines(): 66 | m = re.search(r"sky130_(\S+)__(\S+)_(\d+)", line) 67 | if m is not None: 68 | total += 1 69 | cell_lib = m.group(1) 70 | cell_name = m.group(2) 71 | cell_drive = m.group(3) 72 | assert cell_lib in ["fd_sc_hd", "ef_sc_hd"] 73 | assert int(cell_drive) > 0 74 | try: 75 | cell_count[cell_name] += 1 76 | except KeyError: 77 | cell_count[cell_name] = 1 78 | return cell_count 79 | 80 | 81 | def build_metrics(shuttle_index): 82 | total_seconds = 0 83 | total_wire_length = 0 84 | total_wires_count = 0 85 | total_physical_cells = 0 86 | max_cells = 0 87 | min_cells = 1000 88 | max_cell_project = None 89 | max_util = 0 90 | min_util = 100 91 | max_util_project = None 92 | 93 | for project in shuttle_index["projects"]: 94 | macro: str = project["macro"] 95 | repo_dir = os.path.join(REHARDEN_DIR, macro) 96 | metrics = load_metrics(repo_dir) 97 | if metrics is None: 98 | continue 99 | 100 | try: 101 | dt = datetime.datetime.strptime(metrics["total_runtime"][:-3], "%Hh%Mm%Ss") 102 | except KeyError: 103 | continue 104 | 105 | delt = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second) 106 | total_seconds += delt.total_seconds() 107 | 108 | total_wire_length += int(metrics["wire_length"]) 109 | total_wires_count += int(metrics["wires_count"]) 110 | util = float(metrics["OpenDP_Util"]) 111 | num_cells = get_cell_count_from_synth(repo_dir) 112 | total_physical_cells += num_cells 113 | 114 | if num_cells > max_cells: 115 | max_cells = num_cells 116 | max_cell_project = project 117 | if num_cells < min_cells: 118 | min_cells = num_cells 119 | 120 | if util > max_util: 121 | max_util = util 122 | max_util_project = project 123 | if util < min_util: 124 | min_util = util 125 | 126 | logging.info(f"build time for all projects {total_seconds / 3600} hrs") 127 | logging.info(f"total wire length {total_wire_length} um") 128 | logging.info(f"total cells {total_physical_cells}") 129 | logging.info(f"max cells {max_cells} for project {max_cell_project}") 130 | logging.info(f"min cells {min_cells}") 131 | logging.info(f"max util {max_util} for project {max_util_project}") 132 | logging.info(f"min util {min_util}") 133 | 134 | 135 | if __name__ == "__main__": 136 | parser = argparse.ArgumentParser(description="TT reharden tool") 137 | parser.add_argument( 138 | "--debug", 139 | help="debug logging", 140 | action="store_const", 141 | dest="loglevel", 142 | const=logging.DEBUG, 143 | default=logging.INFO, 144 | ) 145 | parser.add_argument( 146 | "--clone", help="clone the repos", action="store_const", const=True 147 | ) 148 | parser.add_argument( 149 | "--harden", help="harden the projects", action="store_const", const=True 150 | ) 151 | parser.add_argument("--start-from", help="start from", type=int, default=0) 152 | parser.add_argument("--end-at", help="end at", type=int, default=100000) 153 | parser.add_argument("--build-metrics", action="store_const", const=True) 154 | 155 | args = parser.parse_args() 156 | 157 | # setup log 158 | log_format = logging.Formatter( 159 | "%(asctime)s - %(module)-10s - %(levelname)-8s - %(message)s" 160 | ) 161 | # configure the client logging 162 | log = logging.getLogger("") 163 | # has to be set to debug as is the root logger 164 | log.setLevel(args.loglevel) 165 | 166 | # create console handler and set level to info 167 | ch = logging.StreamHandler(sys.stdout) 168 | # create formatter for console 169 | ch.setFormatter(log_format) 170 | log.addHandler(ch) 171 | 172 | shuttle_index = json.load(open("shuttle_index.json")) 173 | differences: List[str] = [] 174 | for number, project in enumerate(shuttle_index["projects"]): 175 | if number < args.start_from: 176 | continue 177 | if number > args.end_at: 178 | continue 179 | 180 | repo = project["repo"] 181 | commit = project["commit"] 182 | macro = project["macro"] 183 | repo_dir = os.path.join(REHARDEN_DIR, macro) 184 | 185 | if macro == "tt_um_chip_rom": 186 | logging.info(f"[{number :03}] skipping chip ROM") 187 | continue 188 | 189 | if "analog_pins" in project: 190 | logging.info(f"[{number :03}] skipping analog project {macro}") 191 | continue 192 | 193 | if args.clone: 194 | if not os.path.exists(repo_dir): 195 | logging.info(f"cloning {number :03} {repo}") 196 | git.Repo.clone_from(repo, repo_dir) 197 | cloned_repo = git.Repo(repo_dir) 198 | cloned_repo.git.submodule("update", "--init") 199 | cloned_repo.git.checkout(commit) 200 | else: 201 | cloned_repo = git.Repo(repo_dir) 202 | if commit != cloned_repo.commit().hexsha: 203 | logging.info(f"updating {number :03} {repo} to commit {commit}") 204 | cloned_repo.git.fetch("origin") 205 | cloned_repo.git.checkout(commit) 206 | cloned_repo.git.submodule("update", "--init") 207 | 208 | # get tt tools setup. 209 | # can't run tt from another directory because gitpython fails 210 | # can't run symlinked because openlane fails 211 | # simple copy won't work because of tt is a submodule and not a standalone repo. 212 | tt_tools_path = os.path.join(repo_dir, "tt") 213 | if not os.path.exists(tt_tools_path): 214 | logging.info(f"cloning tt to {tt_tools_path}") 215 | git.Repo.clone_from("tt", tt_tools_path, depth=1) 216 | 217 | if args.harden: 218 | logging.info(f"hardening {number :03} {repo_dir}") 219 | cwd = os.getcwd() 220 | os.chdir(repo_dir) 221 | new_gds = f"runs/wokwi/results/final/gds/{macro}.gds" 222 | prev_commit = None 223 | if os.path.isfile("commit.txt"): 224 | with open("commit.txt", "r") as f: 225 | prev_commit = f.read().strip() 226 | if not os.path.exists(new_gds) or prev_commit != commit: 227 | os.system(f"tt/tt_tool.py --create-user-config > reharden.log") 228 | res = os.system(f"tt/tt_tool.py --harden --debug >> reharden.log") 229 | if res != 0: 230 | logging.warning(f"failed to harden {macro}") 231 | else: 232 | with open("commit.txt", "w") as f: 233 | f.write(commit) 234 | orig_gds = os.path.join(PROJECTS_DIR, f"{macro}/{macro}.gds") 235 | logging.info(f"Comparing {orig_gds} with {new_gds}") 236 | res = os.system( 237 | f"klayout -b -r tt/gds_compare.py -rd 'gds1={orig_gds}' -rd 'gds2={new_gds}'" 238 | ) 239 | if res != 0: 240 | logging.warning(f"GDS compare failed for {macro}") 241 | differences.append(macro) 242 | os.chdir(cwd) 243 | 244 | if args.harden: 245 | print(f"Found total of {len(differences)} different projects: ") 246 | for diff in differences: 247 | print("- ", diff) 248 | 249 | if args.build_metrics: 250 | build_metrics(shuttle_index) 251 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.13 3 | # by the following command: 4 | # 5 | # pip-compile 6 | # 7 | anyio==4.7.0 8 | # via httpx 9 | brotli==1.1.0 10 | # via tt-support-tools (pyproject.toml) 11 | cairocffi==1.7.1 12 | # via cairosvg 13 | cairosvg==2.7.1 14 | # via tt-support-tools (pyproject.toml) 15 | certifi==2024.8.30 16 | # via 17 | # httpcore 18 | # httpx 19 | # requests 20 | cffi==1.17.1 21 | # via cairocffi 22 | cfgv==3.4.0 23 | # via pre-commit 24 | charset-normalizer==3.4.0 25 | # via requests 26 | chevron==0.14.0 27 | # via tt-support-tools (pyproject.toml) 28 | click==8.1.7 29 | # via 30 | # volare 31 | # yowasp-yosys 32 | cocotb==1.9.2 33 | # via tt-support-tools (pyproject.toml) 34 | cssselect2==0.7.0 35 | # via cairosvg 36 | defusedxml==0.7.1 37 | # via cairosvg 38 | distlib==0.3.9 39 | # via virtualenv 40 | filelock==3.16.1 41 | # via virtualenv 42 | find-libpython==0.4.0 43 | # via cocotb 44 | gdstk==0.9.58 45 | # via tt-support-tools (pyproject.toml) 46 | gitdb==4.0.11 47 | # via gitpython 48 | gitpython==3.1.43 49 | # via tt-support-tools (pyproject.toml) 50 | h11==0.14.0 51 | # via httpcore 52 | httpcore==1.0.7 53 | # via httpx 54 | httpx==0.28.1 55 | # via volare 56 | identify==2.6.3 57 | # via pre-commit 58 | idna==3.10 59 | # via 60 | # anyio 61 | # httpx 62 | # requests 63 | importlib-resources==6.4.5 64 | # via wasmtime 65 | iniconfig==2.0.0 66 | # via pytest 67 | klayout==0.29.10 68 | # via tt-support-tools (pyproject.toml) 69 | markdown-it-py==3.0.0 70 | # via rich 71 | mdurl==0.1.2 72 | # via markdown-it-py 73 | mistune==3.0.2 74 | # via tt-support-tools (pyproject.toml) 75 | nodeenv==1.9.1 76 | # via pre-commit 77 | numpy==1.26.4 78 | # via 79 | # gdstk 80 | # tt-support-tools (pyproject.toml) 81 | packaging==24.2 82 | # via pytest 83 | pcpp==1.30 84 | # via volare 85 | pillow==11.0.0 86 | # via cairosvg 87 | platformdirs==4.3.6 88 | # via 89 | # virtualenv 90 | # yowasp-runtime 91 | pluggy==1.5.0 92 | # via pytest 93 | pre-commit==4.0.1 94 | # via tt-support-tools (pyproject.toml) 95 | pycparser==2.22 96 | # via cffi 97 | pygments==2.18.0 98 | # via rich 99 | pytest==8.3.4 100 | # via tt-support-tools (pyproject.toml) 101 | python-frontmatter==1.1.0 102 | # via tt-support-tools (pyproject.toml) 103 | pyyaml==6.0.2 104 | # via 105 | # pre-commit 106 | # python-frontmatter 107 | # tt-support-tools (pyproject.toml) 108 | # volare 109 | requests==2.32.3 110 | # via tt-support-tools (pyproject.toml) 111 | rich==13.9.4 112 | # via volare 113 | smmap==5.0.1 114 | # via gitdb 115 | sniffio==1.3.1 116 | # via anyio 117 | tinycss2==1.4.0 118 | # via 119 | # cairosvg 120 | # cssselect2 121 | urllib3==2.2.3 122 | # via requests 123 | virtualenv==20.28.0 124 | # via pre-commit 125 | volare==0.19.2 126 | # via tt-support-tools (pyproject.toml) 127 | wasmtime==27.0.2 128 | # via yowasp-runtime 129 | webencodings==0.5.1 130 | # via 131 | # cssselect2 132 | # tinycss2 133 | yowasp-runtime==1.66 134 | # via yowasp-yosys 135 | yowasp-yosys==0.48.0.0.post834 136 | # via tt-support-tools (pyproject.toml) 137 | zstandard==0.23.0 138 | # via volare 139 | -------------------------------------------------------------------------------- /rom.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright (C) 2024, Tiny Tapeout LTD 3 | # Author: Uri Shaked 4 | 5 | # Generates the ROM file for the shuttle. The ROM layout is documented at: 6 | # https://github.com/TinyTapeout/tt-chip-rom/blob/main/docs/info.md. 7 | 8 | import binascii 9 | import os 10 | from urllib.parse import urlparse 11 | 12 | from git.repo import Repo 13 | 14 | from config import Config 15 | 16 | MAX_ROM_TEXT_SIZE = 92 17 | 18 | segment_font = { 19 | " ": 0b00000000, 20 | "0": 0b00111111, 21 | "1": 0b00000110, 22 | "2": 0b01011011, 23 | "3": 0b01001111, 24 | "4": 0b01100110, 25 | "5": 0b01101101, 26 | "6": 0b01111101, 27 | "7": 0b00000111, 28 | "8": 0b01111111, 29 | "9": 0b01101111, 30 | "A": 0b01110111, 31 | "B": 0b01111100, 32 | "C": 0b00111001, 33 | "D": 0b01011110, 34 | "E": 0b01111001, 35 | "F": 0b01110001, 36 | "f": 0b01110001, 37 | "h": 0b01110100, 38 | "i": 0b00010000, 39 | "o": 0b01011100, 40 | "p": 0b01110011, 41 | "t": 0b01111000, 42 | "a": 0b01110111, 43 | "b": 0b01111100, 44 | "c": 0b01011000, 45 | "d": 0b01011110, 46 | } 47 | 48 | 49 | def segment_char(c: str): 50 | return segment_font[c] 51 | 52 | 53 | class ROMFile: 54 | def __init__(self, config: Config): 55 | self.config = config 56 | 57 | def get_git_remote(self) -> str: 58 | repo_url = list(Repo(".").remotes[0].urls)[0] 59 | return urlparse(repo_url).path[1:] 60 | 61 | def get_git_commit_hash(self) -> str: 62 | return Repo(".").commit().hexsha 63 | 64 | def write_rom(self): 65 | rom = bytearray(256) 66 | short_sha = self.get_git_commit_hash()[:8] 67 | 68 | rom_text = f"shuttle={self.config['id']}\n" 69 | rom_text += f"repo={self.get_git_remote()}\n" 70 | rom_text += f"commit={short_sha}\n" 71 | 72 | print(f"\nROM text: {len(rom_text)} bytes (max={MAX_ROM_TEXT_SIZE})\n") 73 | print(" " + "\n ".join(rom_text.split("\n"))) 74 | 75 | assert len(rom_text) < MAX_ROM_TEXT_SIZE, "ROM text too long" 76 | 77 | shuttle_id = self.config["id"][:8] 78 | rom[0 : len(shuttle_id)] = map(segment_char, shuttle_id) 79 | rom[8:16] = map(segment_char, short_sha.upper()) 80 | rom[32 : 32 + len(rom_text)] = rom_text.encode("ascii") 81 | rom[248:252] = b"TT\xFA\xBB" 82 | rom[252:256] = binascii.crc32(rom[0:252]).to_bytes(4, "little") 83 | 84 | with open(os.path.join(os.path.dirname(__file__), "rom/rom.vmem"), "w") as fh: 85 | for line in rom_text.split("\n"): 86 | if len(line) == 0: 87 | continue 88 | fh.write(f"// {line}\n") 89 | fh.write("\n") 90 | for line_offset in range(0, len(rom), 16): 91 | for byte in range(0, 16): 92 | fh.write("{:02x} ".format(rom[line_offset + byte])) 93 | fh.write("\n") 94 | -------------------------------------------------------------------------------- /rom/.gitignore: -------------------------------------------------------------------------------- 1 | runs 2 | rom.vmem 3 | -------------------------------------------------------------------------------- /rom/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "DESIGN_NAME": "tt_um_chip_rom", 3 | "VERILOG_FILES": "dir::tt_um_chip_rom.v", 4 | 5 | "FP_SIZING": "absolute", 6 | "DIE_AREA": [0, 0, 161.00, 111.52], 7 | "FP_DEF_TEMPLATE": "ref::$DESIGN_DIR/../def/tt_block_1x1_pg.def", 8 | 9 | "FP_IO_HLENGTH": 2, 10 | "FP_IO_VLENGTH": 2, 11 | 12 | "//": "use alternative efabless decap cells to solve LI density issue", 13 | "DECAP_CELL": "sky130_fd_sc_hd__decap_3 sky130_fd_sc_hd__decap_4 sky130_fd_sc_hd__decap_6 sky130_fd_sc_hd__decap_8 sky130_ef_sc_hd__decap_12", 14 | 15 | "//": "period is in ns, so 20ns == 50mHz", 16 | "CLOCK_PERIOD": 20, 17 | "CLOCK_PORT": "clk", 18 | 19 | "//": "don't use power rings or met5", 20 | "DESIGN_IS_CORE": false, 21 | "RT_MAX_LAYER": "met4", 22 | 23 | "//": "save some time", 24 | "RUN_KLAYOUT_XOR": false, 25 | 26 | "//": "reduce wasted space", 27 | "TOP_MARGIN_MULT": 1, 28 | "BOTTOM_MARGIN_MULT": 1, 29 | "LEFT_MARGIN_MULT": 6, 30 | "RIGHT_MARGIN_MULT": 6 31 | } 32 | -------------------------------------------------------------------------------- /rom/config_ihp.json: -------------------------------------------------------------------------------- 1 | { 2 | "DESIGN_NAME": "tt_um_chip_rom", 3 | "VERILOG_FILES": "dir::tt_um_chip_rom.v", 4 | 5 | "FP_SIZING": "absolute", 6 | "DIE_AREA": [0, 0, 202.08, 154.98], 7 | "FP_DEF_TEMPLATE": "ref::$DESIGN_DIR/../ihp/def/tt_block_1x1_pgvdd.def", 8 | 9 | "FP_IO_HLENGTH": 2, 10 | "FP_IO_VLENGTH": 2, 11 | 12 | "//": "period is in ns, so 20ns == 50mHz", 13 | "CLOCK_PERIOD": 20, 14 | "CLOCK_PORT": "clk", 15 | 16 | "//": "don't use power rings or TopMetal", 17 | "DESIGN_IS_CORE": false, 18 | "RT_MAX_LAYER": "Metal5", 19 | 20 | "//": "save some time", 21 | "RUN_KLAYOUT_XOR": false, 22 | 23 | "//": "reduce wasted space", 24 | "TOP_MARGIN_MULT": 1, 25 | "BOTTOM_MARGIN_MULT": 1, 26 | "LEFT_MARGIN_MULT": 6, 27 | "RIGHT_MARGIN_MULT": 6 28 | } 29 | -------------------------------------------------------------------------------- /rom/tt_um_chip_rom.v: -------------------------------------------------------------------------------- 1 | /* 2 | * tt_um_chip_rom.v 3 | * 4 | * ROM module for Tiny Tapeout chips. The layout of the ROM is documented at 5 | * https://github.com/TinyTapeout/tt-chip-rom/blob/main/docs/info.md. 6 | * 7 | * Author: Uri Shaked 8 | */ 9 | 10 | `default_nettype none 11 | 12 | `ifndef ROM_VMEM_PATH 13 | `define ROM_VMEM_PATH "rom.vmem" 14 | `endif 15 | 16 | module tt_um_chip_rom ( 17 | input wire [7:0] ui_in, // Dedicated inputs 18 | output wire [7:0] uo_out, // Dedicated outputs 19 | input wire [7:0] uio_in, // IOs: Input path 20 | output wire [7:0] uio_out, // IOs: Output path 21 | output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output) 22 | input wire ena, 23 | input wire clk, 24 | input wire rst_n 25 | ); 26 | 27 | reg [7:0] rom_data[0:255]; 28 | 29 | initial begin 30 | $readmemh(`ROM_VMEM_PATH, rom_data); 31 | end 32 | 33 | // The address counter, only used when rst_n is low 34 | reg [7:0] addr_counter; 35 | wire [7:0] selected_addr = rst_n ? ui_in : addr_counter; 36 | 37 | assign uo_out = rom_data[selected_addr]; 38 | assign uio_out = 8'h00; 39 | assign uio_oe = 8'h00; 40 | 41 | 42 | always @(posedge clk or posedge rst_n) begin 43 | if (rst_n) begin 44 | addr_counter <= 0; 45 | end else begin 46 | addr_counter <= addr_counter + 1; 47 | end 48 | end 49 | 50 | endmodule // tt_um_chip_rom 51 | -------------------------------------------------------------------------------- /shuttle_index.py: -------------------------------------------------------------------------------- 1 | from typing import List, Literal, NotRequired, TypedDict 2 | 3 | 4 | class ShuttleIndexProject(TypedDict): 5 | macro: str 6 | address: int 7 | x: int 8 | y: int 9 | tiles: str 10 | repo: str 11 | commit: str 12 | analog_pins: NotRequired[tuple[int, ...]] 13 | 14 | 15 | class ShuttleIndexLayout(TypedDict): 16 | muxes: List[List[Literal["analog", "digital"]]] 17 | 18 | 19 | class ShuttleIndex(TypedDict): 20 | """TypedDict for Tiny Tapeout's shuttle_index.json file.""" 21 | 22 | name: str 23 | repo: str 24 | commit: str 25 | commit_date: int 26 | version: int 27 | layout: ShuttleIndexLayout 28 | projects: List[ShuttleIndexProject] 29 | -------------------------------------------------------------------------------- /testing/README.md: -------------------------------------------------------------------------------- 1 | ### Wokwi/Tiny Tapeout testing system ### 2 | 3 | 4 | A system has been devised to allow for simplified verification and testing of wokwi projects. 5 | 6 | In this initial iteration, the process basically involves creating a 'truth table' type description of desired inputs and expected outputs, which allows for testing of both combinatorial and sequential/clocked circuits. 7 | 8 | The system is designed to allow for simple extensions to this base functionality: the testing framework itself will be described below to assist in expanding it to encompass additional tests and validation of wokwi or other TT projects. 9 | 10 | 11 | ## Truth-table system ## 12 | 13 | In its simplest incarnation, the truth-table defines a set of inputs provided to the circuit and describes the expected outputs. 14 | 15 | The table itself is written as markdown, consisting of 16 | 17 | * a header 18 | * a separator 19 | * the table rows 20 | using markdown formatting. Eg 21 | 22 | | inputs | outputs | 23 | |-----------|-----------| 24 | | 1111 0000 | xxxx 1100 | 25 | 26 | etc 27 | 28 | 29 | Inputs are in the left-side column, expected outputs on the right, specified 30 | as MSB (i.e. bit 7 to 0, from left to right). 31 | 32 | Any bits specified as 'x' or '-' are treated as "don't change" for 33 | inputs and "don't care" for outputs. 34 | 35 | 36 | The header is ignored, as are whitespaces, so you may format as appropriate. 37 | Each row must have a least 8 non-whitespace bit specifications per column. E.g. 38 | for a 2 bit adder, a table might consist of 39 | 40 | 41 | 42 | | Cin B A | Cout S | 43 | |---------------------|-------------| 44 | | ----- 0 0 0 |-- 0 ---- 0 | 45 | | ----- 0 0 1 |-- 0 ---- 1 | 46 | | ----- 0 1 0 |-- 0 ---- 1 | 47 | | ----- 0 1 1 |-- 1 ---- 0 | 48 | | ----- 1 0 0 |-- 0 ---- 1 | 49 | | ----- 1 0 1 |-- 1 ---- 0 | 50 | | ----- 1 1 0 |-- 1 ---- 0 | 51 | | ----- 1 1 1 |-- 1 ---- 1 | 52 | 53 | 54 | 55 | 56 | 57 | A ‘t’ or ‘T’ bit will be toggled from whatever it was before. 58 | 59 | An empty output column will apply the input, but not perform any test (i.e. behave the same as 8 ‘x’/’-’). 60 | 61 | 62 | 63 | ### Clocked designs #### 64 | 65 | In addition to maintaining state for toggling the clock, one or more ‘c’ bits in an input column will cause the system to: 66 | 67 | 1) Set any bits specified, including toggling an “t” bits, without changing the ‘c’ bits (setup); 68 | 2) It will then toggle all ‘c’ bits, inverting them, but not changing anything else from previous step; and finally 69 | 3) It will toggle the ‘c’ bits again, returning them to their original state. 70 | 71 | This means setup and hold times are respected and signals may be clocked in with a single row in the table, rather than having 3. 72 | 73 | 74 | 75 | 76 | 77 | 78 | Any entry with a bit set to 'c' will: 79 | * setup any specified bits (0 or 1) and toggle any bits as required 80 | * then toggle (i.e. invert) the clock bit(s), without changing anything else 81 | * then re-toggle the same clock bit(s), returning them to their original state 82 | 83 | 84 | |IN: CBA RC | output | comment | 85 | |--------------|--------------|-----------| 86 | | 000 000 00 | -- ----- - | init | 87 | | --- --- 1c | -- ----- - | reset | 88 | | --- --- 0c | -- ----- - | | 89 | | --- 111 -c | -- 11100 - | | 90 | | --- 110 -c | -- 11111 - | success | 91 | | --- 000 tc | -- ----- - | reset | 92 | | --- --- tc | -- 11100 - | locked | 93 | | --- 111 -c | -- 11100 - | bad combo | 94 | | --- 100 -c | -- 11100 - | bad combo | 95 | | --- 101 -c | -- 11100 - | bad combo | 96 | | --- 100 -c | -- 11100 - | bad combo | 97 | | --- 110 -c | -- 11111 - | success | 98 | | --- 000 -c | -- 11111 - | still open| 99 | | --- --- 1c | -- 11100 - | reset | 100 | 101 | 102 | 103 | ============================= 104 | Note that using ‘c’ is optional. A row like 105 | 106 | 107 | |IN: CBA RC |   output   | comment   | 108 | |--------------|--------------|-----------| 109 | | --- 110 -c | -- 11111 - | success | 110 | 111 | 112 | Could be done manually, with 113 | 114 | 115 | |IN: CBA RC |   output   | comment   | 116 | |--------------|--------------|-----------| 117 | | --- 110 -- | -- ----- - | set combo | 118 | | --- --- -1 | -- ----- - | clock | 119 | | --- --- -0 | -- 11111 - | success | 120 | 121 | 122 | ### Test framework function ### 123 | 124 | The tt-tools system, in addition to downloading verilog and JSON for wokwi projects, now attempts to download a file name truthtable.md. 125 | 126 | If this file is found, it assumes that te truthtable test must be run. To do this, the system now: 127 | 128 | 1) copies any directories found under testing/lib to the src/ directory, assuming these are python support packages; and 129 | 2) for each file in testing/src-tpl it will read in the file, replace any instance of WOKWI_ID with the appropriate id, and then write out a corresponding file into the actual src/ directory. 130 | 131 | From this point, all that should be required is to run a make from within the src directory to launch cocotb with the test necessary to put the table through its paces. 132 | 133 | 134 | 2023-04-06 Pat Deegan 135 | 136 | 137 | -------------------------------------------------------------------------------- /testing/lib/testutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyTapeout/tt-support-tools/bebe93d93bfcb5bd86812495faad195f923d7195/testing/lib/testutils/__init__.py -------------------------------------------------------------------------------- /testing/src-tpl/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # See https://docs.cocotb.org/en/stable/quickstart.html for more info 3 | 4 | # defaults 5 | SIM ?= icarus 6 | TOPLEVEL_LANG ?= verilog 7 | SRC_DIR = $(PWD)/../src 8 | 9 | # normal simulation 10 | ifneq ($(GATES),yes) 11 | 12 | # this is the only part you should need to modify: 13 | VERILOG_SOURCES += $(SRC_DIR)/cells.v $(SRC_DIR)/tt_um_wokwi_WOKWI_ID.v $(PWD)/test_wokwi.v 14 | 15 | 16 | else 17 | 18 | # gate level simulation requires some extra setup, you shouldn't need to touch this 19 | COMPILE_ARGS += -DGL_TEST 20 | COMPILE_ARGS += -DFUNCTIONAL 21 | COMPILE_ARGS += -DUSE_POWER_PINS 22 | COMPILE_ARGS += -DSIM 23 | COMPILE_ARGS += -DUNIT_DELAY=#1 24 | VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/primitives.v 25 | VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v 26 | 27 | # this gets copied in by the GDS action workflow 28 | VERILOG_SOURCES += $(PWD)/test_wokwi.v $(PWD)/gate_level_netlist.v 29 | endif 30 | 31 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 32 | TOPLEVEL = test_wokwi 33 | 34 | # MODULE is the basename of the Python test file 35 | MODULE = test 36 | 37 | # include cocotb's make rules to take care of the simulator setup 38 | include $(shell cocotb-config --makefiles)/Makefile.sim 39 | -------------------------------------------------------------------------------- /testing/src-tpl/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Wokwi Tiny Tapeout truth-table based auto test sequence. 4 | 5 | SPDX-FileCopyrightText: © 2023 Pat Deegan, https://psychogenic.com 6 | SPDX-License-Identifier: Apache2.0 7 | """ 8 | 9 | import cocotb 10 | import testutils.truthtable as truthtable 11 | 12 | 13 | @cocotb.test() 14 | async def truthTableCompare(parentDUT): 15 | """ 16 | truthTableCompare -- loads markdown truth table and, if load succeeded, actually performs the tests. 17 | """ 18 | usermodule = parentDUT.dut 19 | i_bus = parentDUT.ui_in 20 | o_bus = parentDUT.uo_out 21 | tt = truthtable.loadMarkdownTruthTable("truthtable.md", usermodule._log) 22 | if tt is None: 23 | usermodule._log.info("No truth table loaded, no table compare test to run") 24 | return 25 | 26 | usermodule._log.debug(str(tt)) 27 | await tt.testAll(i_bus, o_bus, usermodule._log) 28 | -------------------------------------------------------------------------------- /testing/src-tpl/test_wokwi.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache2.0 2 | `timescale 1ns / 1ps 3 | `default_nettype none 4 | 5 | module test_wokwi (); 6 | wire [7:0] uo_out; 7 | wire [7:0] ui_in; 8 | 9 | tt_um_wokwi_WOKWI_ID dut ( 10 | `ifdef GL_TEST 11 | .vccd1( 1'b1), 12 | .vssd1( 1'b0), 13 | `endif 14 | .ui_in(ui_in), 15 | .uo_out(uo_out) 16 | ); 17 | 18 | initial begin 19 | $dumpfile("wokwi_tb_WOKWI_ID.vcd"); 20 | $dumpvars(0, test_wokwi); 21 | end 22 | endmodule 23 | -------------------------------------------------------------------------------- /tile_sizes.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join, realpath 2 | from typing import Dict 3 | 4 | import yaml 5 | 6 | with open(join(dirname(realpath(__file__)), "tile_sizes.yaml")) as fh: 7 | tile_sizes: Dict[str, str] = yaml.safe_load(fh) 8 | -------------------------------------------------------------------------------- /tile_sizes.yaml: -------------------------------------------------------------------------------- 1 | 1x1: "0 0 161.00 111.52" 2 | 1x2: "0 0 161.00 225.76" 3 | 2x2: "0 0 334.88 225.76" 4 | 3x2: "0 0 508.76 225.76" 5 | 3x4: "0 0 508.76 511.36" 6 | 4x2: "0 0 682.64 225.76" 7 | 4x4: "0 0 682.64 511.36" 8 | 5x4: "0 0 856.52 511.36" 9 | 6x2: "0 0 1030.40 225.76" 10 | 8x2: "0 0 1378.16 225.76" 11 | -------------------------------------------------------------------------------- /tt_annotate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from PIL import Image, ImageDraw, ImageFont 4 | 5 | img = Image.open("pics/tinytapeout.png").convert("RGBA") 6 | overlay = Image.new("RGBA", img.size, (0, 0, 0, 0)) 7 | draw = ImageDraw.Draw(overlay) 8 | font = ImageFont.truetype("/usr/share/fonts/dejavu/DejaVuSans.ttf", 30) 9 | 10 | # coordinate mapping 11 | xmap = ((88, 272), (2676, 1646)) # two pairs of (chip x coord, picture x coord) 12 | ymap = ((95, 1879), (3240, 213)) # two pairs of (chip y coord, picture y coord) 13 | xshift, yshift = 40, -43 # text position relative to macro origin 14 | 15 | for line in open("openlane/user_project_wrapper/macro.cfg"): 16 | macro, x, y, orient = line.split() 17 | if macro == "scan_controller": 18 | continue 19 | elif macro.startswith("scanchain_"): 20 | continue 21 | name, num = macro.rsplit("_", 1) 22 | num, x, y = int(num), int(x), int(y) 23 | x = int( 24 | (x - xmap[0][0]) / (xmap[1][0] - xmap[0][0]) * (xmap[1][1] - xmap[0][1]) 25 | + xmap[0][1] 26 | ) 27 | y = int( 28 | (y - ymap[0][0]) / (ymap[1][0] - ymap[0][0]) * (ymap[1][1] - ymap[0][1]) 29 | + ymap[0][1] 30 | ) 31 | x += xshift 32 | y += yshift 33 | msg = str(num) 34 | w, h = draw.textsize(msg, font=font) 35 | draw.text((x - w / 2, y - h / 2), msg, fill=(0, 0, 0, 255), font=font) 36 | 37 | combined = Image.alpha_composite(img, overlay).convert("RGB") 38 | combined.save("pics/tinytapeout_numbered.png") 39 | -------------------------------------------------------------------------------- /tt_tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import logging 4 | import sys 5 | 6 | from project import Project 7 | 8 | if __name__ == "__main__": 9 | parser = argparse.ArgumentParser(description="TT setup") 10 | parser.add_argument("--project-dir", help="location of the project", default=".") 11 | parser.add_argument( 12 | "--yaml", help="the project's yaml configuration file", default="info.yaml" 13 | ) 14 | parser.add_argument( 15 | "--debug", 16 | help="debug logging", 17 | action="store_const", 18 | dest="loglevel", 19 | const=logging.DEBUG, 20 | default=logging.INFO, 21 | ) 22 | parser.add_argument( 23 | "--openlane2", 24 | help="use openlane 2", 25 | action="store_const", 26 | const=True, 27 | default=True, 28 | ) 29 | parser.add_argument( 30 | "--ihp", 31 | help="use IHP PDK", 32 | action="store_const", 33 | const=True, 34 | default=False, 35 | ) 36 | 37 | # reports & summaries 38 | parser.add_argument( 39 | "--print-cell-summary", 40 | help="print summary", 41 | action="store_const", 42 | const=True, 43 | default=False, 44 | ) 45 | parser.add_argument( 46 | "--print-cell-category", 47 | help="print category", 48 | action="store_const", 49 | const=True, 50 | default=False, 51 | ) 52 | parser.add_argument( 53 | "--print-stats", 54 | help="print some stats from the run", 55 | action="store_const", 56 | const=True, 57 | ) 58 | parser.add_argument( 59 | "--print-warnings", help="print any warnings", action="store_const", const=True 60 | ) 61 | parser.add_argument( 62 | "--print-wokwi-id", 63 | help="prints the Wokwi project id", 64 | action="store_const", 65 | const=True, 66 | ) 67 | parser.add_argument( 68 | "--print-top-module", 69 | help="prints the name of the top module", 70 | action="store_const", 71 | const=True, 72 | ) 73 | 74 | # documentation 75 | parser.add_argument( 76 | "--check-docs", 77 | help="check the documentation part of the yaml", 78 | action="store_const", 79 | const=True, 80 | ) 81 | parser.add_argument( 82 | "--create-pdf", 83 | help="create a single page PDF", 84 | action="store_const", 85 | const=True, 86 | ) 87 | parser.add_argument( 88 | "--create-svg", 89 | help="create a svg of the GDS layout", 90 | action="store_const", 91 | const=True, 92 | ) 93 | parser.add_argument( 94 | "--create-png", 95 | help="create a png of the GDS layout", 96 | action="store_const", 97 | const=True, 98 | ) 99 | 100 | # configure 101 | parser.add_argument( 102 | "--create-user-config", 103 | help="create the user_config.json file with top module and source files", 104 | action="store_const", 105 | const=True, 106 | ) 107 | parser.add_argument( 108 | "--harden", 109 | help="use a local OpenLane install to harden the project", 110 | action="store_const", 111 | const=True, 112 | ) 113 | 114 | # FPGA 115 | parser.add_argument( 116 | "--create-fpga-bitstream", 117 | help="create the FPGA bitstream", 118 | action="store_const", 119 | const=True, 120 | ) 121 | 122 | args = parser.parse_args() 123 | 124 | # setup log 125 | log_format = logging.Formatter( 126 | "%(asctime)s - %(module)-10s - %(levelname)-8s - %(message)s" 127 | ) 128 | # configure the client logging 129 | log = logging.getLogger("") 130 | # has to be set to debug as is the root logger 131 | log.setLevel(args.loglevel) 132 | 133 | # create console handler and set level to info 134 | ch = logging.StreamHandler(sys.stdout) 135 | # create formatter for console 136 | ch.setFormatter(log_format) 137 | log.addHandler(ch) 138 | 139 | project = Project(0, "unknown", args.project_dir, args, is_user_project=True) 140 | project.post_clone_setup() 141 | 142 | # handle the options 143 | if args.check_docs: 144 | project.check_docs() 145 | 146 | if args.print_cell_summary or args.print_cell_category: 147 | project.summarize() 148 | 149 | if args.print_stats: 150 | project.print_stats() 151 | 152 | if args.print_warnings: 153 | project.print_warnings() 154 | 155 | if args.print_wokwi_id: 156 | project.print_wokwi_id() 157 | 158 | if args.print_top_module: 159 | project.print_top_module() 160 | 161 | if args.create_user_config: 162 | project.create_user_config() 163 | 164 | if args.harden: 165 | project.harden() 166 | 167 | if args.create_pdf: 168 | project.create_pdf() 169 | 170 | if args.create_png: 171 | project.create_png() 172 | 173 | if args.create_svg: 174 | project.create_svg() 175 | 176 | if args.create_fpga_bitstream: 177 | project.create_fpga_bitstream() 178 | --------------------------------------------------------------------------------