commit:0f8cafbda47797b07a1f3d2a32c1e902d420503b
author:Chip
committer:Chip
date:Fri Aug 9 23:17:53 2024 -0500
parents:
Initial commit
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
line changes: +17/-0
index 0000000..75ca2b7
--- /dev/null
+++ b/.github/workflows/docs.yaml
@@ -0,0 +1,17 @@
+name: docs
+
+on:
+  push:
+  workflow_dispatch:
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: Build docs
+        uses: TinyTapeout/tt-gds-action/docs@tt08

diff --git a/.github/workflows/fpga.yaml b/.github/workflows/fpga.yaml
line changes: +19/-0
index 0000000..8fb5f62
--- /dev/null
+++ b/.github/workflows/fpga.yaml
@@ -0,0 +1,19 @@
+name: fpga
+
+on:
+  push:
+    # Comment out (or remove) the following line to run the FPGA workflow on every push:
+    branches: none
+  workflow_dispatch:
+
+jobs:
+  fpga:
+    runs-on: ubuntu-latest
+    steps:
+      - name: checkout repo
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: FPGA bitstream for TT ASIC Sim (ICE40UP5K)
+        uses: TinyTapeout/tt-gds-action/fpga/ice40up5k@tt08

diff --git a/.github/workflows/gds.yaml b/.github/workflows/gds.yaml
line changes: +47/-0
index 0000000..d1db9b5
--- /dev/null
+++ b/.github/workflows/gds.yaml
@@ -0,0 +1,47 @@
+name: gds
+
+on:
+  push:
+  workflow_dispatch:
+
+jobs:
+  gds:
+    runs-on: ubuntu-latest
+    steps:
+      - name: checkout repo
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: Build GDS
+        uses: TinyTapeout/tt-gds-action@tt08
+        with:
+          flow: openlane2
+
+  precheck:
+    needs: gds
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run Tiny Tapeout Precheck
+        uses: TinyTapeout/tt-gds-action/precheck@tt08
+
+  gl_test:
+    needs: gds
+    runs-on: ubuntu-latest
+    steps:
+      - name: checkout repo
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: GL test
+        uses: TinyTapeout/tt-gds-action/gl_test@tt08
+
+  viewer:
+    needs: gds
+    runs-on: ubuntu-latest
+    permissions:
+      pages: write      # to deploy to Pages
+      id-token: write   # to verify the deployment originates from an appropriate source
+    steps:
+      - uses: TinyTapeout/tt-gds-action/viewer@tt08

diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
line changes: +47/-0
index 0000000..8b1efcb
--- /dev/null
+++ b/.github/workflows/test.yaml
@@ -0,0 +1,47 @@
+name: test
+on: [push, workflow_dispatch]
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+
+      - name: Install iverilog
+        shell: bash
+        run: sudo apt-get update && sudo apt-get install -y iverilog
+
+      # Set Python up and install cocotb
+      - name: Setup python
+        uses: actions/setup-python@v5
+        with:
+          python-version: '3.11'
+
+      - name: Install Python packages
+        shell: bash
+        run: pip install -r test/requirements.txt
+
+      - name: Run tests
+        run: |
+          cd test
+          make clean
+          make
+          # make will return success even if the test fails, so check for failure in the results.xml
+          ! grep failure results.xml
+
+      - name: Test Summary
+        uses: test-summary/action@v2.3
+        with:
+          paths: "test/results.xml"
+        if: always()
+
+      - name: upload vcd
+        if: success() || failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-vcd
+          path: |
+            test/tb.vcd
+            test/results.xml

diff --git a/.gitignore b/.gitignore
line changes: +13/-0
index 0000000..59e1339
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+.DS_Store
+.idea
+.vscode
+*.vcd
+runs
+tt_submission
+src/user_config.json
+src/config_merged.json
+test/sim_build
+test/__pycache__/
+test/results.xml
+test/gate_level_netlist.v
+tt/

diff --git a/LICENSE b/LICENSE
line changes: +201/-0
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

diff --git a/README.md b/README.md
line changes: +41/-0
index 0000000..68ba6ed
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+![](../../workflows/gds/badge.svg) ![](../../workflows/docs/badge.svg) ![](../../workflows/test/badge.svg) ![](../../workflows/fpga/badge.svg)
+
+# Tiny Tapeout Verilog Project Template
+
+- [Read the documentation for project](docs/info.md)
+
+## What is Tiny Tapeout?
+
+Tiny Tapeout is an educational project that aims to make it easier and cheaper than ever to get your digital and analog designs manufactured on a real chip.
+
+To learn more and get started, visit https://tinytapeout.com.
+
+## Set up your Verilog project
+
+1. Add your Verilog files to the `src` folder.
+2. Edit the [info.yaml](info.yaml) and update information about your project, paying special attention to the `source_files` and `top_module` properties. If you are upgrading an existing Tiny Tapeout project, check out our [online info.yaml migration tool](https://tinytapeout.github.io/tt-yaml-upgrade-tool/).
+3. Edit [docs/info.md](docs/info.md) and add a description of your project.
+4. Adapt the testbench to your design. See [test/README.md](test/README.md) for more information.
+
+The GitHub action will automatically build the ASIC files using [OpenLane](https://www.zerotoasiccourse.com/terminology/openlane/).
+
+## Enable GitHub actions to build the results page
+
+- [Enabling GitHub Pages](https://tinytapeout.com/faq/#my-github-action-is-failing-on-the-pages-part)
+
+## Resources
+
+- [FAQ](https://tinytapeout.com/faq/)
+- [Digital design lessons](https://tinytapeout.com/digital_design/)
+- [Learn how semiconductors work](https://tinytapeout.com/siliwiz/)
+- [Join the community](https://tinytapeout.com/discord)
+- [Build your design locally](https://www.tinytapeout.com/guides/local-hardening/)
+
+## What next?
+
+- [Submit your design to the next shuttle](https://app.tinytapeout.com/).
+- Edit [this README](README.md) and explain your design, how it works, and how to test it.
+- Share your project on your social network of choice:
+  - LinkedIn [#tinytapeout](https://www.linkedin.com/search/results/content/?keywords=%23tinytapeout) [@TinyTapeout](https://www.linkedin.com/company/100708654/)
+  - Mastodon [#tinytapeout](https://chaos.social/tags/tinytapeout) [@matthewvenn](https://chaos.social/@matthewvenn)
+  - X (formerly Twitter) [#tinytapeout](https://twitter.com/hashtag/tinytapeout) [@tinytapeout](https://twitter.com/tinytapeout)

diff --git a/docs/info.md b/docs/info.md
line changes: +20/-0
index 0000000..ed1af23
--- /dev/null
+++ b/docs/info.md
@@ -0,0 +1,20 @@
+<!---
+
+This file is used to generate your project datasheet. Please fill in the information below and delete any unused
+sections.
+
+You can also include images in this folder and reference them in the markdown. Each image must be less than
+512 kb in size, and the combined size of all images must be less than 1 MB.
+-->
+
+## How it works
+
+Explain how your project works
+
+## How to test
+
+Explain how to use your project
+
+## External hardware
+
+- [Leo's VGA PMOD](https://github.com/mole99/tiny-vga)

diff --git a/info.yaml b/info.yaml
line changes: +56/-0
index 0000000..c7b3e5b
--- /dev/null
+++ b/info.yaml
@@ -0,0 +1,56 @@
+# Tiny Tapeout project information
+project:
+  title:        "Munch"      # Project title
+  author:       "bytex64"      # Your name
+  discord:      ""      # Your discord username, for communication and automatically assigning you a Tapeout role (optional)
+  description:  "Displays munching squares through VGA PMOD"      # One line description of what your project does
+  language:     "Verilog" # other examples include SystemVerilog, Amaranth, VHDL, etc
+  clock_hz:     25175000       # Clock frequency in Hz (or 0 if not applicable)
+
+  # How many tiles your design occupies? A single tile is about 167x108 uM.
+  tiles: "1x1"          # Valid values: 1x1, 1x2, 2x2, 3x2, 4x2, 6x2 or 8x2
+
+  # Your top module name must start with "tt_um_". Make it unique by including your github username:
+  top_module:  "tt_um_bytex64_munch"
+
+  # List your project's source files here.
+  # Source files must be in ./src and you must list each source file separately, one per line.
+  # Don't forget to also update `PROJECT_SOURCES` in test/Makefile.
+  source_files:
+    - "hvsync_generator.v"
+    - "project.v"
+
+# The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins.
+pinout:
+  # Inputs
+  ui[0]: ""
+  ui[1]: ""
+  ui[2]: ""
+  ui[3]: ""
+  ui[4]: ""
+  ui[5]: ""
+  ui[6]: ""
+  ui[7]: ""
+
+  # Outputs
+  uo[0]: "R1"
+  uo[1]: "G1"
+  uo[2]: "B1"
+  uo[3]: "VSYNC"
+  uo[4]: "R0"
+  uo[5]: "G0"
+  uo[6]: "B0"
+  uo[7]: "HSYNC"
+
+  # Bidirectional pins
+  uio[0]: ""
+  uio[1]: ""
+  uio[2]: ""
+  uio[3]: ""
+  uio[4]: ""
+  uio[5]: ""
+  uio[6]: ""
+  uio[7]: ""
+
+# Do not change!
+yaml_version: 6

diff --git a/src/Makefile b/src/Makefile
line changes: +4/-0
index 0000000..a1d100b
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,4 @@
+SOURCES = hvsync_generator.v project.v
+
+munch.vvp: $(SOURCES)
+	iverilog -o $@ $(SOURCES)

diff --git a/src/config.json b/src/config.json
line changes: +88/-0
index 0000000..2c7c12e
--- /dev/null
+++ b/src/config.json
@@ -0,0 +1,88 @@
+{
+  "//": "DO NOT EDIT THIS FILE before reading the comments below:",
+
+  "//": "This is the default configuration for Tiny Tapeout projects. It should fit most designs.",
+  "//": "If you change it, please make sure you understand what you are doing. We are not responsible",
+  "//": "if your project fails because of a bad configuration.",
+
+  "//": "!!! DO NOT EDIT THIS FILE unless you know what you are doing !!!",
+
+  "//": "If you get stuck with this config, please open an issue or get in touch via the discord.",
+
+  "//": "Here are some of the variables you may want to change:",
+
+  "//": "PL_TARGET_DENSITY - You can increase this if Global Placement fails with error GPL-0302.",
+  "//": "Users have reported that values up to 0.8 worked well for them.",
+  "PL_TARGET_DENSITY": 0.6,
+
+  "//": "CLOCK_PERIOD - Increase this in case you are getting setup time violations.",
+  "//": "The value is in nanoseconds, so 20ns == 50MHz.",
+  "CLOCK_PERIOD": 20,
+
+  "//": "Hold slack margin - Increase them in case you are getting hold violations.",
+  "PL_RESIZER_HOLD_SLACK_MARGIN": 0.1,
+  "GLB_RESIZER_HOLD_SLACK_MARGIN": 0.05,
+
+  "//": "RUN_LINTER, LINTER_INCLUDE_PDK_MODELS - Disabling the linter is not recommended!",
+  "RUN_LINTER": 1,
+  "LINTER_INCLUDE_PDK_MODELS": 1,
+
+  "//": "If you need a custom clock configuration, read the following documentation first:",
+  "//": "https://tinytapeout.com/faq/#how-can-i-map-an-additional-external-clock-to-one-of-the-gpios",
+  "CLOCK_PORT": "clk",
+
+  "//": "Configuration docs: https://openlane.readthedocs.io/en/latest/reference/configuration.html",
+
+  "//": "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
+  "//": "!!! DO NOT CHANGE ANYTHING BELOW THIS POINT !!!",
+  "//": "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
+
+  "//": "Save some time",
+  "RUN_KLAYOUT_XOR": 0,
+  "RUN_KLAYOUT_DRC": 0,
+
+  "//": "Don't put clock buffers on the outputs",
+  "PL_RESIZER_BUFFER_OUTPUT_PORTS": 0,
+
+  "//": "Allow use of specific sky130 cells",
+  "SYNTH_READ_BLACKBOX_LIB": 1,
+
+  "//": "Reduce wasted space",
+  "TOP_MARGIN_MULT": 1,
+  "BOTTOM_MARGIN_MULT": 1,
+  "LEFT_MARGIN_MULT": 6,
+  "RIGHT_MARGIN_MULT": 6,
+
+  "//": "Absolute die size",
+  "FP_SIZING": "absolute",
+
+  "PL_BASIC_PLACEMENT": 0,
+  "GRT_ALLOW_CONGESTION": 1,
+
+  "FP_IO_HLENGTH": 2,
+  "FP_IO_VLENGTH": 2,
+
+  "FP_PDN_VPITCH": 38.87,
+
+  "//": "Use alternative efabless decap cells to solve LI density issue",
+  "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"
+  ],
+
+  "//": "Clock",
+  "RUN_CTS": 1,
+
+  "//": "Don't use power rings or met5 layer",
+  "DESIGN_IS_CORE": 0,
+  "RT_MAX_LAYER": "met4",
+
+  "//": "MAGIC_DEF_LABELS may cause issues with LVS",
+  "MAGIC_DEF_LABELS": 0,
+
+  "//": "Only export pin area in LEF (without any connected nets)",
+  "MAGIC_WRITE_LEF_PINONLY": 1
+}

diff --git a/src/hvsync_generator.v b/src/hvsync_generator.v
line changes: +69/-0
index 0000000..71a957e
--- /dev/null
+++ b/src/hvsync_generator.v
@@ -0,0 +1,69 @@
+`ifndef HVSYNC_GENERATOR_H
+`define HVSYNC_GENERATOR_H
+
+/*
+Video sync generator, used to drive a VGA monitor.
+Timing from: https://en.wikipedia.org/wiki/Video_Graphics_Array
+To use:
+- Wire the hsync and vsync signals to top level outputs
+- Add a 3-bit (or more) "rgb" output to the top level
+*/
+
+module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
+
+  input clk;
+  input reset;
+  output reg hsync, vsync;
+  output display_on;
+  output reg [9:0] hpos;
+  output reg [9:0] vpos;
+
+  // declarations for TV-simulator sync parameters
+  // horizontal constants
+  parameter H_DISPLAY       = 640; // horizontal display width
+  parameter H_BACK          =  48; // horizontal left border (back porch)
+  parameter H_FRONT         =  16; // horizontal right border (front porch)
+  parameter H_SYNC          =  96; // horizontal sync width
+  // vertical constants
+  parameter V_DISPLAY       = 480; // vertical display height
+  parameter V_TOP           =  33; // vertical top border
+  parameter V_BOTTOM        =  10; // vertical bottom border
+  parameter V_SYNC          =   2; // vertical sync # lines
+  // derived constants
+  parameter H_SYNC_START    = H_DISPLAY + H_FRONT;
+  parameter H_SYNC_END      = H_DISPLAY + H_FRONT + H_SYNC - 1;
+  parameter H_MAX           = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;
+  parameter V_SYNC_START    = V_DISPLAY + V_BOTTOM;
+  parameter V_SYNC_END      = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
+  parameter V_MAX           = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1;
+
+  wire hmaxxed = (hpos == H_MAX) || reset;	// set when hpos is maximum
+  wire vmaxxed = (vpos == V_MAX) || reset;	// set when vpos is maximum
+  
+  // horizontal position counter
+  always @(posedge clk)
+  begin
+    hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END);
+    if(hmaxxed)
+      hpos <= 0;
+    else
+      hpos <= hpos + 1;
+  end
+
+  // vertical position counter
+  always @(posedge clk)
+  begin
+    vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END);
+    if(hmaxxed)
+      if (vmaxxed)
+        vpos <= 0;
+      else
+        vpos <= vpos + 1;
+  end
+  
+  // display_on is set when beam is in "safe" visible frame
+  assign display_on = (hpos<H_DISPLAY) && (vpos<V_DISPLAY);
+
+endmodule
+
+`endif

diff --git a/src/project.v b/src/project.v
line changes: +48/-0
index 0000000..70d57f5
--- /dev/null
+++ b/src/project.v
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024 Chip Black
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+`default_nettype none
+
+module tt_um_bytex64_munch (
+    input  wire [7:0] ui_in,    // Dedicated inputs
+    output wire [7:0] uo_out,   // Dedicated outputs
+    input  wire [7:0] uio_in,   // IOs: Input path
+    output wire [7:0] uio_out,  // IOs: Output path
+    output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
+    input  wire       ena,      // always 1 when the design is powered, so you can ignore it
+    input  wire       clk,      // clock
+    input  wire       rst_n     // reset_n - low to reset
+);
+
+  wire [9:0] hpos;
+  wire [9:0] vpos;
+  wire hsync, vsync;
+  wire display_on;
+  wire [1:0] R;
+  wire [1:0] G;
+  wire [1:0] B;
+
+  assign uo_out  = {hsync, B[0], G[0], R[0], vsync, B[1], G[1], R[1]};
+  assign uio_out = 0;
+  assign uio_oe  = 0;
+
+  hvsync_generator hvsync_gen(
+    .clk(clk),
+    .reset(~rst_n),
+    .hsync(hsync),
+    .vsync(vsync),
+    .display_on(display_on),
+    .hpos(hpos),
+    .vpos(hpos)
+  );
+
+  assign R = display_on ? hpos[3:2] : 2'b00;
+  assign G = display_on ? hpos[3:2] : 2'b00;
+  assign B = display_on ? hpos[3:2] : 2'b00;
+
+  // List all unused inputs to prevent warnings
+  wire _unused = &{ena, clk, rst_n, 1'b0};
+
+endmodule

diff --git a/test/Makefile b/test/Makefile
line changes: +42/-0
index 0000000..b9b0b93
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,42 @@
+# Makefile
+# See https://docs.cocotb.org/en/stable/quickstart.html for more info
+
+# defaults
+SIM ?= icarus
+TOPLEVEL_LANG ?= verilog
+SRC_DIR = $(PWD)/../src
+PROJECT_SOURCES = hvsync_generator.v project.v
+
+ifneq ($(GATES),yes)
+
+# RTL simulation:
+SIM_BUILD				= sim_build/rtl
+VERILOG_SOURCES += $(addprefix $(SRC_DIR)/,$(PROJECT_SOURCES))
+COMPILE_ARGS 		+= -I$(SRC_DIR)
+
+else
+
+# Gate level simulation:
+SIM_BUILD				= sim_build/gl
+COMPILE_ARGS    += -DGL_TEST
+COMPILE_ARGS    += -DFUNCTIONAL
+COMPILE_ARGS    += -DUSE_POWER_PINS
+COMPILE_ARGS    += -DSIM
+COMPILE_ARGS    += -DUNIT_DELAY=\#1
+VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/primitives.v
+VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v
+
+# this gets copied in by the GDS action workflow
+VERILOG_SOURCES += $(PWD)/gate_level_netlist.v
+
+endif
+
+# Include the testbench sources:
+VERILOG_SOURCES += $(PWD)/tb.v
+TOPLEVEL = tb
+
+# MODULE is the basename of the Python test file
+MODULE = test
+
+# include cocotb's make rules to take care of the simulator setup
+include $(shell cocotb-config --makefiles)/Makefile.sim

diff --git a/test/README.md b/test/README.md
line changes: +31/-0
index 0000000..172327f
--- /dev/null
+++ b/test/README.md
@@ -0,0 +1,31 @@
+# Sample testbench for a Tiny Tapeout project
+
+This is a sample testbench for a Tiny Tapeout project. It uses [cocotb](https://docs.cocotb.org/en/stable/) to drive the DUT and check the outputs.
+See below to get started or for more information, check the [website](https://tinytapeout.com/hdl/testing/).
+
+## Setting up
+
+1. Edit [Makefile](Makefile) and modify `PROJECT_SOURCES` to point to your Verilog files.
+2. Edit [tb.v](tb.v) and replace `tt_um_example` with your module name.
+
+## How to run
+
+To run the RTL simulation:
+
+```sh
+make -B
+```
+
+To run gatelevel simulation, first harden your project and copy `../runs/wokwi/results/final/verilog/gl/{your_module_name}.v` to `gate_level_netlist.v`.
+
+Then run:
+
+```sh
+make -B GATES=yes
+```
+
+## How to view the VCD file
+
+```sh
+gtkwave tb.vcd tb.gtkw
+```

diff --git a/test/requirements.txt b/test/requirements.txt
line changes: +2/-0
index 0000000..3c2430f
--- /dev/null
+++ b/test/requirements.txt
@@ -0,0 +1,2 @@
+pytest==8.2.2
+cocotb==1.8.1

diff --git a/test/tb.gtkw b/test/tb.gtkw
line changes: +39/-0
index 0000000..c92ca3c
--- /dev/null
+++ b/test/tb.gtkw
@@ -0,0 +1,39 @@
+[*]
+[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
+[*] Mon Nov 20 16:00:28 2023
+[*]
+[dumpfile] "/home/uri/p/tt-new-template-proto/test/tb.vcd"
+[dumpfile_mtime] "Mon Nov 20 15:58:34 2023"
+[dumpfile_size] 1110
+[savefile] "/home/uri/p/tt-new-template-proto/test/tb.gtkw"
+[timestart] 0
+[size] 1376 600
+[pos] -1 -1
+*-24.534533 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+[treeopen] tb.
+[sst_width] 297
+[signals_width] 230
+[sst_expanded] 1
+[sst_vpaned_height] 158
+@28
+tb.user_project.ena
+@29
+tb.user_project.clk
+@28
+tb.user_project.rst_n
+@200
+-Inputs
+@22
+tb.user_project.ui_in[7:0]
+@200
+-Bidirectional Pins
+@22
+tb.user_project.uio_in[7:0]
+tb.user_project.uio_oe[7:0]
+tb.user_project.uio_out[7:0]
+@200
+-Output Pins
+@22
+tb.user_project.uo_out[7:0]
+[pattern_trace] 1
+[pattern_trace] 0

diff --git a/test/tb.v b/test/tb.v
line changes: +45/-0
index 0000000..0d9eea5
--- /dev/null
+++ b/test/tb.v
@@ -0,0 +1,45 @@
+`default_nettype none
+`timescale 1ns / 1ps
+
+/* This testbench just instantiates the module and makes some convenient wires
+   that can be driven / tested by the cocotb test.py.
+*/
+module tb ();
+
+  // Dump the signals to a VCD file. You can view it with gtkwave.
+  initial begin
+    $dumpfile("tb.vcd");
+    $dumpvars(0, tb);
+    #1;
+  end
+
+  // Wire up the inputs and outputs:
+  reg clk;
+  reg rst_n;
+  reg ena;
+  reg [7:0] ui_in;
+  reg [7:0] uio_in;
+  wire [7:0] uo_out;
+  wire [7:0] uio_out;
+  wire [7:0] uio_oe;
+
+  // Replace tt_um_example with your module name:
+  tt_um_bytex64_munch user_project (
+
+      // Include power ports for the Gate Level test:
+`ifdef GL_TEST
+      .VPWR(1'b1),
+      .VGND(1'b0),
+`endif
+
+      .ui_in  (ui_in),    // Dedicated inputs
+      .uo_out (uo_out),   // Dedicated outputs
+      .uio_in (uio_in),   // IOs: Input path
+      .uio_out(uio_out),  // IOs: Output path
+      .uio_oe (uio_oe),   // IOs: Enable path (active high: 0=input, 1=output)
+      .ena    (ena),      // enable - goes high when design is selected
+      .clk    (clk),      // clock
+      .rst_n  (rst_n)     // not reset
+  );
+
+endmodule

diff --git a/test/test.py b/test/test.py
line changes: +35/-0
index 0000000..03d8548
--- /dev/null
+++ b/test/test.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: © 2024 Tiny Tapeout
+# SPDX-License-Identifier: Apache-2.0
+
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import ClockCycles, Timer
+
+
+@cocotb.test()
+async def test_project(dut):
+    dut._log.info("Start")
+
+    # Set the clock period to 10 us (100 KHz)
+    clock = Clock(dut.clk, 10, units="us")
+    cocotb.start_soon(clock.start())
+
+    await Timer(5, "us")
+    dut.ena.value = 1
+    await Timer(5, "us")
+
+    # Reset
+    dut._log.info("Reset")
+    dut.rst_n.value = 1
+    await Timer(5, "us")
+    dut.rst_n.value = 0
+    await Timer(10, "us")
+    dut.ui_in.value = 0
+    dut.uio_in.value = 0
+    dut.rst_n.value = 1
+
+    dut._log.info("Test project behavior")
+
+    await ClockCycles(dut.clk, 20)
+
+    assert dut.uo_out.value == 3