Area mapping minimises the sum of cell areas; delay mapping minimises arrival times against required times, path by path — the fastest cover on each failing path, the cheapest elsewhere. The two modes disagree exactly when the cheapest cover is too slow for its required time, and this lesson records that disagreement.
Mode | Cost | Decision |
|---|---|---|
Area | Sum of cell areas | Cheapest cover that implements the function. |
Delay | Worst arrival against required time | Fastest cover on each failing path, cheapest elsewhere. |
Two definitions carry the lesson. Arrival time is when a signal gets there, as ABC reports in picoseconds. Slack is required minus arrival, and its sign is the verdict: positive meets, zero exactly meets, negative fails.
The design is a small cone of AND, OR, and XOR terms feeding one output. The script synthesises it once, saves the generic gates, then maps the same gates six times against sized.lib, where every cell exists in plain, X2, and X4 — bigger is faster and larger. The -D target steps down from 1000 to 100 ps, and typical.constr hands ABC a 0.05 pF output load. The assertions prove no generic gate survives any mapping.
read_verilog <<EOV
module trade(input [7:0] x, output y);
assign y = ((x[0] & x[1]) | (x[2] & x[3]) | (x[4] ^ x[5])) & (x[6] | x[7]);
endmodule
EOV
synth -top trade -flatten
design -save generic
tee -o abc_1000.txt abc -liberty sized.lib -constr typical.constr -D 1000
tee -o stat_1000.txt stat -liberty sized.lib
write_json net_1000.json
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_700.txt abc -liberty sized.lib -constr typical.constr -D 700
tee -o stat_700.txt stat -liberty sized.lib
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_500.txt abc -liberty sized.lib -constr typical.constr -D 500
tee -o stat_500.txt stat -liberty sized.lib
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_300.txt abc -liberty sized.lib -constr typical.constr -D 300
tee -o stat_300.txt stat -liberty sized.lib
write_json net_300.json
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_200.txt abc -liberty sized.lib -constr typical.constr -D 200
tee -o stat_200.txt stat -liberty sized.lib
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_100.txt abc -liberty sized.lib -constr typical.constr -D 100
tee -o stat_100.txt stat -liberty sized.lib
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
design -load generic
tee -o abc_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 500
tee -o stat_heavy.txt stat -liberty sized.lib
select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
check
log PASS: tighter targets and heavier loads move the same functionThe last run previews the break-it: the same function at -D 500 against heavy.constr with a 0.2 pF load. Same target, heavier load.
The same one: the 100 ps run returns the 200 ps netlist exactly, at 211 ps and area 33.4. In this ABC script no tighter -D target found a faster cover, so the extra pressure buys nothing.
Target -D | Cells | Area | Delay reached | Slack at 211 (common check) | Slack at own target |
|---|---|---|---|---|---|
1000 ps | 5 | 16 | 342 ps | -131 (fails) | +658 (meets) |
700 ps | 5 | 16 | 342 ps | -131 (fails) | +358 (meets) |
500 ps | 5 | 16 | 342 ps | -131 (fails) | +158 (meets) |
300 ps | 5 | 20.8 | 298 ps | -87 (fails) | +2 (meets) |
200 ps | 5 | 33.4 | 211 ps | 0 (exactly meets) | -11 (fails) |
100 ps | 5 | 33.4 | 211 ps | 0 (exactly meets) | -111 (fails) |
Read the table top to bottom. The 1000 ps run is the loose end at area 16, arrival 342 ps, with 0 upsized cells; the 700 and 500 ps runs return exactly that netlist, so tightening the target that far buys nothing: the first plateau. Past it the mapper pays for speed with drive strength: the 300 ps run reports area 20.8 at 298 ps with 1 upsized cell, and the 200 ps run reaches area 33.4 at the tightest arrival in the table. That is 109% more area for 38% less delay across the sweep. The 100 ps run returns the 200 ps netlist unchanged: pressure past the fastest cover ABC found buys no more speed. Both slack columns are required minus arrival, computed, never typed.
Two slack columns, two promises. The common-check column fixes one hypothetical requirement for every row (the tightest arrival in the table); do not read it as any run's own verdict — the loose end asked for 1000 ps. The own-target column checks each run against the -D it ran with: the 1000 through 300 ps runs meet theirs, while the 200 and 100 ps runs cannot, because in this ABC script no target below 200 ps found a faster cover than the 200 ps run's arrival. Same arrivals, opposite lessons: the sign follows the required time you choose, so the answers recompute slack from the table instead of quoting it.
The structure barely moved between the loose and 300 ps netlists; the sizes on the slow path did. Before rewriting logic, the tool buys speed with drive strength — the orange cell is where the money went.
The heavy run keeps the 500 ps target and swaps typical.constr for heavy.constr, raising the output load from 0.05 pF to 0.2 pF: 6 cells at area 19.2 with arrival 448 ps, against the 500 ps typical-load row in the table (5 cells at area 16). The cover changes to carry the load — the heavy histogram reads AND2 x1, AOI21 x2, BUF_X2 x1, NOR2 x1, XNOR2 x1, with a BUF_X2 the typical cover never needed — and the arrival slips by 106 ps. The 500 ps target still meets, but slack falls from +158 (meets) to +52 (meets) while area rises 3.2 units: recheck both after every load change.
Only rows whose Delay reached beats the tightest arrival meet it; slacks are the common-check column, required minus arrival, negatives failing. Promise the loosest arrival in that column and every run meets. The sign follows the required time, never the other way round.
It may upsize cells and insert a buffer: the recording adds a BUF_X2 the typical-load cover never needed and rebuilds the cone as AND2 x1, AOI21 x2, BUF_X2 x1, NOR2 x1, XNOR2 x1 — 6 cells at area 19.2 and arrival 448 ps against the 500 ps typical-load row before (5 cells at area 16). Same logic, a different cover, and the slack at 500 ps falls from +158 (meets) to +52 (meets).
Every number and picture on this page comes from one Yosys 0.69 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): ABC maps one cone against the teaching library sized.lib at six delay targets with typical.constr (BUF driver, 0.05 pF load) plus one run at the middle target with heavy.constr (0.2 pF). The library is fictional, so absolute areas and delays are teaching units; the shape of the trade is the point. Slack values are required minus the recorded arrival, computed on the page.