Manufacturing, DFM & Factory Test¶
Module 3-07 designed a single, hand-assembled board. This module is about what changes when a design has to be built by a contract manufacturer at volume: Design for Manufacturing (DFM) — layout choices that make a board reliably assemblable by machine — and factory test firmware, the code that runs once on every unit before it ships, never again in the field. This is manual technical review of manufacturing practice; no board from this course was actually built at a CM, and that's stated plainly wherever a claim depends on real fab/assembly-line behavior.
DFM is a different discipline from schematic/electrical design¶
A board can be electrically perfect and still be expensive or unreliable to build:
- Component placement for pick-and-place: parts need adequate spacing for a nozzle to place them without disturbing neighbors, and a consistent orientation reference (silkscreen pin-1 marker, matching the actual pin-1 pad) that the CM's placement program relies on.
- Panelization: boards are usually built in a panel (an array of many copies) with breakout tabs or v-score lines, then separated after assembly — a board designed without panelization in mind (components too close to the edge, no tooling holes) costs more per unit or requires layout rework before it can be manufactured at all.
- Solder paste stencil design: a good stencil aperture design (matching pad size and adding paste-reduction for fine-pitch parts) meaningfully affects first-pass yield — an unmodified 1:1 stencil-to-pad ratio on fine-pitch ICs is a well-known source of solder bridging.
- Testability: every net a factory test needs to probe should have an accessible test point — retrofitting this after layout is far more expensive than including it from the start (module 3-07's test-point point applies here at greater stakes).
Design for Test: the boundary-scan and bed-of-nails reality¶
Two dominant approaches to factory electrical test:
- Bed-of-nails / flying probe: physical pogo pins contact test points on the board to inject signals and measure responses — requires the test-point accessibility called out above, and works for verifying solder joints, shorts, and basic circuit function.
- Boundary-scan (JTAG): for parts that support it, lets a test fixture verify pin-level connectivity through the chip's own scan chain without needing physical probe access to every net — valuable on dense boards where physical test points for every signal aren't feasible.
Neither replaces functional test — a board that passes electrical continuity test can still fail to run correct firmware, which is why factory test firmware (below) is a separate, additional stage.
Factory test firmware: not the same firmware that ships¶
A dedicated factory test image, flashed once during manufacturing and overwritten by the final production image before the unit ships, typically exercises every peripheral in a way normal operation never does — testing things application firmware has no reason to check on every boot:
/* conceptual factory test sequence — architecture reviewed, not run on real hardware */
typedef struct { const char *name; int (*test_fn)(void); } factory_test_t;
int test_i2c_sensor_present(void) { /* probe expected I2C address, expect ACK */ return 0; }
int test_flash_write_read(void) { /* write pattern, read back, verify */ return 0; }
int test_led_visual_confirm(void) { /* blink pattern; operator/camera confirms */ return 0; }
int test_button_input(void) { /* wait for operator press within timeout */ return 0; }
factory_test_t factory_tests[] = {
{ "i2c_sensor", test_i2c_sensor_present },
{ "flash_wr", test_flash_write_read },
{ "led_visual", test_led_visual_confirm },
{ "button_input", test_button_input },
};
/* runs every test, reports a single pass/fail plus per-test detail over UART
for the factory operator/fixture to log — every unit gets a permanent
test record tied to its serial number */
int run_factory_tests(void) {
int all_pass = 1;
for (size_t i = 0; i < sizeof(factory_tests)/sizeof(factory_tests[0]); i++) {
int rc = factory_tests[i].test_fn();
report_test_result(factory_tests[i].name, rc);
if (rc != 0) all_pass = 0;
}
return all_pass;
}
Every unit's serial number and test result should be logged permanently (not just pass/fail on a screen the operator glances at) — a field failure investigation months later often needs to know exactly which factory tests that specific serial number passed and what its measured values were, not just that it shipped.
Modeling the factory test aggregation logic¶
The pass/fail aggregation and reporting logic is pure control flow,
compiled and run with gcc against fake test functions standing in for
real hardware probes:
#include <stdio.h>
#include <assert.h>
#include <string.h>
typedef struct { const char *name; int (*test_fn)(void); } factory_test_t;
int fake_pass(void) { return 0; }
int fake_fail(void) { return -1; }
typedef struct { char name[32]; int rc; } test_record_t;
static test_record_t records[16];
static int record_count = 0;
void report_test_result(const char *name, int rc) {
strncpy(records[record_count].name, name, sizeof(records[0].name) - 1);
records[record_count].rc = rc;
record_count++;
}
int run_factory_tests(factory_test_t *tests, size_t n) {
int all_pass = 1;
for (size_t i = 0; i < n; i++) {
int rc = tests[i].test_fn();
report_test_result(tests[i].name, rc);
if (rc != 0) all_pass = 0;
}
return all_pass;
}
int main(void) {
factory_test_t all_ok[] = { {"a", fake_pass}, {"b", fake_pass} };
record_count = 0;
assert(run_factory_tests(all_ok, 2) == 1);
assert(record_count == 2);
factory_test_t one_bad[] = { {"a", fake_pass}, {"b", fake_fail}, {"c", fake_pass} };
record_count = 0;
assert(run_factory_tests(one_bad, 3) == 0); /* overall fail */
assert(record_count == 3); /* but ALL tests still ran and logged */
assert(records[1].rc == -1);
printf("factory test aggregation model OK\n");
return 0;
}
The assertion that record_count == 3 even when test b fails encodes a
real, deliberate design choice: a factory test sequence should run every
test and log every result rather than stopping at the first failure —
diagnosing a real hardware fault usually benefits from knowing everything
that failed, not just the first thing.
Traps in manufacturing and DFM¶
- No test points for signals the factory test actually needs: designed in module 3-07's terms, but the stakes are higher at volume — a missing test point means either an expensive layout revision or a compromised factory test for the entire production run.
- Factory test firmware accidentally shipped to customers: a build pipeline that doesn't clearly separate factory-test and production images can ship a unit still running diagnostic firmware — embarrassing at best, a real functional or security problem at worst.
- Stopping at first test failure: as above, loses diagnostic information that would help root-cause a real yield problem at the CM.
- No serial-number-to-test-record traceability: without it, a field return months later can't be cross-referenced against what passed at manufacturing time, turning every field failure investigation into guesswork.
How It Actually Works¶
Why stencil aperture design directly determines solder-joint quality, not just paste appearance: solder paste is deposited by squeegeeing it through laser-cut openings in a thin steel or polymer stencil aligned over the board's pads, and the volume of paste deposited on a given pad is essentially the aperture's area times the stencil's thickness — too much paste on a fine-pitch IC's tightly-spaced pins means adjacent deposits merge during reflow (when the paste's solder particles melt and the flux activates, letting surface tension pull the joint into shape), creating a solder bridge that electrically shorts two pins that were never meant to connect. A "1:1 stencil-to-pad ratio" applies the same-size opening regardless of pitch, but the paste-to-pad-area ratio that works fine on a 2mm-pitch connector delivers proportionally far too much paste relative to the tiny gap between pins on a 0.4mm-pitch fine-pitch part — which is exactly why stencil design reduces aperture size specifically for fine-pitch parts (a "home plate" or reduced-aperture pattern) rather than using one ratio everywhere: the physical bridging risk scales with pitch, not with paste ratio alone.
Why boundary-scan (JTAG) can verify connectivity without a physical probe touching every net: chips that support the IEEE 1149.1 standard contain a scan chain — a long shift register wired internally so that every I/O pin has an associated boundary-scan cell that can either pass the pin's normal signal through or be loaded/read via a serial shift operation driven entirely through the four/five JTAG pins (TDI, TDO, TMS, TCK). A test fixture can shift a known bit pattern into the output-side scan cells of one chip, then shift out what the input-side scan cells of a neighboring chip on the same net actually captured — if the board's copper trace between them is intact, the captured pattern matches what was driven; a solder-bridge short or an open trace produces a mismatch, detected without a single physical pogo pin touching that specific net. This is what lets boundary-scan replace bed-of-nails testing on boards dense enough that physically fitting a test point onto every net is not mechanically feasible — the verification travels through the chips' own internal shift registers instead of external probes.
Why running every factory test to completion — never stopping at the first
failure — is the economically correct choice, not just thoroughness for its
own sake: a unit that fails one test at the end of a production line has
already consumed the full cost of fabrication, assembly, and reaching that
test station — the marginal cost of running the remaining tests on that
already-built unit is small compared to those sunk costs, while the
diagnostic value of a complete result set is large: a unit failing only the
LED test versus a unit failing the LED test and the flash write/read test
point to very different root causes (a cosmetic assembly defect versus a
serious power or bus problem), and that distinction is invisible if testing
stops at the first failure. This is precisely why the aggregation logic
run_factory_tests deliberately continues through the loop regardless of
rc, only tracking all_pass as a summary flag — the design decision that
every result gets logged is what makes the recorded data useful for
yield-trend analysis across an entire production run, not just a per-unit
ship/no-ship gate.
Cheat sheet¶
| Concept | Detail |
|---|---|
| DFM | Layout choices for reliable machine assembly — placement, panelization, stencil design |
| Bed-of-nails / flying probe | Physical test-point contact for electrical test — needs accessible test points |
| Boundary-scan (JTAG) | Pin-level connectivity test through the chip's own scan chain, no physical probe needed |
| Factory test firmware | Separate image, exercises every peripheral once, never ships to customers |
| Run-all-tests policy | Log every result even after a failure — preserves diagnostic information |
| Serial-number traceability | Permanent record linking each unit to its exact factory test results |
| Verification here | Test-aggregation/logging logic compiled/run with gcc; real DFM/assembly-line behavior reviewed against manufacturing practice only |
Exercise¶
Extend the factory test harness with a test_result_t that includes a
numeric measurement (not just pass/fail) for tests where that matters —
e.g. a supply voltage reading that must fall within a tolerance band rather
than a simple pass/fail probe. Add a test that reports 3.3V ± 0.1V
tolerance, write assertions for a measurement inside the band (pass), just
outside it (fail), and exactly at the boundary (decide and justify in a
comment whether boundary-inclusive is the right choice for a voltage
tolerance check). Compile and run with gcc.