{"id":32524175,"url":"https://github.com/sysprog21/vga-nyancat","last_synced_at":"2025-10-28T08:57:05.401Z","repository":{"id":319439464,"uuid":"1078054043","full_name":"sysprog21/vga-nyancat","owner":"sysprog21","description":"Hardware-accelerated Nyancat animation on VGA display, implemented in Verilog RTL","archived":false,"fork":false,"pushed_at":"2025-10-18T14:35:54.000Z","size":106,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-19T05:15:05.621Z","etag":null,"topics":["nyancat","verilator","verilog-hdl"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sysprog21.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-17T06:23:33.000Z","updated_at":"2025-10-18T14:35:56.000Z","dependencies_parsed_at":"2025-10-19T05:15:23.305Z","dependency_job_id":"21e25deb-e249-44e1-8ee7-e3438464473f","html_url":"https://github.com/sysprog21/vga-nyancat","commit_stats":null,"previous_names":["sysprog21/vga-nyancat"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/sysprog21/vga-nyancat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysprog21%2Fvga-nyancat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysprog21%2Fvga-nyancat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysprog21%2Fvga-nyancat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysprog21%2Fvga-nyancat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sysprog21","download_url":"https://codeload.github.com/sysprog21/vga-nyancat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sysprog21%2Fvga-nyancat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281410756,"owners_count":26496368,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-28T02:00:06.022Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nyancat","verilator","verilog-hdl"],"created_at":"2025-10-28T08:57:00.599Z","updated_at":"2025-10-28T08:57:05.382Z","avatar_url":"https://github.com/sysprog21.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VGA Nyancat\n\n![CI](https://github.com/sysprog21/vga-nyancat/actions/workflows/main.yml/badge.svg)\n\nHardware-accelerated Nyancat (Pop-Tart Cat) animation on VGA display,\nimplemented in Verilog RTL and simulated using [Verilator](https://verilator.org/).\nFeatures real-time hardware scaling, ROM-based animation storage, and a 2-stage rendering pipeline.\n\nNote: This is an educational hardware design project demonstrating VGA timing,\nROM-based graphics, and hardware animation techniques.\nThe Nyancat character and animation are used under fair use for educational purposes.\n\n## Features\n\n- 12-frame animation cycling at ~11 fps (90ms per frame)\n- Real-time 8× hardware scaling from 64×64 source to 512×512 display\n- Efficient storage using 4-bit character indices + 14-color palette (230× compression)\n- Pipelined rendering with 2-stage ROM lookup for minimal latency\n- VESA-compliant timing at 640×480 @ 72Hz (31.5 MHz pixel clock)\n- Automated data generation from upstream [klange/nyancat](https://github.com/klange/nyancat) source\n\n## Prerequisites\n\nEnsure that you have the required dependencies installed:\n\nUbuntu/Debian:\n```shell\nsudo apt-get install libsdl2-dev verilator python3\n```\n\nmacOS:\n```shell\nbrew install sdl2 verilator python3\n```\n\n## Building and Running\n\nTo build and run the interactive simulation:\n```shell\nmake run\n```\n\nThis will automatically:\n1. Download animation source via curl/wget (if needed)\n2. Generate animation data files\n3. Build the Verilator simulation\n4. Launch the interactive display\n\nInteractive controls:\n- p key: Save current frame to test.png\n- ESC key: Reset animation\n- q key: Quit\n\n## Testing\n\nTo run automated tests and generate a test frame:\n```shell\nmake check\n```\n\nThis generates `test.png` containing a single animation frame.\n\n## Code Formatting\n\nFormat all Verilog and C++ source files:\n```shell\nmake indent\n```\n\nThis project follows the `.verilog-style` guidelines for consistent code formatting:\n- Verilog files formatted with `verible-verilog-format`\n- C++ files formatted with `clang-format`\n\nInstall verible from [chipsalliance/verible releases](https://github.com/chipsalliance/verible/releases).\n\n## How It Works\n\n### Data Generation Pipeline\n\nThis project automatically extracts animation data from the upstream [klange/nyancat](https://github.com/klange/nyancat) repository and converts it to hardware-friendly format:\n\n```\n┌────────────────────────────────────────────────────────────┐\n│ 1. Source Acquisition (Automated)                          │\n│    make → Download animation.c (52KB) via curl/wget        │\n│         → Save to build/animation.c                        │\n│                                                            │\n│ 2. Data Extraction (scripts/gen-nyancat.py)                │\n│    Input:  animation.c (ASCII art frames)                  │\n│    Parse:  Extract 12 frames of 64×64 character data       │\n│    Output: Character indices + color palette               │\n│                                                            │\n│ 3. Format Conversion                                       │\n│    ASCII characters → 4-bit indices (0-13)                 │\n│    RGB888 colors    → 6-bit VGA (RRGGBB)                   │\n│                                                            │\n│ 4. Hardware Files Generated                                │\n│    build/nyancat-frames.hex: 49,152 lines (4-bit each)     │\n│    build/nyancat-colors.hex: 14 colors in 6-bit format     │\n└────────────────────────────────────────────────────────────┘\n```\n\nCharacter to Index Mapping:\n\nThe script maps each ASCII character from the animation to a 4-bit index:\n\n| Char | Index | Color | RGB | VGA 6-bit |\n|------|-------|-------|-----|-----------|\n| `,` | 0 | Blue background | (0,49,105) | `000001` |\n| `.` | 1 | White stars | (255,255,255) | `111111` |\n| `'` | 2 | Black border | (0,0,0) | `000000` |\n| `@` | 3 | Tan poptart | (255,205,152) | `111110` |\n| ... | ... | ... | ... | ... |\n| `%` | 13 | Pink cheeks | (255,163,152) | `111010` |\n\nConversion Process:\n\n1. Parse animation.c: Extract frame data using regex patterns\n2. Build color map: Map 14 unique ASCII characters to palette indices\n3. Convert frames: Transform each 64×64 character grid to 4-bit indices\n4. Generate RGB to VGA: Convert 24-bit RGB to 6-bit VGA format (2R2G2B)\n\nResult: 230× compression (24KB vs 5.4MB for raw RGB888 storage)\n\n### System Architecture\n\n```\n                    ┌─────────────────────────────────────┐\n                    │      VGA Nyancat Top Module         │\n                    └───────────┬─────────────────┬───────┘\n                                │                 │\n                   ┌────────────▼──────────┐      │\n                   │   VGA Sync Generator  │      │\n                   │   (vga-sync-gen.v)    │      │\n                   │                       │      │\n                   │  • H/V counters       │      │\n                   │  • Sync pulse gen     │      │\n                   │  • Pixel coordinates  │      │\n                   └────────────┬──────────┘      │\n                                │                 │\n                     {x_px, y_px, activevideo}    │\n                                │                 │\n                   ┌────────────▼─────────────────▼──────┐\n                   │    Nyancat Animation Renderer       │\n                   │         (nyancat.v)                 │\n                   │  ┌──────────────────────────────┐   │\n                   │  │  Coordinate Transformation   │   │\n                   │  │  • Remove offset             │   │\n                   │  │  • Descale by 8              │   │\n                   │  │  • Calculate ROM address     │   │\n                   │  └──────────┬───────────────────┘   │\n                   │             │                       │\n                   │  ┌──────────▼───────────────────┐   │\n                   │  │   2-Stage Pipeline           │   │\n                   │  │                              │   │\n                   │  │  Stage 1: frame_mem[addr]    │   │\n                   │  │           → char_idx         │   │\n                   │  │                              │   │\n                   │  │  Stage 2: color_mem[char_idx]│   │\n                   │  │           → color            │   │\n                   │  └──────────┬───────────────────┘   │\n                   │             │                       │\n                   └─────────────┼───────────────────────┘\n                                 │\n                          rrggbb (6-bit color)\n                                 │\n                                 ▼\n                            VGA Display\n```\n\n### Data Flow Pipeline\n\nThe rendering pipeline transforms pixel coordinates into colors through multiple stages:\n\n```\nClock  Input            Stage 1              Stage 2              Stage 3           Output\nCycle  Coordinates      ROM Addressing       Char Lookup          Color Lookup\n─────  ─────────────    ──────────────       ───────────────      ────────────      ──────\n  N    (x_px, y_px) ──▶ Transform ────────▶ [pipeline reg] ───▶ [pipeline reg] ──▶ (blank)\n                        addr calculated\n\n N+1   (x_px+1, y_px) ─▶ Transform ────────▶ frame_mem[addr] ──▶ [pipeline reg] ──▶ (blank)\n                        addr calculated      char_idx fetched\n\n N+2   (x_px+2, y_px) ─▶ Transform ────────▶ frame_mem[addr] ──▶ color_mem[idx] ──▶ color(N)\n                        addr calculated      char_idx fetched    color fetched        ↑\n                                                                                      |\n                                                            2-clock latency ───────────\n```\n\n### Memory Organization\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│ Frame Memory (frame_mem): 49,152 × 4 bits = 24 KB                   │\n├─────────────────────────────────────────────────────────────────────┤\n│  Frame 0 (4096 entries)  ┌──────────────────────┐                   │\n│  [0..4095]               │  64 × 64 = 4096      │                   │\n│                          │  4-bit char indices  │                   │\n│  Frame 1 (4096 entries)  │  Values: 0-13        │                   │\n│  [4096..8191]            └──────────────────────┘                   │\n│                                                                     │\n│  ...                                                                │\n│                                                                     │\n│  Frame 11 (4096 entries) ┌──────────────────────┐                   │\n│  [45056..49151]          │  Last frame data     │                   │\n│                          └──────────────────────┘                   │\n│                                                                     │\n│  ROM Address Calculation:                                           │\n│    addr = (frame_index × 4096) + (src_y × 64) + src_x               │\n└─────────────────────────────────────────────────────────────────────┘\n\n┌─────────────────────────────────────────────────────────────────────┐\n│ Color Palette (color_mem): 16 × 6 bits = 12 bytes                   │\n├─────────────────────────────────────────────────────────────────────┤\n│  Index   Color         6-bit (RRGGBB)   RGB888                      │\n│  ─────   ──────────    ──────────────   ───────────────────────     │\n│    0     Dark Blue     000001           (  0,  49, 105)             │\n│    1     White         111111           (255, 255, 255)             │\n│    2     Black         000000           (  0,   0,   0)             │\n│   ...    (10 more colors)                                           │\n│   13     Light Pink    111010           (255, 163, 152)             │\n│  14-15   (unused)                                                   │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\n### VGA Timing Diagram\n\n```\nOne complete frame (640×480 @ 72Hz):\n\nHorizontal timing (per line, 832 pixels):\n  ├────────┬──────────┬───────────┬──────────────────────────┤\n  │   FP   │   SYNC   │    BP     │        ACTIVE            │\n  │  24px  │   40px   │  128px    │        640px             │\n  │        │ (hsync=0)│           │    (visible data)        │\n  └────────┴──────────┴───────────┴──────────────────────────┘\n  ◄──────────────────────────────────────────────────────────►\n           832 pixels × 31.5 MHz = 26.4 µs per line\n\nVertical timing (per frame, 520 lines):\n  ├────────┬──────────┬───────────┬──────────────────────────┤\n  │   FP   │   SYNC   │    BP     │        ACTIVE            │\n  │  9 ln  │   3 ln   │  28 ln    │       480 ln             │\n  │        │ (vsync=0)│           │   (visible lines)        │\n  └────────┴──────────┴───────────┴──────────────────────────┘\n  ◄──────────────────────────────────────────────────────────►\n         520 lines × 26.4 µs = 13.73 ms per frame (~72.8 Hz)\n\nActive video region (where animation is rendered):\n  ┌───────────────────────────────────────────────────┐\n  │ 640 × 480 VGA display                             │\n  │    ┌─────────────────────────────────┐            │\n  │    │                                 │            │\n  │    │    512 × 512 Nyancat            │  ◄─ Centered horizontally\n  │ 64 │    (8× scaled from 64×64)       │  64        │\n  │ px │                                 │  px        │\n  │    │                                 │  margin    │\n  │    └─────────────────────────────────┘            │\n  │                                                   │\n  └───────────────────────────────────────────────────┘\n  Note: Bottom 32 lines of animation are clipped (512 \u003e 480)\n```\n\n## Command-Line Options\n\n```shell\n./obj_dir/Vvga_nyancat --save-png output.png  # Save a single frame and exit\n./obj_dir/Vvga_nyancat --help                 # Show help message\n```\n\n## Technical Details\n\n### Animation Data Storage\n\n| Parameter | Value | Details |\n|-----------|-------|---------|\n| Source frame size | 64×64 pixels | Original animation resolution |\n| Total frames | 12 | Complete animation loop |\n| Storage format | 4-bit indices | Character codes (0-13) |\n| Frame memory | 49,152 × 4 bits | 24 KB total (12 × 4096 entries) |\n| Color palette | 14 colors × 6 bits | 12 bytes (2R2G2B VGA format) |\n| Total ROM | ~24 KB | 230× smaller than raw RGB888 (5.4 MB) |\n\n### VGA Display Timing\n\n| Parameter | Value | Calculation |\n|-----------|-------|-------------|\n| Resolution | 640×480 @ 72Hz | VESA standard timing |\n| Pixel clock | 31.5 MHz | Standard VGA clock |\n| Horizontal total | 832 pixels/line | FP(24) + SYNC(40) + BP(128) + ACTIVE(640) |\n| Vertical total | 520 lines/frame | FP(9) + SYNC(3) + BP(28) + ACTIVE(480) |\n| Line period | 26.4 µs | 832 ÷ 31.5 MHz |\n| Frame period | 13.73 ms | 520 × 26.4 µs |\n| Refresh rate | 72.8 Hz | 1 ÷ 13.73 ms |\n| Clocks/frame | 432,640 | 832 × 520 |\n\n### Animation Timing\n\n| Parameter | Value | Details |\n|-----------|-------|---------|\n| Frame duration | 90 ms | Target animation speed |\n| Clocks/frame | 2,835,000 | 90 ms × 31.5 MHz |\n| Animation rate | ~11.1 fps | 31.5 MHz ÷ 2,835,000 |\n| Total loop time | 1.08 seconds | 12 frames × 90 ms |\n\n### Hardware Implementation\n\n| Feature | Implementation | Benefit |\n|---------|----------------|---------|\n| Scaling | 8× nearest-neighbor | Simple bit-shift (÷8 = \u003e\u003e3) |\n| Pipeline stages | 2 (frame ROM → palette ROM) | 2-clock latency, full throughput |\n| Display area | 512×512 centered | Symmetric margins (64px sides) |\n| Coordinate transform | Offset removal + descaling | Minimal logic complexity |\n| Frame sequencing | 22-bit counter + 4-bit index | Automatic wrap at 12 frames |\n| ROM address calc | Bit concatenation + OR | Zero-delay, no multipliers |\n| ROM reads | Synchronous block RAM | Synthesis-friendly implementation |\n\n### Data Generation Automation\n\nThe build system automatically handles all data generation:\n\nMakefile workflow:\n```\nmake all\n  ↓\n1. Check if build/animation.c exists\n  ↓ (if not)\n2. Download animation.c (52KB) via curl or wget\n   Source: https://raw.githubusercontent.com/klange/nyancat/...\n  ↓\n3. Run scripts/gen-nyancat.py build/animation.c build/\n  ↓\n4. Generate build/nyancat-frames.hex (49,152 lines)\n  ↓\n5. Generate build/nyancat-colors.hex (14 colors)\n  ↓\n6. Run Verilator to generate C++ files\n  ↓\n7. Compile C++ simulation binary\n  ↓\nBuild complete (~4.7 seconds from clean state)\n```\n\nAvailable Make targets:\n```shell\nmake all         # Build everything (default)\nmake build       # Same as 'all', explicit build target\nmake run         # Build and launch interactive simulation\nmake check       # Build and generate test.png\nmake clean       # Remove build artifacts (keep build/ directory)\nmake distclean   # Remove everything including build/ directory\nmake regen-data  # Force regeneration of animation data\nmake indent      # Format all Verilog and C++ source files\n```\n\nManual data regeneration:\n```shell\n# Force regeneration using existing upstream source\nmake regen-data\n\n# Clean everything and rebuild from scratch\nmake distclean \u0026\u0026 make all\n\n# Generate from custom source file\npython3 scripts/gen-nyancat.py /path/to/animation.c\n```\n\nData file format:\n\n`nyancat-frames.hex` - One hex digit per line (4 bits):\n```\n// Frame 0\n0    ← Background pixel (char ',')\n0\n1    ← Star pixel (char '.')\n...\n```\n\n`nyancat-colors.hex` - VGA 6-bit colors with comments:\n```\n01  //  0: ',' RGB(0,49,105)      ← Background\n3f  //  1: '.' RGB(255,255,255)   ← Stars\n00  //  2: ''' RGB(0,0,0)         ← Black\n...\n```\n\n## Project Structure\n\n```\nvga-nyancat/\n├── rtl/                              # Hardware RTL modules\n│   ├── vga-sync-gen.v               # VGA sync generator (640×480@72Hz)\n│   │                                 # • Generates hsync/vsync pulses\n│   │                                 # • Outputs pixel coordinates\n│   │                                 # • Provides activevideo flag\n│   │\n│   ├── nyancat.v                    # Nyancat animation renderer\n│   │                                 # • Frame sequencing (12 frames)\n│   │                                 # • Coordinate transformation\n│   │                                 # • 2-stage ROM pipeline\n│   │                                 # • ROM: 49,152×4b + 16×6b\n│   │\n│   └── vga-nyancat.v                # Top-level integration\n│                                     # • Connects sync gen to renderer\n│                                     # • Reset polarity conversion\n│\n├── sim/                              # Simulation testbench\n│   └── main.cpp                     # Verilator + SDL2 wrapper\n│                                     # • SDL2 framebuffer rendering\n│                                     # • Standalone PNG encoder (no deps)\n│                                     # • Interactive controls\n│\n├── scripts/                          # Data generation tools\n│   └── gen-nyancat.py               # Animation data extractor\n│                                     # • Downloads klange/nyancat source\n│                                     # • Parses ASCII art frames\n│                                     # • Generates hex files\n│\n├── build/                            # Generated files (gitignored)\n│   ├── animation.c                  # Downloaded source (52KB)\n│   ├── nyancat-frames.hex           # Frame data (49,152 lines)\n│   ├── nyancat-colors.hex           # Color palette (14 colors)\n│   ├── Vvga_nyancat                 # Simulation binary\n│   └── test.png                     # Generated test image\n│\n├── Makefile                          # Build automation\n│                                     # • Data generation\n│                                     # • Verilator compilation\n│                                     # • Test targets\n│\n└── README.md                         # This file\n```\n\n### Module Hierarchy\n\n```\nvga_nyancat (top)\n    ├── vga_sync_gen\n    │   ├── Inputs:  px_clk, reset\n    │   └── Outputs: hsync, vsync, x_px[9:0], y_px[9:0], activevideo\n    │\n    └── nyancat\n        ├── Inputs:  px_clk, reset, x_px[9:0], y_px[9:0], activevideo\n        └── Outputs: rrggbb[5:0]\n```\n\n## License\n\nVGA Nyancat is available under a permissive MIT-style license.\nUse of this source code is governed by a MIT license that can be found in the [LICENSE](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysprog21%2Fvga-nyancat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsysprog21%2Fvga-nyancat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsysprog21%2Fvga-nyancat/lists"}