KILN

A Cargo-style CLI for SystemVerilog

[package]

KeyTypeNotes
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]

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

Canonical lint rules

NameDefaultWhat it covers
width-truncwarnImplicit truncation when assigning a wider value to a narrower type.
case-incompletewarncase statement does not cover all values and has no default.
unusedwarnNet or variable declared but never driven or read.
implicit-netwarnNet used without an explicit declaration.
port-coercionwarnType 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]

KeyTypeDefault
path optional path found on PATH
extra_args optional list of strings []

[tool.verilator]

KeyTypeDefault
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]

KeyTypeDefault
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]

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

Complete example

[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