Synthesis · lesson 8

Running synthesis

syn_generic, syn_map and syn_opt: what each stage does, how effort levels change the result, and how to synthesize a single submodule.

7 min read · Free, no login

With the library loaded, the RTL elaborated and the constraints applied, running synthesis takes only a few commands. The skill is in knowing what each command does, what it reads from the constraints, and how to tell when a run has gone wrong.

Reading time: about 7 minutes.

8.1 The two main stages

Genus performs synthesis in two steps, each with its own command.

  1. syn_generic โ€“ synthesizes the design to generic logic. This is where RTL-level optimization takes place: arithmetic is restructured, redundant logic is removed and shared resources are identified. No library cells are used yet.
  2. syn_map โ€“ maps the generic logic onto library cells and optimizes the result. It performs Boolean optimization, technology mapping and gate-level optimization, and it queries the library for detailed timing.
syn_generic
syn_map

After syn_map, the design is a real netlist. Reports can be generated and the netlist can be written out.

8.2 The third stage

syn_opt

In terms of the stages in Lesson 1, elaboration comes from elaborate, and syn_generic, syn_map and syn_opt are the generic, mapping and incremental stages. syn_opt runs incremental optimization on an already mapped design. It is used to squeeze out the last violations, and in flows that use physical information it can use placement-based estimates through its spatial option. Running it after syn_map is common practice. Running it more than once rarely helps, because each pass starts from where the previous one stopped.

8.3 Effort levels

Each stage has an effort setting.

set_db syn_generic_effort medium
set_db syn_map_effort     medium
set_db syn_opt_effort     medium

To set all three at once, use syn_global_effort. The values are low, medium and high.

  • low โ€“ the design is mapped, but little RTL optimization, clean-up or redundancy removal is done. Generally not recommended for anything beyond a quick check.
  • medium โ€“ better timing-driven restructuring and incremental clean-up. A sensible default.
  • high โ€“ timing-driven restructuring across larger sections of logic, and more attempts at clean-up. Runtime goes up, and the gain is often small.

The usual habit is to explore at low or medium, and to run high only on the final pass. Thus, effort is a trade between runtime and result, and a design that fails by a wide margin at medium will rarely be rescued by high. A wide miss normally points to constraints or RTL.

8.4 Using more CPUs

Genus can spread optimization over several threads, and over several machines.

set_db max_cpus_per_server 8

The value should not exceed the number of cores you are allowed to use. The aim is the same optimization in less time, although results can differ between runs with different thread counts, so check the determinism settings for your version. Work can also be spread over several machines with the super_thread_servers attribute, whose exact options are listed in the tool help. On shared servers, use the number your job scheduler granted, because over-subscribing slows everyone.

8.5 Synthesizing a submodule

Sometimes only one block of a larger design needs work. The tool can promote a submodule to a top-level design while keeping the constraints that apply to it. The sequence is:

elaborate chip_top
# apply chip-level constraints, then do a quick, low-effort run
set_db syn_generic_effort low
syn_generic
set_db syn_map_effort low
syn_map

# promote the submodule to its own top-level design
create_derived_design -name core u_core

# save the constraints that were derived for it
write_script core > core.con

For best results, remove the derived design, re-read the RTL, elaborate the block on its own, and apply the saved constraints:

delete_obj core
read_hdl core.v      ;# defines the RTL module named core
elaborate core
include core.con

Here core is the RTL module behind instance u_core, and the derived design is given the same name for simplicity. Why not use the derived design directly? The first run was low effort, so its netlist is a rough one. The point of that run is to extract realistic constraints for the block, not to keep the netlist. A clean re-run from RTL then gives a better result.

8.6 Watching the run

The log of each stage prints a running summary: the worst slack, the total negative slack, and the area. Three patterns are worth recognising.

  • Slack improves each iteration and stops at a positive value โ€“ a healthy run.
  • Slack stops improving at a negative value โ€“ the tool ran out of options. Look at the critical path before raising effort.
  • Area grows much faster than slack improves โ€“ the tool is trading area for small timing gains, and the constraints might be too tight.

If an interactive run is stopped partway, the tool can usually resume from a recorded optimization state with the same inputs, but the exact attribute depends on the version, so check the guide for your release.

8.7 Exercise

Use the files in the exercise folder: acc8.v, acc8.sdc and the script run_genus.tcl. Run the script at a period of 2.0 ns, note the worst slack and total area, then change the period in acc8.sdc to 1.5 and run again. Do not go below 1.5. The adder paths from sum and din_q to ovf, and from sum back to sum, run out of time first. The short path from en to ovf is a single AND gate, but it only has the period minus 0.4 + 0.5 + 0.1 = 1.0 ns, so at a 1.0 ns period it would have nothing left. Compare the area numbers. The smaller period should produce a larger netlist, and if it does not, the original run was already limited by something other than the period, such as relaxed constraints. Note that Yosys does not read the SDC, so the port delays are not modelled in the free flow. For a free alternative, run run_yosys.sh with any open Liberty file and compare cell counts the same way.

8.8 Why an interviewer asks about this

The question "what do syn_generic and syn_map do" checks that the candidate has seen the flow. Follow-ups tend to be about effort: "what would you change if timing is not met" โ€“ the good answer is to examine the critical path and the constraints first, not to raise effort immediately.

Key takeaways

  • syn_generic optimizes technology-independent logic, syn_map maps to library cells, and syn_opt cleans up what remains.
  • Effort levels trade runtime for result, and a large miss is rarely cured by raising effort.
  • Use max_cpus_per_server to speed up runs, within the cores you are allowed.
  • To work on a submodule, promote it with create_derived_design, save its constraints, and re-run from RTL for the best netlist.