# Tiny-Vedas: a generic accelerator interface for RISC-V, from PyTorch op to GDS

> Source: <https://github.com/spzbrnmrc/Tiny-Vedas>
> Published: 2026-09-18 17:11:27+00:00

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/`](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/hw/README.md) 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](https://youtu.be/izPdo7n1uI).

| Layer | Role | 
|---|---|
| **RTL** | Synthesizable RISC-V cores, GEMM accelerator, and SoC integration ( `rtl/` ) | 
| **Verification** | Python ISS + RTL trace comparison ( `tools/rv_iss.py` ,`sim_manager.py` ); GEMM directed/random co-sim (`tools/gemm_cosim.py` ) | 
| **Decode** | YAML-driven instruction tables → SystemVerilog ( `open-decode-tables/` ) | 
| **Primitives** | Reusable arithmetic and register blocks ( `SVLib/` ) | 
| **Software** | Bare-metal runtime, printf, assembly/C/PyTorch tests | 
| **PyVedas** | `torch.compile` → C → RV32 ELF for on-core inference kernels | 
| **PD** | Optional ASIC flow: sv2v + OpenROAD ( `pd/` ) —`core_gemm_top` (CPU + GEMM) | 

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.

Tiny Vedas is built to support **multiple CPU organizations** behind one hardware-config contract. Presets in [`hw/presets/`](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/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.

- **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

- **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)

- 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).

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

| Item | Value | 
|---|---|
| Array | 8×8 output-stationary PEs | 
| Datatypes | `int8 × int8 → int32` accumulators | 
| K tiling | 32-element tiles, dual ping-pong A/B buffers | 
| DMA | 32-bit AXI4 INCR bursts (A along K, B along N, C int32 along N) | 
| Wait | Core `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`.

```
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
```

| Tool | Purpose | 
|---|---|
| **Verilator** | RTL simulation (primary; used in CI) | 
| **riscv64-unknown-elf-gcc** | Bare-metal cross-compiler for test programs (RV32IM / ILP32) | 
| **Python 3** | `sim_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.

```
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
```

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
# Verilator (recommended; same as CI)
make smoke-verilator

# Xilinx XSim (requires Vivado — optional)
make smoke
./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
```

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](https://github.com/riscv-collab/riscv-gnu-toolchain/releases) page and installs it to `.local/riscv/`. The Ubuntu series (22.04 or 24.04) is detected automatically.

1. Go to [riscv-gnu-toolchain releases](https://github.com/riscv-collab/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](https://github.com/riscv-collab/riscv-gnu-toolchain). 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.

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

| Prefix | Source | Example | 
|---|---|---|
| `asm.` | `tests/asm/<name>.s` | `asm.basic_mul` | 
| `c.` | `tests/c/<name>.c` | `c.helloworld` | 
| `elf.` | `tests/elf/<name>` (prebuilt) | `elf.dhrystone` | 
| `pyvedas.` | `tests/pyvedas/<name>.py` (JIT → ELF) | `pyvedas.vector_add` | 

```
./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.

| Target | Command | 
|---|---|
| `make deps` | Install system packages, Python venv, RISC-V toolchain, and Verilator | 
| `make smoke-verilator` | Run the smoke regression via Verilator (CI default) | 
| `make smoke` | Run the smoke regression via XSim (requires Vivado) | 
| `make fpga alveo_u280` | Build the Alveo U280 bitstream (Vivado 2023.2) | 
| `make fpga_smoke alveo_u280` | Run `tests/smoke.tlist` on the programmed Alveo (needs sudo) | 
| `make gemm-directed` | Directed GEMM RTL vs golden ( `tools/gemm_cosim.py` ) | 
| `make gemm-cosim` | Directed + 100 random GEMM seeds | 
| `make rtl2gds` | ASIC PD: sv2v + OpenROAD ( `core_gemm_top` ; see[pd/README.md](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/pd/README.md) ) | 
| `make decodes` | Regenerate `rtl/idu/rv32im_decoder.sv` from YAML | 
| `make soc` | Regenerate `mmio_map.svh` and`sw/include/soc_defines.h` from`hw/soc/` | 
| `make clean` | Remove build artifacts ( `work/` ,`obj_dir/` , logs, VCDs) | 

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

| File | Contents | 
|---|---|
| `iss.log` | Golden ISS execution trace | 
| `rtl.log` | RTL architectural trace | 
| `sim.log` | Simulator stdout and comparison errors | 
| `console.log` | Program UART output | 
| `stats.txt` | IPC/CPI performance metrics | 
| `core_top.vcd` | Waveform (Verilator `--vcd` only; off by default) | 

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`.

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 instruction | rs1 sign | rs2 sign | 
|---|---|---|
| MUL, MULH | signed | signed | 
| MULHU | unsigned | unsigned | 
| MULHSU | signed | unsigned | 

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](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/pd/README.md).

**Final CPA** (`CPA_ALGORITHM` on SVLib `mul`):

| Value | Module | Notes | 
|---|---|---|
| `0` | `adder_pipe` + RCA | `PIPE_STAGES_CPA` splits width | 
| `1` | `adder_pipe` + 4-bit CLA | Default for generic builds | 
| `2` | `kogge_stone_pipe` | 2-cycle CPA, **one flop** mid prefix tree; production`exu_mul` uses this | 

| Path | When | Latency | 
|---|---|---|
| **Fast** | Divide by zero/one, zero dividend, signed overflow, or both magnitudes ≤4 bits ( `small_div` ) | 1 cycle after issue | 
| **Slow** | Everything 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.

| Module | Registers | Use | 
|---|---|---|
| `adder` | No | Generic wrapper: `ALGORITHM` 0=RCA, 1=CLA, 2=Kogge-Stone (comb.) | 
| `kogge_stone_adder` | No | Combinational Kogge-Stone prefix adder (power-of-2 width) | 
| `kogge_stone_pipe` | One | Pipelined Kogge-Stone (prefix tree split across two cycles) | 
| `adder_pipe` | Optional | Multi-lane pipelined CPA for non-Kogge multiplier configs | 

See [SVLib/README.md](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/SVLib/README.md) for the full library inventory.

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 | Depth | Width | Notes | 
|---|---|---|---|
| ICCM (instructions) | 2^18 words | 32-bit | Loaded from ELF `.text` section | 
| DCCM (data) | 2^18 words | 32-bit | Dual 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](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/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`](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/hw/soc/default.yaml); software uses generated [`sw/include/soc_defines.h`](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/sw/include/soc_defines.h).

| Address | Purpose | 
|---|---|
| `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 | 
| `0x80000000` | Default initial stack pointer (register x2) | 

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

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`.

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`.

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
```

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`.

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](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/pyvedas/README.md).

Create `tests/pyvedas/my_add.py`:

``` python
"""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),
)
```

| Symbol | Purpose | 
|---|---|
| `MODEL` | `torch.compile` module exported by the JIT | 
| `TRACE_INPUTS` | Tuple 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](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/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](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/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](https://github.com/zachjs/sv2v), then OpenROAD-flow-scripts), see
[pd/README.md](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/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/
```

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

| Benchmark | Instructions | Cycles | IPC | 
|---|---|---|---|
| c.helloworld | 760 | 2293 | 0.3314 | 
| c.iaxpy | 109 | 235 | 0.4638 | 
| elf.dhrystone | 640720 | 1274337 | 0.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).

| Submodule | Repository | Purpose | 
|---|---|---|
| `SVLib` | [siliscale/SVLib](https://github.com/siliscale/SVLib) | Registers, program counter, arithmetic primitives | 
| `open-decode-tables` | [siliscale/open-decode-tables](https://github.com/siliscale/open-decode-tables) | YAML → SystemVerilog decode generator | 

After pulling submodule updates:

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

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>
```

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](mailto:marco@siliscale.com)**.

Apache License 2.0 — see [LICENSE](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/LICENSE).

- [NOTICE](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/NOTICE) — attribution for this repo and bundled submodules
- [THIRD_PARTY.md](https://github.com/spzbrnmrc/Tiny-Vedas/blob/main/THIRD_PARTY.md) — dev-only tools vs shipped components

SPDX: `Apache-2.0`
