spzbrnmrc/Tiny-Vedas

RISC-V Infrastructure for AI Accelerators

SystemVerilog

53

100 commits

updated Sep 18, 2026

See the code
risc-v
rtl
systemverilog

See what people are saying (1)

README

Tiny Vedas — Open Infrastructure for RISC-V AI Accelerators

Tiny Vedas is an open-source stack for designing, verifying, and bringing up RISC-V AI accelerators — from synthesizable processor RTL and spec-driven decode, through ISS/RTL co-simulation, to a PyTorch JIT that targets bare-metal firmware on the core.

Today, the repo ships a complete RV32IM reference core: a 4-stage in-order pipeline with Harvard memory, hazard handling, and end-to-end test infrastructure. Next, the same contracts extend to additional microarchitectures (VLIW, superscalar, out-of-order) and vector units — hardware presets and software hooks are already scaffolded in hw/ so RTL, simulation, and PyVedas can evolve together without breaking the workflow.

It is also used as a reference for the free course on RISC-V Processor Design.

What's in the stack

LayerRole
RTLSynthesizable RISC-V cores, GEMM accelerator, and SoC integration (rtl/)
VerificationPython ISS + RTL trace comparison (tools/rv_iss.py, sim_manager.py); GEMM directed/random co-sim (tools/gemm_cosim.py)
DecodeYAML-driven instruction tables → SystemVerilog (open-decode-tables/)
PrimitivesReusable arithmetic and register blocks (SVLib/)
SoftwareBare-metal runtime, printf, assembly/C/PyTorch tests
PyVedastorch.compile → C → RV32 ELF for on-core inference kernels
PDOptional ASIC flow: sv2v + OpenROAD (pd/) — core_gemm_top (CPU + GEMM)

Current focus: RV32IM

The shipping RTL is a 4-stage pipelined RV32IM processor written in SystemVerilog, plus an 8×8 int8 GEMM MMIO accelerator that shares DCCM over AXI4. The CPU flavor (hw/presets/rv32im_scalar.yaml) is the baseline used by CI, examples, and the course.

Roadmap: microarchitectures and vector

Tiny Vedas is built to support multiple CPU organizations behind one hardware-config contract. Presets in hw/presets/ already describe scalar, VLIW, superscalar, and out-of-order variants with optional vector units; only rv32im_scalar matches implemented RTL today. As new microarchitectures land, sim_manager, PyVedas, and the test suite will target them through the same --hw-config YAML — so accelerator exploration stays one toolchain, not a fork per design.

Features

Architecture

  • ISA: RISC-V RV32IM (32-bit integer + multiply/divide)
  • Pipeline: 4-stage (IFU → IDU0 → IDU1 → EXU)
  • Memory: Harvard architecture — separate ICCM and DCCM (true dual-port, both ports RW). The core keeps custom fetch/LSU ports; soc_top and the FPGA SoC convert those to AXI4 (32-bit, ID width 4, two DCCM masters) into on-chip CCM slaves. FPGA muxes DCCM port B between the core and the host (halt-and-load). ASIC PD synthesizes core_gemm_top (CPU + GEMM; memories stay off-chip IOs).
  • GEMM: Output-stationary 8×8 PE array (int8 × int8 → int32, K-tile 32) at MMIO_GEMM_ADDR (0x00300000). Packed AXI4 INCR DMA loads A/B from DCCM and writes C; the core is held via accel_hold for the duration of one START job (software does not poll DONE).
  • Decode: Spec-driven via the open-decode-tables submodule (YAML → SystemVerilog)
  • Verification: Python instruction-set simulator (ISS) compared against RTL traces

Instruction Set Support

  • Arithmetic: ADD, SUB, ADDI, LUI, AUIPC
  • Logical: AND, OR, XOR, ANDI, ORI, XORI
  • Shifts: SLL, SRL, SRA, SLLI, SRLI, SRAI
  • Comparison: SLT, SLTU, SLTI, SLTIU
  • Branches: BEQ, BNE, BLT, BGE, BLTU, BGEU
  • Jumps: JAL, JALR
  • Memory: LB, LH, LW, LBU, LHU, SB, SH, SW
  • Multiply/Divide: MUL, MULH, MULHU, MULHSU, DIV, DIVU, REM, REMU
  • System: NOP (addi x0, x0, 0), ECALL (decoded; no trap handler yet — behaves as NOP)

Advanced Features

  • Register forwarding from EXU to IDU1
  • Pipeline flush on taken branches and jumps
  • Register scoreboard for RAW hazard detection
  • Multi-cycle multiplier and divider
  • Booth-encoded 32×32 multiplier with per-operand signedness (MUL / MULH / MULHU / MULHSU)
  • Non-restoring divider with combinational Kogge-Stone adders on the iteration path
  • Unaligned load/store support with byte-strobe DCCM writes (no store RMW) and strobe-aware store-to-load forwarding. Dual RW DCCM ports complete both beats of an unaligned access in one cycle (stall only on a same-cycle load/store port conflict).

GEMM accelerator (rtl/accel/)

One START programs a single 2-D DCCM matrix multiply C = A × B:

ItemValue
Array8×8 output-stationary PEs
Datatypesint8 × int8 → int32 accumulators
K tiling32-element tiles, dual ping-pong A/B buffers
DMA32-bit AXI4 INCR bursts (A along K, B along N, C int32 along N)
WaitCore accel_hold for the job; tests must not poll STATUS before reading C

CSRs (rtl/include/gemm_csrs.svh): BASE_A/B/C, M, N, K, CTRL (START / soft reset), STATUS (BUSY / DONE). DONE is sticky until the next START. Firmware examples: tests/asm/gemm_8x8.s, tests/c/gemm_8x8.c, tests/c/gemm_multi.c.

Project Structure

Tiny-Vedas/
├── rtl/                     # Processor + accelerator RTL
│   ├── core_top.sv          # CPU pipeline (memory ports exposed)
│   ├── soc_top.sv           # core_top + GEMM + AXI4 adapters + ICCM/DCCM
│   ├── core_top.flist       # Sim file list (core + SoC + bus + GEMM)
│   ├── accel/               # GEMM MMIO engine (CSR, DMA, 8×8 PE array)
│   │   ├── gemm_top.sv      # Job FSM + ping-pong tile orchestration
│   │   ├── gemm_csr.sv      # AXI-Lite CSRs at 0x00300000
│   │   ├── gemm_dma.sv      # Packed AXI4 INCR bursts to DCCM
│   │   ├── gemm_datapath.sv # Systolic array + accumulators
│   │   └── gemm_pe.sv       # int8 MAC PE
│   ├── bus/                 # AXI4 fetch/LSU masters, CCM slaves, master mux
│   ├── ifu/                 # Instruction fetch unit
│   ├── idu/                 # Decode stages, regfile, scoreboard
│   │   ├── rv32im_decoder.sv   # Generated — do not hand-edit
│   │   └── decode_out_t.svh      # Generated — do not hand-edit
│   ├── exu/                 # ALU, MUL, DIV, LSU
│   ├── include/             # global.svh, types.svh, axi4.svh, gemm_csrs.svh, mmio_map.svh
│   └── lib/                 # Byte-write ICCM/DCCM (`sync_tdp_mem`)
├── fpga/alveo_u280/         # Alveo U280 bitstream, host load, card smoke
├── pd/                      # ASIC PD: sv2v + OpenROAD (`core_gemm_top`)
│   ├── rtl/core_gemm_top.sv # PD wrapper: core_top + gemm_top
│   ├── platforms/           # ASAP7 / sky130 YAML
│   └── README.md
├── dv/
│   ├── sv/                  # core_top_tb.sv, gemm_top_tb.sv, lsu_tb.sv
│   └── verilator/           # Verilator C++ harness
├── hw/                      # Hardware presets (scalar, VLIW, OoO + vector)
│   ├── presets/             # YAML configs shared by RTL/SW (see hw/README.md)
│   ├── soc/                 # SoC device map (UART, GEMM, EOT)
│   └── types.py             # Typed HwConfig loader
├── tests/
│   ├── asm/                 # Assembly test programs (incl. gemm_8x8)
│   ├── c/                   # C tests (helloworld, iaxpy, gemm_8x8, gemm_multi)
│   ├── elf/                 # Prebuilt ELF binaries (dhrystone)
│   ├── pyvedas/             # PyTorch → JIT model specs (incl. gemm_mmio)
│   ├── smoke.tlist          # Regression test list
│   └── gemm.tlist           # GEMM-only regression
├── pyvedas/                 # PyTorch → Tiny-Vedas JIT
├── tools/
│   ├── sim_manager.py       # Main test runner (compile → ISS → RTL → compare)
│   ├── rv_iss.py            # Reference instruction-set simulator
│   └── gemm_cosim.py        # Directed / random GEMM co-simulation
├── sw/
│   ├── include/             # soc_defines.h (generated — do not hand-edit)
│   └── vedas_printf/        # Bare-metal printf library for C tests
├── SVLib/                   # Git submodule — reusable SystemVerilog primitives
├── open-decode-tables/      # Git submodule — YAML decode table generator
├── scripts/
│   ├── install_deps.sh      # Dependency installer (`make deps`)
│   ├── env.sh               # Generated PATH + venv (by `make deps`)
│   ├── with_env.sh          # Wrapper used by Makefile targets
│   └── pd_docker.sh         # OpenROAD Docker wrapper for rtl2gds
├── .github/workflows/ci.yml # GitHub Actions CI pipeline
├── Makefile
├── requirements.txt
└── LICENSE

Prerequisites

ToolPurpose
VerilatorRTL simulation (primary; used in CI)
riscv64-unknown-elf-gccBare-metal cross-compiler for test programs (RV32IM / ILP32)
Python 3sim_manager.py, rv_iss.py, decode generation
Xilinx Vivado (optional)XSim simulation — only needed if you prefer make smoke over Verilator

Tested on Ubuntu 22.04 and 24.04. Other Linux distributions should work with equivalent packages installed manually.

Quick Start

1. Clone with submodules

git clone --recurse-submodules https://github.com/siliscale/Tiny-Vedas.git
cd Tiny-Vedas

If you already cloned without submodules:

git submodule update --init --recursive

2. Install dependencies

On Ubuntu, make deps installs everything needed for simulation and verification:

  • System build packages (build-essential, Verilator build deps)
  • Python virtual environment with packages from requirements.txt
  • Prebuilt RISC-V GNU bare-metal toolchain (riscv64-unknown-elf-gcc) into .local/riscv/
  • Latest stable Verilator compiled from source into .local/verilator/
make deps

make deps also generates scripts/env.sh (PATH + venv) and verifies the toolchain. All Makefile test targets use it automatically via scripts/with_env.sh, so CI and local runs work without manual setup.

For interactive shells, source the environment once per session:

source scripts/env.sh
riscv64-unknown-elf-gcc --version
verilator --version

Override pinned versions if needed:

RISCV_TOOLCHAIN_VERSION=2026.06.05 make deps   # default
VERILATOR_TAG=v5.048 make deps                   # pin a specific Verilator release
FORCE_RISCV_TOOLCHAIN_REINSTALL=1 make deps      # re-download toolchain
FORCE_VERILATOR_REBUILD=1 make deps              # rebuild Verilator

Do not run make deps with sudo — only the apt step needs elevated privileges. If a previous sudo make deps left deps/verilator root-owned, fix ownership then rebuild:

sudo chown -R "$USER:$USER" deps/verilator
FORCE_VERILATOR_REBUILD=1 make deps

3. Run the smoke regression

# Verilator (recommended; same as CI)
make smoke-verilator

# Xilinx XSim (requires Vivado — optional)
make smoke

4. Run a single test

./tools/sim_manager.py -s verilator -n asm.basic_alu_r
./tools/sim_manager.py -s verilator -n c.helloworld
./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n pyvedas.vector_add

RISC-V GNU Toolchain

Tiny Vedas compiles bare-metal test programs with riscv64-unknown-elf-gcc using -march=rv32im -mabi=ilp32. Do not use the Linux cross-compiler (riscv64-linux-gnu-gcc) or distribution packages that lack newlib — they will not produce working bare-metal ELFs.

make deps downloads a prebuilt riscv64-unknown-elf toolchain from the riscv-collab/riscv-gnu-toolchain releases page and installs it to .local/riscv/. The Ubuntu series (22.04 or 24.04) is detected automatically.

Manual install

  1. Go to riscv-gnu-toolchain releases.
  2. Download the riscv64-elf-ubuntu-<version>-gcc.tar.xz archive matching your Ubuntu version.
  3. Extract and add to your PATH:
# Example for Ubuntu 22.04, release 2026.06.05
wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2026.06.05/riscv64-elf-ubuntu-22.04-gcc.tar.xz
mkdir -p ~/.local
tar -xJf riscv64-elf-ubuntu-22.04-gcc.tar.xz -C ~/.local

# Add to ~/.bashrc
export PATH="$HOME/.local/riscv/bin:$PATH"
source ~/.bashrc
  1. Verify RV32IM support:
riscv64-unknown-elf-gcc --version
echo 'int main(void) { return 0; }' | riscv64-unknown-elf-gcc -march=rv32im -mabi=ilp32 -nostdlib -x c -

If prebuilt binaries are unavailable for your platform, follow the build instructions in the riscv-gnu-toolchain README. Configure for bare metal:

./configure --prefix=/opt/riscv --with-arch=rv32im --with-abi=ilp32
make -j$(nproc)

This takes a long time. Prefer the prebuilt nightly releases for development and CI.

Running Tests

All tests are driven by tools/sim_manager.py. Tests are named <type>.<name>:

PrefixSourceExample
asm.tests/asm/<name>.sasm.basic_mul
c.tests/c/<name>.cc.helloworld
elf.tests/elf/<name> (prebuilt)elf.dhrystone
pyvedas.tests/pyvedas/<name>.py (JIT → ELF)pyvedas.vector_add

sim_manager.py usage

./scripts/with_env.sh ./tools/sim_manager.py -s <simulator> (-n <test> | -t <task-list>)

  -s, --simulator   verilator | xsim
  -n, --test-name   Run a single test (e.g. asm.basic_alu_r)
  -t, --task-list   Run all tests listed in a file (e.g. tests/smoke.tlist)
  --hw-config       Hardware preset YAML (default: hw/presets/rv32im_scalar.yaml)
  --vcd             Verilator waveform (core_top.vcd); omit for smoke/CI

make smoke-verilator and make smoke invoke with_env.sh automatically.

Makefile targets

TargetCommand
make depsInstall system packages, Python venv, RISC-V toolchain, and Verilator
make smoke-verilatorRun the smoke regression via Verilator (CI default)
make smokeRun the smoke regression via XSim (requires Vivado)
make fpga alveo_u280Build the Alveo U280 bitstream (Vivado 2023.2)
make fpga_smoke alveo_u280Run tests/smoke.tlist on the programmed Alveo (needs sudo)
make gemm-directedDirected GEMM RTL vs golden (tools/gemm_cosim.py)
make gemm-cosimDirected + 100 random GEMM seeds
make rtl2gdsASIC PD: sv2v + OpenROAD (core_gemm_top; see pd/README.md)
make decodesRegenerate rtl/idu/rv32im_decoder.sv from YAML
make socRegenerate mmio_map.svh and sw/include/soc_defines.h from hw/soc/
make cleanRemove build artifacts (work/, obj_dir/, logs, VCDs)

Per-test output

Each test writes artifacts to work/<test>/:

FileContents
iss.logGolden ISS execution trace
rtl.logRTL architectural trace
sim.logSimulator stdout and comparison errors
console.logProgram UART output
stats.txtIPC/CPI performance metrics
core_top.vcdWaveform (Verilator --vcd only; off by default)

Verification

Tiny Vedas uses co-simulation: a Python ISS generates a golden trace, the RTL simulator produces its own trace, and sim_manager.py compares them instruction by instruction (PC, opcode, register writes, memory stores, branches).

Programs signal completion by storing EOT_MAGIC (0xdeadbeef) to MMIO_EOT_ADDR (0x10000000). See tests/asm/eot_sequence.s and sw/include/soc_defines.h.

Arithmetic units

Multiply (rtl/exu/exu_mul.sv → SVLib mul)

The multiply unit is a Booth-encoded 32×32 multiplier. Operands enter at EXU stage e2; the 64-bit product is registered at e3 and written when sideband latency (MUL_LAT) expires.

RV32M instructionrs1 signrs2 sign
MUL, MULHsignedsigned
MULHUunsignedunsigned
MULHSUsignedunsigned

Inside mul, the signed operand is always the multiplicand and the unsigned operand is Booth-scanned as the multiplier. When rs1 is unsigned and rs2 is signed, operands are swapped (product is commutative). Separate controls drive multiplicand sign extension (mc_sign) and unsigned-multiplier correction (mult_unsign); a single global unsigned flag is not sufficient for MULHSU.

Pipeline placement is configured in rtl/include/mul_pd_config.svh (included by exu_mul). At most one internal register stage should be enabled for PD experiments — see pd/README.md.

Final CPA (CPA_ALGORITHM on SVLib mul):

ValueModuleNotes
0adder_pipe + RCAPIPE_STAGES_CPA splits width
1adder_pipe + 4-bit CLADefault for generic builds
2kogge_stone_pipe2-cycle CPA, one flop mid prefix tree; production exu_mul uses this

Divide (rtl/exu/div.sv)

PathWhenLatency
FastDivide by zero/one, zero dividend, signed overflow, or both magnitudes ≤4 bits (small_div)1 cycle after issue
SlowEverything else — 32-step non-restoring divider on absolute magnitudes~33 cycles

The slow path uses combinational kogge_stone_adder instances for the per-iteration trial add/subtract and remainder correction. Do not use kogge_stone_pipe here — that module has a pipeline register and is reserved for the multiplier CPA.

SVLib adders (SVLib/src/arith/)

ModuleRegistersUse
adderNoGeneric wrapper: ALGORITHM 0=RCA, 1=CLA, 2=Kogge-Stone (comb.)
kogge_stone_adderNoCombinational Kogge-Stone prefix adder (power-of-2 width)
kogge_stone_pipeOnePipelined Kogge-Stone (prefix tree split across two cycles)
adder_pipeOptionalMulti-lane pipelined CPA for non-Kogge multiplier configs

See SVLib/README.md for the full library inventory.

Smoke regression (tests/smoke.tlist)

Smoke tests cover ALU, forwarding, multiply, divide (asm.basic_div, asm.div_regression), load/store, branches, jumps, C programs, PyVedas JIT tests (pyvedas.{vector,matrix,tensor}_{add,mul}), GEMM (asm.gemm_8x8, c.gemm_8x8, c.gemm_multi, pyvedas.gemm_mmio), and Dhrystone. tests/gemm.tlist runs the GEMM subset alone.

Memory Map

Processor memories

MemoryDepthWidthNotes
ICCM (instructions)2^18 words32-bitLoaded from ELF .text section
DCCM (data)2^18 words32-bitDual RW ports (byte strobes); loaded from .data, .rodata, .bss, etc.

Configured in rtl/include/global.svh. The Alveo overlay uses smaller windows (32 KiB ICCM / 1 MiB DCCM, BAR2 2 MiB); see fpga/alveo_u280/README.md. UART (0x00200000), GEMM (0x00300000), and EOT (0x10000000) writes are decoded on the core store path and do not enter DCCM as MMIO. Addresses come from hw/soc/default.yaml; software uses generated sw/include/soc_defines.h.

Software-visible addresses

AddressPurpose
SOC_LINK_ADDRESS (0x00100000)Default link address for test programs (-Wl,-Ttext=0x100000)
MMIO_UART_ADDR (0x00200000)MMIO UART — bare-metal printf output (sw/vedas_printf)
MMIO_GEMM_ADDR (0x00300000)GEMM CSRs (BASE_A/B/C, M/N/K, CTRL, STATUS) — see rtl/include/gemm_csrs.svh
MMIO_EOT_ADDR (0x10000000)End-of-test flag — write EOT_MAGIC to halt simulation
0x80000000Default initial stack pointer (register x2)

The reset vector is taken from the ELF _start symbol, not hardcoded.

Decode Table Generation

Instruction decode logic is generated from YAML, not hand-written. The source of truth is open-decode-tables/tables/rv32im.yaml.

make decodes

This regenerates:

  • rtl/idu/rv32im_decoder.sv
  • rtl/idu/decode_out_t.svh

To add or modify instructions, edit the YAML in the open-decode-tables submodule, commit and push there, then update the submodule pointer in this repo and run make decodes.

SoC device map

MMIO devices (UART, GEMM, EOT) are described in hw/soc/default.yaml, not hardcoded in RTL or C. CPU presets select the map with soc: default.

make soc

This regenerates:

  • rtl/include/mmio_map.svh — address ranges and indices for rtl/bus/mmio_mux.sv
  • sw/include/soc_defines.h — C / preprocessed .S macros (MMIO_UART_ADDR, EOT_MAGIC, …)
  • sw/include/soc_defines.inc — gas .include for .s tests

sim_manager.py runs the same generation at the start of a test. To add a device, edit the YAML and re-run make soc. Bare-metal software includes soc_defines.h and uses the generated macros — see sw/vedas_printf/vedas_printf.c.

Writing Tests

Assembly test

Create tests/asm/my_test.s:

    .globl   _start
    .section .text

_start:
    li   x1, 42
    add  x2, x1, x1
    .include "eot_sequence.s"

Run with:

./tools/sim_manager.py -s verilator -n asm.my_test

C test

Create tests/c/my_test.c using vedas_printf for output. sim_manager.py compiles sw/vedas_printf/vedas_printf.c alongside the test with -march=rv32im -mabi=ilp32 -nostdlib -lgcc (required by the prebuilt bare-metal toolchain). The end-of-test sequence comes from tests/c/asm_functions/eot_sequence.s.

Python test (PyVedas)

PyVedas tests are model spec files under tests/pyvedas/. Each file describes a small torch.compile module and concrete trace inputs. sim_manager.py JIT-compiles the model to C, links it with the PyVedas runtime, builds an RV32 ELF, and runs the usual ISS/RTL comparison.

Prerequisites: run make deps once — it installs CPU PyTorch into the repo venv/ (used automatically by sim_manager.py). For JIT-only debugging you can also use pyvedas/.venv; see pyvedas/README.md.

Create tests/pyvedas/my_add.py:

"""PyVedas smoke test: elementwise add."""

import torch


class MyAdd(torch.nn.Module):
    def forward(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
        return x + y


MODEL = torch.compile(MyAdd())
TRACE_INPUTS = (
    torch.tensor([1, 2, 3, 4], dtype=torch.int32),
    torch.tensor([10, 20, 30, 40], dtype=torch.int32),
)
SymbolPurpose
MODELtorch.compile module exported by the JIT
TRACE_INPUTSTuple of concrete tensors — used for torch.export tracing and to bake static buffer values into generated.c

Constraints today

  • Use torch.int32 tensors (bare-metal target has no soft-float).
  • Every graph op must have a 1:1 entry in pyvedas/runtime/ops.yaml with a matching C kernel (e.g. aten.add.Tensor, aten.mul.Tensor). Adding a new op requires a registry entry and runtime implementation — see pyvedas/README.md.

Run with:

./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n pyvedas.my_add

Add the test name to tests/smoke.tlist to include it in make smoke-verilator:

pyvedas.my_add

What happens under the hood

  1. JIT (pyvedas/jit) exports the graph and writes work/pyvedas.my_add/generated.c, graph.txt, and manifest.json.
  2. The RISC-V linker builds test.elf from generated.c, runtime sources from the manifest, and eot_sequence.s.
  3. ISS and Verilator traces are compared like any other test.

Inspect JIT output on failure: work/pyvedas.my_add/jit.log, compile.log, sim.log.

The core and GEMM are synthesizable. Simulation and FPGA SoCs sit outside core_top: AXI4 adapters, ICCM/DCCM slaves, and gemm_top (rtl/bus/, rtl/accel/, rtl/soc_top.sv, fpga/alveo_u280/rtl/). ASIC PD nets core_gemm_top (CPU + GEMM, memories as IOs) — see pd/README.md. FPGA build/program/smoke:

make fpga alveo_u280          # bitstream (Vivado 2023.2)
make fpga_smoke alveo_u280    # PCIe load + EOT on the card (sudo)

For ASIC physical design (SystemVerilog → Verilog via sv2v, then OpenROAD-flow-scripts), see pd/README.md:

make config                    # CPU flavor + PDK platform
make sv2v                      # convert RTL only
make rtl2gds                   # sv2v + synthesis/place/route/GDS
make rtl2gds ORFS_TARGET=synth # stop after synthesis

# Docker (ORFS image; used when host Yosys/OpenROAD is missing)
ORFS_TARGET=all PD_PLATFORM=ci-asap7 ./scripts/pd_docker.sh make rtl2gds
make decodes   # ensure decoder is up to date before synthesis
make soc       # ensure MMIO map + soc_defines.h match hw/soc/

Performance Scoreboard

From RTL simulation (see work/<test>/stats.txt after a run):

BenchmarkInstructionsCyclesIPC
c.helloworld76022930.3314
c.iaxpy1092350.4638
elf.dhrystone64072012743370.5028

On Alveo U280 (100 MHz core, host-timed EOT) elf.dhrystone is ~12.9 ms for 2000 runs → ~155k dps / ~88.5 DMIPS (~0.89 DMIPS/MHz). That matches the sim cycle count (1.274M cycles ≈ 12.7 ms at 100 MHz).

Submodules

SubmoduleRepositoryPurpose
SVLibsiliscale/SVLibRegisters, program counter, arithmetic primitives
open-decode-tablessiliscale/open-decode-tablesYAML → SystemVerilog decode generator

After pulling submodule updates:

git submodule update --init --recursive
make decodes
make soc

Continuous Integration

GitHub Actions runs on every push and pull request to main. The workflow (.github/workflows/ci.yml) mirrors a from-scratch developer setup:

  1. Checkout with submodules
  2. make deps — system packages, Python venv, RISC-V toolchain, Verilator, scripts/env.sh
  3. make decodes — regenerate the instruction decoder
  4. make soc — regenerate the MMIO map and soc_defines.h
  5. make smoke-verilator — full smoke regression (tests/smoke.tlist)

No Vivado license is required. make deps writes scripts/env.sh; subsequent make targets load it automatically — no manual PATH or source venv/bin/activate in CI.

If CI fails, check the job log for the failing test name, then reproduce locally with:

make deps   # if not already done
./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n <test.name>

Contributing

This repository is developed and maintained by Siliscale. We do not accept external contributions — please do not open pull requests or submit patches.

The project is open source under the Apache License 2.0; you are free to use, study, and fork it for your own work. For collaboration, partnerships, or commercial engagement, see Business inquiries.

Business inquiries

For partnerships, consulting, custom accelerator work, or commercial licensing questions, contact marco@siliscale.com.

License

Apache License 2.0 — see LICENSE.

  • NOTICE — attribution for this repo and bundled submodules
  • THIRD_PARTY.md — dev-only tools vs shipped components

SPDX: Apache-2.0

Contributors

spzbrnmrc

100 commits

spzbrnmrc/Tiny-Vedas

RISC-V Infrastructure for AI Accelerators

SystemVerilog

53

100 commits

updated Sep 18, 2026

See the code
risc-v
rtl
systemverilog

See what people are saying (1)

README

Tiny Vedas — Open Infrastructure for RISC-V AI Accelerators

Tiny Vedas is an open-source stack for designing, verifying, and bringing up RISC-V AI accelerators — from synthesizable processor RTL and spec-driven decode, through ISS/RTL co-simulation, to a PyTorch JIT that targets bare-metal firmware on the core.

Today, the repo ships a complete RV32IM reference core: a 4-stage in-order pipeline with Harvard memory, hazard handling, and end-to-end test infrastructure. Next, the same contracts extend to additional microarchitectures (VLIW, superscalar, out-of-order) and vector units — hardware presets and software hooks are already scaffolded in hw/ so RTL, simulation, and PyVedas can evolve together without breaking the workflow.

It is also used as a reference for the free course on RISC-V Processor Design.

What's in the stack

LayerRole
RTLSynthesizable RISC-V cores, GEMM accelerator, and SoC integration (rtl/)
VerificationPython ISS + RTL trace comparison (tools/rv_iss.py, sim_manager.py); GEMM directed/random co-sim (tools/gemm_cosim.py)
DecodeYAML-driven instruction tables → SystemVerilog (open-decode-tables/)
PrimitivesReusable arithmetic and register blocks (SVLib/)
SoftwareBare-metal runtime, printf, assembly/C/PyTorch tests
PyVedastorch.compile → C → RV32 ELF for on-core inference kernels
PDOptional ASIC flow: sv2v + OpenROAD (pd/) — core_gemm_top (CPU + GEMM)

Current focus: RV32IM

The shipping RTL is a 4-stage pipelined RV32IM processor written in SystemVerilog, plus an 8×8 int8 GEMM MMIO accelerator that shares DCCM over AXI4. The CPU flavor (hw/presets/rv32im_scalar.yaml) is the baseline used by CI, examples, and the course.

Roadmap: microarchitectures and vector

Tiny Vedas is built to support multiple CPU organizations behind one hardware-config contract. Presets in hw/presets/ already describe scalar, VLIW, superscalar, and out-of-order variants with optional vector units; only rv32im_scalar matches implemented RTL today. As new microarchitectures land, sim_manager, PyVedas, and the test suite will target them through the same --hw-config YAML — so accelerator exploration stays one toolchain, not a fork per design.

Features

Architecture

  • ISA: RISC-V RV32IM (32-bit integer + multiply/divide)
  • Pipeline: 4-stage (IFU → IDU0 → IDU1 → EXU)
  • Memory: Harvard architecture — separate ICCM and DCCM (true dual-port, both ports RW). The core keeps custom fetch/LSU ports; soc_top and the FPGA SoC convert those to AXI4 (32-bit, ID width 4, two DCCM masters) into on-chip CCM slaves. FPGA muxes DCCM port B between the core and the host (halt-and-load). ASIC PD synthesizes core_gemm_top (CPU + GEMM; memories stay off-chip IOs).
  • GEMM: Output-stationary 8×8 PE array (int8 × int8 → int32, K-tile 32) at MMIO_GEMM_ADDR (0x00300000). Packed AXI4 INCR DMA loads A/B from DCCM and writes C; the core is held via accel_hold for the duration of one START job (software does not poll DONE).
  • Decode: Spec-driven via the open-decode-tables submodule (YAML → SystemVerilog)
  • Verification: Python instruction-set simulator (ISS) compared against RTL traces

Instruction Set Support

  • Arithmetic: ADD, SUB, ADDI, LUI, AUIPC
  • Logical: AND, OR, XOR, ANDI, ORI, XORI
  • Shifts: SLL, SRL, SRA, SLLI, SRLI, SRAI
  • Comparison: SLT, SLTU, SLTI, SLTIU
  • Branches: BEQ, BNE, BLT, BGE, BLTU, BGEU
  • Jumps: JAL, JALR
  • Memory: LB, LH, LW, LBU, LHU, SB, SH, SW
  • Multiply/Divide: MUL, MULH, MULHU, MULHSU, DIV, DIVU, REM, REMU
  • System: NOP (addi x0, x0, 0), ECALL (decoded; no trap handler yet — behaves as NOP)

Advanced Features

  • Register forwarding from EXU to IDU1
  • Pipeline flush on taken branches and jumps
  • Register scoreboard for RAW hazard detection
  • Multi-cycle multiplier and divider
  • Booth-encoded 32×32 multiplier with per-operand signedness (MUL / MULH / MULHU / MULHSU)
  • Non-restoring divider with combinational Kogge-Stone adders on the iteration path
  • Unaligned load/store support with byte-strobe DCCM writes (no store RMW) and strobe-aware store-to-load forwarding. Dual RW DCCM ports complete both beats of an unaligned access in one cycle (stall only on a same-cycle load/store port conflict).

GEMM accelerator (rtl/accel/)

One START programs a single 2-D DCCM matrix multiply C = A × B:

ItemValue
Array8×8 output-stationary PEs
Datatypesint8 × int8 → int32 accumulators
K tiling32-element tiles, dual ping-pong A/B buffers
DMA32-bit AXI4 INCR bursts (A along K, B along N, C int32 along N)
WaitCore accel_hold for the job; tests must not poll STATUS before reading C

CSRs (rtl/include/gemm_csrs.svh): BASE_A/B/C, M, N, K, CTRL (START / soft reset), STATUS (BUSY / DONE). DONE is sticky until the next START. Firmware examples: tests/asm/gemm_8x8.s, tests/c/gemm_8x8.c, tests/c/gemm_multi.c.

Project Structure

Tiny-Vedas/
├── rtl/                     # Processor + accelerator RTL
│   ├── core_top.sv          # CPU pipeline (memory ports exposed)
│   ├── soc_top.sv           # core_top + GEMM + AXI4 adapters + ICCM/DCCM
│   ├── core_top.flist       # Sim file list (core + SoC + bus + GEMM)
│   ├── accel/               # GEMM MMIO engine (CSR, DMA, 8×8 PE array)
│   │   ├── gemm_top.sv      # Job FSM + ping-pong tile orchestration
│   │   ├── gemm_csr.sv      # AXI-Lite CSRs at 0x00300000
│   │   ├── gemm_dma.sv      # Packed AXI4 INCR bursts to DCCM
│   │   ├── gemm_datapath.sv # Systolic array + accumulators
│   │   └── gemm_pe.sv       # int8 MAC PE
│   ├── bus/                 # AXI4 fetch/LSU masters, CCM slaves, master mux
│   ├── ifu/                 # Instruction fetch unit
│   ├── idu/                 # Decode stages, regfile, scoreboard
│   │   ├── rv32im_decoder.sv   # Generated — do not hand-edit
│   │   └── decode_out_t.svh      # Generated — do not hand-edit
│   ├── exu/                 # ALU, MUL, DIV, LSU
│   ├── include/             # global.svh, types.svh, axi4.svh, gemm_csrs.svh, mmio_map.svh
│   └── lib/                 # Byte-write ICCM/DCCM (`sync_tdp_mem`)
├── fpga/alveo_u280/         # Alveo U280 bitstream, host load, card smoke
├── pd/                      # ASIC PD: sv2v + OpenROAD (`core_gemm_top`)
│   ├── rtl/core_gemm_top.sv # PD wrapper: core_top + gemm_top
│   ├── platforms/           # ASAP7 / sky130 YAML
│   └── README.md
├── dv/
│   ├── sv/                  # core_top_tb.sv, gemm_top_tb.sv, lsu_tb.sv
│   └── verilator/           # Verilator C++ harness
├── hw/                      # Hardware presets (scalar, VLIW, OoO + vector)
│   ├── presets/             # YAML configs shared by RTL/SW (see hw/README.md)
│   ├── soc/                 # SoC device map (UART, GEMM, EOT)
│   └── types.py             # Typed HwConfig loader
├── tests/
│   ├── asm/                 # Assembly test programs (incl. gemm_8x8)
│   ├── c/                   # C tests (helloworld, iaxpy, gemm_8x8, gemm_multi)
│   ├── elf/                 # Prebuilt ELF binaries (dhrystone)
│   ├── pyvedas/             # PyTorch → JIT model specs (incl. gemm_mmio)
│   ├── smoke.tlist          # Regression test list
│   └── gemm.tlist           # GEMM-only regression
├── pyvedas/                 # PyTorch → Tiny-Vedas JIT
├── tools/
│   ├── sim_manager.py       # Main test runner (compile → ISS → RTL → compare)
│   ├── rv_iss.py            # Reference instruction-set simulator
│   └── gemm_cosim.py        # Directed / random GEMM co-simulation
├── sw/
│   ├── include/             # soc_defines.h (generated — do not hand-edit)
│   └── vedas_printf/        # Bare-metal printf library for C tests
├── SVLib/                   # Git submodule — reusable SystemVerilog primitives
├── open-decode-tables/      # Git submodule — YAML decode table generator
├── scripts/
│   ├── install_deps.sh      # Dependency installer (`make deps`)
│   ├── env.sh               # Generated PATH + venv (by `make deps`)
│   ├── with_env.sh          # Wrapper used by Makefile targets
│   └── pd_docker.sh         # OpenROAD Docker wrapper for rtl2gds
├── .github/workflows/ci.yml # GitHub Actions CI pipeline
├── Makefile
├── requirements.txt
└── LICENSE

Prerequisites

ToolPurpose
VerilatorRTL simulation (primary; used in CI)
riscv64-unknown-elf-gccBare-metal cross-compiler for test programs (RV32IM / ILP32)
Python 3sim_manager.py, rv_iss.py, decode generation
Xilinx Vivado (optional)XSim simulation — only needed if you prefer make smoke over Verilator

Tested on Ubuntu 22.04 and 24.04. Other Linux distributions should work with equivalent packages installed manually.

Quick Start

1. Clone with submodules

git clone --recurse-submodules https://github.com/siliscale/Tiny-Vedas.git
cd Tiny-Vedas

If you already cloned without submodules:

git submodule update --init --recursive

2. Install dependencies

On Ubuntu, make deps installs everything needed for simulation and verification:

  • System build packages (build-essential, Verilator build deps)
  • Python virtual environment with packages from requirements.txt
  • Prebuilt RISC-V GNU bare-metal toolchain (riscv64-unknown-elf-gcc) into .local/riscv/
  • Latest stable Verilator compiled from source into .local/verilator/
make deps

make deps also generates scripts/env.sh (PATH + venv) and verifies the toolchain. All Makefile test targets use it automatically via scripts/with_env.sh, so CI and local runs work without manual setup.

For interactive shells, source the environment once per session:

source scripts/env.sh
riscv64-unknown-elf-gcc --version
verilator --version

Override pinned versions if needed:

RISCV_TOOLCHAIN_VERSION=2026.06.05 make deps   # default
VERILATOR_TAG=v5.048 make deps                   # pin a specific Verilator release
FORCE_RISCV_TOOLCHAIN_REINSTALL=1 make deps      # re-download toolchain
FORCE_VERILATOR_REBUILD=1 make deps              # rebuild Verilator

Do not run make deps with sudo — only the apt step needs elevated privileges. If a previous sudo make deps left deps/verilator root-owned, fix ownership then rebuild:

sudo chown -R "$USER:$USER" deps/verilator
FORCE_VERILATOR_REBUILD=1 make deps

3. Run the smoke regression

# Verilator (recommended; same as CI)
make smoke-verilator

# Xilinx XSim (requires Vivado — optional)
make smoke

4. Run a single test

./tools/sim_manager.py -s verilator -n asm.basic_alu_r
./tools/sim_manager.py -s verilator -n c.helloworld
./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n pyvedas.vector_add

RISC-V GNU Toolchain

Tiny Vedas compiles bare-metal test programs with riscv64-unknown-elf-gcc using -march=rv32im -mabi=ilp32. Do not use the Linux cross-compiler (riscv64-linux-gnu-gcc) or distribution packages that lack newlib — they will not produce working bare-metal ELFs.

make deps downloads a prebuilt riscv64-unknown-elf toolchain from the riscv-collab/riscv-gnu-toolchain releases page and installs it to .local/riscv/. The Ubuntu series (22.04 or 24.04) is detected automatically.

Manual install

  1. Go to riscv-gnu-toolchain releases.
  2. Download the riscv64-elf-ubuntu-<version>-gcc.tar.xz archive matching your Ubuntu version.
  3. Extract and add to your PATH:
# Example for Ubuntu 22.04, release 2026.06.05
wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2026.06.05/riscv64-elf-ubuntu-22.04-gcc.tar.xz
mkdir -p ~/.local
tar -xJf riscv64-elf-ubuntu-22.04-gcc.tar.xz -C ~/.local

# Add to ~/.bashrc
export PATH="$HOME/.local/riscv/bin:$PATH"
source ~/.bashrc
  1. Verify RV32IM support:
riscv64-unknown-elf-gcc --version
echo 'int main(void) { return 0; }' | riscv64-unknown-elf-gcc -march=rv32im -mabi=ilp32 -nostdlib -x c -

If prebuilt binaries are unavailable for your platform, follow the build instructions in the riscv-gnu-toolchain README. Configure for bare metal:

./configure --prefix=/opt/riscv --with-arch=rv32im --with-abi=ilp32
make -j$(nproc)

This takes a long time. Prefer the prebuilt nightly releases for development and CI.

Running Tests

All tests are driven by tools/sim_manager.py. Tests are named <type>.<name>:

PrefixSourceExample
asm.tests/asm/<name>.sasm.basic_mul
c.tests/c/<name>.cc.helloworld
elf.tests/elf/<name> (prebuilt)elf.dhrystone
pyvedas.tests/pyvedas/<name>.py (JIT → ELF)pyvedas.vector_add

sim_manager.py usage

./scripts/with_env.sh ./tools/sim_manager.py -s <simulator> (-n <test> | -t <task-list>)

  -s, --simulator   verilator | xsim
  -n, --test-name   Run a single test (e.g. asm.basic_alu_r)
  -t, --task-list   Run all tests listed in a file (e.g. tests/smoke.tlist)
  --hw-config       Hardware preset YAML (default: hw/presets/rv32im_scalar.yaml)
  --vcd             Verilator waveform (core_top.vcd); omit for smoke/CI

make smoke-verilator and make smoke invoke with_env.sh automatically.

Makefile targets

TargetCommand
make depsInstall system packages, Python venv, RISC-V toolchain, and Verilator
make smoke-verilatorRun the smoke regression via Verilator (CI default)
make smokeRun the smoke regression via XSim (requires Vivado)
make fpga alveo_u280Build the Alveo U280 bitstream (Vivado 2023.2)
make fpga_smoke alveo_u280Run tests/smoke.tlist on the programmed Alveo (needs sudo)
make gemm-directedDirected GEMM RTL vs golden (tools/gemm_cosim.py)
make gemm-cosimDirected + 100 random GEMM seeds
make rtl2gdsASIC PD: sv2v + OpenROAD (core_gemm_top; see pd/README.md)
make decodesRegenerate rtl/idu/rv32im_decoder.sv from YAML
make socRegenerate mmio_map.svh and sw/include/soc_defines.h from hw/soc/
make cleanRemove build artifacts (work/, obj_dir/, logs, VCDs)

Per-test output

Each test writes artifacts to work/<test>/:

FileContents
iss.logGolden ISS execution trace
rtl.logRTL architectural trace
sim.logSimulator stdout and comparison errors
console.logProgram UART output
stats.txtIPC/CPI performance metrics
core_top.vcdWaveform (Verilator --vcd only; off by default)

Verification

Tiny Vedas uses co-simulation: a Python ISS generates a golden trace, the RTL simulator produces its own trace, and sim_manager.py compares them instruction by instruction (PC, opcode, register writes, memory stores, branches).

Programs signal completion by storing EOT_MAGIC (0xdeadbeef) to MMIO_EOT_ADDR (0x10000000). See tests/asm/eot_sequence.s and sw/include/soc_defines.h.

Arithmetic units

Multiply (rtl/exu/exu_mul.sv → SVLib mul)

The multiply unit is a Booth-encoded 32×32 multiplier. Operands enter at EXU stage e2; the 64-bit product is registered at e3 and written when sideband latency (MUL_LAT) expires.

RV32M instructionrs1 signrs2 sign
MUL, MULHsignedsigned
MULHUunsignedunsigned
MULHSUsignedunsigned

Inside mul, the signed operand is always the multiplicand and the unsigned operand is Booth-scanned as the multiplier. When rs1 is unsigned and rs2 is signed, operands are swapped (product is commutative). Separate controls drive multiplicand sign extension (mc_sign) and unsigned-multiplier correction (mult_unsign); a single global unsigned flag is not sufficient for MULHSU.

Pipeline placement is configured in rtl/include/mul_pd_config.svh (included by exu_mul). At most one internal register stage should be enabled for PD experiments — see pd/README.md.

Final CPA (CPA_ALGORITHM on SVLib mul):

ValueModuleNotes
0adder_pipe + RCAPIPE_STAGES_CPA splits width
1adder_pipe + 4-bit CLADefault for generic builds
2kogge_stone_pipe2-cycle CPA, one flop mid prefix tree; production exu_mul uses this

Divide (rtl/exu/div.sv)

PathWhenLatency
FastDivide by zero/one, zero dividend, signed overflow, or both magnitudes ≤4 bits (small_div)1 cycle after issue
SlowEverything else — 32-step non-restoring divider on absolute magnitudes~33 cycles

The slow path uses combinational kogge_stone_adder instances for the per-iteration trial add/subtract and remainder correction. Do not use kogge_stone_pipe here — that module has a pipeline register and is reserved for the multiplier CPA.

SVLib adders (SVLib/src/arith/)

ModuleRegistersUse
adderNoGeneric wrapper: ALGORITHM 0=RCA, 1=CLA, 2=Kogge-Stone (comb.)
kogge_stone_adderNoCombinational Kogge-Stone prefix adder (power-of-2 width)
kogge_stone_pipeOnePipelined Kogge-Stone (prefix tree split across two cycles)
adder_pipeOptionalMulti-lane pipelined CPA for non-Kogge multiplier configs

See SVLib/README.md for the full library inventory.

Smoke regression (tests/smoke.tlist)

Smoke tests cover ALU, forwarding, multiply, divide (asm.basic_div, asm.div_regression), load/store, branches, jumps, C programs, PyVedas JIT tests (pyvedas.{vector,matrix,tensor}_{add,mul}), GEMM (asm.gemm_8x8, c.gemm_8x8, c.gemm_multi, pyvedas.gemm_mmio), and Dhrystone. tests/gemm.tlist runs the GEMM subset alone.

Memory Map

Processor memories

MemoryDepthWidthNotes
ICCM (instructions)2^18 words32-bitLoaded from ELF .text section
DCCM (data)2^18 words32-bitDual RW ports (byte strobes); loaded from .data, .rodata, .bss, etc.

Configured in rtl/include/global.svh. The Alveo overlay uses smaller windows (32 KiB ICCM / 1 MiB DCCM, BAR2 2 MiB); see fpga/alveo_u280/README.md. UART (0x00200000), GEMM (0x00300000), and EOT (0x10000000) writes are decoded on the core store path and do not enter DCCM as MMIO. Addresses come from hw/soc/default.yaml; software uses generated sw/include/soc_defines.h.

Software-visible addresses

AddressPurpose
SOC_LINK_ADDRESS (0x00100000)Default link address for test programs (-Wl,-Ttext=0x100000)
MMIO_UART_ADDR (0x00200000)MMIO UART — bare-metal printf output (sw/vedas_printf)
MMIO_GEMM_ADDR (0x00300000)GEMM CSRs (BASE_A/B/C, M/N/K, CTRL, STATUS) — see rtl/include/gemm_csrs.svh
MMIO_EOT_ADDR (0x10000000)End-of-test flag — write EOT_MAGIC to halt simulation
0x80000000Default initial stack pointer (register x2)

The reset vector is taken from the ELF _start symbol, not hardcoded.

Decode Table Generation

Instruction decode logic is generated from YAML, not hand-written. The source of truth is open-decode-tables/tables/rv32im.yaml.

make decodes

This regenerates:

  • rtl/idu/rv32im_decoder.sv
  • rtl/idu/decode_out_t.svh

To add or modify instructions, edit the YAML in the open-decode-tables submodule, commit and push there, then update the submodule pointer in this repo and run make decodes.

SoC device map

MMIO devices (UART, GEMM, EOT) are described in hw/soc/default.yaml, not hardcoded in RTL or C. CPU presets select the map with soc: default.

make soc

This regenerates:

  • rtl/include/mmio_map.svh — address ranges and indices for rtl/bus/mmio_mux.sv
  • sw/include/soc_defines.h — C / preprocessed .S macros (MMIO_UART_ADDR, EOT_MAGIC, …)
  • sw/include/soc_defines.inc — gas .include for .s tests

sim_manager.py runs the same generation at the start of a test. To add a device, edit the YAML and re-run make soc. Bare-metal software includes soc_defines.h and uses the generated macros — see sw/vedas_printf/vedas_printf.c.

Writing Tests

Assembly test

Create tests/asm/my_test.s:

    .globl   _start
    .section .text

_start:
    li   x1, 42
    add  x2, x1, x1
    .include "eot_sequence.s"

Run with:

./tools/sim_manager.py -s verilator -n asm.my_test

C test

Create tests/c/my_test.c using vedas_printf for output. sim_manager.py compiles sw/vedas_printf/vedas_printf.c alongside the test with -march=rv32im -mabi=ilp32 -nostdlib -lgcc (required by the prebuilt bare-metal toolchain). The end-of-test sequence comes from tests/c/asm_functions/eot_sequence.s.

Python test (PyVedas)

PyVedas tests are model spec files under tests/pyvedas/. Each file describes a small torch.compile module and concrete trace inputs. sim_manager.py JIT-compiles the model to C, links it with the PyVedas runtime, builds an RV32 ELF, and runs the usual ISS/RTL comparison.

Prerequisites: run make deps once — it installs CPU PyTorch into the repo venv/ (used automatically by sim_manager.py). For JIT-only debugging you can also use pyvedas/.venv; see pyvedas/README.md.

Create tests/pyvedas/my_add.py:

"""PyVedas smoke test: elementwise add."""

import torch


class MyAdd(torch.nn.Module):
    def forward(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
        return x + y


MODEL = torch.compile(MyAdd())
TRACE_INPUTS = (
    torch.tensor([1, 2, 3, 4], dtype=torch.int32),
    torch.tensor([10, 20, 30, 40], dtype=torch.int32),
)
SymbolPurpose
MODELtorch.compile module exported by the JIT
TRACE_INPUTSTuple of concrete tensors — used for torch.export tracing and to bake static buffer values into generated.c

Constraints today

  • Use torch.int32 tensors (bare-metal target has no soft-float).
  • Every graph op must have a 1:1 entry in pyvedas/runtime/ops.yaml with a matching C kernel (e.g. aten.add.Tensor, aten.mul.Tensor). Adding a new op requires a registry entry and runtime implementation — see pyvedas/README.md.

Run with:

./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n pyvedas.my_add

Add the test name to tests/smoke.tlist to include it in make smoke-verilator:

pyvedas.my_add

What happens under the hood

  1. JIT (pyvedas/jit) exports the graph and writes work/pyvedas.my_add/generated.c, graph.txt, and manifest.json.
  2. The RISC-V linker builds test.elf from generated.c, runtime sources from the manifest, and eot_sequence.s.
  3. ISS and Verilator traces are compared like any other test.

Inspect JIT output on failure: work/pyvedas.my_add/jit.log, compile.log, sim.log.

The core and GEMM are synthesizable. Simulation and FPGA SoCs sit outside core_top: AXI4 adapters, ICCM/DCCM slaves, and gemm_top (rtl/bus/, rtl/accel/, rtl/soc_top.sv, fpga/alveo_u280/rtl/). ASIC PD nets core_gemm_top (CPU + GEMM, memories as IOs) — see pd/README.md. FPGA build/program/smoke:

make fpga alveo_u280          # bitstream (Vivado 2023.2)
make fpga_smoke alveo_u280    # PCIe load + EOT on the card (sudo)

For ASIC physical design (SystemVerilog → Verilog via sv2v, then OpenROAD-flow-scripts), see pd/README.md:

make config                    # CPU flavor + PDK platform
make sv2v                      # convert RTL only
make rtl2gds                   # sv2v + synthesis/place/route/GDS
make rtl2gds ORFS_TARGET=synth # stop after synthesis

# Docker (ORFS image; used when host Yosys/OpenROAD is missing)
ORFS_TARGET=all PD_PLATFORM=ci-asap7 ./scripts/pd_docker.sh make rtl2gds
make decodes   # ensure decoder is up to date before synthesis
make soc       # ensure MMIO map + soc_defines.h match hw/soc/

Performance Scoreboard

From RTL simulation (see work/<test>/stats.txt after a run):

BenchmarkInstructionsCyclesIPC
c.helloworld76022930.3314
c.iaxpy1092350.4638
elf.dhrystone64072012743370.5028

On Alveo U280 (100 MHz core, host-timed EOT) elf.dhrystone is ~12.9 ms for 2000 runs → ~155k dps / ~88.5 DMIPS (~0.89 DMIPS/MHz). That matches the sim cycle count (1.274M cycles ≈ 12.7 ms at 100 MHz).

Submodules

SubmoduleRepositoryPurpose
SVLibsiliscale/SVLibRegisters, program counter, arithmetic primitives
open-decode-tablessiliscale/open-decode-tablesYAML → SystemVerilog decode generator

After pulling submodule updates:

git submodule update --init --recursive
make decodes
make soc

Continuous Integration

GitHub Actions runs on every push and pull request to main. The workflow (.github/workflows/ci.yml) mirrors a from-scratch developer setup:

  1. Checkout with submodules
  2. make deps — system packages, Python venv, RISC-V toolchain, Verilator, scripts/env.sh
  3. make decodes — regenerate the instruction decoder
  4. make soc — regenerate the MMIO map and soc_defines.h
  5. make smoke-verilator — full smoke regression (tests/smoke.tlist)

No Vivado license is required. make deps writes scripts/env.sh; subsequent make targets load it automatically — no manual PATH or source venv/bin/activate in CI.

If CI fails, check the job log for the failing test name, then reproduce locally with:

make deps   # if not already done
./scripts/with_env.sh ./tools/sim_manager.py -s verilator -n <test.name>

Contributing

This repository is developed and maintained by Siliscale. We do not accept external contributions — please do not open pull requests or submit patches.

The project is open source under the Apache License 2.0; you are free to use, study, and fork it for your own work. For collaboration, partnerships, or commercial engagement, see Business inquiries.

Business inquiries

For partnerships, consulting, custom accelerator work, or commercial licensing questions, contact marco@siliscale.com.

License

Apache License 2.0 — see LICENSE.

  • NOTICE — attribution for this repo and bundled submodules
  • THIRD_PARTY.md — dev-only tools vs shipped components

SPDX: Apache-2.0

Contributors

spzbrnmrc

100 commits

Languages

SystemVerilog

53.6%

Python

31.9%

Tcl

3.7%

C

3.1%

Shell

2.7%

Assembly

2.6%

Makefile

1.1%