| name | lint-triage |
| description | Run RTL lint / methodology checks and triage each violation into a real fix vs a justified, documented waiver. Use when the user says "lint my RTL", "code quality", "review my RTL", "check my Verilog/VHDL", "any latches / unintended latches", "clean up warnings before synthesis", "is my design lint-clean", or asks for a code-quality / methodology report on an FPGA design. Requires the SynthPilot MCP server (Xilinx Vivado running with the Tcl server connected on TCP:9999). Methodology-stage checks additionally require an open synthesized design (open_synthesized_design / open_implemented_design).
|
Lint Triage โ RTL Lint & Methodology Violation Triage
A playbook for driving SynthPilot's lint/methodology tools to find real RTL
problems, then sorting every violation into one of three buckets: fix it,
waive it (with a stated justification), or ask the human. The deliverable
is a triaged report, not a green number. A lint run that is "clean" because real
violations were silenced is a failure of this skill.
This skill follows one discipline above all: measure โ classify โ fix the
smallest safe thing โ re-measure. It never invents a tool, never downgrades a
severity to hide a problem, and never claims "lint clean" without fresh tool
output proving it on the current source.
When to use
- User asks to "lint my RTL", "review my RTL", "check code quality", "find
unintended latches", "clean up warnings", or "is this design lint-clean".
- Before synthesis sign-off, before a design review, or before handing RTL to a
teammate, to catch latches, incomplete sensitivity lists, width mismatches,
unconnected ports, blocking/non-blocking misuse, and methodology violations.
- After an RTL edit, to confirm a class of violations is actually gone (not just
moved or masked).
- To produce a defensible waiver list โ each waiver tied to an explicit reason โ
for items that are intentional and safe.
When NOT to use
- Timing closure (WNS/TNS negative, failing setup/hold). Lint is not timing.
Use the timing-closure skill.
check_async_reset here surfaces reset-path
methodology issues, not slack.
- Pure CDC sign-off (metastability, gray-coding, handshake correctness).
check_cdc_lint / report_cdc are a first screen only; deep CDC analysis is a
separate playbook. Lint-triage flags CDC violations and hands them off.
- Functional verification (does the logic compute the right answer). That is
simulation (
sim_*), not lint. Lint cannot tell you a correct-looking design
is functionally wrong.
- DRC / route sign-off (
report_drc on a placed/routed design). Different
stage, different tool.
- No project is open / Vivado is not reachable. Resolve Prerequisites first.
Prerequisites (verify first)
Do not run any check until these pass. If one fails, stop and report it โ do not
guess.
- MCP / Vivado reachable. Call
test_connection. If it fails, the Tcl
server inside Vivado is not connected on TCP:9999 โ report and stop.
- A project is open with sources. Call
get_project_info and
list_source_files. If no project or no RTL sources, stop and ask the user to
open the project / point at the files.
- Top module is set (needed for elaboration-stage checks). If
get_project_info shows no top, call set_top_module only with a name the
user gave or that is unambiguous from list_source_files; otherwise ask.
- Know which stage you can reach. Syntax-stage checks need only sources.
Methodology-stage checks (
run_full_lint_check, report_methodology,
get_lint_violations, check_latches, check_fsm, check_coding_style,
check_cdc_lint, check_simulation_mismatch) require an open synthesized
design. Plan to reach that stage in Step 3 โ do not call those tools before
the design is open or they will error.
Methodology (numbered, real tool calls)
Work the stages in order. Each stage is cheaper/earlier than the next; do not
skip ahead, because a syntax error makes every later check meaningless.
Step 1 โ Establish the file baseline
list_source_files and (if useful) check_compile_order. A wrong compile
order produces phantom "undefined module" / black-box noise downstream;
fix ordering before trusting any later result.
- Record the file set and top module. This is the scope every later claim is
bounded to ("lint-clean" means these files, this top, nothing more).
Step 2 โ Syntax-stage checks (no synthesis required)
Run the checks that work directly on source. These are fast and catch the errors
that would otherwise abort synthesis:
check_syntax (all sources) โ must be clean before anything else is trustworthy.
check_compile_order โ ordering / include problems.
check_black_box โ undefined modules (missing files, typo'd module names, or a
genuinely intended black box / external IP).
check_port_width_mismatch โ connection width mismatches at instantiation.
check_unconnected_ports โ dangling/unconnected ports.
If check_syntax reports errors, STOP the lint flow here. Classify and
surface the syntax errors (Step 5 table) and do not pretend later stages are
clean โ you cannot run them on broken source.
Step 3 โ Reach a synthesized design for methodology checks
Most high-value RTL lint (latches, FSM health, coding style, CDC, sim/synth
mismatch) requires an elaborated/synthesized netlist.
- If a synthesized design is not already open, run synthesis. For anything beyond
a tiny design prefer the async pattern:
run_synthesis_async, then poll
get_run_status until complete (multi-minute operations must not block).
- Then
open_synthesized_design. (An already-open open_implemented_design also
satisfies the prerequisite.)
- Also pull
get_synthesis_warnings โ synthesis warnings frequently are the
lint findings (inferred latch, truncation, dropped logic) and corroborate the
dedicated checks.
If synthesis fails, the failure itself is the top-priority violation: capture it,
classify it (Step 5), and stop โ there is nothing to lint on a netlist that
doesn't exist.
Step 4 โ Methodology / RTL-quality checks (on the open synth design)
Run the methodology suite and the targeted RTL checks:
run_full_lint_check โ full syntax + methodology pass; the broad sweep.
report_methodology then get_lint_violations โ the structured violation list
you will triage (each item has a check name / ID).
check_latches โ inferred latches, the single most important RTL lint
signal; an unintended latch is almost always a real bug.
check_fsm โ FSM encoding / unreachable-state / dead-state health.
check_coding_style โ synthesis-relevant coding issues.
check_cdc_lint โ first-screen clock-domain-crossing flags (hand deep CDC off).
check_simulation_mismatch โ constructs that simulate differently than they
synthesize (the worst class: it works in sim, fails in hardware).
For any violation you do not understand, pull detail with
report_lint_violation_detail(violation_id=...) before deciding what to do with
it. Never triage blind.
Step 5 โ Triage every violation
For each violation, read its detail and place it in exactly one bucket using
the table below. Do not batch-waive. Do not batch-ignore. One verdict per item,
each with a one-line reason.
Step 6 โ Apply the smallest safe change, one class at a time
- Fixes are recommended, not silently applied. Latches, width mismatches, and
sim/synth mismatches are RTL bugs โ describe the exact edit (which signal, which
always/process block, default assignment vs full case, blocking vs non-blocking)
and let the human approve before you touch source with
update_file /
replace_in_file. Use read_file_lines to ground the recommendation in the
real code.
- Waivers require a stated assumption. Only after the item is confirmed
intentional do you call
waive_lint_violation(check_name=..., reason="<why this is safe and intended>", objects=...).
The reason string is mandatory and must be a real justification, not "waived".
- Change one class per iteration (e.g. fix latches, OR waive a reviewed style
rule โ not both), so the re-measure attributes cause to effect.
Step 7 โ Re-measure (close the loop)
After each change class, re-run the relevant check on the current source/
netlist (re-synthesize if RTL changed) and diff the violation list. A fix is only
"done" when the violation it targeted is gone from fresh tool output and no new
violation appeared. Record before/after counts.
Classification / decision table
Map each violation to a cause and the smallest safe response. The default
verdict is FIX; WAIVE is allowed only with an explicit, true
justification; ASK when intent is unknown.
| Symptom / check | Likely cause | Smallest safe response | Default verdict |
|---|
check_latches finds inferred latch | Incomplete if/case or missing default in comb logic | Recommend full assignment (default value / else / default:); confirm with read_file_lines | FIX (RTL) |
check_port_width_mismatch | Instantiation port width โ signal width | Recommend resizing the signal/port; never widen blindly โ verify intent | FIX (RTL) / ASK |
check_unconnected_ports | Dangling port | If functionally required โ FIX (connect); if intentionally unused โ tie-off/objects waiver with reason | FIX / WAIVE |
check_black_box | Missing file, typo'd name, OR intended external IP/stub | If missing โ add file / fix check_compile_order; if intended โ WAIVE with reason | FIX / WAIVE |
check_simulation_mismatch | Init values, #delay, sensitivity-list, X-pessimism gaps | RTL fix โ these are silent hardware bugs | FIX (RTL) |
check_fsm unreachable/dead state | Unhandled case / wrong reset / dead code | Recommend handling or removing the state; do not waive a reachable hole | FIX (RTL) |
check_coding_style violation | Blocking/non-blocking misuse, mixed assigns, style rule | If functional risk โ FIX; if cosmetic & team-approved โ WAIVE with reason | FIX / WAIVE |
check_cdc_lint flag | Unsynchronized crossing / missing handshake | Hand to CDC sign-off; never waive a real crossing to clear lint | ASK / FIX |
report_methodology / get_lint_violations generic item | Vivado methodology rule (e.g. SYNTH-/TIMING- family) | Read report_lint_violation_detail; fix or justify per the item | depends โ triage |
| Rule is a known false positive for this design | Tool over-reports a safe pattern | WAIVE the specific check_name/objects with a reason naming why it's safe | WAIVE |
| Intent genuinely unknown | Can't tell if the construct is deliberate | Present the finding + options; hand back to human | ASK |
Iteration loop & STOP conditions
LOOP:
1. Run the relevant check(s) (Step 2 / Step 4)
2. get_lint_violations -> list
3. For each item: report_lint_violation_detail -> classify (table)
4. Apply ONE change class:
- FIX (recommend RTL edit, get approval, then update_file/replace_in_file)
- WAIVE (waive_lint_violation with a real reason) [reviewed items only]
5. Re-synthesize if RTL changed; re-run the check (Step 7)
6. Diff before/after violation list
-> repeat
STOP the loop when any of these is true:
- Clean: the targeted check returns zero violations on fresh output, and
every remaining item is an explicit, justified waiver in
list_waivers. This is
the only "lint clean" you may claim โ and only with the fresh output in hand.
- All remaining items need human judgment (ASK bucket): present options +
trade-offs and hand back. Do not thrash trying to auto-resolve intent.
- A fix would require unapproved RTL surgery beyond the smallest safe change
(architectural change, retiming logic, restructuring an FSM): surface the
recommendation and stop; do not silently apply it.
- No progress / oscillation: the same violation count persists or a "fix"
spawns new violations after two iterations. Stop, report the conflict, hand back.
- An upstream blocker reappears (syntax error, synthesis failure): drop back
to that stage; the lower stage owns the fix.
Safety rails (do not violate)
- No fake-pass. Never make the number green by hiding a real problem. Do
not call
set_lint_severity to downgrade a check, disable_lint_rule, or
waive_lint_violation on an item that could be a genuine bug just to clear it.
A latch, width mismatch, CDC crossing, or sim/synth mismatch is presumed real
until proven intentional.
- Waivers need a stated, true justification. Every
waive_lint_violation
call carries a reason that explains why the construct is safe and intended โ
scoped with objects to the specific cell/net where possible, never a blanket
waiver of an entire check. If you cannot articulate the justification, you may
not waive it; ask the user.
- Smallest safe change first. Prefer connecting a port, fixing compile order,
or scoping a waiver over rewriting RTL. Constraint/config/style fixes before
source edits; source edits before architectural changes.
- Recommend RTL edits; don't silently apply them. Show the exact change
(file, line range from
read_file_lines, before/after) and get approval before
update_file / replace_in_file. Latch and mismatch fixes especially must be
reviewed.
- Evidence before claims. "Lint clean" / "no latches" / "violation fixed"
REQUIRES fresh output from the relevant tool after the change, on the
current source/netlist. Never infer it from "I made the edit." If RTL changed,
re-synthesize before re-checking โ stale netlist results are not evidence.
- Stay in scope. Lint-triage flags timing/CDC/DRC items and hands them to the
right skill. It does not pretend a CDC waiver or a severity downgrade closes a
timing/CDC issue.
- Real tools only. Use only the SynthPilot tool names referenced here. If a
needed capability is missing, say so plainly rather than inventing a tool.
Output
Deliver a triage report, not a verdict-by-assertion:
- Scope โ files (
list_source_files) + top module + the stage reached
(syntax-only vs synthesized). Every claim is bounded to this.
- Violation summary โ counts by severity/check, before vs after, from
get_lint_violations / get_synthesis_warnings (fresh output, quoted/cited).
- Triage table โ one row per violation:
check / ID | location | cause | verdict (FIX / WAIVE / ASK) | action taken or recommended.
- Fixes applied โ only those the user approved; show before/after snippet
(
read_file_lines) and the re-measured result proving the violation cleared.
- Waivers โ the current
list_waivers output, each with its justification.
Flag any waiver whose reason is weak so the human can revisit it.
- Open items / hand-offs โ ASK-bucket items with options + trade-offs, and
anything routed to timing / CDC / DRC / simulation skills.
- Honest status line โ e.g. "Lint clean on / at synthesis stage:
0 unwaived violations, N justified waivers (fresh
get_lint_violations output
attached)." Never state "clean" without that fresh evidence; if blocked, say
exactly what blocks it and what decision you need from the user.