A Cargo-style CLI for SystemVerilog
[package]| Key | Type | Notes | |
|---|---|---|---|
name |
required | string | Valid SystemVerilog identifier: starts with a letter or _, followed by letters, digits, or _. |
version |
required | string | Semver string, e.g. "0.1.0". |
authors |
optional | list of strings | Free-form. E.g. ["Jane <jane@example.com>"]. |
description |
optional | string | Single-line description. |
license |
optional | string | SPDX expression, e.g. "MIT OR Apache-2.0". |
[design]| Key | Type | Default | |
|---|---|---|---|
top |
required | string | |
sources |
optional | list of glob strings | ["src/**/*.sv", "src/**/*.svh", "src/**/*.v"] |
timescale |
optional | string | none |
language |
optional | "sv2005" | "sv2009" | "sv2012" | "sv2017" | "sv2023" |
tool default |
include_dirs |
optional | list of paths | [] |
defines |
optional | string-to-string map | {} |
libraries |
optional | list of strings | [] |
test_sources |
optional | list of glob strings | [] (falls back to tests/*.sv) |
timescale passes --timescale 1ns/1ps to both slang and verilator —
useful for designs that mix files with and without `timescale directives.
language maps to --std (slang) and --default-language (verilator).
defines entries with an empty string become +define+FOO;
non-empty values become +define+FOO=bar.
include_dirs entries must exist on disk when loading an existing project.
[design]
top = "soc_top"
sources = ["rtl/**/*.sv", "ip/**/*.sv"]
timescale = "1ns/1ps"
language = "sv2017"
include_dirs = ["rtl/include", "ip/include"]
defines = { SIMULATION = "", WIDTH = "8" }
[dependencies]Each entry is a named dependency in one of three forms:
[dependencies]
# git dep with semver constraint (matched against repo tags)
axi = { git = "https://github.com/pulp-platform/axi.git", version = "0.39" }
# git dep pinned to a specific tag or commit SHA
common_cells = { git = "https://github.com/pulp-platform/common_cells.git", rev = "v1.32.0" }
# path dep relative to the project root (or absolute)
local_ip = { path = "../local_ip" }
Resolved versions are written to Kiln.lock, which should be committed.
Use kiln update to refresh it.
[lint]
The lint table has three layers. Canonical rules (top-level keys) are
kiln-defined names that map to equivalents in both slang and verilator.
[lint.slang] and [lint.verilator]
accept tool-native names and overlay on top of the canonical rules, winning on conflict.
Severity values: "error", "warn", "off", "deny".
[lint]
width-trunc = "error" # promote truncation warnings to errors
case-incomplete = "warn"
unused = "warn"
[lint.slang]
relax-enum-conversions = "off" # slang-only option
[lint.verilator]
GENUNNAMED = "warn" # verilator-only warning code
DECLFILENAME = "off"
If you're unsure of the name for a specific warning, kiln check
prints it in brackets after the message — e.g. warning: ... [width-trunc].
Run kiln lint list to see all known canonical rules and
kiln lint explain <name> for a description.
| Name | Default | What it covers |
|---|---|---|
width-trunc | warn | Implicit truncation when assigning a wider value to a narrower type. |
case-incomplete | warn | case statement does not cover all values and has no default. |
unused | warn | Net or variable declared but never driven or read. |
implicit-net | warn | Net used without an explicit declaration. |
port-coercion | warn | Type coercion at a port connection boundary. |
Rules not in this table must be specified under [lint.slang] or
[lint.verilator] using tool-native names.
For slang, any name accepted by slang -W<name> is valid;
run slang --help-diagnostics for the full list.
[tool.slang]| Key | Type | Default | |
|---|---|---|---|
path |
optional | path | found on PATH |
extra_args |
optional | list of strings | [] |
[tool.verilator]| Key | Type | Default | |
|---|---|---|---|
path |
optional | path | found on PATH |
threads |
optional | integer | none |
trace |
optional | false | "vcd" | "fst" |
false |
coverage |
optional | bool | false |
extra_args |
optional | list of strings | [] |
[tool.verible]| Key | Type | Default | |
|---|---|---|---|
path |
optional | path | found on PATH |
extra_args |
optional | list of strings | [] |
extra_args is a permanent escape hatch — not a deprecation target.
The typed fields cover the common cases; extra_args handles the rest.
At -v (verbose), kiln logs the fully resolved command line for each
subprocess invocation so you can verify what extra_args produces.
[profile.<name>]
Profiles override design, lint, and tool settings per build context.
Built-in names: dev (default for kiln build/check),
release (--release shorthand), test (default for kiln test).
Custom names are allowed.
Vec fields in profile overrides replace the base value (not append) — this
makes it possible to remove a flag in one profile that exists in another.
Map fields merge with the overlay winning on key conflicts.
[profile.release.tool.verilator]
extra_args = ["-O3"]
[profile.test.tool.verilator]
trace = "fst"
coverage = true
[profile.test.lint]
unused = "error"
Select a profile with --profile <name> or --release.
[wave]| Key | Type | Default | |
|---|---|---|---|
format |
optional | "fst" | "vcd" |
"fst" |
enabled_by_default |
optional | bool | false |
When enabled_by_default = true, every kiln test run
behaves as if --trace was passed. FST is smaller and faster than VCD;
prefer it unless your waveform viewer requires VCD.
[package]
name = "my_soc"
version = "0.1.0"
authors = ["Jane <jane@example.com>"]
description = "A parameterized SoC"
license = "MIT OR Apache-2.0"
[design]
top = "soc_top"
sources = ["rtl/**/*.sv"]
timescale = "1ns/1ps"
language = "sv2017"
include_dirs = ["rtl/include"]
defines = { SIMULATION = "", WIDTH = "8" }
[dependencies]
common_cells = { git = "https://github.com/pulp-platform/common_cells.git", version = "1.32" }
[lint]
width-trunc = "error"
case-incomplete = "warn"
[lint.slang]
relax-enum-conversions = "off"
[tool.slang]
extra_args = ["--allow-hierarchical-const"]
[tool.verilator]
threads = 4
trace = false
extra_args = ["--x-assign", "0"]
[profile.release.tool.verilator]
extra_args = ["-O3"]
[profile.test.tool.verilator]
trace = "fst"
coverage = true
[profile.test.lint]
unused = "error"
[wave]
format = "fst"
enabled_by_default = false