Lint triage
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).From its SKILL.md
npx -y skills add LNC0831/oh-my-fpga --skill lint-triageAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 16 stars16 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
16.1 KB, as published. Nobody here has run it
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_resethere surfaces reset-path methodology issues, not slack. - Pure CDC sign-off (metastability, gray-coding, handshake correctness).
check_cdc_lint/report_cdcare 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_drcon 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_infoandlist_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_infoshows no top, callset_top_moduleonly with a name the user gave or that is unambiguous fromlist_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_filesand (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 pollget_run_statusuntil complete (multi-minute operations must not block). - Then
open_synthesized_design. (An already-openopen_implemented_designalso 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_methodologythenget_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. Useread_file_linesto 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_severityto downgrade a check,disable_lint_rule, orwaive_lint_violationon 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_violationcall carries areasonthat explains why the construct is safe and intended — scoped withobjectsto 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 beforeupdate_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_waiversoutput, 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 <files>/<top> at synthesis stage:
0 unwaived violations, N justified waivers (fresh
get_lint_violationsoutput attached)." Never state "clean" without that fresh evidence; if blocked, say exactly what blocks it and what decision you need from the user.