| name | vsynth |
| description | Synthesize a Verilog module or full IP with Vivado and report resource utilization, Fmax, and power |
| effort | medium |
| model | sonnet |
| allowed-tools | Read, Write, Bash, Grep, Glob |
Verilog Synthesizer
Overview
This guide defines the workflow for running Vivado out-of-context (OOC) synthesis on a single module or a full IP hierarchy and producing a structured synthesis report.
The input is a module or IP name. The outputs are:
- A Vivado TCL synthesis script in
synth/<module>/
- Raw Vivado report files (utilization, timing, power)
- A structured synthesis report
synth/<module>/synth_report.md
Prerequisites
| Tool | Purpose | Check |
|---|
| Vivado | Synthesis, optimization, and reporting | vivado -version |
Before running synthesis, verify Vivado is installed using the check command
above. If it is missing, stop and report that Vivado is unavailable — never
fabricate utilization, timing, power, or Fmax results. Steps 1–4 (scope,
filelist, script generation) can still be done without the tool, but Steps 5–7
must not be reported as completed unless synthesis actually ran. (When invoked
under vflow, this surfaces as ✗ BLOCKED (tool unavailable: vivado) for the
Synthesis phase.)
Arguments
The skill takes a module or IP name. Optional overrides may follow as
key=value pairs:
part=<vivado_part> — target device (default xczu7ev-ffvc1156-2-e)
period=<ns> — clock constraint period in ns (default 2.800)
Example: vsynth foo_top part=xc7a100t-csg324-1 period=5.0. When no overrides
are given, use the defaults in Step 4.
Before You Start — Re-run Safety
Check whether synth/<module>/synth_report.md already exists:
| State | Detect | Action |
|---|
| First run | no synth/<module>/synth_report.md | Generate the script and run synthesis normally. |
| Already synthesized | report exists | Do not silently re-synthesize. Re-run only if the user explicitly asks, an argument override changed (part/period), or an RTL source in the hierarchy is newer than the report (find <sources> -newer synth/<module>/synth_report.md). Otherwise report the existing result and stop. When you do re-run, overwrite the prior outputs in place rather than accumulating stale files. |
Step 1 - Identify Synthesis Scope
Determine whether the target is a single module or a full IP:
- Single module: the named module has no submodules in
./rtl/. Its only dependencies are library modules from ./lib/.
- Full IP: the named module instantiates other modules from
./rtl/. Traverse the full hierarchy.
Read the module documentation from ./doc/<module>.md or ./lib/<module>/<module>.md to understand the hierarchy. Then read the top-level Verilog file to extract the port list and identify instantiated submodules.
Step 2 - Build Source Filelist
Collect all required Verilog files by walking the instantiation hierarchy from
the top module, following the shared procedure in
.claude/skills/shared/HierarchyFilelist.md.
Record the complete filelist. Express all paths relative to synth/<module>/
using ../../ prefixes.
Example:
./rtl/foo_top.v → ../../rtl/foo_top.v
./lib/fifo/fifo.v → ../../lib/fifo/fifo.v
Split the list into two groups for the script:
- Design files — the top module and project RTL from
./rtl/. Read these
with plain read_verilog so they are always elaborated.
- Library files — reusable modules from
./lib/. Read these with
read_verilog -v (Vivado treats -v files as on-demand library files,
elaborated only when referenced). Do not list the top module under -v —
a -v file is pulled in only if instantiated, which makes the top resolution
fragile.
If the whole target is a single library module (no ./rtl/ design files), read
it as a design file with plain read_verilog.
Step 3 - Detect Clock Ports
Scan the top-level module port list for clock signals (ports named clk or matching *_clk).
- One clock found: use it for the timing constraint.
- Multiple clocks found: ask the user which one is the primary constraint clock. Apply
create_clock to each clock port at the same period.
- No clock found: the module is combinational — skip
create_clock and report_timing_summary in Step 4. Skip the Timing Summary section in Step 7.
Step 4 - Generate Synthesis Script
Create the output directory synth/<module>/ and write synth/<module>/vsynth.tcl.
Default parameters (override with user-provided arguments if given):
- Part:
xczu7ev-ffvc1156-2-e
- Clock period:
2.800 ns
TCL script template:
# vsynth.tcl — generated by vsynth
# Module : <module>
# Part : <part>
# Clock : <period> ns
# ── Sources ────────────────────────────────────────────────────────────────────
# Design files (./rtl/) — always elaborated
read_verilog [list \
<rel_path_design1> \
<rel_path_design2> \
]
# Library files (./lib/) — on-demand library elaboration
read_verilog -v [list \
<rel_path_lib1> \
<rel_path_lib2> \
]
# ── Synthesis ──────────────────────────────────────────────────────────────────
synth_design -top <top_module> -part <part> -mode out_of_context
# ── Timing constraints ─────────────────────────────────────────────────────────
create_clock -period <period> -name <clock_name> [get_ports <clock_port>]
# ── Optimization ───────────────────────────────────────────────────────────────
opt_design
# ── Reports ────────────────────────────────────────────────────────────────────
report_utilization -file utilization.rpt -hierarchical
report_timing_summary -file timing_summary.rpt -max_paths 100 -warn_on_violation
report_power -file power.rpt
For combinational modules (no clock), omit the create_clock block and the report_timing_summary command. When the hierarchy has no ./lib/ modules, omit the read_verilog -v block.
Step 5 - Run Synthesis
Run Vivado in batch mode from inside synth/<module>/:
cd synth/<module>
vivado -mode batch -nolog -nojournal -source vsynth.tcl 2>&1 | tee vivado_run.log
Review vivado_run.log with grep rather than reading it whole — this keeps the
counts accurate and the context small:
-
Errors: stop the flow if any error is present.
grep -E '^(ERROR|CRITICAL WARNING):' vivado_run.log
If this matches, stop. Fix the TCL script or filelist, then re-run. Do not
proceed to Step 6 with a failed run.
-
Warnings: get the total count and a by-code summary; do not abort on
warnings unless they invalidate results.
grep -cE '^WARNING:' vivado_run.log
grep -E '^WARNING:' vivado_run.log \
| grep -oE '\[[A-Za-z]+ [0-9]+-[0-9]+\]' | sort | uniq -c | sort -rn
Put the by-code table and the total in the report's Synthesis Warnings
section. Then quote the actual lines for inference-related codes (latch
inferred, multi-driven net, unconnected/undriven port), e.g.:
grep -E '^WARNING:.*\[Synth 8-327\]' vivado_run.log
Step 6 - Parse Reports and Compute Fmax
After a successful run, parse the three report files.
Utilization — utilization.rpt
Locate the first utilization summary table ("Slice Logic", "Block RAM / FIFO", "DSP"). Extract:
| Resource | Used | Available | Utilization % |
|---|
| LUT | | | |
| FF | | | |
| BRAM | | | |
| DSP | | | |
Timing — timing_summary.rpt
Locate the "Design Timing Summary" section. Extract WNS and TNS values.
Compute Fmax using the constraint clock period and the WNS:
Fmax (MHz) = 1000.0 / (clock_period_ns - WNS_ns)
Where WNS_ns is negative when timing is violated. Report timing status as PASS if WNS ≥ 0, FAIL otherwise.
This Fmax reflects only the named constraint clock's critical path. If the design
has multiple clocks (Step 3), note in the report that Fmax applies to the
constrained clock and that other clock domains are not bounded by this number.
Power — power.rpt
Extract total on-chip power broken into Dynamic and Static components (mW or W; normalize to mW).
Step 7 - Write Synthesis Report
Write synth/<module>/synth_report.md using the template below.
# Synthesis Report: <module>
## Configuration
| Field | Value |
|------------------|--------------------------|
| Scope | Single module / Full IP (<N> submodules) |
| Target Part | <part> |
| Clock Constraint | <period> ns (<freq> MHz) |
| Synthesis Mode | Out-of-Context |
| Date | <date> (current date) |
## Resource Utilization
| Resource | Used | Available | Utilization |
|----------|------|-----------|-------------|
| LUT | | | % |
| FF | | | % |
| BRAM | | | % |
| DSP | | | % |
## Timing Summary
| Metric | Value |
|--------------|-----------|
| Clock Period | <period> ns |
| WNS | <wns> ns |
| TNS | <tns> ns |
| **Fmax** | **<fmax> MHz** |
| Status | PASS / FAIL |
## Power Estimate
| Domain | Power |
|-------------|--------|
| Dynamic | mW |
| Static | mW |
| **Total** | **mW** |
## Synthesis Warnings
Total: <N> warnings (or "None" if clean).
By message code:
| Count | Code | Meaning |
|-------|------|---------|
| | | |
Notable inference warnings (quote the actual lines, or "None"):
<latch-inferred / multi-driven / unconnected-port lines, or "None">
For combinational modules, omit the Timing Summary section entirely.
Known Issues and Fixes
This section records issues encountered when running this skill and how they were resolved.