Skip to content
VLSI Mentor

Tcl · Module 26

Scenarios: Mode × Corner

Eighteen combinations, eight actually required. Getting from one to the other needs a reason per drop and a coverage check — and the obvious coverage check reports two failures that are not failures.

Advanced23 min readtcledapart-iimcmmscenariossignoff

Part II · Module 26 · Chapter 26.4 · Scenarios: Mode × Corner

A scenario is one mode paired with one corner, analysed for one check. It is the unit of work in MCMM: one scenario is one analysis, one set of reports, one row in the summary.

Chapter 26.2 and Chapter 26.3 built the two lists. This chapter is about what to do with their product.

1. The cross-product is a default, not a plan

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the full cross-product ===
    3 modes x 3 corners x 2 checks = 18 scenarios
Two parallel left-to-right flows. Above, the mode list and the corner list combine into the full cross-product of scenarios, which is a default rather than a plan. Below, each dropped combination is recorded with a reason, leaving a smaller required set, and a coverage check confirms that nothing the dropped scenarios would have revealed is missing.Modesfunctional, shift, captureCornersslow, typical, fastCross-product18 scenarios, a defaultPrune with a reasonevery drop justifiedRequired set8 scenarios, a planCoverage checkednothing revealed is lost12
Figure 1 — getting from the cross-product to a plan. Above: the mode list and the corner list combine into every possible scenario, which is a default nobody chose. Below: each dropped combination is recorded with its reason, leaving a smaller required set, and a coverage check confirms that nothing a dropped scenario would have revealed has been lost.

Running the whole product is the easy decision and it is usually the wrong one, for reasons that pull in opposite directions:

Against running everything: analysis time, licence cost, and the fact that eighteen reports nobody reads are worse than eight somebody does. On a real design the product is hundreds, and each one is hours.

Against pruning: every combination you do not run is a combination you are claiming cannot fail. Chapter 26.1 §3 showed 7 of 18 violating — the product is not full of harmless cells.

So the pruning has to be a decision with a reason, and the reason has to be checkable. That is the rest of the chapter.

2. Measure the redundancy before pruning it

The first thing to establish is how much of the product is actually telling you something different. That is measurable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== how much of the cross-product is distinct? ===
    setup   9 scenarios -> 9 distinct worst-slack value(s)
    hold    9 scenarios -> 3 distinct worst-slack value(s)

Nine setup scenarios give nine different answers. Nine hold scenarios give three.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the hold results, grouped by corner ===
    functional    ss_0p72v_125c    0.0430
    functional    tt_0p80v_25c     0.0100
    functional    ff_0p88v_m40c   -0.0208
    scan_shift    ss_0p72v_125c    0.0430
    scan_shift    tt_0p80v_25c     0.0100
    scan_shift    ff_0p88v_m40c   -0.0208
    scan_capture  ss_0p72v_125c    0.0430
    scan_capture  tt_0p80v_25c     0.0100
    scan_capture  ff_0p88v_m40c   -0.0208

The three modes give identical hold numbers at each corner. The hold cross-product is threefold redundant, and the reason is the one Chapter 26.2 §2 noted: hold requirement is a property of the capturing flip-flop, not of the clock period, so changing the mode does not change it.

3. Pruning with a reason per drop

The pruning rules are data, each carrying its justification, matched with string match so wildcards work:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set PRUNE {
    {scan_shift   *             setup {shift runs at 20 ns; setup here is not the limiter}}
    {scan_capture *             hold  {capture is one edge; hold is covered by the functional run}}
    {*            tt_0p80v_25c  *     {typical is for tracking, not for sign-off}}
}

proc pruned_reason {mode corner check prune} {
    foreach rule $prune {
        lassign $rule m c k why
        if {[string match $m $mode] && [string match $c $corner] && [string match $k $check]} {
            return $why
        }
    }
    return ""
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== dropped, each with its reason ===
    functional    tt_0p80v_25c   setup  typical is for tracking, not for sign-off
    functional    tt_0p80v_25c   hold   typical is for tracking, not for sign-off
    scan_shift    ss_0p72v_125c  setup  shift runs at 20 ns; setup here is not the limiter
    scan_shift    tt_0p80v_25c   setup  shift runs at 20 ns; setup here is not the limiter
    scan_shift    tt_0p80v_25c   hold   typical is for tracking, not for sign-off
    scan_shift    ff_0p88v_m40c  setup  shift runs at 20 ns; setup here is not the limiter
    scan_capture  ss_0p72v_125c  hold   capture is one edge; hold is covered by the functional run
    scan_capture  tt_0p80v_25c   setup  typical is for tracking, not for sign-off
    scan_capture  tt_0p80v_25c   hold   capture is one edge; hold is covered by the functional run
    scan_capture  ff_0p88v_m40c  hold   capture is one edge; hold is covered by the functional run
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== required, and therefore run ===
    functional    ss_0p72v_125c  setup
    functional    ss_0p72v_125c  hold
    functional    ff_0p88v_m40c  setup
    functional    ff_0p88v_m40c  hold
    scan_shift    ss_0p72v_125c  hold
    scan_shift    ff_0p88v_m40c  hold
    scan_capture  ss_0p72v_125c  setup
    scan_capture  ff_0p88v_m40c  setup

    required 8, dropped 10, total 18

18 down to 8. Four decisions in that rule list are worth naming.

required + dropped = total is printed. Every scenario is accounted for as either required or deliberately dropped — there is no third category of "nobody looked". That is Chapter 23.2 §4's declared-list argument: the full product is the denominator, so a combination cannot go missing by being forgotten.

The rules use string match, not regexp. The patterns are globs over names, so * means "any", and Chapter 13.10 prefers the glob where the pattern is a glob. Note the hazard: string match is anchored at both ends by definition (Chapter 5.5), so tt_0p80v_25c matches only that corner — which is what is wanted here, and would not be what you wanted if you had meant "any typical corner".

First match wins. pruned_reason returns on the first matching rule, so rule order decides which reason gets recorded when two apply. scan_shift / tt / hold is dropped by the typical rule rather than the shift rule because the shift rule only covers setup — but scan_capture / tt / hold is dropped by the capture rule, which comes first. Rule order is therefore part of the meaning, and a rule list where order matters should say so.

The typical corner is dropped for sign-off entirely. That is a real and defensible project decision — typical is where you track convergence, and sign-off happens at the extremes — and it is the rule doing most of the work here, removing 4 of the 10.

4. Checking the pruning: the obvious way is wrong

A pruning plan is a claim that nothing important was dropped. That claim is testable, because the mock can run the dropped scenarios too and see what they would have said.

The obvious test:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the naive check: did any dropped scenario violate? ===
    scan_capture  tt_0p80v_25c   setup   -4.1500
    scan_capture  ff_0p88v_m40c  hold    -0.0208
    2 dropped scenario(s) violated

Two dropped scenarios violate. That reads like two pruning mistakes, and on that evidence the plan should be rejected.

5. The criterion that is actually rigorous

The right question is whether the required set reveals the same (check, path) pair:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc violating_pairs {mode corner check} {
    reset_session ; apply_mode $mode ; apply_corner $corner
    set out {}
    foreach r [report_timing $check] {
        if {[dict get $r slack] < 0} { lappend out [list $check [dict get $r path]] }
    }
    return $out
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the (check, path) pairs the REQUIRED set reveals as violating ===
    hold   short_hold
    setup  io_to_core
    setup  scan_chain
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== every dropped scenario's violating pairs, checked against that ===
    scan_capture  tt_0p80v_25c   setup  scan_chain     covered
    scan_capture  ff_0p88v_m40c  hold   short_hold     covered
    pairs a dropped scenario would have revealed and nothing required does: 0

Zero, on the rigorous criterion. scan_chain failing setup is revealed by scan_capture at both the slow and fast corners, which are required. short_hold failing hold is revealed by functional at the fast corner, which is required. Both dropped violations are genuinely covered, by name.

6. What even the rigorous check cannot tell you

Being honest about the limit, because §5 looks conclusive.

It validates the pruning against the design as it is now. A design change can make a pruned scenario the only one that reveals a new violation — a retimed path, a new clock, a different capture flop — and the check's previous "0 missed" says nothing about that. So the coverage check belongs in the flow, re-run when the required set or the design changes, not performed once and recorded as settled.

It can only see what the mock can compute. The real version of this check runs every dropped scenario, which is precisely the cost the pruning was meant to avoid. The practical resolution is to run the full product rarely — at a milestone, or when the mode or corner lists change — and the pruned set nightly. The full run then exists to validate the pruning, not to sign off.

It cannot justify a scenario nobody has thought of. The product is modes × corners × checks, and a mode that was never declared is not in the denominator. Chapter 26.2 §5's -owner and -reason fields exist for the opposite problem — a mode nobody can justify — but an absent mode is invisible to every check in this chapter.

7. Scenarios as data, for the driver

The output of all this is a list the driver walks:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc required_scenarios {modes corners checks prune} {
    set out {}
    foreach mode $modes {
        foreach corner $corners {
            foreach check $checks {
                if {[pruned_reason $mode $corner $check $prune] eq ""} {
                    lappend out [dict create mode $mode corner $corner check $check]
                }
            }
        }
    }
    return $out
}

A list of dicts, each one a complete description of one unit of work — which is what Chapter 26.5 iterates and what Chapter 26.6 names directories after.

Two properties of that shape matter downstream. A scenario is self-describing, so nothing later has to re-derive which mode a result came from. And the list is built once, so the driver, the reporter and the aggregator all walk the same thing — which is the defence against Chapter 26.6 §3's aggregation-by-directory-scan.

8. Exercises

Exercise 1. What is a scenario, and why is it the unit of work?

Exercise 2. Give the argument for and the argument against running the full cross-product.

Exercise 3. Nine hold scenarios gave three distinct answers. Why, in this design?

Exercise 4. Name two changes that would break that collapse.

Exercise 5. Why print required + dropped = total?

Exercise 6. Why does rule order matter in the prune list, and give an example from the output.

Exercise 7. The naive coverage check flagged two mistakes. Why is it the wrong question?

Exercise 8. Why is the per-check criterion worse than a wrong answer?

Exercise 9. State the rigorous criterion, and what it found.

Exercise 10. Why must the coverage check live in the flow rather than be run once?

Exercise 11. What can no check in this chapter see?

Where this fits

Part of the Tcl curriculum.