Perl Part II · Module 23 · Static Timing Analysis Report Parsing
Tempus Report Formats
A second STA tool describes the same timing in a different layout, and the useful deliverable is not a second parser — it is a concrete list of what differs, so that a parser written for one tool fails loudly rather than quietly when it is handed the other's output. This chapter measures the same four paths in both layouts, finds that every fact is recoverable from each, and identifies the single difference that turns a missed hold violation into a clean report.
Advanced11 min readPerlPart IISTA Reports
Part II · Module 23 · Chapter 23.7 · Tempus Report Formats
1. The Same Four Paths, Two Layouts
use strict;
use warnings;
# Both files describe THE SAME four paths with THE SAME numbers.
# slow_0v90_125c.pt.rpt -- generated from the real netlist (23.1)
# slow_0v90_125c.tempus.rpt -- the same numbers re-laid-out in the shape
# Tempus emits, by a variant writer
# So any difference below is the FORMAT's, not the data's. Neither tool
# exists on this machine; these are labelled format samples (21.1's rule).
my $PT = "reports/slow_0v90_125c.pt.rpt";
my $TE = "reports/slow_0v90_125c.tempus.rpt";
sub slurp { open my $f, "<", $_[0] or die "$_[0]: $!"; local $/; my $t = <$f>; close $f; $t }
# ---- the same five facts, located by two different sets of patterns
my %PT_PAT = (
endpoint => qr/^[ \t]*Endpoint:[ \t]*(\S+)/m,
start => qr/^[ \t]*Startpoint:[ \t]*(\S+)/m,
group => qr/^[ \t]*Path Group:[ \t]*(\S+)/m,
required => qr/^[ \t]*data required time[ \t]+[\d.-]+[ \t]+([\d.-]+)/m,
slack => qr/^[ \t]*slack[ \t]*\((\w+)\)[ \t]*(-?[\d.]+)/m,
);
my %TE_PAT = (
endpoint => qr/^Endpoint:[ \t]+(\S+)/m,
start => qr/^Beginpoint:[ \t]+(\S+)/m,
group => qr/^Path Groups:[ \t]*\{([^}]*)\}/m,
required => qr/^=[ \t]*Required Time[ \t]+([\d.-]+)/m,
slack => qr/^=[ \t]*Slack Time[ \t]+(-?[\d.]+)/m,
);
sub split_pt { my @b = split /(?=^[ \t]*Startpoint:)/m, $_[0]; shift @b while @b && $b[0] !~ /Startpoint:/; @b }
sub split_te { my @b = split /(?=^Path \d+:)/m, $_[0]; shift @b while @b && $b[0] !~ /^Path \d+:/; @b }
my @pt = split_pt(slurp($PT));
my @te = split_te(slurp($TE));
printf "%-10s %d path block(s)\n", "PrimeTime", scalar @pt;
printf "%-10s %d path block(s)\n\n", "Tempus", scalar @te;
# ---- do the two formats yield the same facts?
printf "%-14s %-22s %-22s %s\n", "FIELD", "FROM PRIMETIME", "FROM TEMPUS", "same?";
my $same = 0;
my ($pb, $tb) = ($pt[3], $te[3]); # the fourth path in each, bit3/D
for my $k (qw(endpoint start group required slack)) {
my ($pv) = $pb =~ $PT_PAT{$k};
my ($tv) = $tb =~ $TE_PAT{$k};
# the slack pattern in PT captures the annotation first
if ($k eq "slack") { (undef, $pv) = $pb =~ $PT_PAT{slack} }
my $eq = (defined $pv && defined $tv &&
($pv eq $tv || (($pv =~ /^-?[\d.]+$/) && ($tv =~ /^-?[\d.]+$/)
&& abs($pv - $tv) < 5e-5))) ? "yes" : "NO";
$same++ if $eq eq "yes";
printf "%-14s %-22s %-22s %s\n", $k, ($pv // "-"), ($tv // "-"), $eq;
}
printf "\n%d of 5 facts are identical across the two formats\n", $same;
# ---- the concrete differences, enumerated
print "\nthe differences, field by field:\n";
my @DIFF = (
[ "the launching pin", "Startpoint:", "Beginpoint:" ],
[ "the path group", "Path Group: clk", "Path Groups: {clk}" ],
[ "required time", "data required time", "= Required Time" ],
[ "slack", "slack (VIOLATED)", "= Slack Time" ],
[ "block start", "Startpoint:", "Path N: <VERDICT> Setup Check" ],
[ "the verdict", "annotating the slack", "a word in the Path header" ],
);
printf " %-18s %-22s %s\n", "FACT", "PRIMETIME", "TEMPUS";
printf " %-18s %-22s %s\n", @$_ for @DIFF;
# ---- where the verdict lives is the one that bites
print "\nwhere is the VIOLATED/MET verdict?\n";
my ($pt_ann) = $pb =~ /^[ \t]*slack[ \t]*\((\w+)\)/m;
my ($te_ann) = $tb =~ /^Path \d+:[ \t]*(\w+)[ \t]+Setup Check/m;
printf " PrimeTime: on the slack line -> %s\n", ($pt_ann // "not found");
printf " Tempus: in the Path header line -> %s\n", ($te_ann // "not found");
print " So 23.3's rule -- trust the annotation -- needs a DIFFERENT pattern\n";
print " per tool, and a PrimeTime slack pattern run on a Tempus report finds\n";
print " a number with no annotation, which 23.2 then classifies by SIGN.\n";
# ---- and what that silently does to a hold path
print "\nwhat that costs on a hold path:\n";
print " a Tempus hold violation has a POSITIVE Slack Time and VIOLATED in\n";
print " its Path header. Read with a PrimeTime pattern the annotation is\n";
print " missing, the sign is positive, and the path is reported MET --\n";
print " which is 23.3's 'real violation missed', caused purely by pointing\n";
print " one tool's parser at the other tool's report.\n";PrimeTime 4 path block(s)
Tempus 4 path block(s)
FIELD FROM PRIMETIME FROM TEMPUS same?
endpoint bit3/D bit3/D yes
start bit1/Q bit1/Q yes
group clk clk yes
required 0.9050 0.905 yes
slack -0.0900 -0.0900 yes
5 of 5 facts are identical across the two formats
the differences, field by field:
FACT PRIMETIME TEMPUS
the launching pin Startpoint: Beginpoint:
the path group Path Group: clk Path Groups: {clk}
required time data required time = Required Time
slack slack (VIOLATED) = Slack Time
block start Startpoint: Path N: <VERDICT> Setup Check
the verdict annotating the slack a word in the Path header
where is the VIOLATED/MET verdict?
PrimeTime: on the slack line -> VIOLATED
Tempus: in the Path header line -> VIOLATED
So 23.3's rule -- trust the annotation -- needs a DIFFERENT pattern
per tool, and a PrimeTime slack pattern run on a Tempus report finds
a number with no annotation, which 23.2 then classifies by SIGN.
what that costs on a hold path:
a Tempus hold violation has a POSITIVE Slack Time and VIOLATED in
its Path header. Read with a PrimeTime pattern the annotation is
missing, the sign is positive, and the path is reported MET --
which is 23.3's 'real violation missed', caused purely by pointing
one tool's parser at the other tool's report.All five facts are recoverable from both formats. That is the first useful result: this is a layout problem, not an information problem. Nothing a reviewer needs is present in one tool's output and absent from the other's.
2. The Six Differences
FACT PRIMETIME TEMPUS
the launching pin Startpoint: Beginpoint:
the path group Path Group: clk Path Groups: {clk}
required time data required time = Required Time
slack slack (VIOLATED) = Slack Time
block start Startpoint: Path N: <VERDICT> Setup Check
the verdict annotating the slack a word in the Path headerFour of those are ordinary renames, and two are structural:
The block boundary is a different line. PrimeTime blocks begin at Startpoint:; Tempus blocks begin at Path N:. Chapter 23.1's lookahead split needs a different delimiter per tool, and splitting a Tempus report on Startpoint: yields one block containing everything — which then parses as a single path with the last values in the file.
The group is braced. Path Groups: {clk} is plural and wrapped, so (\S+) captures {clk} with the braces attached. That is the kind of difference that produces a working parser whose group names all have stray punctuation, and which nobody notices until the groups are used as hash keys and {clk} and clk become two groups.
3. Where The Verdict Lives
This is the difference that matters, and the measurement isolates it:
PrimeTime: on the slack line -> VIOLATED
Tempus: in the Path header line -> VIOLATEDBoth tools state the verdict. They put it in different places, and that has a specific, silent consequence.
Point a PrimeTime parser at a Tempus report and the slack pattern qr/slack[ \t]*\((\w+)\)[ \t]*(-?[\d.]+)/ finds no slack line at all. A tolerant slack pattern — Chapter 23.2's, which makes the annotation optional — finds = Slack Time -0.0900 and recovers the number with no annotation.
Then Chapter 23.2's classifier does exactly what it was built to do: falls back to the sign.
And on a hold path that is wrong. A Tempus hold violation has a positive Slack Time with VIOLATED in its path header; read without the header, the annotation is absent, the sign is positive, and the path is reported MET. Chapter 23.3's "real violation missed", produced purely by pointing one tool's parser at the other tool's report — with no error, no warning, and a plausible report.
So the fallback that Chapter 23.2 introduced as a reasonable safety net becomes the mechanism of the failure. That is worth sitting with: a tolerant parser degrades silently on the wrong input, where a strict one would have failed loudly.
4. One Parser Or Two?
Three options, and the measurement makes the choice concrete.
Two parsers. Clean, and it doubles the maintenance. Worse, it does not solve the problem in this chapter — nothing stops somebody running the wrong one.
One parser with a table of per-tool patterns, selected by detecting the format. This is Chapter 18.8's spellings table at the level of a whole layout:
my %FORMAT = (
primetime => { block => qr/^[ \t]*Startpoint:/m, verdict => qr/slack[ \t]*\((\w+)\)/, ... },
tempus => { block => qr/^Path \d+:/m, verdict => qr/^Path \d+:[ \t]*(\w+)/, ... },
);Detect the format first and refuse to guess. This is the part that matters more than the table:
my $fmt = $text =~ /^Path \d+:.*Setup Check/m ? "tempus"
: $text =~ /^[ \t]*Startpoint:/m ? "primetime"
: undef;
die "unrecognised timing report format\n" unless defined $fmt;An unrecognised format must be an error, not a default to whichever parser was written first. That is Chapter 18.8's rule once more, and this chapter is the strongest case for it in the module: the cost of guessing wrong is a missed violation rather than a missing number.
One extra line earns its place — report which format was detected:
print STDERR "note: reading as '$fmt' format\n";Chapter 18.8's provenance label. When a report comes out wrong, "it was read as primetime" is the first fact somebody needs, and it is the one nobody can reconstruct afterwards.
5. What To Remember
- A second STA tool is a layout problem, not an information problem.
- Measured: all 5 facts are recoverable from both formats.
- Four differences are renames:
Startpoint:/Beginpoint:,data required time/= Required Time, and so on. - Two are structural. The block boundary is a different line, and splitting a Tempus report on
Startpoint:yields ONE block containing everything. - The group is braced in Tempus —
{clk}— so(\S+)keeps the braces, and{clk}andclkbecome two hash keys. 0.9050versus0.905is formatting, not disagreement. Compare numbers numerically with a tolerance (3.2) and names as strings.- A comparison routine with one rule for both kinds is wrong in one direction.
- The verdict lives in a different place: PrimeTime annotates the slack line, Tempus puts it in the path header.
- A PrimeTime parser on a Tempus report finds the number with no annotation, and 23.2's classifier then falls back to the sign.
- On a hold path that reports MET for a VIOLATED path — 23.3's missed violation, caused purely by the wrong parser.
- A tolerant parser degrades silently on the wrong input; a strict one fails loudly. The fallback becomes the failure mechanism.
- Prefer one parser with a per-tool pattern table (18.8) over two parsers nobody keeps in step.
- Detect the format and refuse to guess — an unrecognised report is an error, not a default.
- Report which format was detected. It is the first fact needed afterwards and the one nobody can reconstruct.
Chapter 23.8 takes the consequence seriously. If a downstream consumer should not have to know which tool produced a report, something has to normalise the two into one stable record format first.
Where this fits
Part of the Perl curriculum.
