derivedTests to buildWaferMap and it becomes an
ordinary test from that moment on: a plot mode, a colorbar, a tooltip line, a row in
every statistic and chart. Expressions are parsed to a typed tree and walked; there
is no eval, no new Function and no third-party
expression engine, so a set of them is safe to share between teams as plain JSON.
buildWaferMap), a sweep is
a chart (the renderer's insights options).
Passed to buildWaferMap as derivedTests, beside the
measured testDefs: from then on each is a test like any other. In
tsmap they are rows of the
test-definitions file, with an expression column.
const result = buildWaferMap({
results, testDefs, waferConfig, dieConfig,
derivedTests: [
{ testNumber: 900001, name: 'Switching Window', unit: 'V',
expression: 't[1202] - t[1212]',
limitLow: 0.33 },
// …the entries below go in this same list
],
});
The gap between the two curves at a fixed read step — the
switching window, per die: how far apart the two states are at the level a
read would use. Its limitLow is the margin a reliable read needs, so
the few dies below it — mostly at the edge, where the window narrows — get
the ▽ marker on the map like any measured test. SET Span below reduces a range: there is
no vector arithmetic in the grammar, so a range must always be reduced.
// derivedTests, continued
{ testNumber: 900002, name: 'SET Span', unit: 'V',
expression: 'max(t[1200..1208]) - min(t[1200..1208])' }
{ testNumber: 900003, name: 'Vth/Idsat Ratio', unit: '',
expression: 't[1060] / t[1050]' }
A missing input makes the value absent on that die — it renders no-data grey and is excluded from the statistics. Never zero.
// derivedTests, continued
{ testNumber: 900004, name: 'Window OK', testType: 'F',
expression: 'specPass[900001]' }
A boolean expression is a verdict: declare
testType: 'F' and it lands in die.testPass, never
as a 1/0 in testValues where it would enter the correlation matrix
and the Cpk table. This one reads another derived test — nesting is ordered by
dependency, so declaration order does not matter.
{ testNumber: 900005, name: 'All Steps Pass', testType: 'F',
expression: 'all(specPass[1200..1208]) and testPass[1080]' }
specPass[n] is the spec-limit judgement;
testPass[n] is the tester's recorded verdict. Different questions —
a value can be out of spec while the tester recorded a pass — so they are
separate accessors.
Not a test and not in the same list: a sweep is a chart definition,
passed to the renderer as insights.sweeps and drawn in the Insights
tab. It reads tests — measured or derived — but adds none. In tsmap it is a
separate sweeps file (Setup ▾ → Sweeps…), not part of the test definitions.
This page has one sweep. The parametric sweeps example has five, each a different use of the crossing and the width — temperature inversion, DIBL on a log scale, data retention, output drive with an x unit, and an RRAM resistance distribution read from the test names onto a log axis.
// The pulse amplitude at each of the nine steps, in volts.
const PULSE_V = [0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4];
renderWaferGallery(container, items, {
insights: {
enabled: true, // adds the Insights tab to the toolbar
defaultView: 'sweeps', // …and opens it on Sweeps
sweeps: [{
id: 'threshold',
title: 'SET / RESET threshold — crossing and width',
series: [
{ label: 'SET', tests: ['1200..1208'], xValues: PULSE_V },
{ label: 'RESET', tests: ['1210..1218'], xValues: PULSE_V },
],
separationAt: [0.40, 0.70, 1.00], // levels to measure the width at
xLabel: 'Pulse amplitude', xUnit: 'V',
}],
},
});
A sweep is a chart, not a test, so it is not in the map's plot-mode menu: it is a card in the Insights tab's Sweeps view. The button under the code opens it.
'1200..1208' is the same range syntax as
t[1200..1208] in the SET Span expression above,
read by the same code, so the same text always names the same tests. It expands
to the tests declared inside it, ascending, and is checked against
xValues: if a test in the range is missing from the data, the card
lists the tests it did find and does not measure the crossing or widths, rather
than pairing the remaining amplitudes with the wrong tests.
SET falls, RESET rises, and they cross near the bottom — so the pair traces a V. Each line is monotonic, so a level above the crossing meets each curve exactly once and the width of the V there is unambiguous. It widens as the level rises: 0.40 V is a narrow slot just above the crossing, 1.00 V is nearly the full sweep.
xValues gives the real swept quantity, so the crossing
is reported as a pulse amplitude — in volts, because of xUnit —
rather than "between the 3rd and 4th test". Without
it the axis is the ordinal position — test numbers are identifiers and nothing
guarantees they are evenly spaced.
Each line is the population median with a p10–p90 band, not one trace per die. The per-die view of the same data is the derived tests above, plotted on the map where position is visible.