Optimization

Setting up an optimization run

Objective, effort and target device; the three choices that shape the whole run.

"Make it better" is not one request. Area and logic depth trade against each other, and both trade against readability, so the run asks what you are optimising for before it starts rather than reporting whichever number happened to move.

Objective

ObjectiveWhat it goes after
AreaFewer cells, LUTs and flip-flops. The right default when you are running out of fabric, or fitting a design into a smaller part.
TimingLess combinational depth between registers — the proxy we can measure, not a timing report. The right choice when you are missing timing and the critical path is combinational.
BalancedTake the wins that do not cost much on the other axis. Good for a design nobody has been through in a while.

The objective is what the verdict is computed against. A rewrite that cuts logic depth while the objective was area does not count as a win, and it does not get reported as one; it shows up as a change with its trade-off stated.

Effort

Effort controls how much exploring the run may do: how many steps it can take and how much it may spend. It does not change the rules, the measurement or the proof.

EffortRoughly
LowA quick pass. Finds the obvious wins on a small module and stops. Cheapest and fastest.
MediumThe default. Enough room to try several transformations and measure each one properly.
HighFor a large or dense design where the first three ideas are unlikely to be the good ones.

Tip

Start at Medium. If the answer comes back as nothing worth changing on a design you are sure has slack in it, raising the effort is the right next move; raising it on a design that has already been optimised will mostly buy you a longer explanation of why not.

Target device

This genuinely changes the answer, which is why it is asked rather than assumed. Logic that costs three four-input lookup tables costs one six-input lookup table, so a win measured against a generic library can be a regression on the part you are actually using.

TargetMeasured as
XilinxAMD / Xilinx 7-series primitives
IntelIntel / Altera MAX 10 primitives
LatticeLattice ECP5 primitives
GowinGowin primitives
GenericAbstract gates. Useful for comparing structure, misleading as a resource estimate for a real part.

Pick the family you are actually building for. If you do not know yet, Generic is honest about being approximate, which is better than a precise number for a chip you will not use.

The optional fields

FieldWhen to fill it in
Top moduleWhen the project has more than one candidate. Everything is measured through this module, so naming the wrong one measures the wrong design.
FocusWhen you already know where the cost is. “The address decoder in mem_ctrl” saves the run from measuring its way there.
ConstraintsAnything that must not change: a module another team owns, an interface timing contract, a coding standard you have to keep to.

Note

The interface is preserved in every case, whether or not you say so. Changing ports would break every instantiation of the module and would also make the equivalence check impossible to run, so it is not an option that can be switched off.

What to expect from a run

An optimize run measures your design as you supplied it before it changes anything, and every number you see afterwards is a comparison against that starting point. Changes are made and checked one at a time, so a rewrite that does not actually help is dropped rather than left in the diff for you to argue with.

The consequence worth knowing is that the final measurement and the final behaviour check both come after the last edit. That is what makes the badge on the proposal card mean something; evidence gathered before the last change says nothing about that change.

Tip

An optimize run takes longer than a build of the same design, because measuring and checking are most of the work. If you are exploring rather than committing, start with a single module and the Medium effort setting.