A Cargo-style CLI for SystemVerilog
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
kiln new counter
cd counter
kiln check # elaboration via slang
kiln build # compile via verilator
kiln test # run testbenches under tests/
kiln doctor # env probe + project sanity checks
kiln env prints every external tool kiln drives, the
version it found, and where it lives on PATH. kiln doctor
runs the same probe and additionally validates your manifest, lockfile,
vendor source globs, and firmware paths. Useful for filing bug reports
and diagnosing CI environments.
kiln env # tools + versions only
kiln doctor # tools + project-level health
kiln watch
kiln watch watches the project tree (excluding
target/ and .git/) and re-runs a
subcommand on every relevant change, debounced 200 ms so a single
editor save fires once. It runs the subcommand once immediately so
you see current state without waiting for an edit.
kiln watch check # fast slang elaboration on every save
kiln watch test # full test sweep
kiln watch test alu mul # filter to substring matches
kiln watch build # rebuild via verilator
kiln watch fmt # `kiln fmt --check` (CI gate)
--locked and --frozen
Two global flags gate dependency resolution against drift, mirroring
cargo and uv. --locked errors if a refresh would change
Kiln.lock; --frozen implies
--locked and additionally refuses to make any network
request during dependency resolution.
kiln test --locked # CI: fail if Kiln.lock drifted from Kiln.toml
kiln build --frozen # CI: also no network; use existing Kiln.lock as-is
kiln update --locked # no-op; verify the lockfile is current
kiln update --frozen # error: contradictory
Both flags compose with every plan-producing subcommand
(build, run, check,
test, doc, tree). They also
compose with --features and --profile.
kiln test --reporter junit writes
target/kiln/junit.xml in the de-facto Jenkins / GitLab
JUnit XML dialect and suppresses human output. GitHub Actions, GitLab
CI, Buildkite, Jenkins, and CircleCI all consume this format directly.
kiln test --reporter junit --locked
# target/kiln/junit.xml — one <testcase> per discovered test
Pass -v to also include captured stdout in the XML as
<system-out>. Failures and timeouts emit
<failure type="failure"> and
<failure type="timeout"> respectively, with the
captured stderr inside.
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.
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.
-- ~/.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.
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 {}
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.
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.
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
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
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.
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.