rtl-design · diff
v1.0.0 to v1.0.0
77 added, 4 removed. Audit A to A.
---
name: rtl-design
description: >
SystemVerilog RTL design — module planning, coding standards enforcement, lint
checking, CDC/RDC analysis, and synthesis readiness verification. Use when
writing, reviewing, or debugging RTL for ASIC or FPGA targets, or when
checking an existing RTL package for synthesis readiness.
version: 1.0.0
author: chuanseng-ng
license: MIT
allowed-tools: Read, Write, Bash
---
# Skill: RTL Design (SystemVerilog)
## Invocation
When this skill is loaded and a user presents an RTL design task, **do not
execute stages directly**. Immediately spawn the
`digital-chip-design-agents:rtl-design-orchestrator` agent and pass the full
user request and any available context to it. The orchestrator enforces the stage
sequence, loop-back rules, and sign-off criteria defined below.
Use the domain rules in this file only when the orchestrator reads this skill
mid-flow for stage-specific guidance, or when the user asks a targeted reference
question rather than requesting a full flow execution.
## Pre-run Context
Before executing or advising on **any** stage, read the following files if they exist:
1. `memory/rtl-design/knowledge.md` — known failure patterns, successful tool flags, PDK/tool quirks.
Incorporate its guidance into every stage decision. If absent, proceed without it.
2. `memory/rtl-design/run_state.md` — current run identity (`run_id`, `design_name`, `tool`,
`last_stage`). Use this to resume correctly after interruption. If absent, a new run
is starting; the orchestrator will create this file before the first stage.
This pre-run read applies whether this skill is loaded by a user or called by the
orchestrator mid-flow. It ensures the fix database is consulted before any diagnosis step.
## Purpose
Guide RTL development from module hierarchy planning through lint-clean,
CDC-clean, synthesis-ready RTL. Enforces industry-standard SystemVerilog
coding practices and produces a signed-off RTL package ready for simulation
and synthesis handoff.
---
## Supported EDA Tools
### Open-Source
- **Verilator** (`verilator --lint-only`) — fast lint and simulation
- - **Slang** (`slang`) — modern, standards-compliant SV parser and elaborator
+ - **Slang** (`slang`) — modern, standards-compliant SV parser and elaborator; lint with
+ `slang -Weverything --ignore-unknown-modules` (full elaboration — see `lint_check` rule 6)
- **Surelog** (`surelog`) — SystemVerilog pre-processor and front-end for Yosys
- **sv2v** (`sv2v`) — SystemVerilog-to-Verilog converter
- **Icarus Verilog** (`iverilog`) — Verilog/SV simulator for quick sanity checks
### Proprietary
- **Synopsys SpyGlass** (`spyglass`) — lint, CDC, RDC, and clock-domain analysis
- **Cadence JasperGold CDC** (`jg`) — formal CDC verification
- **Siemens Questa CDC** (`vsim`) — CDC analysis and sign-off
---
## Stage: module_planning
### Domain Rules
1. Top-down decomposition: start with top-level module, recurse to leaf cells
2. Each module: single clear responsibility (single responsibility principle)
3. Define all port lists before coding (direction, width, type)
4. Identify all clock domains per module; mark CDC crossings explicitly
5. Identify all reset domains; mark synchronous vs asynchronous
6. Parameterise widths and depths wherever possible
7. No logic in top-level integration modules — wiring only
8. Separate datapath and control into distinct sub-modules
### Output Required
- Module hierarchy tree
- Module descriptor (name, purpose, clock domain, ports, sub-modules) per module
- Interface/port list document
---
## Stage: rtl_coding
+ Scope: the rules in this stage govern synthesisable RTL. Testbenches (`*_tb.sv`, `tb_*.sv`,
+ anything under `tb/`), bind-only assertion files and simulation-only behavioural models are
+ out of scope — `initial`, `#delay` and blocking assignments are correct there, and the
+ functional-verification skill carries the coding rules for them.
+
### Domain Rules — General
1. Always use `logic` type (not wire/reg distinction)
2. All ports: explicitly typed and directioned
3. `default_nettype none` at top of every file
4. No latches: all always_comb blocks must have complete case and assignment coverage
5. No blocking assignments (=) in always_ff blocks
6. No non-blocking assignments (<=) in always_comb blocks
7. One always block per register or coherent register group
8. Reset all registers explicitly; synchronous reset preferred for ASIC
### Domain Rules — Naming Conventions
- Clocks: `clk_[domain]`
- Resets: `rst_n_[domain]` (active-low) or `rst_[domain]`
- Active-low: `signal_n` suffix
- Registered: `signal_q` suffix
- Next-state: `signal_d` suffix
- Parameters: `UPPER_SNAKE_CASE`
- Modules/Signals: `lower_snake_case`
+ - Port direction: `_i` / `_o` suffixes are permitted, not required
+ Precedence: these conventions are this suite's project standard for new RTL. When modifying
+ an existing file, match that file's conventions instead — one block in a different style is a
+ worse outcome than the deviation — and record the difference as informational, not as a
+ lint finding.
+
### Domain Rules — Synthesis Safety
1. No delays (#) in RTL — simulation only
- 2. No initial blocks in ASIC RTL
- 3. Use `unique case` with explicit don't-cares instead of casez/casex
+ 2. No `initial` blocks for logic in ASIC RTL. One exception: an elaboration-time parameter
+ assertion (`initial` + `$fatal` on an illegal parameter value), wrapped in
+ `// synthesis translate_off` / `// synthesis translate_on` so it is never read as
+ synthesised logic. A parameterised block must refuse to elaborate on a value it cannot
+ implement (e.g. a gray-coded FIFO at a non-power-of-two depth) rather than build broken logic
+ 3. No `casex`; `casez` only with a written justification; no `full_case` / `parallel_case`
+ pragmas. Write don't-care decodes as explicit `case` items
4. Flag any net with fanout > `design_state.constraints.timing.fanout_max` (default: 32) for buffering intent review
5. No combinational loops — will cause synthesis errors
6. Pipeline registers: clearly marked with `_q` suffix at each stage
+ 7. FSM case policy — pick one per FSM and state it in a comment:
+ - **Default:** plain `case` with a `default` arm that goes to a recovery or error state.
+ Illegal states are reachable in silicon (SEU, X during bring-up); the FSM must not wedge
+ - **Alternative:** `unique case` with no `default`, plus a concurrent assertion that covers
+ illegal-state recovery. Keeps the simulation/formal uniqueness check
+ - Never `unique case` together with `default`: `unique` asserts the illegal state cannot
+ occur, `default` exists to recover when it does (slang: `-Wcase-redundant-default`)
### Domain Rules — CDC
1. Two-FF synchroniser for every single-bit CDC crossing
2. Async FIFO for multi-bit CDC data paths
3. Gray-coded pointers for async FIFO crossing
4. Never sample asynchronous data directly in synchronous logic
+ ### Domain Rules — Scan Readiness
+ RTL that blocks scan is cheapest to fix here; the DFT flow can only report it after insertion.
+ 1. No RTL-generated clocks (`assign gclk = clk & en;`): flops behind one are unreachable in
+ scan mode. Use a flop enable, or a library clock-gate cell with a test-enable input (Power
+ Intent rule 5)
+ 2. Every asynchronous set/reset must be controllable from a primary input in test mode. A
+ reset derived from internal logic cannot be held inactive during scan shift — give it a
+ `scan_mode` bypass to the top-level reset
+ 3. No asynchronous set/reset generated from combinational logic
+ 4. No on-chip tri-state buses — contention is untestable; use a mux
+ 5. A deliberate latch (lockup latch, clock-gate cell internals) is instantiated as a library
+ cell and waived by instance name in `lint_waivers.csv`, never inferred from RTL
+
### Domain Rules — Power Intent (Clock Gating)
Apply these rules for every clock domain. Read `clock_power_budget` from the architecture
hand-off package. **For orchestrated Architecture → RTL runs, the `clock_power_budget` table
is a required handoff contract; if missing, treat as a handoff violation and abort with a
clear error directing the user to notify upstream packaging.** For non-orchestrated or local
RTL-only runs, classify domains using toggle-count estimates from Verilator simulation as
a fallback.
1. **High gating opportunity domains** (α < `design_state.constraints.power.activity_factors.default` (default: 0.15) from architecture, or toggle rate below that threshold from Verilator): insert an ICG cell (`CLKGATETST_X*` or technology-equivalent) at the outermost clock enable boundary. Do not rely on synthesis to infer clock gates — explicit ICG insertion at RTL is required.
2. **Moderate gating opportunity domains** (`activity_factors.default` ≤ α < `activity_factors.high` (defaults: 0.15–0.40)): insert ICG at the sub-block level for any register file or datapath wider than 32 bits.
3. **Always-on domains** (α ≥ `activity_factors.high` (default: 0.40), or documented as always-on in architecture hand-off):
no ICG required; add a `/* always-on: <reason> */` comment at the clock port declaration.
4. ICG enable signal: must be registered (setup-timing safe); combinational enable
is a lint error.
5. ICG cells: use only library-approved cells (`CLKGATETST_*` for testability with
scan-enable override); do not use behavioural `if (enable) clk_gated = clk` constructs.
6. After inserting ICGs, measure `clock_gating_coverage`:
`coverage = (register bits behind an ICG) / (total register bits in domain) × 100%`
Report this metric in the `rtl_signoff` output.
### Supported Tools for Power Intent
| Tool | Type | Use |
|------|------|-----|
| Verilator | Open-source | Toggle coverage → activity factor for gating classification |
| SpyGlass (Synopsys) | Proprietary | RTL power lint, missing ICG detection |
| VC Static (Synopsys) | Proprietary | Power-intent rule checking |
| Questa PowerPro (Siemens) | Proprietary | Formal power analysis |
### Output Required
- RTL source files (.sv) per module
- SVA assertion files per module
- Inline comments on all non-obvious logic
- `clock_gating_coverage` metric per domain (appended to sign-off record)
---
## Stage: lint_check
### Domain Rules
1. ERROR level (must fix): latches, incomplete sensitivity lists,
undriven outputs, multiply-driven signals, X-propagation sources
2. WARNING level (review): unused ports, truncated assignments,
bit-width mismatches, constant conditions
3. All waivers: must include signal name, rule ID, justification, approver
4. No ERROR-level waivers without architect approval
5. All waivers logged in `lint_waivers.csv`
+ 6. Slang must run full elaboration: `slang -Weverything --ignore-unknown-modules <files>`.
+ Never pass `--lint-only` — it skips elaboration and silently drops inferred-latch and
+ multiple-driver diagnostics, both ERROR level here, so a latch reports as clean. `-Wall` is
+ not a slang option. Verilator is unaffected: `verilator --lint-only -Wall` is correct
+ 7. Lint in filelist context: compile the block's filelist as one unit, then report findings
+ for the files written or changed. A file linted alone reports its submodules as unknown and
+ its cross-file widths as unchecked
+ 8. A module missing from the filelist (library cell, hard macro, black-boxed IP) is a stub.
+ Undriven or unused findings on nets that only a stub drives are not ERRORs; record them as
+ informational and name the stubbed module
+ 9. Every finding states its evidence. Tool-proven: quote the tool's message and rule name.
+ Reasoned without a tool run: label it `UNVERIFIED`. A clean lint run proves nothing about
+ CDC, reset sequencing, FSM reachability, protocol deadlock or arithmetic overflow
+ 10. Fixing a finding must not change what the module does. After each fix compare the set of
+ findings, not the count: a new ERROR is a regression — revert it; the same findings twice
+ running is no progress — escalate rather than spend the remaining iterations; a fix that
+ changes behaviour to silence a warning (narrowing a signal to stop a truncation warning
+ implements the truncation) is intent drift — revert and escalate
+ 11. Where a tool reports its own severities, map them onto the levels above so waivers still
+ apply: `BLOCKER` / `HIGH` → ERROR, `MEDIUM` → WARNING, `LOW` / `INFO` → informational
### QoR Metrics to Evaluate
- ERROR count: must be 0 before proceeding
- WARNING count: review all; waive with documented justification
- All RTL files checked (not just top-level)
### Output Required
- Lint report (per file, per rule)
- Waiver file
- Clean lint summary
---
## Stage: cdc_rdc_analysis
### CDC Rules
1. Every CDC crossing: approved synchroniser primitive
2. Single-bit control: 2-FF synchroniser minimum
3. Multi-bit data: async FIFO or handshake protocol
4. Pulse crossings: pulse stretcher + synchroniser
5. Zero CDC violations (unwaived) before proceeding
### RDC Rules
1. All reset domains explicitly defined in constraints
2. Reset de-assertion: synchronous to receiving clock domain
3. No combinational logic between reset sources
4. Retention registers: correct UPF annotation
### QoR Metrics to Evaluate
- CDC violations (unwaived): 0
- RDC violations (unwaived): 0
- All clock domains verified in tool constraints
### Output Required
- CDC/RDC report
- Synchroniser instance list
- Waiver file
---
## Stage: synth_check
### Domain Rules
1. Run synthesis at target frequency with typical corner
2. Check for unmapped cells (technology library gaps)
3. Identify critical paths — report to architect if WNS < −0.5 ns
4. Check area vs microarch estimate (< 120% acceptable)
5. Check for multi-driven nets or unresolved X
6. Flag high-fanout nets needing buffering strategy
7. Verify all clock definitions synthesise correctly
### QoR Metrics to Evaluate
- WNS at target frequency: > −0.5 ns acceptable at this stage (sign-off target: `design_state.constraints.timing.wns_ns_target`, default: 0)
- Area: < 120% of microarch estimate
- No unmapped cells
- No multi-driven nets
### Output Required
- Synthesis area report
- Timing report (critical paths)
- Recommendations for RTL fixes if needed
---
## Stage: rtl_signoff
### Sign-off Checklist
- [ ] All modules from planning implemented
- [ ] Lint: 0 errors, all warnings reviewed
- [ ] CDC: 0 unwaived violations
- [ ] RDC: 0 unwaived violations
- [ ] Synthesis check: WNS within acceptable range
- [ ] All ports connected in integration
- [ ] SVA assertions in place for key properties
- - [ ] Code review completed
+ - [ ] Code review completed; any CDC, reset or protocol conclusion not closed by a tool run is recorded as `UNVERIFIED`
- [ ] File list and compile order documented
- [ ] ICG cells inserted for all high/moderate gating opportunity domains
- [ ] Always-on domains annotated with `/* always-on: <reason> */`
- [ ] `clock_gating_coverage` ≥ `design_state.constraints.power.gating_coverage_pct_min`% for high-opportunity domains (default: 60%); reported in sign-off record
### Output Required
- RTL file package (all .sv files)
- File list (filelist.f)
- Compile order document
- Assertion library (.sva files)
- RTL sign-off record
+ - Unverified-claims list: every `UNVERIFIED` conclusion from lint and code review, one entry
+ per claim as `{module, category, claim}` with `category` one of `cdc`, `reset`, `fsm`,
+ `protocol`, `arithmetic`, `parameter`. Include one `fsm` entry for every FSM that uses
+ `unique case` without `default`, naming its recovery assertion. This list is the hand-off to
+ verification and formal — a claim left off it is a claim nobody downstream will check. An
+ empty list must be stated as empty, not omitted
---
## Constraint Validation
See `plugins/meta/skills/pipeline-orchestration/SKILL.md` §Constraints Schema for the authoritative schema and stage-entry validation rule.
**Required at entry (`module_planning`) — hard-fail if missing:**
- `constraints.clock.clk_mhz` — target frequency for synth_check timing evaluation
**Optional (schema defaults apply when absent):**
- `constraints.timing.fanout_max` (default: 32) — high-fanout threshold
- `constraints.timing.wns_ns_target` (default: 0) — WNS sign-off target
- `constraints.power.gating_coverage_pct_min` (default: 60%) — ICG coverage target
- `constraints.power.activity_factors` (defaults: `{default: 0.15, high: 0.40}`) — domain classification
---
## Memory
### Write on stage completion
After each stage completes (regardless of whether an orchestrator session is active),
write or overwrite one JSON record in `memory/rtl-design/experiences.jsonl` keyed by
`run_id`. This ensures data is persisted even if the flow is interrupted or called
without full orchestrator context.
Use `run_id` = `rtl-design_<YYYYMMDD>_<HHMMSS>` (set once at flow start; reuse on each
stage update). Set `signoff_achieved: false` until the final sign-off stage completes.
### Run state (write before first stage, update after each stage)
Write `memory/rtl-design/run_state.md` as the **first action** before launching any tool:
```markdown
run_id: rtl-design_<YYYYMMDD>_<HHMMSS>
design_name: <design>
tool: <primary tool>
start_time: <ISO-8601>
last_stage: null
```
Update `last_stage` to the completed stage name only after each stage finishes successfully. This file lets wakeup-loop prompts
and resumed sessions identify the correct run without relying on in-memory state.
Create the file and parent directories if they do not exist.
### Optional: claude-mem index
If `mcp__plugin_ecc_memory__add_observations` is available in this session, emit each
applied fix as an observation to entity `chip-design-rtl-design-fixes` after writing to
`experiences.jsonl`. Skip silently if the tool is absent — JSONL is the canonical record.
+
+ ### Optional: hdl-rtl-skill
+ If the `hdl-rtl-skill` skills (`rtl-style-guide`, `rtl-golden-templates`, `rtl-anti-patterns`,
+ `rtl-review-signoff`, `rtl-workflow`) are available in this session, use them alongside this
+ skill: its `rtl-lint` script as the slang runner at `lint_check` (it applies rules 6–8 and
+ reports the severities mapped by rule 11), its golden templates as the starting point at
+ `rtl_coding` for FIFOs, synchronisers, arbiters, FSMs and ready/valid stages, and its
+ anti-pattern catalogue during code review. The rules in this file are the project standard
+ its style guide defers to, so where the two differ — clock/reset naming, `_i`/`_o` — follow
+ this file. Skip silently if it is absent — every rule above stands on its own.