KILN

A Cargo-style CLI for SystemVerilog

tl;dr

Install

kiln itself only needs a Rust toolchain. The runtime tools (Slang, Verilator, etc.) are installed separately via kiln install-tools.

# macOS and Linux (no Rust required)
curl -fsSL https://raw.githubusercontent.com/tejasprabhune/kiln/main/install.sh | sh

# or via cargo
cargo install kiln-sv

# install the runtime tools (slang, verilator, verible, surfer, bender)
kiln install-tools

# or build slang and verilator from source (needs cmake + C++17)
kiln install-tools --build-from-source

New project

kiln new counter
cd counter
kiln check    # elaboration via slang
kiln build    # compile via verilator
kiln test     # run testbenches under tests/

Migrate an existing project

Run kiln init in the project root. It creates a Kiln.toml and a .gitignore entry for build artifacts. Then point [design] at your existing source tree:

cd my_existing_project
kiln init

# edit Kiln.toml: set top module and adjust sources glob
[package]
name    = "my_design"
version = "0.1.0"

[design]
top     = "my_top"
sources = ["rtl/**/*.sv", "rtl/**/*.v"]

If you have existing bender or git dependencies, add them under [dependencies] — see the Dependencies section.

Editor setup

kiln lsp wraps slang-server, giving you diagnostics, hover, go-to-definition (including into dependencies), completions, references, and inlay hints. Install it first:

kiln install-tools --tools slang-server

The binary lands at ~/.local/share/kiln/bin/. Make sure that's on your PATH, or set KILN_SLANG_SERVER_PATH to override discovery.

Neovim ≥ 0.11

-- ~/.config/nvim/lsp/kiln.lua
return {
  cmd          = { 'kiln', 'lsp' },
  filetypes    = { 'systemverilog', 'verilog' },
  root_markers = { 'Kiln.toml' },
}
-- ~/.config/nvim/init.lua
vim.lsp.enable('kiln')

Open any .sv file inside a project. Neovim launches kiln lsp from the project root and diagnostics appear within a second. To restart after editing Kiln.toml: :LspRestart.

Neovim with nvim-lspconfig

require('lspconfig.configs').kiln = {
  default_config = {
    cmd       = { 'kiln', 'lsp' },
    filetypes = { 'systemverilog', 'verilog' },
    root_dir  = require('lspconfig.util').root_pattern('Kiln.toml'),
  },
}
require('lspconfig').kiln.setup {}

Autoformat on save (Neovim)

kiln fmt wraps Verible. Wire it as a format command so your editor calls it on save:

-- format .sv files with kiln fmt on save
vim.api.nvim_create_autocmd('BufWritePost', {
  pattern  = { '*.sv', '*.svh', '*.v' },
  callback = function()
    local file = vim.fn.expand('%:p')
    vim.fn.jobstart({ 'kiln', 'fmt', file }, {
      on_exit = function() vim.cmd('edit') end,
    })
  end,
})

Or use kiln fmt --check in CI to fail on unformatted files without modifying anything.

Build, test, and waveforms

Check and build

kiln check           # fast: slang elaboration only, no simulation
kiln build           # compile with verilator (debug profile)
kiln build --release
kiln run             # build + execute the simulator binary
kiln clean           # remove target/kiln/

Builds are content-hash cached. A recompile only happens when source files, defines, or the build profile change.

Tests

Place testbenches under tests/. Each .sv file is a test; its filename stem is the top module name. A test passes when it prints PASS to stdout and exits 0.

kiln test                  # run all tests in parallel
kiln test smoke            # substring filter
kiln test --jobs 1         # serial
kiln test --fail-fast      # stop on first failure
kiln test --list           # print test names and exit

Waveforms

kiln test --trace          # dump FST files to target/kiln/waves/
kiln wave                  # open the most recent FST in surfer
kiln wave smoke            # open a specific test's FST
kiln wave --print-path     # print the path without opening surfer

Dependencies

kiln manages dependencies via Bender. Resolved versions are locked in Kiln.lock, which should be committed.

# add a git dependency
kiln add axi --git https://github.com/pulp-platform/axi.git --version 0.39

# add a path dependency
kiln add local_ip --path ../local_ip

# remove a dependency
kiln remove axi

# update all deps to the latest matching versions
kiln update

# print the dependency graph
kiln tree

Dependency sources are picked up automatically by kiln build, kiln check, and kiln lsp — no manual include path management.

Kiln.toml

A minimal manifest. The only required fields are name, version, and top.

[package]
name        = "counter"
version     = "0.1.0"
description = "A simple counter"

[design]
top          = "counter"
sources      = ["src/**/*.sv"]     # default: src/**/*.sv + .v + .svh
include_dirs = ["include"]         # passed as -I to slang and verilator
defines      = { SIMULATION = "" } # +define+ flags

[dependencies]
axi = { git = "https://github.com/pulp-platform/axi.git", version = "0.39" }

[lint]
width-trunc  = "error"   # promote to error
unused-net   = "warn"    # emit as warning
implicit-net = "allow"   # suppress entirely

Lint rule keys are slang's optionName strings. Unknown keys anywhere in the file are rejected to catch typos early.