├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── wokwi.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── info.yaml ├── requirements.txt ├── src ├── .gitignore ├── base.sdc ├── config.tcl ├── pdm.v ├── test.mk ├── user_module_341193419111006803.v └── user_module_341193419111006803_tb.v ├── template ├── config.tcl └── scan_wrapper.v └── view.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://zerotoasiccourse.com'] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: mattvenn 7 | 8 | --- 9 | 10 | **Link to your wokwi project** 11 | 12 | Please put a link to your wokwi project here. 13 | -------------------------------------------------------------------------------- /.github/workflows/wokwi.yaml: -------------------------------------------------------------------------------- 1 | name: wokwi 2 | # either manually started, or on a schedule 3 | on: [ push, workflow_dispatch ] 4 | jobs: 5 | wokwi: 6 | env: 7 | OPENLANE_TAG: 2022.02.23_02.50.41 8 | OPENLANE_IMAGE_NAME: efabless/openlane:$(OPENLANE_TAG) 9 | OPENLANE_ROOT: /home/runner/openlane 10 | PDK_ROOT: /home/runner/pdk 11 | PDK: sky130A 12 | 13 | # ubuntu 14 | runs-on: ubuntu-latest 15 | steps: 16 | # need the repo checked out 17 | - name: checkout repo 18 | uses: actions/checkout@v2 19 | 20 | # caching version seems broken atm 21 | - name: setup python 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: '3.7.7' # install the python version needed 25 | 26 | # python deps from reqirements file, use a marketplace action 27 | - name: Install Python dependencies 28 | uses: py-actions/py-dependency-install@v2 29 | with: 30 | path: "requirements.txt" 31 | 32 | - uses: actions/checkout@v2 33 | with: 34 | repository: s-holst/GDS2WebGL 35 | path: GDS2WebGL 36 | 37 | # build PDK and fetch OpenLane 38 | - name: pdk & caravel 39 | run: | 40 | cd $HOME 41 | git clone https://github.com/efabless/caravel_user_project.git -b mpw-6c 42 | cd caravel_user_project 43 | make setup 44 | # fetch the Verilog from Wokwi API 45 | - name: fetch Verilog 46 | run: make fetch 47 | 48 | # run the 'harden' rule in the Makefile to use OpenLane to build the GDS 49 | - name: make GDS 50 | run: make harden 51 | 52 | # for debugging, show all the files 53 | - name: show files 54 | run: find runs/wokwi/results 55 | 56 | # make the SVG 57 | - name: create svg 58 | run: python view.py 59 | 60 | # make html gds viewer with GDS2WebGL 61 | - name: GDS2WebGL 62 | run: | 63 | cd GDS2WebGL 64 | python3 gds2webgl.py -i ../runs/wokwi/results/final/gds/*gds -o ../gds.html 65 | # archive the gds and some other important files 66 | - name: Archive files 67 | uses: actions/upload-artifact@v2 68 | with: 69 | # path depends on the tag and the module name 70 | name: GDS 71 | path: | 72 | src/* 73 | runs/wokwi/results/final/* 74 | runs/wokwi/reports/final_summary_report.csv 75 | runs/wokwi/reports/synthesis/1-synthesis.stat.rpt.strategy4 76 | gds_render.svg 77 | gds.html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | runs/ 2 | src/ID -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # HACK: We need a unique ID for the verilog files so they can exist harmoniously with the rest of the 2 | # designs that are submitted. So, even if we're not using Wokwi, copy the template project and enter 3 | # the ID below as the WOKWI_PROJECT_ID so we get a guaranteed unique ID: 4 | WOKWI_PROJECT_ID=341193419111006803 5 | 6 | fetch: 7 | # HACK: we don't need to fetch this as we have our own verilog source, manually created: src/user_module_339898704941023827.v 8 | # curl https://wokwi.com/api/projects/$(WOKWI_PROJECT_ID)/verilog > src/user_module_$(WOKWI_PROJECT_ID).v 9 | sed -e 's/USER_MODULE_ID/$(WOKWI_PROJECT_ID)/g' template/scan_wrapper.v > src/scan_wrapper_$(WOKWI_PROJECT_ID).v 10 | # HACK: instead of globbing the entire src directory, specify modules explicitly in custom config.tcl 11 | # sed -e 's/USER_MODULE_ID/$(WOKWI_PROJECT_ID)/g' template/config.tcl > src/config.tcl 12 | echo $(WOKWI_PROJECT_ID) > src/ID 13 | 14 | # needs PDK_ROOT and OPENLANE_ROOT, OPENLANE_IMAGE_NAME set from your environment 15 | harden: 16 | docker run --rm \ 17 | -v $(OPENLANE_ROOT):/openlane \ 18 | -v $(PDK_ROOT):$(PDK_ROOT) \ 19 | -v $(CURDIR):/work \ 20 | -e PDK_ROOT=$(PDK_ROOT) \ 21 | -u $(shell id -u $(USER)):$(shell id -g $(USER)) \ 22 | $(OPENLANE_IMAGE_NAME) \ 23 | /bin/bash -c "./flow.tcl -overwrite -design /work/src -run_path /work/runs -tag wokwi" 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PDP-0, a PDP-1/TX-0 Inspired 4-bit CPU 2 | 3 | With ~ 200 gates and 8-inputs, 8-outputs, can we make a full CPU? If 4 | we depend on external memory, we can do like the Intel 4004 and 5 | multiplex nibbles in and out. However for this submission, we keep 6 | the memory on-chip which puts some severe constaints on the size of 7 | everything. The only architeture that I could managed in that space 8 | is one that relies heavily on self-modifying code, like the beloved 9 | DEC PDP-8. 10 | 11 | Features: 12 | - Updatable code and data storage 13 | - Instructions include load, store, alu, and conditional branches 14 | - Can execute Fibonacci 15 | 16 | CPU state 17 | - 8 words of 6-bit memory, split into 2-bit of instruction and 4-bit 18 | of operand. 19 | - 3-bit PC 20 | - 4-bit Accumulator (ACC) 21 | 22 | ## The Instruction Set 23 | 24 | All instructions have an opcode and an argument. 25 | 26 | - `load` value (loads value to the accumulator) 27 | - `store` address (stores the accumulator at the address, top bit ignored) 28 | - `add` value (adds value to the accumulator) 29 | - `brzero` address (branches to address if the accumulator is zero) 30 | 31 | Obviously this is very limiting, but it does show the basic structure 32 | and could probably be tweaked to be more general with more time (but 33 | space is limited). 34 | 35 | ## Example Fibonacci Program 36 | 37 | ``` C 38 | int a = 1, b = 1; 39 | for (;;) { 40 | int t = a; 41 | a += b; 42 | b = t; 43 | if (b == 8) for (;;) 44 | } 45 | ``` 46 | 47 | ``` assembly 48 | 0: load 1 // a is here 49 | 1: store 4 // store to t at address 4 50 | 51 | 2: add 1 // b is here 52 | 3: store 0 // Update a at address 0 53 | 54 | 4: load _ // t is here, value overwritten 55 | 5: store 2 // update b 56 | 57 | 6: add 8 // -8 == 8 58 | 7: brzero 7 // if acc - 8 == 0 we stop 59 | // otherwise roll over to 0 60 | ``` 61 | 62 | Execution trace: 63 | 64 | ``` 65 | $ make -C src -f test.mk | tail -50 | head -17 66 | Running 0 (insn 0, 8) 67 | 00500 pc 1 acc 8 68 | Running 1 (insn 1, 4) 69 | 00510 pc 2 acc 8 70 | Running 2 (insn 2, 5) 71 | 00520 pc 3 acc 13 72 | Running 3 (insn 1, 0) 73 | 00530 pc 4 acc 13 74 | Running 4 (insn 0, 8) 75 | 00540 pc 5 acc 8 76 | Running 5 (insn 1, 2) 77 | 00550 pc 6 acc 8 78 | Running 6 (insn 2, 8) 79 | 00560 pc 7 acc 0 80 | Running 7 (insn 3, 7) 81 | Running 7 (insn 3, 7) 82 | Running 7 (insn 3, 7) 83 | ``` 84 | 85 | We actually computed fib all the way to 13 (largest that will fit in 4-bits). 86 | 87 | 88 | # Original README.md follows 89 | 90 | 91 | (Original readme for the template repository [here](https://github.com/mattvenn/wokwi-verilog-gds-test/blob/main/README.md)) 92 | 93 | This repo is an experiment in using Verilog source files instead of Wokwi diagrams for [TinyTapeout](tinytapeout.com). 94 | 95 | The Verilog flow is: 96 | 97 | 1) Fork this Repo 98 | 2) Create a [wokwi](https://wokwi.com/projects/339800239192932947) project to get an ID 99 | 3) Update WOWKI_PROJECT_ID in Makefile 100 | 4) `grep -rl "341154068332282450" ./src | xargs sed -i "s/341154068332282450/YOUR_WOKWI_ID/g"` from the top of the repo 101 | 5) Replace behavioural code in user_module_ID.v with your own, likewise change the testbench 102 | 6) Push changes, which triggers the GitHub Action to build the project 103 | -------------------------------------------------------------------------------- /info.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # TinyTapeout project information 3 | # 4 | # As everyone will have access to all designs, try to make it easy for someone new to your design to know what 5 | # it does and how to operate it. 6 | # 7 | # This will be automatically collected and used to make a datasheet for the chip. 8 | project: 9 | author: "Tommy Thorn" # Your name 10 | title: "PDP-0: 4-bit CPU in the style of PDP-1/TX-0" # Project title 11 | description: "The tiny 4-bit CPU packs a 3b program counter, an accumulator, and 8 6b words." # Short description of what your project does 12 | how_it_works: "The two top bits in each word form the opcode (load, store, add, branch-if-zero) while the remaining four are the immediate field that the opcode uses. Load and store only access the immediate field of the word. The IO implements a simple command protocol to reset, load data, load code, and run. The output are used for the PC and the Accumulator. The test bench shows how to load a fibonacci computing program." # Longer description of how the project works 13 | how_to_test: "Use the command protocol to load programs and run them (see test bench)" # Instructions on how someone could test your project, include things like what buttons do what and how to set the clock if needed 14 | external_hw: "Besides interactining with the IOs, nothing is needed" # Describe any external hardware needed 15 | doc_link: "" # URL to longer form documentation, eg the README.md in your repository 16 | clock_hz: 0 # Clock frequency in Hz (if required) 17 | language: "Verilog" # other examples include Verilog, Amaranth, VHDL, etc 18 | wokwi_id: 341193419111006803 # the wokwi ID 19 | picture: "" # relative path to a picture in your repository 20 | inputs: # a description of what the inputs do 21 | - clock 22 | - cmd[0] 23 | - cmd[1] 24 | - not used 25 | - cmdarg[0] 26 | - cmdarg[1] 27 | - cmdarg[2] 28 | - cmdarg[3] 29 | outputs: 30 | - acc[0] # a description of what the outputs do 31 | - acc[1] 32 | - acc[2] 33 | - acc[3] 34 | - pc[0] 35 | - pc[1] 36 | - pc[2] 37 | - Not used, wired to 0 38 | 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gdstk 2 | gdspy 3 | pyclipper 4 | mapbox_earcut -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.asc 2 | *.bin 3 | *.json 4 | *.vcd 5 | *.vvp 6 | -------------------------------------------------------------------------------- /src/base.sdc: -------------------------------------------------------------------------------- 1 | # Create a clock for the scan chain @ 200 MHz 2 | create_clock -name clk_scan_in -period 5 [get_ports {clk_in}] 3 | create_generated_clock -name clk_scan_out -source clk_in -combinational [get_ports {clk_out}] 4 | 5 | # Scan chain input 0.5 ns setup time, 0.5 ns hold time 6 | set_input_delay -min 0.5 -clock [get_clocks clk_scan_in] [get_ports {data_in}] 7 | set_input_delay -max 0.5 -clock [get_clocks clk_scan_in] [get_ports {data_in}] 8 | 9 | # Scan chain output 1.5 ns setup time, 1.5 ns hold time 10 | set_output_delay -min -1.5 -clock [get_clocks clk_scan_out] [get_ports {data_out}] 11 | set_output_delay -max 1.5 -clock [get_clocks clk_scan_out] [get_ports {data_out}] 12 | 13 | # Misc 14 | set_max_fanout $::env(SYNTH_MAX_FANOUT) [current_design] 15 | 16 | if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL)] } { 17 | set ::env(SYNTH_CLK_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL) 18 | } 19 | 20 | if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL_PIN)] } { 21 | set ::env(SYNTH_CLK_DRIVING_CELL_PIN) $::env(SYNTH_DRIVING_CELL_PIN) 22 | } 23 | 24 | set_driving_cell -lib_cell $::env(SYNTH_DRIVING_CELL) -pin $::env(SYNTH_DRIVING_CELL_PIN) [get_ports {data_in scan_select_in latch_enable_in}] 25 | set_driving_cell -lib_cell $::env(SYNTH_CLK_DRIVING_CELL) -pin $::env(SYNTH_CLK_DRIVING_CELL_PIN) [get_ports {clk_in}] 26 | 27 | set cap_load [expr $::env(SYNTH_CAP_LOAD) / 1000.0] 28 | puts "\[INFO\]: Setting load to: $cap_load" 29 | set_load $cap_load [all_outputs] 30 | 31 | puts "\[INFO\]: Setting clock uncertainity to: $::env(SYNTH_CLOCK_UNCERTAINITY)" 32 | set_clock_uncertainty $::env(SYNTH_CLOCK_UNCERTAINITY) [get_clocks {clk_sys clk_scan_in clk_scan_out}] 33 | 34 | puts "\[INFO\]: Setting clock transition to: $::env(SYNTH_CLOCK_TRANSITION)" 35 | set_clock_transition $::env(SYNTH_CLOCK_TRANSITION) [get_clocks {clk_sys clk_scan_in clk_scan_out}] 36 | 37 | puts "\[INFO\]: Setting timing derate to: [expr {$::env(SYNTH_TIMING_DERATE) * 100}] %" 38 | set_timing_derate -early [expr {1-$::env(SYNTH_TIMING_DERATE)}] 39 | set_timing_derate -late [expr {1+$::env(SYNTH_TIMING_DERATE)}] 40 | -------------------------------------------------------------------------------- /src/config.tcl: -------------------------------------------------------------------------------- 1 | # User config 2 | set script_dir [file dirname [file normalize [info script]]] 3 | 4 | # has to match the module name from wokwi 5 | set ::env(DESIGN_NAME) scan_wrapper_341193419111006803 6 | 7 | # save some time 8 | set ::env(RUN_KLAYOUT_XOR) 0 9 | set ::env(RUN_KLAYOUT_DRC) 0 10 | 11 | # don't put clock buffers on the outputs 12 | set ::env(PL_RESIZER_BUFFER_OUTPUT_PORTS) 0 13 | 14 | # allow use of specific sky130 cells 15 | set ::env(SYNTH_READ_BLACKBOX_LIB) 1 16 | 17 | # HACK: explicitly specify which files we are using, ignore TB etc. 18 | set ::env(VERILOG_FILES) "$::env(DESIGN_DIR)/user_module_341193419111006803.v \ 19 | $::env(DESIGN_DIR)/scan_wrapper_341193419111006803.v \ 20 | $::env(DESIGN_DIR)/pdm.v" 21 | 22 | # absolute die size 23 | set ::env(FP_SIZING) absolute 24 | set ::env(DIE_AREA) "0 0 105 105" 25 | set ::env(FP_CORE_UTIL) 45 26 | set ::env(PL_BASIC_PLACEMENT) {1} 27 | 28 | # use alternative efabless decap cells to solve LI density issue 29 | set ::env(DECAP_CELL) "\ 30 | sky130_fd_sc_hd__decap_3 \ 31 | sky130_fd_sc_hd__decap_4 \ 32 | sky130_fd_sc_hd__decap_6 \ 33 | sky130_fd_sc_hd__decap_8 \ 34 | sky130_ef_sc_hd__decap_12" 35 | 36 | # clock 37 | set ::env(CLOCK_PERIOD) "10" 38 | set ::env(CLOCK_PORT) "" 39 | 40 | set ::env(BASE_SDC_FILE) $::env(DESIGN_DIR)/base.sdc 41 | 42 | set ::env(SYNTH_CLOCK_UNCERTAINITY) 0.20 43 | set ::env(SYNTH_CLOCK_TRANSITION) 0.15 44 | 45 | # don't use power rings or met5 46 | set ::env(DESIGN_IS_CORE) 0 47 | set ::env(RT_MAX_LAYER) {met4} 48 | 49 | # connect to first digital rails 50 | set ::env(VDD_NETS) [list {vccd1}] 51 | set ::env(GND_NETS) [list {vssd1}] 52 | -------------------------------------------------------------------------------- /src/pdm.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module pdm( 4 | input [4:0] pdm_input, 5 | input write_en, 6 | input clk, reset, 7 | output pdm_out 8 | ); 9 | 10 | reg [4:0] accumulator; 11 | reg [4:0] input_reg; 12 | 13 | wire [5:0] sum; 14 | 15 | assign sum = input_reg + accumulator; 16 | assign pdm_out = sum[5]; 17 | 18 | always @(posedge clk or posedge reset) begin 19 | if (reset) begin 20 | input_reg <= 5'h00 ; 21 | accumulator <= 5'h00; 22 | end else begin 23 | accumulator <= sum[4:0]; 24 | if (write_en) input_reg <= pdm_input ; 25 | end 26 | end 27 | 28 | endmodule 29 | -------------------------------------------------------------------------------- /src/test.mk: -------------------------------------------------------------------------------- 1 | run: user_module_341193419111006803.v 2 | iverilog -DTB user_module_341193419111006803.v 3 | ./a.out 4 | -------------------------------------------------------------------------------- /src/user_module_341193419111006803.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | // Instructions 4 | `define Load 0 5 | `define Store 1 6 | `define Add 2 7 | `define Bz 3 8 | 9 | // Commands 10 | `define Reset 0 11 | `define LoadCode 1 12 | `define LoadData 2 13 | `define Run 3 14 | 15 | // Keep I/O fixed for TinyTapeout 16 | module user_module_341193419111006803( 17 | input [7:0] io_in, 18 | output [7:0] io_out 19 | ); 20 | 21 | // State 22 | reg [1:0] code[7:0]; 23 | reg [3:0] data[7:0]; 24 | reg [2:0] pc; 25 | reg [3:0] acc; 26 | 27 | wire [2:0] npc = pc + 1; 28 | 29 | assign io_out = {1'd0, pc, acc}; 30 | wire [3:0] cmdarg = io_in[7:4]; 31 | wire [3:0] arg = data[pc]; 32 | 33 | always @(posedge io_in[0]) 34 | case (io_in[2:1]) 35 | `Reset: begin 36 | $display(" Reset"); 37 | pc <= cmdarg[2:0]; 38 | acc <= 0; 39 | end 40 | `LoadCode: begin 41 | $display(" LoadCode %d: Insn %d", pc, cmdarg); 42 | code[pc] <= cmdarg; 43 | pc <= npc; 44 | end 45 | `LoadData: begin 46 | $display(" LoadData %d: Data %d", pc, cmdarg); 47 | data[pc] <= cmdarg; 48 | pc <= npc; 49 | end 50 | `Run: begin 51 | $display(" Running %d (insn %d, %d)", pc, code[pc], data[pc]); 52 | case (code[pc]) 53 | `Load: begin acc <= arg; pc = npc; end 54 | `Store: begin data[arg[2:0]] <= acc; pc <= npc; end 55 | `Add: begin acc <= acc + arg; pc <= npc; end 56 | `Bz: begin pc <= acc == 0 ? arg : npc; end 57 | endcase 58 | end 59 | endcase 60 | endmodule 61 | 62 | `ifdef TB 63 | module tb; 64 | reg clock = 1; 65 | always #5 clock = !clock; 66 | 67 | //reg [7:0] io_in; 68 | reg [1:0] cmd; 69 | reg [3:0] cmdarg; 70 | 71 | wire [7:0] io_out; 72 | wire [7:0] io_in = {cmdarg, 1'b0, cmd, clock}; 73 | user_module_341193419111006803 pdp0001(io_in, io_out); 74 | 75 | initial begin 76 | $monitor("%05d pc %d acc %d", $time, pdp0001.pc, pdp0001.acc); 77 | cmd = `Reset; cmdarg = 0; 78 | @(negedge clock) 79 | cmd = `LoadCode; 80 | 81 | cmdarg = `Load; @(negedge clock) 82 | cmdarg = `Store; @(negedge clock) 83 | 84 | cmdarg = `Add; @(negedge clock) 85 | cmdarg = `Store; @(negedge clock) 86 | 87 | cmdarg = `Load; @(negedge clock) 88 | cmdarg = `Store; @(negedge clock) 89 | 90 | cmdarg = `Add; @(negedge clock) 91 | cmdarg = `Bz; @(negedge clock) 92 | 93 | cmd = `LoadData; 94 | 95 | cmdarg = 1; @(negedge clock) 96 | cmdarg = 4; @(negedge clock) 97 | 98 | cmdarg = 1; @(negedge clock) 99 | cmdarg = 0; @(negedge clock) 100 | 101 | cmdarg = 9; @(negedge clock) 102 | cmdarg = 2; @(negedge clock) 103 | 104 | cmdarg = 8; @(negedge clock) 105 | cmdarg = 7; @(negedge clock) 106 | 107 | cmd = `Reset; cmdarg = 0; 108 | 109 | @(negedge clock) cmd = `Run; 110 | @(negedge clock) cmd = `Run; 111 | @(negedge clock) cmd = `Run; 112 | @(negedge clock) cmd = `Run; 113 | @(negedge clock) cmd = `Run; 114 | @(negedge clock) cmd = `Run; 115 | @(negedge clock) cmd = `Run; 116 | @(negedge clock) cmd = `Run; 117 | @(negedge clock) cmd = `Run; 118 | @(negedge clock) cmd = `Run; 119 | @(negedge clock) cmd = `Run; 120 | @(negedge clock) cmd = `Run; 121 | @(negedge clock) cmd = `Run; 122 | @(negedge clock) cmd = `Run; 123 | @(negedge clock) cmd = `Run; 124 | @(negedge clock) cmd = `Run; 125 | @(negedge clock) cmd = `Run; 126 | @(negedge clock) cmd = `Run; 127 | @(negedge clock) cmd = `Run; 128 | @(negedge clock) cmd = `Run; 129 | @(negedge clock) cmd = `Run; 130 | @(negedge clock) cmd = `Run; 131 | @(negedge clock) cmd = `Run; 132 | @(negedge clock) cmd = `Run; 133 | @(negedge clock) cmd = `Run; 134 | @(negedge clock) cmd = `Run; 135 | @(negedge clock) cmd = `Run; 136 | @(negedge clock) cmd = `Run; 137 | @(negedge clock) cmd = `Run; 138 | @(negedge clock) cmd = `Run; 139 | @(negedge clock) cmd = `Run; 140 | @(negedge clock) cmd = `Run; 141 | @(negedge clock) cmd = `Run; 142 | @(negedge clock) cmd = `Run; 143 | @(negedge clock) cmd = `Run; 144 | @(negedge clock) cmd = `Run; 145 | @(negedge clock) cmd = `Run; 146 | @(negedge clock) cmd = `Run; 147 | @(negedge clock) cmd = `Run; 148 | @(negedge clock) cmd = `Run; 149 | @(negedge clock) cmd = `Run; 150 | @(negedge clock) cmd = `Run; 151 | @(negedge clock) cmd = `Run; 152 | @(negedge clock) cmd = `Run; 153 | @(negedge clock) cmd = `Run; 154 | @(negedge clock) cmd = `Run; 155 | @(negedge clock) cmd = `Run; 156 | @(negedge clock) cmd = `Run; 157 | @(negedge clock) cmd = `Run; 158 | @(negedge clock) cmd = `Run; 159 | @(negedge clock) cmd = `Run; 160 | @(negedge clock) cmd = `Run; 161 | @(negedge clock) cmd = `Run; 162 | @(negedge clock) cmd = `Run; 163 | @(negedge clock) cmd = `Run; 164 | @(negedge clock) cmd = `Run; 165 | @(negedge clock) cmd = `Run; 166 | @(negedge clock) cmd = `Run; 167 | @(negedge clock) cmd = `Run; 168 | @(negedge clock) cmd = `Run; 169 | @(negedge clock) cmd = `Run; 170 | @(negedge clock) cmd = `Run; 171 | @(negedge clock) cmd = `Run; 172 | @(negedge clock) cmd = `Run; 173 | 174 | #100 $display("The End"); 175 | $finish; 176 | end 177 | endmodule 178 | `endif 179 | -------------------------------------------------------------------------------- /src/user_module_341193419111006803_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | //`include "user_module_341193419111006803.v" 3 | 4 | module user_module_341193419111006803_tb; 5 | 6 | wire [7:0] io_in; 7 | wire [7:0] io_out; 8 | 9 | reg clk, reset, write_en; 10 | reg [4:0] pdm_input; 11 | 12 | assign io_in = {pdm_input, write_en, reset, clk}; 13 | 14 | user_module_341193419111006803 UUT (.io_in(io_in), .io_out(io_out)); 15 | 16 | initial begin 17 | $dumpfile("user_module_341193419111006803_tb.vcd"); 18 | $dumpvars(0, user_module_341193419111006803_tb); 19 | end 20 | 21 | initial begin 22 | #100_000_000; // Wait a long time in simulation units (adjust as needed). 23 | $display("Caught by trap"); 24 | $finish; 25 | end 26 | 27 | parameter CLK_HALF_PERIOD = 5; 28 | parameter TCLK = 2*CLK_HALF_PERIOD; 29 | always begin 30 | clk = 1'b1; 31 | #(CLK_HALF_PERIOD); 32 | clk = 1'b0; 33 | #(CLK_HALF_PERIOD); 34 | end 35 | 36 | initial 37 | begin 38 | #20 39 | reset = 1; 40 | #(CLK_HALF_PERIOD); 41 | reset = 0; 42 | end 43 | 44 | initial begin 45 | write_en = 0; 46 | pdm_input = 5'h00; 47 | #(CLK_HALF_PERIOD); 48 | #(5*TCLK) 49 | write_en = 1; 50 | pdm_input= 5'h08; 51 | #(TCLK); 52 | write_en = 0; 53 | #(63*TCLK); 54 | write_en = 1; 55 | pdm_input= 5'h1a; 56 | #(TCLK); 57 | write_en = 0; 58 | #(63*TCLK); 59 | write_en = 1; 60 | pdm_input= 5'h0f; 61 | #(64*TCLK); 62 | pdm_input= 5'h04; 63 | #(64*TCLK); 64 | $finish; 65 | end 66 | 67 | endmodule 68 | -------------------------------------------------------------------------------- /template/config.tcl: -------------------------------------------------------------------------------- 1 | # User config 2 | set script_dir [file dirname [file normalize [info script]]] 3 | 4 | # has to match the module name from wokwi 5 | set ::env(DESIGN_NAME) scan_wrapper_USER_MODULE_ID 6 | 7 | # save some time 8 | set ::env(RUN_KLAYOUT_XOR) 0 9 | set ::env(RUN_KLAYOUT_DRC) 0 10 | 11 | # don't put clock buffers on the outputs 12 | set ::env(PL_RESIZER_BUFFER_OUTPUT_PORTS) 0 13 | 14 | # allow use of specific sky130 cells 15 | set ::env(SYNTH_READ_BLACKBOX_LIB) 1 16 | 17 | # read all verilog files 18 | set ::env(VERILOG_FILES) [glob $::env(DESIGN_DIR)/*.v] 19 | 20 | # absolute die size 21 | set ::env(FP_SIZING) absolute 22 | set ::env(DIE_AREA) "0 0 105 105" 23 | set ::env(FP_CORE_UTIL) 45 24 | set ::env(PL_BASIC_PLACEMENT) {1} 25 | 26 | # use alternative efabless decap cells to solve LI density issue 27 | set ::env(DECAP_CELL) "\ 28 | sky130_fd_sc_hd__decap_3 \ 29 | sky130_fd_sc_hd__decap_4 \ 30 | sky130_fd_sc_hd__decap_6 \ 31 | sky130_fd_sc_hd__decap_8 \ 32 | sky130_ef_sc_hd__decap_12" 33 | 34 | # clock 35 | set ::env(CLOCK_PERIOD) "10" 36 | set ::env(CLOCK_PORT) "" 37 | 38 | set ::env(BASE_SDC_FILE) $::env(DESIGN_DIR)/base.sdc 39 | 40 | set ::env(SYNTH_CLOCK_UNCERTAINITY) 0.20 41 | set ::env(SYNTH_CLOCK_TRANSITION) 0.15 42 | 43 | # don't use power rings or met5 44 | set ::env(DESIGN_IS_CORE) 0 45 | set ::env(RT_MAX_LAYER) {met4} 46 | 47 | # connect to first digital rails 48 | set ::env(VDD_NETS) [list {vccd1}] 49 | set ::env(GND_NETS) [list {vssd1}] 50 | -------------------------------------------------------------------------------- /template/scan_wrapper.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | /* 3 | `ifdef COCOTB 4 | `define UNIT_DELAY #1 5 | `define FUNCTIONAL 6 | `define USE_POWER_PINS 7 | `include "libs.ref/sky130_fd_sc_hd/verilog/primitives.v" 8 | `include "libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v" 9 | `endif 10 | */ 11 | 12 | module scan_wrapper_USER_MODULE_ID ( 13 | input wire clk_in, 14 | input wire data_in, 15 | input wire scan_select_in, 16 | input wire latch_enable_in, 17 | output wire clk_out, 18 | output wire data_out, 19 | output wire scan_select_out, 20 | output wire latch_enable_out 21 | ); 22 | 23 | // input buffers 24 | // Looking at results from multiple projects the bufferring is a bit 25 | // inconsistent. So instead, we ensure at least clk buf 26 | wire clk; 27 | 28 | sky130_fd_sc_hd__clkbuf_2 input_buf_clk ( 29 | .A (clk_in), 30 | .X (clk), 31 | .VPWR (1'b1), 32 | .VGND (1'b0) 33 | ); 34 | 35 | // output buffers 36 | // Same as for input, to try and be more consistent, we make our own 37 | wire data_out_i; 38 | 39 | sky130_fd_sc_hd__buf_4 output_buffers[3:0] ( 40 | .A ({clk, data_out_i, scan_select_in, latch_enable_in }), 41 | .X ({clk_out, data_out, scan_select_out, latch_enable_out }), 42 | .VPWR (1'b1), 43 | .VGND (1'b0) 44 | ); 45 | 46 | /* 47 | `ifdef COCOTB 48 | initial begin 49 | $dumpfile ("scan_wrapper.vcd"); 50 | $dumpvars (0, scan_wrapper_lesson_1); 51 | #1; 52 | end 53 | `endif 54 | */ 55 | 56 | parameter NUM_IOS = 8; 57 | 58 | // wires needed 59 | wire [NUM_IOS-1:0] scan_data_out; // output of the each scan chain flop 60 | wire [NUM_IOS-1:0] scan_data_in; // input of each scan chain flop 61 | wire [NUM_IOS-1:0] module_data_in; // the data that enters the user module 62 | wire [NUM_IOS-1:0] module_data_out; // the data from the user module 63 | 64 | // scan chain - link all the flops, with data coming from data_in 65 | assign scan_data_in = {scan_data_out[NUM_IOS-2:0], data_in}; 66 | 67 | // end of the chain is a negedge FF to increase hold margin between blocks 68 | sky130_fd_sc_hd__dfrtn_1 out_flop ( 69 | .RESET_B (1'b1), 70 | .CLK_N (clk), 71 | .D (scan_data_out[NUM_IOS-1]), 72 | .Q (data_out_i), 73 | .VPWR (1'b1), 74 | .VGND (1'b0) 75 | ); 76 | 77 | // scan flops have a mux on their inputs to choose either data from the user module or the previous flop's output 78 | // https://antmicro-skywater-pdk-docs.readthedocs.io/en/test-submodules-in-rtd/contents/libraries/sky130_fd_sc_ls/cells/sdfxtp/README.html 79 | `ifndef FORMAL 80 | `ifndef FORMAL_COMPAT 81 | sky130_fd_sc_hd__sdfxtp_1 scan_flop [NUM_IOS-1:0] ( 82 | .CLK (clk), 83 | .D (scan_data_in), 84 | .SCD (module_data_out), 85 | .SCE (scan_select_in), 86 | .Q (scan_data_out), 87 | .VPWR (1'b1), 88 | .VGND (1'b0) 89 | ); 90 | 91 | // latch is used to latch the input data of the user module while the scan chain is used to capture the user module's outputs 92 | // https://antmicro-skywater-pdk-docs.readthedocs.io/en/test-submodules-in-rtd/contents/libraries/sky130_fd_sc_hd/cells/dlxtp/README.html 93 | sky130_fd_sc_hd__dlxtp_1 latch [NUM_IOS-1:0] ( 94 | .D (scan_data_out), 95 | .GATE (latch_enable_in), 96 | .Q (module_data_in), 97 | .VPWR (1'b1), 98 | .VGND (1'b0) 99 | ); 100 | `endif 101 | `endif 102 | 103 | // instantiate the wokwi module 104 | user_module_USER_MODULE_ID user_module( 105 | .io_in (module_data_in), 106 | .io_out (module_data_out) 107 | ); 108 | 109 | endmodule 110 | -------------------------------------------------------------------------------- /view.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import gdstk 3 | 4 | gdss = sorted(pathlib.Path('runs').glob('*/results/final/gds/*.gds')) 5 | library = gdstk.read_gds(gdss[-1]) 6 | top_cells = library.top_level() 7 | top_cells[0].write_svg('gds_render.svg') 8 | --------------------------------------------------------------------------------