Skip to content

roman01la/vera

Repository files navigation

Vera

Browser-based Verilog simulation IDE. Write RTL, compile, simulate, and inspect waveforms — entirely in the browser, no backend required.

How it works

Vera chains three WASM compilers in the browser:

  1. Verilator (WASM) transpiles Verilog to C++
  2. Clang/LLVM (WASM, via Emception) compiles C++ to a simulation WASM module
  3. V8 hot-loads and runs the simulation at 20-67 MHz
.v file → [Verilator.wasm] → C++ → [Clang.wasm] → sim.wasm → [WebAssembly.instantiate] → running simulation

Performance

Design Compile time Simulation speed
8-bit counter 3s ~22 MHz
32-bit ALU (7 ops) 3s ~67 MHz
UART TX 3s ~20 MHz

Compile times are for incremental recompilation (cached runtime). First compile after page load takes ~7s. Parallel compilation across 4 Web Workers.

Setup

Prerequisites

  • Node.js 20+
  • Emscripten SDK (for building Verilator WASM)
  • Docker (for building Emception artifacts)

Build Verilator WASM

# Install emsdk if needed
./scripts/setup-emsdk.sh

# Build verilator_bin.wasm
./scripts/build-verilator-wasm.sh

Build Emception artifacts

Emception provides Clang/LLVM compiled to WASM. Build on a machine with Docker:

git clone https://github.com/jprendes/emception.git /tmp/emception
cd /tmp/emception
./build-with-docker.sh

Copy artifacts to public/emception/.

Build optimized sysroot

The default Emscripten sysroot uses JS-based exception handling which causes slow WASM-JS boundary crossings. Rebuild with native WASM exceptions:

docker run -i --rm -v ./public/wasm/sysroot-lib:/output emscripten/emsdk:3.1.61 bash -c '
  export EMCC_CFLAGS="-fwasm-exceptions"
  embuilder build libc libc++ libc++abi libcompiler_rt libdlmalloc --force
  cp /emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/lib*.a /output/
'

Run

npm install
npm run dev

Open http://localhost:5173 in Chrome.

Deploy (Cloudflare Workers + Workers Assets)

./scripts/deploy.sh

The deploy script:

  1. Concatenates the sysroot .a libs into public/wasm/sysroot-libs.bin + a JSON offset manifest (scripts/bundle-sysroot-libs.mjs)
  2. Content-hashes runtime assets, brotli-compresses .wasm/.pack files, and splits anything >25 MB into parts (scripts/hash-assets.mjs). The resulting URL/size/parts manifest is written to src/wasm-bundle/asset-urls.json and statically imported by the workers.
  3. Runs vite build
  4. Assembles deploy/ with only the hashed (compressed/split) artifacts
  5. Deploys to Cloudflare via wrangler deploy

There is no R2 binding — every asset, including the brotli-compressed emscripten sysroot pack, is served from Workers Assets.

src/wasm-bundle/ is generated by the build scripts and gitignored.

Architecture

src/
├── workers/
│   ├── verilator.worker.ts    # Verilator WASM (Verilog → C++)
│   ├── compiler.worker.ts     # Emception/Clang (C++ → WASM)
│   ├── clang-worker.ts        # Parallel clang sub-workers
│   └── simulation.worker.ts   # Hot-loaded simulation runner
├── pipeline/
│   ├── pipeline.ts            # Orchestrates compile → link → simulate
│   ├── verilator-api.ts       # Verilator worker API
│   ├── emception-api.ts       # Compiler worker API
│   └── simulation-api.ts      # Simulation worker API
├── lib/
│   ├── harness-template.ts    # C++ harness generator (wraps Verilator model)
│   ├── verilated-light.ts     # Minimal Verilator runtime (~200 lines vs 3900)
│   ├── signal-extractor.ts    # Parses Verilator headers for signal metadata
│   ├── asset-fetch.ts         # OPFS-cached fetch + brotli decode for runtime assets
│   ├── asset-urls.ts          # Build-time hashed asset manifest (generated)
│   ├── opfs-store.ts          # OPFS read/write helpers
│   ├── preloader.ts           # Pre-startup byte-progress preloader
│   └── sysroot-libs.ts        # Loads concatenated sysroot bundle from OPFS
├── components/                # Vue 3 IDE components
├── composables/
│   └── usePipeline.ts         # Vue composable bridging pipeline to UI
├── stores/
│   └── project.ts             # Pinia store
└── bridge/
    ├── signal-protocol.ts     # SharedArrayBuffer layout for ArduPilot bridge
    └── shpak-bridge.ts        # Connection to Shpak flight simulator

Key optimizations

  • Minimal verilated runtime — 200 lines replacing 3900, eliminates C++ exception overhead
  • -fwasm-exceptions — native WASM exception handling, no JS invoke_* boundary crossings
  • Parallel compilation — 4 clang sub-workers compile .cpp files concurrently
  • Incremental compilation — object file cache, only recompile changed files
  • -O3 -msimd128 — aggressive optimization + WASM SIMD for all builds
  • Dead code elimination--gc-sections + -ffunction-sections removes unused code
  • Prewarm — compiles a tiny module on startup to warm V8 JIT and cache runtime objects
  • Brotli + OPFS asset pipeline.wasm/.pack assets are content-hashed and brotli-compressed at build time, decoded on the client via brotli-wasm, and cached (decompressed) in OPFS so subsequent loads are network-free
  • Split assets — anything over the 25 MB Workers Assets limit (e.g. the 74 MB emscripten sysroot) is sliced into ≤25 MB parts at build time and fetched in parallel
  • Sysroot bundlelibc.a, libc++.a, etc. are concatenated into a single sysroot-libs.bin + JSON offset manifest, eliminating per-file requests

Stack

Layer Technology
UI Vue 3 + Composition API
Editor Monaco Editor
Waveforms Surfer (Rust/WASM)
State Pinia
Build Vite
Verilator v5.046 compiled to WASM via Emscripten
Compiler Emception (LLVM 15 + Clang + LLD + Binaryen)
Sysroot emsdk 3.1.61 libc/libc++/libc++abi with -fwasm-exceptions

License

MIT

About

Browser-based Verilog simulation IDE. Write RTL, compile, simulate, and inspect waveforms — entirely in the browser, no backend required.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors